Merge "Add Constants for UsageSetting"
diff --git a/MULTIUSER_OWNERS b/MULTIUSER_OWNERS
index fbc611a..9d92e0f 100644
--- a/MULTIUSER_OWNERS
+++ b/MULTIUSER_OWNERS
@@ -1,4 +1,5 @@
 # OWNERS of Multiuser related files
 bookatz@google.com
+olilan@google.com
 omakoto@google.com
 yamasani@google.com
diff --git a/StubLibraries.bp b/StubLibraries.bp
index d090296..31fecd1 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -59,19 +59,13 @@
     },
     dists: [
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/public/api",
             dest: "android-non-updatable.txt",
             tag: ".api.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/public/api",
             dest: "android-non-updatable-removed.txt",
             tag: ".removed-api.txt",
@@ -115,19 +109,13 @@
     },
     dists: [
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/system/api",
             dest: "android-non-updatable.txt",
             tag: ".api.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/system/api",
             dest: "android-non-updatable-removed.txt",
             tag: ".removed-api.txt",
@@ -151,37 +139,25 @@
     },
     dists: [
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/test/api",
             dest: "android.txt",
             tag: ".api.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/test/api",
             dest: "removed.txt",
             tag: ".removed-api.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/test/api",
             dest: "android-non-updatable.txt",
             tag: ".api.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/test/api",
             dest: "android-non-updatable-removed.txt",
             tag: ".removed-api.txt",
@@ -210,19 +186,13 @@
     },
     dists: [
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/module-lib/api",
             dest: "android-non-updatable.txt",
             tag: ".api.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/module-lib/api",
             dest: "android-non-updatable-removed.txt",
             tag: ".removed-api.txt",
@@ -282,10 +252,7 @@
     java_version: "1.8",
     compile_dex: true,
     dist: {
-        targets: [
-            "sdk",
-            "win_sdk",
-        ],
+        targets: ["sdk"],
         tag: ".jar",
         dest: "android-non-updatable.jar",
     },
@@ -341,10 +308,7 @@
 java_defaults {
     name: "android_stubs_dists_default",
     dist: {
-        targets: [
-            "sdk",
-            "win_sdk",
-        ],
+        targets: ["sdk"],
         tag: ".jar",
         dest: "android.jar",
     },
@@ -376,10 +340,7 @@
     dists: [
         {
             // Legacy dist path
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             tag: ".jar",
             dest: "android_system.jar",
         },
@@ -412,6 +373,7 @@
     static_libs: [
         "android-non-updatable.stubs.module_lib",
         "art.module.public.api.stubs.module_lib",
+        "i18n.module.public.api.stubs",
     ],
     dist: {
         dir: "apistubs/android/module-lib",
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java
index c251529a..e5b0742 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobService.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java
@@ -125,7 +125,7 @@
      * will not be invoked.
      *
      * @param params Parameters specifying info about this job, including the optional
-     *     extras configured with {@link JobInfo.Builder#setExtras(android.os.PersistableBundle).
+     *     extras configured with {@link JobInfo.Builder#setExtras(android.os.PersistableBundle)}.
      *     This object serves to identify this specific running job instance when calling
      *     {@link #jobFinished(JobParameters, boolean)}.
      * @return {@code true} if your service will continue running, using a separate thread
diff --git a/api/Android.bp b/api/Android.bp
index ed2247b..c6ea175 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -120,10 +120,7 @@
             dest: "current.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/public/api",
             dest: "android.txt",
         },
@@ -205,10 +202,7 @@
             dest: "removed.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/public/api",
             dest: "removed.txt",
         },
@@ -244,10 +238,7 @@
             dest: "system-current.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/system/api",
             dest: "android.txt",
         },
@@ -302,10 +293,7 @@
             dest: "system-removed.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/system/api",
             dest: "removed.txt",
         },
@@ -342,10 +330,7 @@
             dest: "module-lib-current.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/module-lib/api",
             dest: "android.txt",
         },
@@ -402,10 +387,7 @@
             dest: "module-lib-removed.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/module-lib/api",
             dest: "removed.txt",
         },
@@ -446,10 +428,7 @@
             dest: "system-server-current.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/system-server/api",
             dest: "android.txt",
         },
@@ -473,10 +452,7 @@
             dest: "system-server-removed.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/system-server/api",
             dest: "removed.txt",
         },
@@ -514,9 +490,6 @@
     tools: ["api_versions_trimmer"],
     cmd: "$(location api_versions_trimmer) $(out) $(in)",
     dist: {
-        targets: [
-            "sdk",
-            "win_sdk",
-        ],
+        targets: ["sdk"],
     },
 }
diff --git a/core/api/current.txt b/core/api/current.txt
index 4f96abe..b42d6aa 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -42580,6 +42580,7 @@
     method @Deprecated public int getMnc();
     method @Nullable public String getMncString();
     method public String getNumber();
+    method public int getPortIndex();
     method public int getSimSlotIndex();
     method public int getSubscriptionId();
     method public int getSubscriptionType();
@@ -43126,14 +43127,28 @@
     method public int describeContents();
     method public int getCardId();
     method @Nullable public String getEid();
-    method @Nullable public String getIccId();
-    method public int getSlotIndex();
+    method @Deprecated @Nullable public String getIccId();
+    method public int getPhysicalSlotIndex();
+    method @NonNull public java.util.Collection<android.telephony.UiccPortInfo> getPorts();
+    method @Deprecated public int getSlotIndex();
     method public boolean isEuicc();
+    method public boolean isMultipleEnabledProfilesSupported();
     method public boolean isRemovable();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccCardInfo> CREATOR;
   }
 
+  public final class UiccPortInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public String getIccId();
+    method @IntRange(from=0) public int getLogicalSlotIndex();
+    method @IntRange(from=0) public int getPortIndex();
+    method public boolean isActive();
+    method public void writeToParcel(@Nullable android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccPortInfo> CREATOR;
+    field public static final String ICCID_REDACTED = "FFFFFFFFFFFFFFFFFFFF";
+  }
+
   public abstract class VisualVoicemailService extends android.app.Service {
     ctor public VisualVoicemailService();
     method public android.os.IBinder onBind(android.content.Intent);
@@ -43434,6 +43449,7 @@
     method @Nullable public String getEid();
     method @Nullable public android.telephony.euicc.EuiccInfo getEuiccInfo();
     method public boolean isEnabled();
+    method public boolean isSimPortAvailable(int);
     method public void startResolutionActivity(android.app.Activity, int, android.content.Intent, android.app.PendingIntent) throws android.content.IntentSender.SendIntentException;
     method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, android.app.PendingIntent);
     method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void updateSubscriptionNickname(int, @Nullable String, @NonNull android.app.PendingIntent);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6321aaa..9bca7fc 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5252,6 +5252,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups();
     method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
+    method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int getLastAudibleStreamVolume(int);
     method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
@@ -12140,6 +12141,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimSlotMapping(@NonNull java.util.Collection<android.telephony.UiccSlotMapping>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
     method @Deprecated public void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
@@ -12151,7 +12153,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPinReportResult(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPuk(String, String);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPukReportResult(String, String);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff();
     method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
     method public void updateServiceLocation();
@@ -12325,10 +12327,11 @@
     method public int describeContents();
     method public String getCardId();
     method public int getCardStateInfo();
-    method public boolean getIsActive();
+    method @Deprecated public boolean getIsActive();
     method public boolean getIsEuicc();
     method public boolean getIsExtendedApduSupported();
-    method public int getLogicalSlotIdx();
+    method @Deprecated public int getLogicalSlotIdx();
+    method @NonNull public java.util.Collection<android.telephony.UiccPortInfo> getPorts();
     method public boolean isRemovable();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
@@ -12338,6 +12341,15 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccSlotInfo> CREATOR;
   }
 
+  public final class UiccSlotMapping implements android.os.Parcelable {
+    method public int describeContents();
+    method @IntRange(from=0) public int getLogicalSlotIndex();
+    method @IntRange(from=0) public int getPhysicalSlotIndex();
+    method @IntRange(from=0) public int getPortIndex();
+    method public void writeToParcel(@Nullable android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccSlotMapping> CREATOR;
+  }
+
   public abstract class VisualVoicemailService extends android.app.Service {
     method public static final void sendVisualVoicemailSms(android.content.Context, android.telecom.PhoneAccountHandle, String, short, String, android.app.PendingIntent);
     method public static final void setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings);
@@ -12520,6 +12532,7 @@
     method public final int getSlotIndex();
     method public final void notifyApnUnthrottled(@NonNull String);
     method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+    method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
     method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
     method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
     method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
@@ -12530,6 +12543,7 @@
   public class DataServiceCallback {
     method public void onApnUnthrottled(@NonNull String);
     method public void onDataCallListChanged(@NonNull java.util.List<android.telephony.data.DataCallResponse>);
+    method public void onDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
     method public void onDeactivateDataCallComplete(int);
     method public void onRequestDataCallListComplete(int, @NonNull java.util.List<android.telephony.data.DataCallResponse>);
     method public void onSetDataProfileComplete(int);
@@ -12629,7 +12643,8 @@
     method public void authenticateServer(String, String, byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
     method public void cancelSession(String, byte[], @android.telephony.euicc.EuiccCardManager.CancelReason int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
     method public void deleteProfile(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
-    method public void disableProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+    method @Deprecated public void disableProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+    method public void disableProfile(@Nullable String, @Nullable String, int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
     method public void listNotifications(String, @android.telephony.euicc.EuiccNotification.Event int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
     method public void loadBoundProfilePackage(String, byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
     method public void prepareDownload(String, @Nullable byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
@@ -12647,7 +12662,8 @@
     method public void retrieveNotificationList(String, @android.telephony.euicc.EuiccNotification.Event int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
     method public void setDefaultSmdpAddress(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
     method public void setNickname(String, String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
-    method public void switchToProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
+    method @Deprecated public void switchToProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
+    method public void switchToProfile(@Nullable String, @Nullable String, int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
     field public static final int CANCEL_REASON_END_USER_REJECTED = 0; // 0x0
     field public static final int CANCEL_REASON_POSTPONED = 1; // 0x1
     field public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3; // 0x3
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 9b37457..c6c64b0 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3086,6 +3086,9 @@
             BluetoothCsipSetCoordinator csipSetCoordinator =
                     new BluetoothCsipSetCoordinator(context, listener, this);
             return true;
+        } else if (profile == BluetoothProfile.LE_CALL_CONTROL) {
+            BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener);
+            return true;
         } else {
             return false;
         }
@@ -3188,6 +3191,10 @@
                         (BluetoothCsipSetCoordinator) proxy;
                 csipSetCoordinator.close();
                 break;
+            case BluetoothProfile.LE_CALL_CONTROL:
+                BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy;
+                tbs.close();
+                break;
         }
     }
 
diff --git a/core/java/android/bluetooth/BluetoothLeCall.java b/core/java/android/bluetooth/BluetoothLeCall.java
new file mode 100644
index 0000000..fb7789d
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeCall.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.bluetooth;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelUuid;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * Representation of Call
+ *
+ * @hide
+ */
+public final class BluetoothLeCall implements Parcelable {
+
+    /** @hide */
+    @IntDef(prefix = "STATE_", value = {
+            STATE_INCOMING,
+            STATE_DIALING,
+            STATE_ALERTING,
+            STATE_ACTIVE,
+            STATE_LOCALLY_HELD,
+            STATE_REMOTELY_HELD,
+            STATE_LOCALLY_AND_REMOTELY_HELD
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {
+    }
+
+    /**
+     * A remote party is calling (incoming call).
+     *
+     * @hide
+     */
+    public static final int STATE_INCOMING = 0x00;
+
+    /**
+     * The process to call the remote party has started but the remote party is not
+     * being alerted (outgoing call).
+     *
+     * @hide
+     */
+    public static final int STATE_DIALING = 0x01;
+
+    /**
+     * A remote party is being alerted (outgoing call).
+     *
+     * @hide
+     */
+    public static final int STATE_ALERTING = 0x02;
+
+    /**
+     * The call is in an active conversation.
+     *
+     * @hide
+     */
+    public static final int STATE_ACTIVE = 0x03;
+
+    /**
+     * The call is connected but held locally. “Locally Held” implies that either
+     * the server or the client can affect the state.
+     *
+     * @hide
+     */
+    public static final int STATE_LOCALLY_HELD = 0x04;
+
+    /**
+     * The call is connected but held remotely. “Remotely Held” means that the state
+     * is controlled by the remote party of a call.
+     *
+     * @hide
+     */
+    public static final int STATE_REMOTELY_HELD = 0x05;
+
+    /**
+     * The call is connected but held both locally and remotely.
+     *
+     * @hide
+     */
+    public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06;
+
+    /**
+     * Whether the call direction is outgoing.
+     *
+     * @hide
+     */
+    public static final int FLAG_OUTGOING_CALL = 0x00000001;
+
+    /**
+     * Whether the call URI and Friendly Name are withheld by server.
+     *
+     * @hide
+     */
+    public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002;
+
+    /**
+     * Whether the call URI and Friendly Name are withheld by network.
+     *
+     * @hide
+     */
+    public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004;
+
+    /** Unique UUID that identifies this call */
+    private UUID mUuid;
+
+    /** Remote Caller URI */
+    private String mUri;
+
+    /** Caller friendly name */
+    private String mFriendlyName;
+
+    /** Call state */
+    private @State int mState;
+
+    /** Call flags */
+    private int mCallFlags;
+
+    /** @hide */
+    public BluetoothLeCall(@NonNull BluetoothLeCall that) {
+        mUuid = new UUID(that.getUuid().getMostSignificantBits(),
+                that.getUuid().getLeastSignificantBits());
+        mUri = that.mUri;
+        mFriendlyName = that.mFriendlyName;
+        mState = that.mState;
+        mCallFlags = that.mCallFlags;
+    }
+
+    /** @hide */
+    public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName,
+            @State int state, int callFlags) {
+        mUuid = uuid;
+        mUri = uri;
+        mFriendlyName = friendlyName;
+        mState = state;
+        mCallFlags = callFlags;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        BluetoothLeCall that = (BluetoothLeCall) o;
+        return mUuid.equals(that.mUuid) && mUri.equals(that.mUri)
+                && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState
+                && mCallFlags == that.mCallFlags;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags);
+    }
+
+    /**
+     * Returns a string representation of this BluetoothLeCall.
+     *
+     * <p>
+     * Currently this is the UUID.
+     *
+     * @return string representation of this BluetoothLeCall
+     */
+    @Override
+    public String toString() {
+        return mUuid.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeParcelable(new ParcelUuid(mUuid), 0);
+        out.writeString(mUri);
+        out.writeString(mFriendlyName);
+        out.writeInt(mState);
+        out.writeInt(mCallFlags);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothLeCall> CREATOR =
+    						    new Parcelable.Creator<BluetoothLeCall>() {
+        public BluetoothLeCall createFromParcel(Parcel in) {
+            return new BluetoothLeCall(in);
+        }
+
+        public BluetoothLeCall[] newArray(int size) {
+            return new BluetoothLeCall[size];
+        }
+    };
+
+    private BluetoothLeCall(Parcel in) {
+        mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
+        mUri = in.readString();
+        mFriendlyName = in.readString();
+        mState = in.readInt();
+        mCallFlags = in.readInt();
+    }
+
+    /**
+     * Returns an UUID of this BluetoothLeCall.
+     *
+     * <p>
+     * An UUID is unique identifier of a BluetoothLeCall.
+     *
+     * @return UUID of this BluetoothLeCall
+     * @hide
+     */
+    public @NonNull UUID getUuid() {
+        return mUuid;
+    }
+
+    /**
+     * Returns a URI of the remote party of this BluetoothLeCall.
+     *
+     * @return string representation of this BluetoothLeCall
+     * @hide
+     */
+    public @NonNull String getUri() {
+        return mUri;
+    }
+
+    /**
+     * Returns a friendly name of the call.
+     *
+     * @return friendly name representation of this BluetoothLeCall
+     * @hide
+     */
+    public @NonNull String getFriendlyName() {
+        return mFriendlyName;
+    }
+
+    /**
+     * Returns the call state.
+     *
+     * @return the state of this BluetoothLeCall
+     * @hide
+     */
+    public @State int getState() {
+        return mState;
+    }
+
+    /**
+     * Returns the call flags.
+     *
+     * @return call flags
+     * @hide
+     */
+    public int getCallFlags() {
+        return mCallFlags;
+    }
+
+    /**
+     * Whether the call direction is incoming.
+     *
+     * @return true if incoming call, false otherwise
+     * @hide
+     */
+    public boolean isIncomingCall() {
+        return (mCallFlags & FLAG_OUTGOING_CALL) == 0;
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothLeCallControl.java b/core/java/android/bluetooth/BluetoothLeCallControl.java
new file mode 100644
index 0000000..5283e08
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeCallControl.java
@@ -0,0 +1,911 @@
+/*
+ * Copyright 2019 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.bluetooth;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.Log;
+import android.annotation.SuppressLint;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+
+/**
+ * This class provides the APIs to control the Call Control profile.
+ *
+ * <p>
+ * This class provides Bluetooth Telephone Bearer Service functionality,
+ * allowing applications to expose a GATT Service based interface to control the
+ * state of the calls by remote devices such as LE audio devices.
+ *
+ * <p>
+ * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
+ * BluetoothLeCallControl proxy object.
+ *
+ * @hide
+ */
+public final class BluetoothLeCallControl implements BluetoothProfile {
+    private static final String TAG = "BluetoothLeCallControl";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
+
+    /** @hide */
+    @IntDef(prefix = "RESULT_", value = {
+            RESULT_SUCCESS,
+            RESULT_ERROR_UNKNOWN_CALL_ID,
+            RESULT_ERROR_INVALID_URI,
+            RESULT_ERROR_APPLICATION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Result {
+    }
+
+    /**
+     * Opcode write was successful.
+     *
+     * @hide
+     */
+    public static final int RESULT_SUCCESS = 0;
+
+    /**
+     * Unknown call Id has been used in the operation.
+     *
+     * @hide
+     */
+    public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1;
+
+    /**
+     * The URI provided in {@link Callback#onPlaceCallRequest} is invalid.
+     *
+     * @hide
+     */
+    public static final int RESULT_ERROR_INVALID_URI = 2;
+
+    /**
+     * Application internal error.
+     *
+     * @hide
+     */
+    public static final int RESULT_ERROR_APPLICATION = 3;
+
+    /** @hide */
+    @IntDef(prefix = "TERMINATION_REASON_", value = {
+            TERMINATION_REASON_INVALID_URI,
+            TERMINATION_REASON_FAIL,
+            TERMINATION_REASON_REMOTE_HANGUP,
+            TERMINATION_REASON_SERVER_HANGUP,
+            TERMINATION_REASON_LINE_BUSY,
+            TERMINATION_REASON_NETWORK_CONGESTION,
+            TERMINATION_REASON_CLIENT_HANGUP,
+            TERMINATION_REASON_NO_SERVICE,
+            TERMINATION_REASON_NO_ANSWER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TerminationReason {
+    }
+
+    /**
+     * Remote Caller ID value used to place a call was formed improperly.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_INVALID_URI = 0x00;
+
+    /**
+     * Call fail.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_FAIL = 0x01;
+
+    /**
+     * Remote party ended call.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02;
+
+    /**
+     * Call ended from the server.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03;
+
+    /**
+     * Line busy.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_LINE_BUSY = 0x04;
+
+    /**
+     * Network congestion.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05;
+
+    /**
+     * Client terminated.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06;
+
+    /**
+     * No service.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_NO_SERVICE = 0x07;
+
+    /**
+     * No answer.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_NO_ANSWER = 0x08;
+
+    /*
+     * Flag indicating support for hold/unhold call feature.
+     *
+     * @hide
+     */
+    public static final int CAPABILITY_HOLD_CALL = 0x00000001;
+
+    /**
+     * Flag indicating support for joining calls feature.
+     *
+     * @hide
+     */
+    public static final int CAPABILITY_JOIN_CALLS = 0x00000002;
+
+    private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102;
+    private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103;
+
+    private static final int REG_TIMEOUT = 10000;
+
+    /**
+     * The template class is used to call callback functions on events from the TBS
+     * server. Callback functions are wrapped in this class and registered to the
+     * Android system during app registration.
+     *
+     * @hide
+     */
+    public abstract static class Callback {
+
+        private static final String TAG = "BluetoothLeCallControl.Callback";
+
+        /**
+         * Called when a remote client requested to accept the call.
+         *
+         * <p>
+         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+         * request.
+         *
+         * @param requestId The Id of the request
+         * @param callId    The call Id requested to be accepted
+         * @hide
+         */
+        public abstract void onAcceptCall(int requestId, @NonNull UUID callId);
+
+        /**
+         * A remote client has requested to terminate the call.
+         *
+         * <p>
+         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+         * request.
+         *
+         * @param requestId The Id of the request
+         * @param callId    The call Id requested to terminate
+         * @hide
+         */
+        public abstract void onTerminateCall(int requestId, @NonNull UUID callId);
+
+        /**
+         * A remote client has requested to hold the call.
+         *
+         * <p>
+         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+         * request.
+         *
+         * @param requestId The Id of the request
+         * @param callId    The call Id requested to be put on hold
+         * @hide
+         */
+        public void onHoldCall(int requestId, @NonNull UUID callId) {
+            Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
+        }
+
+        /**
+         * A remote client has requested to unhold the call.
+         *
+         * <p>
+         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+         * request.
+         *
+         * @param requestId The Id of the request
+         * @param callId    The call Id requested to unhold
+         * @hide
+         */
+        public void onUnholdCall(int requestId, @NonNull UUID callId) {
+            Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
+        }
+
+        /**
+         * A remote client has requested to place a call.
+         *
+         * <p>
+         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+         * request.
+         *
+         * @param requestId The Id of the request
+         * @param callId    The Id to be assigned for the new call
+         * @param uri       The caller URI requested
+         * @hide
+         */
+        public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri);
+
+        /**
+         * A remote client has requested to join the calls.
+         *
+         * <p>
+         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+         * request.
+         *
+         * @param requestId The Id of the request
+         * @param callIds   The call Id list requested to join
+         * @hide
+         */
+        public void onJoinCalls(int requestId, @NonNull List<UUID> callIds) {
+            Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!");
+        }
+    }
+
+    private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub {
+
+        private final Executor mExecutor;
+        private final Callback mCallback;
+
+        CallbackWrapper(Executor executor, Callback callback) {
+            mExecutor = executor;
+            mCallback = callback;
+        }
+
+        @Override
+        public void onBearerRegistered(int ccid) {
+            synchronized (mServerIfLock) {
+                if (mCallback != null) {
+                    mCcid = ccid;
+                    mServerIfLock.notifyAll();
+                } else {
+                    // registration timeout
+                    Log.e(TAG, "onBearerRegistered: mCallback is null");
+                }
+            }
+        }
+
+        @Override
+        public void onAcceptCall(int requestId, ParcelUuid uuid) {
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid()));
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+
+        @Override
+        public void onTerminateCall(int requestId, ParcelUuid uuid) {
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid()));
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+
+        @Override
+        public void onHoldCall(int requestId, ParcelUuid uuid) {
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid()));
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+
+        @Override
+        public void onUnholdCall(int requestId, ParcelUuid uuid) {
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid()));
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+
+        @Override
+        public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) {
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri));
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+
+        @Override
+        public void onJoinCalls(int requestId, List<ParcelUuid> parcelUuids) {
+            List<UUID> uuids = new ArrayList<>();
+            for (ParcelUuid parcelUuid : parcelUuids) {
+                uuids.add(parcelUuid.getUuid());
+            }
+
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids));
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+    };
+
+    private Context mContext;
+    private ServiceListener mServiceListener;
+    private volatile IBluetoothLeCallControl mService;
+    private BluetoothAdapter mAdapter;
+    private int mCcid = 0;
+    private String mToken;
+    private Callback mCallback = null;
+    private Object mServerIfLock = new Object();
+
+    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+        new IBluetoothStateChangeCallback.Stub() {
+        public void onBluetoothStateChange(boolean up) {
+            if (DBG)
+                Log.d(TAG, "onBluetoothStateChange: up=" + up);
+            if (!up) {
+                doUnbind();
+            } else {
+                doBind();
+            }
+        }
+    };
+
+    /**
+     * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth
+     * telephone bearer service.
+     */
+    /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) {
+        mContext = context;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mServiceListener = listener;
+
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+
+        doBind();
+    }
+
+    private boolean doBind() {
+        synchronized (mConnection) {
+            if (mService == null) {
+                if (VDBG)
+                    Log.d(TAG, "Binding service...");
+                try {
+                    return mAdapter.getBluetoothManager().
+                            bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
+                            mConnection);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to bind TelephoneBearerService", e);
+                }
+            }
+        }
+        return false;
+    }
+
+    private void doUnbind() {
+        synchronized (mConnection) {
+            if (mService != null) {
+                if (VDBG)
+                    Log.d(TAG, "Unbinding service...");
+                try {
+                    mAdapter.getBluetoothManager().
+                        unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
+                        mConnection);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to unbind TelephoneBearerService", e);
+                } finally {
+                    mService = null;
+                }
+            }
+        }
+    }
+
+    /* package */ void close() {
+        if (VDBG)
+            log("close()");
+        unregisterBearer();
+
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException re) {
+                Log.e(TAG, "", re);
+            }
+        }
+        mServiceListener = null;
+        doUnbind();
+    }
+
+    private IBluetoothLeCallControl getService() {
+        return mService;
+    }
+
+    /**
+     * Not supported
+     *
+     * @throws UnsupportedOperationException
+     */
+    @Override
+    public int getConnectionState(@Nullable BluetoothDevice device) {
+        throw new UnsupportedOperationException("not supported");
+    }
+
+    /**
+     * Not supported
+     *
+     * @throws UnsupportedOperationException
+     */
+    @Override
+    public @NonNull List<BluetoothDevice> getConnectedDevices() {
+        throw new UnsupportedOperationException("not supported");
+    }
+
+    /**
+     * Not supported
+     *
+     * @throws UnsupportedOperationException
+     */
+    @Override
+    public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
+        @NonNull int[] states) {
+        throw new UnsupportedOperationException("not supported");
+    }
+
+    /**
+     * Register Telephone Bearer exposing the interface that allows remote devices
+     * to track and control the call states.
+     *
+     * <p>
+     * This is an asynchronous call. The callback is used to notify success or
+     * failure if the function returns true.
+     *
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * <!-- The UCI is a String identifier of the telephone bearer as defined at
+     * https://www.bluetooth.com/specifications/assigned-numbers/uniform-caller-identifiers
+     * (login required). -->
+     *
+     * <!-- The examples of common URI schemes can be found in
+     * https://iana.org/assignments/uri-schemes/uri-schemes.xhtml -->
+     *
+     * <!-- The Technology is an integer value. The possible values are defined at
+     * https://www.bluetooth.com/specifications/assigned-numbers (login required).
+     * -->
+     *
+     * @param uci          Bearer Unique Client Identifier
+     * @param uriSchemes   URI Schemes supported list
+     * @param capabilities bearer capabilities
+     * @param provider     Network provider name
+     * @param technology   Network technology
+     * @param executor     {@link Executor} object on which callback will be
+     *                     executed. The Executor object is required.
+     * @param callback     {@link Callback} object to which callback messages will
+     *                     be sent. The Callback object is required.
+     * @return true on success, false otherwise
+     * @hide
+     */
+    @SuppressLint("ExecutorRegistration")
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean registerBearer(@Nullable String uci,
+                    @NonNull List<String> uriSchemes, int capabilities,
+                    @NonNull String provider, int technology,
+                    @NonNull Executor executor, @NonNull Callback callback) {
+        if (DBG) {
+            Log.d(TAG, "registerBearer");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("null parameter: " + callback);
+        }
+        if (mCcid != 0) {
+            return false;
+        }
+
+        mToken = uci;
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            synchronized (mServerIfLock) {
+                if (mCallback != null) {
+                    Log.e(TAG, "Bearer can be opened only once");
+                    return false;
+                }
+
+                mCallback = callback;
+                try {
+                    CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback);
+                    service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities,
+                                            provider, technology);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "", e);
+                    mCallback = null;
+                    return false;
+                }
+
+                try {
+                    mServerIfLock.wait(REG_TIMEOUT);
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "" + e);
+                    mCallback = null;
+                }
+
+                if (mCcid == 0) {
+                    mCallback = null;
+                    return false;
+                }
+
+                return true;
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return false;
+    }
+
+    /**
+     * Unregister Telephone Bearer Service and destroy all the associated data.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void unregisterBearer() {
+        if (DBG) {
+            Log.d(TAG, "unregisterBearer");
+        }
+        if (mCcid == 0) {
+            return;
+        }
+
+        int ccid = mCcid;
+        mCcid = 0;
+        mCallback = null;
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.unregisterBearer(mToken);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+    }
+
+    /**
+     * Get the Content Control ID (CCID) value.
+     *
+     * @return ccid Content Control ID value
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public int getContentControlId() {
+        return mCcid;
+    }
+
+    /**
+     * Notify about the newly added call.
+     *
+     * <p>
+     * This shall be called as early as possible after the call has been added.
+     *
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param call Newly added call
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void onCallAdded(@NonNull BluetoothLeCall call) {
+        if (DBG) {
+            Log.d(TAG, "onCallAdded: call=" + call);
+        }
+        if (mCcid == 0) {
+            return;
+        }
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.callAdded(mCcid, call);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+    }
+
+    /**
+     * Notify about the removed call.
+     *
+     * <p>
+     * This shall be called as early as possible after the call has been removed.
+     *
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param callId The Id of a call that has been removed
+     * @param reason Call termination reason
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) {
+        if (DBG) {
+            Log.d(TAG, "callRemoved: callId=" + callId);
+        }
+        if (mCcid == 0) {
+            return;
+        }
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.callRemoved(mCcid, new ParcelUuid(callId), reason);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+    }
+
+    /**
+     * Notify the call state change
+     *
+     * <p>
+     * This shall be called as early as possible after the state of the call has
+     * changed.
+     *
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param callId The call Id that state has been changed
+     * @param state  Call state
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) {
+        if (DBG) {
+            Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state);
+        }
+        if (mCcid == 0) {
+            return;
+        }
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.callStateChanged(mCcid, new ParcelUuid(callId), state);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+    }
+
+    /**
+     * Provide the current calls list
+     *
+     * <p>
+     * This function must be invoked after registration if application has any
+     * calls.
+     *
+     * @param calls current calls list
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+     public void currentCallsList(@NonNull List<BluetoothLeCall> calls) {
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.currentCallsList(mCcid, calls);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+    }
+
+    /**
+     * Provide the network current status
+     *
+     * <p>
+     * This function must be invoked on change of network state.
+     *
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * <!-- The Technology is an integer value. The possible values are defined at
+     * https://www.bluetooth.com/specifications/assigned-numbers (login required).
+     * -->
+     *
+     * @param provider   Network provider name
+     * @param technology Network technology
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void networkStateChanged(@NonNull String provider, int technology) {
+        if (DBG) {
+            Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology);
+        }
+        if (mCcid == 0) {
+            return;
+        }
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.networkStateChanged(mCcid, provider, technology);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+    }
+
+    /**
+     * Send a response to a call control request to a remote device.
+     *
+     * <p>
+     * This function must be invoked in when a request is received by one of these
+     * callback methods:
+     *
+     * <ul>
+     * <li>{@link Callback#onAcceptCall}
+     * <li>{@link Callback#onTerminateCall}
+     * <li>{@link Callback#onHoldCall}
+     * <li>{@link Callback#onUnholdCall}
+     * <li>{@link Callback#onPlaceCall}
+     * <li>{@link Callback#onJoinCalls}
+     * </ul>
+     *
+     * @param requestId The ID of the request that was received with the callback
+     * @param result    The result of the request to be sent to the remote devices
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void requestResult(int requestId, @Result int result) {
+        if (DBG) {
+            Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result);
+        }
+        if (mCcid == 0) {
+            return;
+        }
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.requestResult(mCcid, requestId, result);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+    }
+
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    private static boolean isValidDevice(@Nullable BluetoothDevice device) {
+        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
+    }
+
+    private static void log(String msg) {
+        Log.d(TAG, msg);
+    }
+
+    private final IBluetoothProfileServiceConnection mConnection =
+                                    new IBluetoothProfileServiceConnection.Stub() {
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            if (DBG) {
+                Log.d(TAG, "Proxy object connected");
+            }
+            mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service));
+            mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED));
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            if (DBG) {
+                Log.d(TAG, "Proxy object disconnected");
+            }
+            doUnbind();
+            mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED));
+        }
+    };
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case MESSAGE_TBS_SERVICE_CONNECTED: {
+                if (mServiceListener != null) {
+                    mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL,
+                        BluetoothLeCallControl.this);
+                }
+                break;
+            }
+            case MESSAGE_TBS_SERVICE_DISCONNECTED: {
+                if (mServiceListener != null) {
+                    mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL);
+                }
+                break;
+            }
+            }
+        }
+    };
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index e047e5d..d0f74e9 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -240,12 +240,19 @@
     int LE_AUDIO_BROADCAST = 26;
 
     /**
+     * @hide
+     * Telephone Bearer Service from Call Control Profile
+     *
+     */
+    int LE_CALL_CONTROL = 27;
+
+    /**
      * Max profile ID. This value should be updated whenever a new profile is added to match
      * the largest value assigned to a profile.
      *
      * @hide
      */
-    int MAX_PROFILE_ID = 26;
+    int MAX_PROFILE_ID = 27;
 
     /**
      * Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 858819e..2a8ff51 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -189,7 +189,7 @@
     @NonNull
     @SystemApi
     public static final ParcelUuid CAP =
-        ParcelUuid.fromString("EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE");
+            ParcelUuid.fromString("00001853-0000-1000-8000-00805F9B34FB");
     /** @hide */
     @NonNull
     @SystemApi
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 6c39365..07babb1 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -913,7 +913,7 @@
 
     private static final class DisplayListenerDelegate extends Handler {
         public final DisplayListener mListener;
-        public long mEventsMask;
+        public volatile long mEventsMask;
 
         private final DisplayInfo mDisplayInfo = new DisplayInfo();
 
@@ -933,12 +933,12 @@
             removeCallbacksAndMessages(null);
         }
 
-        public synchronized void setEventsMask(@EventsMask long newEventsMask) {
+        public void setEventsMask(@EventsMask long newEventsMask) {
             mEventsMask = newEventsMask;
         }
 
         @Override
-        public synchronized void handleMessage(Message msg) {
+        public void handleMessage(Message msg) {
             switch (msg.what) {
                 case EVENT_DISPLAY_ADDED:
                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index d7e1857..8b3d49e 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -369,11 +369,13 @@
 
         try {
             final NetworkTemplate.Builder builder = new NetworkTemplate.Builder(matchRule)
-                    .setWifiNetworkKey(wifiNetworkKey)
                     .setMeteredness(metered);
             if (subscriberId != null) {
                 builder.setSubscriberIds(Set.of(subscriberId));
             }
+            if (wifiNetworkKey != null) {
+                builder.setWifiNetworkKeys(Set.of(wifiNetworkKey));
+            }
             return builder.build();
         } catch (IllegalArgumentException e) {
             throw new BackupUtils.BadVersionException(
@@ -393,7 +395,7 @@
             case MATCH_MOBILE:
                 return !template.getSubscriberIds().isEmpty();
             case MATCH_WIFI:
-                if (Objects.equals(template.getWifiNetworkKey(), null)
+                if (template.getWifiNetworkKeys().isEmpty()
                         && template.getSubscriberIds().isEmpty()) {
                     return false;
                 }
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 1e424d1..5d9f2189 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -10,9 +10,8 @@
 # BatteryStats
 per-file *BatteryConsumer* = file:/BATTERY_STATS_OWNERS
 per-file BatteryManager* = file:/BATTERY_STATS_OWNERS
-per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
-per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS
 per-file PowerComponents.java = file:/BATTERY_STATS_OWNERS
+per-file *Stats* = file:/BATTERY_STATS_OWNERS
 
 # Multiuser
 per-file IUser* = file:/MULTIUSER_OWNERS
diff --git a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
index 121caef..3c8f90c 100644
--- a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
+++ b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
@@ -32,14 +32,14 @@
 import kotlin.test.assertTrue
 
 private const val TEST_IMSI1 = "TESTIMSI1"
-private const val TEST_SSID1 = "TESTISSID1"
+private const val TEST_WIFI_NETWORK_KEY1 = "TESTKEY1"
 
 @RunWith(AndroidJUnit4::class)
 class NetworkPolicyTest {
     @Test
     fun testTemplateBackupRestore() {
         assertPolicyBackupRestore(createTestPolicyForTemplate(
-                NetworkTemplate.buildTemplateWifi(TEST_SSID1)))
+                NetworkTemplate.buildTemplateWifi(TEST_WIFI_NETWORK_KEY1)))
         assertPolicyBackupRestore(createTestPolicyForTemplate(
                 NetworkTemplate.buildTemplateMobileAll(TEST_IMSI1)))
         assertPolicyBackupRestore(createTestPolicyForTemplate(
@@ -79,6 +79,6 @@
 
         // Verify wifi template can be persistable if the Wifi Network Key is supplied.
         assertTrue(NetworkPolicy.isTemplatePersistable(NetworkTemplate.Builder(MATCH_WIFI)
-                .setWifiNetworkKey(TEST_SSID1).build()))
+                .setWifiNetworkKeys(setOf(TEST_WIFI_NETWORK_KEY1)).build()))
     }
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 982cf07..b7e8c8c 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1172,7 +1172,8 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
+    @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
     public int getLastAudibleStreamVolume(int streamType) {
         final IAudioService service = getService();
         try {
diff --git a/omapi/aidl/Android.bp b/omapi/aidl/Android.bp
index 2b81200..d80317b 100644
--- a/omapi/aidl/Android.bp
+++ b/omapi/aidl/Android.bp
@@ -28,8 +28,5 @@
         rust: {
             enabled: true,
         },
-        ndk: {
-            separate_platform_variant: false,
-        },
     },
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
index eb8f43e..8f1115e 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
 import android.service.NetworkIdentityProto;
 import android.telephony.Annotation.NetworkType;
 import android.util.proto.ProtoOutputStream;
@@ -228,11 +227,11 @@
         final int oemManaged = getOemBitfield(snapshot.getNetworkCapabilities());
 
         if (legacyType == TYPE_WIFI) {
-            networkId = snapshot.getNetworkCapabilities().getSsid();
-            if (networkId == null) {
-                final WifiManager wifi = context.getSystemService(WifiManager.class);
-                final WifiInfo info = wifi.getConnectionInfo();
-                networkId = info != null ? info.getSSID() : null;
+            final TransportInfo transportInfo = snapshot.getNetworkCapabilities()
+                    .getTransportInfo();
+            if (transportInfo instanceof WifiInfo) {
+                final WifiInfo info = (WifiInfo) transportInfo;
+                networkId = info != null ? info.getCurrentNetworkKey() : null;
             }
         }
 
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
index 591605d9..b64fbdb 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.TrafficStats.UID_REMOVED;
 import static android.net.TrafficStats.UID_TETHERING;
@@ -106,7 +107,7 @@
 
     /** Returns the {@link NetworkStatsAccess.Level} for the given caller. */
     public static @NetworkStatsAccess.Level int checkAccessLevel(
-            Context context, int callingUid, String callingPackage) {
+            Context context, int callingPid, int callingUid, String callingPackage) {
         final DevicePolicyManager mDpm = context.getSystemService(DevicePolicyManager.class);
         final TelephonyManager tm = (TelephonyManager)
                 context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -123,8 +124,12 @@
         final boolean isDeviceOwner = mDpm != null && mDpm.isDeviceOwnerApp(callingPackage);
         final int appId = UserHandle.getAppId(callingUid);
 
+        final boolean isNetworkStack = context.checkPermission(
+                android.Manifest.permission.NETWORK_STACK, callingPid, callingUid)
+                == PERMISSION_GRANTED;
+
         if (hasCarrierPrivileges || isDeviceOwner
-                || appId == Process.SYSTEM_UID || appId == Process.NETWORK_STACK_UID) {
+                || appId == Process.SYSTEM_UID || isNetworkStack) {
             // Carrier-privileged apps and device owners, and the system (including the
             // network stack) can access data usage for all apps on the device.
             return NetworkStatsAccess.Level.DEVICE;
@@ -155,6 +160,8 @@
      */
     public static boolean isAccessibleToUser(int uid, int callerUid,
             @NetworkStatsAccess.Level int accessLevel) {
+        final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+        final int callerUserId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
         switch (accessLevel) {
             case NetworkStatsAccess.Level.DEVICE:
                 // Device-level access - can access usage for any uid.
@@ -165,13 +172,13 @@
                 // anonymized uids
                 return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
                         || uid == UID_TETHERING || uid == UID_ALL
-                        || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
+                        || userId == callerUserId;
             case NetworkStatsAccess.Level.USER:
                 // User-level access - can access usage for any app running in the same user, along
                 // with some special uids (system, removed, or tethering).
                 return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
                         || uid == UID_TETHERING
-                        || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
+                        || userId == callerUserId;
             case NetworkStatsAccess.Level.DEFAULT:
             default:
                 // Default access level - can only access one's own usage.
@@ -185,8 +192,8 @@
             AppOpsManager appOps = (AppOpsManager) context.getSystemService(
                     Context.APP_OPS_SERVICE);
 
-            final int mode = appOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS,
-                    callingUid, callingPackage);
+            final int mode = appOps.noteOp(AppOpsManager.OPSTR_GET_USAGE_STATS,
+                    callingUid, callingPackage, null /* attributionTag */, null /* message */);
             if (mode == AppOpsManager.MODE_DEFAULT) {
                 // The default behavior here is to check if PackageManager has given the app
                 // permission.
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index 9fa777d..659ad06 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -35,7 +35,6 @@
 import static android.net.NetworkStats.ROAMING_ALL;
 import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.ROAMING_YES;
-import static android.net.wifi.WifiInfo.sanitizeSsid;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -57,6 +56,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -213,11 +213,14 @@
     public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
             @NetworkType int ratType, int metered) {
         if (TextUtils.isEmpty(subscriberId)) {
-            return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null,
-                    metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
+            return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null /* subscriberId */,
+                    null /* matchSubscriberIds */,
+                    new String[0] /* matchWifiNetworkKeys */, metered, ROAMING_ALL,
+                    DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
                     NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
         }
-        return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null,
+        return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[] { subscriberId },
+                new String[0] /* matchWifiNetworkKeys */,
                 metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
                 NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
@@ -235,7 +238,7 @@
 
     /**
      * Template to match all metered {@link ConnectivityManager#TYPE_WIFI} networks,
-     * regardless of SSID.
+     * regardless of key of the wifi network.
      *
      * @hide
      */
@@ -255,33 +258,40 @@
 
     /**
      * Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
-     * given SSID.
+     * given key of the wifi network.
      *
+     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+     *                  to know details about the key.
      * @hide
      */
-    public static NetworkTemplate buildTemplateWifi(@NonNull String networkId) {
-        Objects.requireNonNull(networkId);
+    public static NetworkTemplate buildTemplateWifi(@NonNull String wifiNetworkKey) {
+        Objects.requireNonNull(wifiNetworkKey);
         return new NetworkTemplate(MATCH_WIFI, null /* subscriberId */,
                 new String[] { null } /* matchSubscriberIds */,
-                networkId, METERED_ALL, ROAMING_ALL,
+                new String[] { wifiNetworkKey }, METERED_ALL, ROAMING_ALL,
                 DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
                 NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL);
     }
 
     /**
-     * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks with the given SSID,
-     * and IMSI.
+     * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks with the given
+     * key of the wifi network and IMSI.
      *
-     * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code networkId} to get result regardless
-     * of SSID.
+     * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code wifiNetworkKey} to get result regardless
+     * of key of the wifi network.
+     *
+     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+     *                  to know details about the key.
+     * @param subscriberId the IMSI associated to this wifi network.
      *
      * @hide
      */
-    public static NetworkTemplate buildTemplateWifi(@Nullable String networkId,
+    public static NetworkTemplate buildTemplateWifi(@Nullable String wifiNetworkKey,
             @Nullable String subscriberId) {
         return new NetworkTemplate(MATCH_WIFI, subscriberId, new String[] { subscriberId },
-                networkId, METERED_ALL, ROAMING_ALL,
-                DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
+                wifiNetworkKey != null
+                        ? new String[] { wifiNetworkKey } : new String[0],
+                METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
                 NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
 
@@ -324,7 +334,9 @@
     public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
         Objects.requireNonNull(subscriberId);
         return new NetworkTemplate(MATCH_CARRIER, subscriberId,
-                new String[] { subscriberId }, null /* networkId */, METERED_YES, ROAMING_ALL,
+                new String[] { subscriberId },
+                new String[0] /* matchWifiNetworkKeys */,
+                METERED_YES, ROAMING_ALL,
                 DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
                 NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
@@ -342,8 +354,8 @@
      */
     private final String[] mMatchSubscriberIds;
 
-    // TODO: Change variable name to match the Api surface.
-    private final String mNetworkId;
+    @NonNull
+    private final String[] mMatchWifiNetworkKeys;
 
     // Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
     private final int mMetered;
@@ -377,18 +389,19 @@
     /** @hide */
     // TODO: Deprecate this constructor, mark it @UnsupportedAppUsage(maxTargetSdk = S)
     @UnsupportedAppUsage
-    public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
-        this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
+    public NetworkTemplate(int matchRule, String subscriberId, String wifiNetworkKey) {
+        this(matchRule, subscriberId, new String[] { subscriberId }, wifiNetworkKey);
     }
 
     /** @hide */
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
-            String networkId) {
+            String wifiNetworkKey) {
         // Older versions used to only match MATCH_MOBILE and MATCH_MOBILE_WILDCARD templates
         // to metered networks. It is now possible to match mobile with any meteredness, but
         // in order to preserve backward compatibility of @UnsupportedAppUsage methods, this
         //constructor passes METERED_YES for these types.
-        this(matchRule, subscriberId, matchSubscriberIds, networkId,
+        this(matchRule, subscriberId, matchSubscriberIds,
+                wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0],
                 (matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD) ? METERED_YES
                 : METERED_ALL , ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
                 OEM_MANAGED_ALL, NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
@@ -397,23 +410,25 @@
     /** @hide */
     // TODO: Remove it after updating all of the caller.
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
-            String networkId, int metered, int roaming, int defaultNetwork, int subType,
+            String wifiNetworkKey, int metered, int roaming, int defaultNetwork, int subType,
             int oemManaged) {
-        this(matchRule, subscriberId, matchSubscriberIds, networkId, metered, roaming,
-                defaultNetwork, subType, oemManaged,
+        this(matchRule, subscriberId, matchSubscriberIds,
+                wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0],
+                metered, roaming, defaultNetwork, subType, oemManaged,
                 NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
 
     /** @hide */
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
-            String networkId, int metered, int roaming, int defaultNetwork, int subType,
-            int oemManaged, int subscriberIdMatchRule) {
+            String[] matchWifiNetworkKeys, int metered, int roaming,
+            int defaultNetwork, int subType, int oemManaged, int subscriberIdMatchRule) {
+        Objects.requireNonNull(matchWifiNetworkKeys);
         mMatchRule = matchRule;
         mSubscriberId = subscriberId;
         // TODO: Check whether mMatchSubscriberIds = null or mMatchSubscriberIds = {null} when
         // mSubscriberId is null
         mMatchSubscriberIds = matchSubscriberIds;
-        mNetworkId = networkId;
+        mMatchWifiNetworkKeys = matchWifiNetworkKeys;
         mMetered = metered;
         mRoaming = roaming;
         mDefaultNetwork = defaultNetwork;
@@ -431,7 +446,7 @@
         mMatchRule = in.readInt();
         mSubscriberId = in.readString();
         mMatchSubscriberIds = in.createStringArray();
-        mNetworkId = in.readString();
+        mMatchWifiNetworkKeys = in.createStringArray();
         mMetered = in.readInt();
         mRoaming = in.readInt();
         mDefaultNetwork = in.readInt();
@@ -445,7 +460,7 @@
         dest.writeInt(mMatchRule);
         dest.writeString(mSubscriberId);
         dest.writeStringArray(mMatchSubscriberIds);
-        dest.writeString(mNetworkId);
+        dest.writeStringArray(mMatchWifiNetworkKeys);
         dest.writeInt(mMetered);
         dest.writeInt(mRoaming);
         dest.writeInt(mDefaultNetwork);
@@ -471,9 +486,7 @@
             builder.append(", matchSubscriberIds=").append(
                     Arrays.toString(NetworkIdentityUtils.scrubSubscriberIds(mMatchSubscriberIds)));
         }
-        if (mNetworkId != null) {
-            builder.append(", networkId=").append(mNetworkId);
-        }
+        builder.append(", matchWifiNetworkKeys=").append(Arrays.toString(mMatchWifiNetworkKeys));
         if (mMetered != METERED_ALL) {
             builder.append(", metered=").append(NetworkStats.meteredToString(mMetered));
         }
@@ -497,8 +510,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
-                mDefaultNetwork, mSubType, mOemManaged, mSubscriberIdMatchRule);
+        return Objects.hash(mMatchRule, mSubscriberId, Arrays.hashCode(mMatchWifiNetworkKeys),
+                mMetered, mRoaming, mDefaultNetwork, mSubType, mOemManaged, mSubscriberIdMatchRule);
     }
 
     @Override
@@ -507,13 +520,13 @@
             final NetworkTemplate other = (NetworkTemplate) obj;
             return mMatchRule == other.mMatchRule
                     && Objects.equals(mSubscriberId, other.mSubscriberId)
-                    && Objects.equals(mNetworkId, other.mNetworkId)
                     && mMetered == other.mMetered
                     && mRoaming == other.mRoaming
                     && mDefaultNetwork == other.mDefaultNetwork
                     && mSubType == other.mSubType
                     && mOemManaged == other.mOemManaged
-                    && mSubscriberIdMatchRule == other.mSubscriberIdMatchRule;
+                    && mSubscriberIdMatchRule == other.mSubscriberIdMatchRule
+                    && Arrays.equals(mMatchWifiNetworkKeys, other.mMatchWifiNetworkKeys);
         }
         return false;
     }
@@ -579,14 +592,22 @@
      */
     @Nullable
     public String getWifiNetworkKey() {
-        return mNetworkId;
+        return CollectionUtils.isEmpty(mMatchWifiNetworkKeys) ? null : mMatchWifiNetworkKeys[0];
+    }
+
+    /**
+     * Get set of Wifi Network Keys of the template.
+     */
+    @Nullable
+    public Set<String> getWifiNetworkKeys() {
+        return new ArraySet<>(Arrays.asList(mMatchWifiNetworkKeys));
     }
 
     /** @hide */
     // TODO: Remove this and replace all callers with {@link #getWifiNetworkKey()}.
     @Nullable
     public String getNetworkId() {
-        return mNetworkId;
+        return getWifiNetworkKey();
     }
 
     /**
@@ -707,16 +728,21 @@
     }
 
     /**
-     * Check if network with matching SSID. Returns true when the SSID matches, or when
-     * {@code mNetworkId} is {@code WIFI_NETWORK_KEY_ALL}.
+     * Check if network matches key of the wifi network.
+     * Returns true when the key matches, or when {@code mMatchWifiNetworkKeys} is
+     * empty.
+     *
+     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+     *                  to know details about the key.
      */
-    private boolean matchesWifiNetworkId(@Nullable String networkId) {
-        return Objects.equals(mNetworkId, WIFI_NETWORK_KEY_ALL)
-                || Objects.equals(sanitizeSsid(mNetworkId), sanitizeSsid(networkId));
+    private boolean matchesWifiNetworkKey(@NonNull String wifiNetworkKey) {
+        Objects.requireNonNull(wifiNetworkKey);
+        return CollectionUtils.isEmpty(mMatchWifiNetworkKeys)
+                || CollectionUtils.contains(mMatchWifiNetworkKeys, wifiNetworkKey);
     }
 
     /**
-     * Check if mobile network with matching IMSI.
+     * Check if mobile network matches IMSI.
      */
     private boolean matchesMobile(NetworkIdentity ident) {
         if (ident.mType == TYPE_WIMAX) {
@@ -814,7 +840,7 @@
         switch (ident.mType) {
             case TYPE_WIFI:
                 return matchesSubscriberId(ident.mSubscriberId)
-                        && matchesWifiNetworkId(ident.mNetworkId);
+                        && matchesWifiNetworkKey(ident.mNetworkId);
             default:
                 return false;
         }
@@ -956,8 +982,10 @@
             if (CollectionUtils.contains(merged, template.mSubscriberId)) {
                 // Requested template subscriber is part of the merge group; return
                 // a template that matches all merged subscribers.
+                final String[] matchWifiNetworkKeys = template.mMatchWifiNetworkKeys;
                 return new NetworkTemplate(template.mMatchRule, merged[0], merged,
-                        template.mNetworkId);
+                        CollectionUtils.isEmpty(matchWifiNetworkKeys)
+                                ? null : matchWifiNetworkKeys[0]);
             }
         }
 
@@ -984,9 +1012,10 @@
         private final int mMatchRule;
         // Use a SortedSet to provide a deterministic order when fetching the first one.
         @NonNull
-        private final SortedSet<String> mMatchSubscriberIds = new TreeSet<>();
-        @Nullable
-        private String mWifiNetworkKey;
+        private final SortedSet<String> mMatchSubscriberIds =
+                new TreeSet<>(Comparator.nullsFirst(Comparator.naturalOrder()));
+        @NonNull
+        private final SortedSet<String> mMatchWifiNetworkKeys = new TreeSet<>();
 
         // Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
         private int mMetered;
@@ -1006,7 +1035,6 @@
             assertRequestableMatchRule(matchRule);
             // Initialize members with default values.
             mMatchRule = matchRule;
-            mWifiNetworkKey = WIFI_NETWORK_KEY_ALL;
             mMetered = METERED_ALL;
             mRoaming = ROAMING_ALL;
             mDefaultNetwork = DEFAULT_NETWORK_ALL;
@@ -1030,15 +1058,28 @@
         }
 
         /**
-         * Set the Wifi Network Key.
+         * Set the Wifi Network Keys. Calling this function with an empty set represents
+         * the intention of matching any Wifi Network Key.
          *
-         * @param wifiNetworkKey the Wifi Network Key, see {@link WifiInfo#getCurrentNetworkKey()}.
-         *                       Or null to match all networks.
+         * @param wifiNetworkKeys the list of Wifi Network Key,
+         *                        see {@link WifiInfo#getCurrentNetworkKey()}.
+         *                        Or an empty list to match all networks.
+         *                        Note that {@code getCurrentNetworkKey()} might get null key
+         *                        when wifi disconnects. However, the caller should never invoke
+         *                        this function with a null Wifi Network Key since such statistics
+         *                        never exists.
          * @return this builder.
          */
         @NonNull
-        public Builder setWifiNetworkKey(@Nullable String wifiNetworkKey) {
-            mWifiNetworkKey = wifiNetworkKey;
+        public Builder setWifiNetworkKeys(@NonNull Set<String> wifiNetworkKeys) {
+            Objects.requireNonNull(wifiNetworkKeys);
+            for (String key : wifiNetworkKeys) {
+                if (key == null) {
+                    throw new IllegalArgumentException("Null is not a valid key");
+                }
+            }
+            mMatchWifiNetworkKeys.clear();
+            mMatchWifiNetworkKeys.addAll(wifiNetworkKeys);
             return this;
         }
 
@@ -1122,9 +1163,17 @@
         }
 
         private void assertRequestableParameters() {
+            validateWifiNetworkKeys();
             // TODO: Check all the input are legitimate.
         }
 
+        private void validateWifiNetworkKeys() {
+            if (mMatchRule != MATCH_WIFI && !mMatchWifiNetworkKeys.isEmpty()) {
+                throw new IllegalArgumentException("Trying to build non wifi match rule: "
+                        + mMatchRule + " with wifi network keys");
+            }
+        }
+
         /**
          * For backward compatibility, deduce match rule to a wildcard match rule
          * if the Subscriber Ids are empty.
@@ -1133,7 +1182,7 @@
             if (mMatchRule == MATCH_MOBILE && mMatchSubscriberIds.isEmpty()) {
                 return MATCH_MOBILE_WILDCARD;
             } else if (mMatchRule == MATCH_WIFI && mMatchSubscriberIds.isEmpty()
-                    && mWifiNetworkKey == WIFI_NETWORK_KEY_ALL) {
+                    && mMatchWifiNetworkKeys.isEmpty()) {
                 return MATCH_WIFI_WILDCARD;
             }
             return mMatchRule;
@@ -1153,8 +1202,8 @@
             return new NetworkTemplate(getWildcardDeducedMatchRule(),
                     mMatchSubscriberIds.isEmpty() ? null : mMatchSubscriberIds.iterator().next(),
                     mMatchSubscriberIds.toArray(new String[0]),
-                    mWifiNetworkKey, mMetered, mRoaming, mDefaultNetwork, mRatType, mOemManaged,
-                    subscriberIdMatchRule);
+                    mMatchWifiNetworkKeys.toArray(new String[0]), mMetered, mRoaming,
+                    mDefaultNetwork, mRatType, mOemManaged, subscriberIdMatchRule);
         }
     }
 }
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
index e6433db..bb123a3 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
@@ -24,19 +24,19 @@
 
 import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.INetd;
 import android.net.NetworkStats;
 import android.net.UnderlyingNetworkInfo;
-import android.net.util.NetdService;
 import android.os.RemoteException;
 import android.os.StrictMode;
 import android.os.SystemClock;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ProcFileReader;
+import com.android.net.module.util.CollectionUtils;
 
 import libcore.io.IoUtils;
 
@@ -70,7 +70,7 @@
 
     private final boolean mUseBpfStats;
 
-    private INetd mNetdService;
+    private final INetd mNetd;
 
     /**
      * Guards persistent data access in this class
@@ -158,12 +158,12 @@
         NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces);
     }
 
-    public NetworkStatsFactory() {
-        this(new File("/proc/"), true);
+    public NetworkStatsFactory(@NonNull INetd netd) {
+        this(new File("/proc/"), true, netd);
     }
 
     @VisibleForTesting
-    public NetworkStatsFactory(File procRoot, boolean useBpfStats) {
+    public NetworkStatsFactory(File procRoot, boolean useBpfStats, @NonNull INetd netd) {
         mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
         mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
         mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
@@ -172,6 +172,7 @@
             mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
             mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
         }
+        mNetd = netd;
     }
 
     public NetworkStats readBpfNetworkStatsDev() throws IOException {
@@ -298,10 +299,7 @@
         // Ask netd to do a active map stats swap. When the binder call successfully returns,
         // the system server should be able to safely read and clean the inactive map
         // without race problem.
-        if (mNetdService == null) {
-            mNetdService = NetdService.getInstance();
-        }
-        mNetdService.trafficSwapActiveStatsMap();
+        mNetd.trafficSwapActiveStatsMap();
     }
 
     /**
@@ -434,7 +432,7 @@
                 entry.txBytes = reader.nextLong();
                 entry.txPackets = reader.nextLong();
 
-                if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
+                if ((limitIfaces == null || CollectionUtils.contains(limitIfaces, entry.iface))
                         && (limitUid == UID_ALL || limitUid == entry.uid)
                         && (limitTag == TAG_ALL || limitTag == entry.tag)) {
                     stats.insertEntry(entry);
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
index 1a0866d..b57a4f9 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
@@ -18,8 +18,6 @@
 
 import static android.app.usage.NetworkStatsManager.MIN_THRESHOLD_BYTES;
 
-import static com.android.internal.util.Preconditions.checkArgument;
-
 import android.app.usage.NetworkStatsManager;
 import android.net.DataUsageRequest;
 import android.net.NetworkIdentitySet;
@@ -38,7 +36,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.ArrayMap;
-import android.util.Slog;
+import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -83,7 +81,7 @@
         RequestInfo requestInfo = buildRequestInfo(request, messenger, binder, callingUid,
                 accessLevel);
 
-        if (LOGV) Slog.v(TAG, "Registering observer for " + request);
+        if (LOGV) Log.v(TAG, "Registering observer for " + request);
         getHandler().sendMessage(mHandler.obtainMessage(MSG_REGISTER, requestInfo));
         return request;
     }
@@ -116,7 +114,7 @@
         if (mHandler == null) {
             synchronized (this) {
                 if (mHandler == null) {
-                    if (LOGV) Slog.v(TAG, "Creating handler");
+                    if (LOGV) Log.v(TAG, "Creating handler");
                     mHandler = new Handler(getHandlerLooperLocked(), mHandlerCallback);
                 }
             }
@@ -172,15 +170,15 @@
         RequestInfo requestInfo;
         requestInfo = mDataUsageRequests.get(request.requestId);
         if (requestInfo == null) {
-            if (LOGV) Slog.v(TAG, "Trying to unregister unknown request " + request);
+            if (LOGV) Log.v(TAG, "Trying to unregister unknown request " + request);
             return;
         }
         if (Process.SYSTEM_UID != callingUid && requestInfo.mCallingUid != callingUid) {
-            Slog.w(TAG, "Caller uid " + callingUid + " is not owner of " + request);
+            Log.w(TAG, "Caller uid " + callingUid + " is not owner of " + request);
             return;
         }
 
-        if (LOGV) Slog.v(TAG, "Unregistering " + request);
+        if (LOGV) Log.v(TAG, "Unregistering " + request);
         mDataUsageRequests.remove(request.requestId);
         requestInfo.unlinkDeathRecipient();
         requestInfo.callCallback(NetworkStatsManager.CALLBACK_RELEASED);
@@ -201,7 +199,7 @@
         // Cap the minimum threshold to a safe default to avoid too many callbacks
         long thresholdInBytes = Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes);
         if (thresholdInBytes < request.thresholdInBytes) {
-            Slog.w(TAG, "Threshold was too low for " + request
+            Log.w(TAG, "Threshold was too low for " + request
                     + ". Overriding to a safer default of " + thresholdInBytes + " bytes");
         }
         return new DataUsageRequest(mNextDataUsageRequestId.incrementAndGet(),
@@ -216,7 +214,10 @@
                     accessLevel);
         } else {
             // Safety check in case a new access level is added and we forgot to update this
-            checkArgument(accessLevel >= NetworkStatsAccess.Level.DEVICESUMMARY);
+            if (accessLevel < NetworkStatsAccess.Level.DEVICESUMMARY) {
+                throw new IllegalArgumentException(
+                        "accessLevel " + accessLevel + " is less than DEVICESUMMARY.");
+            }
             return new NetworkUsageRequestInfo(this, request, messenger, binder, callingUid,
                     accessLevel);
         }
@@ -255,8 +256,9 @@
 
         @Override
         public void binderDied() {
-            if (LOGV) Slog.v(TAG, "RequestInfo binderDied("
-                    + mRequest + ", " + mBinder + ")");
+            if (LOGV) {
+                Log.v(TAG, "RequestInfo binderDied(" + mRequest + ", " + mBinder + ")");
+            }
             mStatsObserver.unregister(mRequest, Process.SYSTEM_UID);
             callCallback(NetworkStatsManager.CALLBACK_RELEASED);
         }
@@ -299,13 +301,13 @@
             msg.setData(bundle);
             try {
                 if (LOGV) {
-                    Slog.v(TAG, "sending notification " + callbackTypeToName(callbackType)
+                    Log.v(TAG, "sending notification " + callbackTypeToName(callbackType)
                             + " for " + mRequest);
                 }
                 mMessenger.send(msg);
             } catch (RemoteException e) {
                 // May occur naturally in the race of binder death.
-                Slog.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
+                Log.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
             }
         }
 
@@ -341,7 +343,7 @@
         protected boolean checkStats() {
             long bytesSoFar = getTotalBytesForNetwork(mRequest.template);
             if (LOGV) {
-                Slog.v(TAG, bytesSoFar + " bytes so far since notification for "
+                Log.v(TAG, bytesSoFar + " bytes so far since notification for "
                         + mRequest.template);
             }
             if (bytesSoFar > mRequest.thresholdInBytes) {
@@ -416,7 +418,7 @@
                 return history.getTotalBytes();
             } catch (SecurityException e) {
                 if (LOGV) {
-                    Slog.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid "
+                    Log.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid "
                             + uid);
                 }
                 return 0;
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
index 5e27c77..c371f08 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
@@ -32,15 +32,12 @@
 import android.os.Binder;
 import android.os.DropBoxManager;
 import android.service.NetworkStatsRecorderProto;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
-
-import com.google.android.collect.Sets;
+import com.android.net.module.util.NetworkStatsUtils;
 
 import libcore.io.IoUtils;
 
@@ -132,8 +129,8 @@
     }
 
     public void setPersistThreshold(long thresholdBytes) {
-        if (LOGV) Slog.v(TAG, "setPersistThreshold() with " + thresholdBytes);
-        mPersistThresholdBytes = MathUtils.constrain(
+        if (LOGV) Log.v(TAG, "setPersistThreshold() with " + thresholdBytes);
+        mPersistThresholdBytes = NetworkStatsUtils.constrain(
                 thresholdBytes, 1 * KB_IN_BYTES, 100 * MB_IN_BYTES);
     }
 
@@ -185,7 +182,7 @@
     }
 
     private NetworkStatsCollection loadLocked(long start, long end) {
-        if (LOGD) Slog.d(TAG, "loadLocked() reading from disk for " + mCookie);
+        if (LOGD) Log.d(TAG, "loadLocked() reading from disk for " + mCookie);
         final NetworkStatsCollection res = new NetworkStatsCollection(mBucketDuration);
         try {
             mRotator.readMatching(res, start, end);
@@ -207,7 +204,7 @@
      */
     public void recordSnapshotLocked(NetworkStats snapshot,
             Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) {
-        final HashSet<String> unknownIfaces = Sets.newHashSet();
+        final HashSet<String> unknownIfaces = new HashSet<>();
 
         // skip recording when snapshot missing
         if (snapshot == null) return;
@@ -272,7 +269,7 @@
         mLastSnapshot = snapshot;
 
         if (LOGV && unknownIfaces.size() > 0) {
-            Slog.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
+            Log.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
         }
     }
 
@@ -296,7 +293,7 @@
     public void forcePersistLocked(long currentTimeMillis) {
         Objects.requireNonNull(mRotator, "missing FileRotator");
         if (mPending.isDirty()) {
-            if (LOGD) Slog.d(TAG, "forcePersistLocked() writing for " + mCookie);
+            if (LOGD) Log.d(TAG, "forcePersistLocked() writing for " + mCookie);
             try {
                 mRotator.rewriteActive(mPendingRewriter, currentTimeMillis);
                 mRotator.maybeRotate(currentTimeMillis);
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index df066c21..ef84ce0 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -25,7 +25,6 @@
 import static android.content.Intent.EXTRA_UID;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.NetworkIdentity.SUBTYPE_COMBINED;
-import static android.net.NetworkStack.checkNetworkStackPermission;
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.IFACE_VT;
@@ -90,6 +89,7 @@
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.net.DataUsageRequest;
+import android.net.INetd;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkStatsService;
 import android.net.INetworkStatsSession;
@@ -141,28 +141,29 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.net.module.util.BinderUtils;
+import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.NetworkStatsUtils;
+import com.android.net.module.util.PermissionUtils;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.time.Clock;
 import java.time.ZoneOffset;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -410,10 +411,11 @@
         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         PowerManager.WakeLock wakeLock =
                 powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
+        final INetd netd = INetd.Stub.asInterface(
+                (IBinder) context.getSystemService(Context.NETD_SERVICE));
         final NetworkStatsService service = new NetworkStatsService(context, networkManager,
                 alarmManager, wakeLock, getDefaultClock(),
-                new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(),
+                new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(netd),
                 new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
                 new Dependencies());
         service.registerLocalService();
@@ -646,7 +648,7 @@
         try {
             mNetworkManager.setGlobalAlert(mGlobalAlertBytes);
         } catch (IllegalStateException e) {
-            Slog.w(TAG, "problem registering for global alert: " + e);
+            Log.w(TAG, "problem registering for global alert: " + e);
         } catch (RemoteException e) {
             // ignored; service lives in system_server
         }
@@ -764,7 +766,7 @@
                     return stats;
                 } catch (NullPointerException e) {
                     // TODO: Track down and fix the cause of this crash and remove this catch block.
-                    Slog.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
+                    Log.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
                     throw e;
                 }
             }
@@ -809,7 +811,7 @@
 
     private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) {
         return NetworkStatsAccess.checkAccessLevel(
-                mContext, Binder.getCallingUid(), callingPackage);
+                mContext, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage);
     }
 
     /**
@@ -821,7 +823,7 @@
         SubscriptionPlan plan = null;
         if ((flags & NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN) != 0
                 && mSettings.getAugmentEnabled()) {
-            if (LOGD) Slog.d(TAG, "Resolving plan for " + template);
+            if (LOGD) Log.d(TAG, "Resolving plan for " + template);
             final long token = Binder.clearCallingIdentity();
             try {
                 plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
@@ -829,7 +831,7 @@
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
-            if (LOGD) Slog.d(TAG, "Resolved to plan " + plan);
+            if (LOGD) Log.d(TAG, "Resolved to plan " + plan);
         }
         return plan;
     }
@@ -932,7 +934,7 @@
     @Override
     public String[] getMobileIfaces() {
         // TODO (b/192758557): Remove debug log.
-        if (ArrayUtils.contains(mMobileIfaces, null)) {
+        if (CollectionUtils.contains(mMobileIfaces, null)) {
             throw new NullPointerException(
                     "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
         }
@@ -981,7 +983,7 @@
             @NonNull NetworkStateSnapshot[] networkStates,
             @Nullable String activeIface,
             @NonNull UnderlyingNetworkInfo[] underlyingNetworkInfos) {
-        checkNetworkStackPermission(mContext);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -1010,9 +1012,10 @@
 
     private void advisePersistThreshold(long thresholdBytes) {
         // clamp threshold into safe range
-        mPersistThreshold = MathUtils.constrain(thresholdBytes, 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
+        mPersistThreshold = NetworkStatsUtils.constrain(thresholdBytes,
+                128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
         if (LOGV) {
-            Slog.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
+            Log.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
                     + mPersistThreshold);
         }
 
@@ -1196,13 +1199,13 @@
             // On background handler thread, and USER_REMOVED is protected
             // broadcast.
 
-            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-            if (userId == -1) return;
+            final UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+            if (userHandle == null) return;
 
             synchronized (mStatsLock) {
                 mWakeLock.acquire();
                 try {
-                    removeUserLocked(userId);
+                    removeUserLocked(userHandle);
                 } finally {
                     mWakeLock.release();
                 }
@@ -1227,7 +1230,7 @@
         @Override
         public void limitReached(String limitName, String iface) {
             // only someone like NMS should be calling us
-            NetworkStack.checkNetworkStackPermission(mContext);
+            PermissionUtils.enforceNetworkStackPermission(mContext);
 
             if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
                 // kick off background poll to collect network stats unless there is already
@@ -1275,7 +1278,7 @@
     private void handleNotifyNetworkStatusLocked(@NonNull Network[] defaultNetworks,
             @NonNull NetworkStateSnapshot[] snapshots) {
         if (!mSystemReady) return;
-        if (LOGV) Slog.v(TAG, "handleNotifyNetworkStatusLocked()");
+        if (LOGV) Log.v(TAG, "handleNotifyNetworkStatusLocked()");
 
         // take one last stats snapshot before updating iface mapping. this
         // isn't perfect, since the kernel may already be counting traffic from
@@ -1299,7 +1302,8 @@
             final int displayTransport =
                     getDisplayTransport(snapshot.getNetworkCapabilities().getTransportTypes());
             final boolean isMobile = (NetworkCapabilities.TRANSPORT_CELLULAR == displayTransport);
-            final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, snapshot.getNetwork());
+            final boolean isDefault = CollectionUtils.contains(
+                    mDefaultNetworks, snapshot.getNetwork());
             final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
                     : getSubTypeForStateSnapshot(snapshot);
             final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
@@ -1382,7 +1386,7 @@
 
         mMobileIfaces = mobileIfaces.toArray(new String[0]);
         // TODO (b/192758557): Remove debug log.
-        if (ArrayUtils.contains(mMobileIfaces, null)) {
+        if (CollectionUtils.contains(mMobileIfaces, null)) {
             throw new NullPointerException(
                     "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
         }
@@ -1397,7 +1401,7 @@
         if (spec instanceof TelephonyNetworkSpecifier) {
              return ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
         } else {
-            Slog.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
+            Log.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
             return INVALID_SUBSCRIPTION_ID;
         }
     }
@@ -1481,7 +1485,7 @@
         try {
             recordSnapshotLocked(currentTime);
         } catch (IllegalStateException e) {
-            Slog.w(TAG, "problem reading network stats: " + e);
+            Log.w(TAG, "problem reading network stats: " + e);
         } catch (RemoteException e) {
             // ignored; service lives in system_server
         }
@@ -1506,7 +1510,7 @@
     @GuardedBy("mStatsLock")
     private void performPollLocked(int flags) {
         if (!mSystemReady) return;
-        if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
+        if (LOGV) Log.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
         Trace.traceBegin(TRACE_TAG_NETWORK, "performPollLocked");
 
         final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
@@ -1628,7 +1632,7 @@
      */
     @GuardedBy("mStatsLock")
     private void removeUidsLocked(int... uids) {
-        if (LOGV) Slog.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
+        if (LOGV) Log.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
 
         // Perform one last poll before removing
         performPollLocked(FLAG_PERSIST_ALL);
@@ -1646,20 +1650,20 @@
      * Clean up {@link #mUidRecorder} after user is removed.
      */
     @GuardedBy("mStatsLock")
-    private void removeUserLocked(int userId) {
-        if (LOGV) Slog.v(TAG, "removeUserLocked() for userId=" + userId);
+    private void removeUserLocked(@NonNull UserHandle userHandle) {
+        if (LOGV) Log.v(TAG, "removeUserLocked() for UserHandle=" + userHandle);
 
         // Build list of UIDs that we should clean up
-        int[] uids = new int[0];
+        final ArrayList<Integer> uids = new ArrayList<>();
         final List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
                 PackageManager.MATCH_ANY_USER
                 | PackageManager.MATCH_DISABLED_COMPONENTS);
         for (ApplicationInfo app : apps) {
-            final int uid = UserHandle.getUid(userId, app.uid);
-            uids = ArrayUtils.appendInt(uids, uid);
+            final int uid = userHandle.getUid(app.uid);
+            uids.add(uid);
         }
 
-        removeUidsLocked(uids);
+        removeUidsLocked(CollectionUtils.toIntArray(uids));
     }
 
     private class NetworkStatsManagerInternalImpl extends NetworkStatsManagerInternal {
@@ -1702,7 +1706,7 @@
         public void setStatsProviderWarningAndLimitAsync(
                 @NonNull String iface, long warning, long limit) {
             if (LOGV) {
-                Slog.v(TAG, "setStatsProviderWarningAndLimitAsync("
+                Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
                         + iface + "," + warning + "," + limit + ")");
             }
             invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
@@ -1712,7 +1716,7 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter rawWriter, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, rawWriter)) return;
+        if (!PermissionUtils.checkDumpPermission(mContext, TAG, rawWriter)) return;
 
         long duration = DateUtils.DAY_IN_MILLIS;
         final HashSet<String> argSet = new HashSet<String>();
@@ -1773,15 +1777,15 @@
 
             pw.println("Configs:");
             pw.increaseIndent();
-            pw.printPair(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled());
+            pw.print(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled());
             pw.println();
             pw.decreaseIndent();
 
             pw.println("Active interfaces:");
             pw.increaseIndent();
             for (int i = 0; i < mActiveIfaces.size(); i++) {
-                pw.printPair("iface", mActiveIfaces.keyAt(i));
-                pw.printPair("ident", mActiveIfaces.valueAt(i));
+                pw.print("iface", mActiveIfaces.keyAt(i));
+                pw.print("ident", mActiveIfaces.valueAt(i));
                 pw.println();
             }
             pw.decreaseIndent();
@@ -1789,8 +1793,8 @@
             pw.println("Active UID interfaces:");
             pw.increaseIndent();
             for (int i = 0; i < mActiveUidIfaces.size(); i++) {
-                pw.printPair("iface", mActiveUidIfaces.keyAt(i));
-                pw.printPair("ident", mActiveUidIfaces.valueAt(i));
+                pw.print("iface", mActiveUidIfaces.keyAt(i));
+                pw.print("ident", mActiveUidIfaces.valueAt(i));
                 pw.println();
             }
             pw.decreaseIndent();
@@ -1863,7 +1867,7 @@
 
     @GuardedBy("mStatsLock")
     private void dumpProtoLocked(FileDescriptor fd) {
-        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        final ProtoOutputStream proto = new ProtoOutputStream(new FileOutputStream(fd));
 
         // TODO Right now it writes all history.  Should it limit to the "since-boot" log?
 
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 5646c75..93d0ae7 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -33,7 +33,7 @@
 import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.CollectionUtils;
+import com.android.net.module.util.CollectionUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -99,18 +99,19 @@
         // prevent binder call to telephony when querying RAT. Keep listener registration with empty
         // IMSI is meaningless since the RAT type changed is ambiguous for multi-SIM if reported
         // with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration.
-        final List<Pair<Integer, String>> filteredNewSubs =
-                CollectionUtils.mapNotNull(newSubs, subId -> {
-                    final String subscriberId = mTeleManager.getSubscriberId(subId);
-                    return TextUtils.isEmpty(subscriberId) ? null : new Pair(subId, subscriberId);
-                });
+        final List<Pair<Integer, String>> filteredNewSubs = new ArrayList<>();
+        for (final int subId : newSubs) {
+            final String subscriberId = mTeleManager.getSubscriberId(subId);
+            if (!TextUtils.isEmpty(subscriberId)) {
+                filteredNewSubs.add(new Pair(subId, subscriberId));
+            }
+        }
 
         for (final Pair<Integer, String> sub : filteredNewSubs) {
             // Fully match listener with subId and IMSI, since in some rare cases, IMSI might be
             // suddenly change regardless of subId, such as switch IMSI feature in modem side.
             // If that happens, register new listener with new IMSI and remove old one later.
-            if (CollectionUtils.find(mRatListeners,
-                    it -> it.equalsKey(sub.first, sub.second)) != null) {
+            if (CollectionUtils.any(mRatListeners, it -> it.equalsKey(sub.first, sub.second))) {
                 continue;
             }
 
@@ -126,8 +127,8 @@
 
         for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
             // If there is no subId and IMSI matched the listener, removes it.
-            if (CollectionUtils.find(filteredNewSubs,
-                    it -> listener.equalsKey(it.first, it.second)) == null) {
+            if (!CollectionUtils.any(filteredNewSubs,
+                    it -> listener.equalsKey(it.first, it.second))) {
                 handleRemoveRatTypeListener(listener);
             }
         }
@@ -148,9 +149,10 @@
      * @return collapsed RatType for the given subscriberId
      */
     public int getRatTypeForSubscriberId(@NonNull String subscriberId) {
-        final RatTypeListener match = CollectionUtils.find(mRatListeners,
+        final int index = CollectionUtils.indexOf(mRatListeners,
                 it -> TextUtils.equals(subscriberId, it.mSubscriberId));
-        return match != null ? match.mLastCollapsedRatType : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        return index != -1 ? mRatListeners.get(index).mLastCollapsedRatType
+                : TelephonyManager.NETWORK_TYPE_UNKNOWN;
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 36d9cf8..cc56a21 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -68,7 +68,7 @@
             }
 
             for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
-                if (entry.getValue().equals(BluetoothUuid.BASE_UUID)) {
+                if (entry.getValue().equals(BluetoothUuid.CAP)) {
                     return entry.getKey();
                 }
             }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 3347920..5e2f310 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -338,7 +338,7 @@
                             .getGroupUuidMapByDevice(cachedDevice.getDevice());
                     if (groupIdMap != null) {
                         for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
-                            if (entry.getValue().equals(BluetoothUuid.BASE_UUID)) {
+                            if (entry.getValue().equals(BluetoothUuid.CAP)) {
                                 cachedDevice.setGroupId(entry.getKey());
                                 break;
                             }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9de1c5e..8dc18c0 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -251,6 +251,8 @@
     <uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
     <!-- For handling silent audio recordings -->
     <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
+    <!-- For asking AudioManager audio information -->
+    <uses-permission android:name="android.permission.QUERY_AUDIO_STATE"/>
 
     <!-- to read and change hvac values in a car -->
     <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
diff --git a/services/Android.bp b/services/Android.bp
index e11b044..841edc7 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -221,19 +221,13 @@
     },
     dists: [
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/system-server/api",
             dest: "android-non-updatable.txt",
             tag: ".api.txt",
         },
         {
-            targets: [
-                "sdk",
-                "win_sdk",
-            ],
+            targets: ["sdk"],
             dir: "apistubs/android/system-server/api",
             dest: "android-non-updatable-removed.txt",
             tag: ".removed-api.txt",
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 450e988..c8b4f11 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -46,6 +46,7 @@
 import android.bluetooth.IBluetoothManagerCallback;
 import android.bluetooth.IBluetoothProfileServiceConnection;
 import android.bluetooth.IBluetoothStateChangeCallback;
+import android.bluetooth.IBluetoothLeCallControl;
 import android.content.ActivityNotFoundException;
 import android.content.AttributionSource;
 import android.content.BroadcastReceiver;
@@ -1328,11 +1329,15 @@
                             + bluetoothProfile);
                 }
 
-                if (bluetoothProfile != BluetoothProfile.HEADSET) {
+                Intent intent;
+                if (bluetoothProfile == BluetoothProfile.HEADSET) {
+                    intent = new Intent(IBluetoothHeadset.class.getName());
+                } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) {
+                    intent = new Intent(IBluetoothLeCallControl.class.getName());
+                } else {
                     return false;
                 }
 
-                Intent intent = new Intent(IBluetoothHeadset.class.getName());
                 psc = new ProfileServiceConnections(intent);
                 if (!psc.bindService()) {
                     return false;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index df5d60c..e00c8a3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3193,6 +3193,13 @@
         }
     }
 
+    private void enforceQueryStatePermission() {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.QUERY_AUDIO_STATE)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Missing QUERY_AUDIO_STATE permissions");
+        }
+    }
+
     private void enforceQueryStateOrModifyRoutingPermission() {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
                 != PackageManager.PERMISSION_GRANTED
@@ -4094,6 +4101,7 @@
 
     /** Get last audible volume before stream was muted. */
     public int getLastAudibleStreamVolume(int streamType) {
+        enforceQueryStatePermission();
         ensureValidStreamType(streamType);
         int device = getDeviceForStream(streamType);
         return (mStreamStates[streamType].getIndex(device) + 5) / 10;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index bf4ef48..2c666b5 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1446,7 +1446,10 @@
             // parameters. If that fails, disconnect.
             if (oldConfig != null
                     && updateLinkPropertiesInPlaceIfPossible(mNetworkAgent, oldConfig)) {
-                // Keep mNetworkAgent unchanged
+                // Update underlying networks if it is changed.
+                if (!Arrays.equals(oldConfig.underlyingNetworks, config.underlyingNetworks)) {
+                    setUnderlyingNetworks(config.underlyingNetworks);
+                }
             } else {
                 // Initialize the state for a new agent, while keeping the old one connected
                 // in case this new connection fails.
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index c315b5c..2ca057d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2025,7 +2025,7 @@
         for (final NetworkStateSnapshot snapshot : snapshots) {
             mNetIdToSubId.put(snapshot.getNetwork().getNetId(), parseSubId(snapshot));
 
-            // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
+            // Policies matched by NPMS only match by subscriber ID or by network ID. Thus subtype
             // in the object created here is never used and its value doesn't matter, so use
             // NETWORK_TYPE_UNKNOWN.
             final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
@@ -2455,7 +2455,6 @@
                         }
                         final NetworkTemplate.Builder builder =
                                 new NetworkTemplate.Builder(templateType)
-                                        .setWifiNetworkKey(networkId)
                                         .setMeteredness(templateMeteredness);
                         if (subscriberIdMatchRule
                                 == NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT) {
@@ -2463,6 +2462,9 @@
                             ids.add(subscriberId);
                             builder.setSubscriberIds(ids);
                         }
+                        if (networkId != null) {
+                            builder.setWifiNetworkKeys(Set.of(networkId));
+                        }
                         final NetworkTemplate template = builder.build();
                         if (NetworkPolicy.isTemplatePersistable(template)) {
                             mNetworkPolicy.put(template, new NetworkPolicy(template, cycleRule,
@@ -2613,35 +2615,39 @@
      * into {@link WifiConfiguration}.
      */
     private void upgradeWifiMeteredOverride() {
-        final ArrayMap<String, Boolean> wifiNetworkIds = new ArrayMap<>();
+        final ArrayMap<String, Boolean> wifiNetworkKeys = new ArrayMap<>();
         synchronized (mNetworkPoliciesSecondLock) {
             for (int i = 0; i < mNetworkPolicy.size();) {
                 final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
                 if (policy.template.getMatchRule() == NetworkTemplate.MATCH_WIFI
                         && !policy.inferred) {
                     mNetworkPolicy.removeAt(i);
-                    wifiNetworkIds.put(policy.template.getNetworkId(), policy.metered);
+                    final Set<String> keys = policy.template.getWifiNetworkKeys();
+                    wifiNetworkKeys.put(keys.isEmpty() ? null : keys.iterator().next(),
+                            policy.metered);
                 } else {
                     i++;
                 }
             }
         }
 
-        if (wifiNetworkIds.isEmpty()) {
+        if (wifiNetworkKeys.isEmpty()) {
             return;
         }
         final WifiManager wm = mContext.getSystemService(WifiManager.class);
         final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
         for (int i = 0; i < configs.size(); ++i) {
             final WifiConfiguration config = configs.get(i);
-            final String networkId = resolveNetworkId(config);
-            final Boolean metered = wifiNetworkIds.get(networkId);
-            if (metered != null) {
-                Slog.d(TAG, "Found network " + networkId + "; upgrading metered hint");
-                config.meteredOverride = metered
-                        ? WifiConfiguration.METERED_OVERRIDE_METERED
-                        : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
-                wm.updateNetwork(config);
+            for (String key : config.getAllPersistableNetworkKeys()) {
+                final Boolean metered = wifiNetworkKeys.get(key);
+                if (metered != null) {
+                    Slog.d(TAG, "Found network " + key + "; upgrading metered hint");
+                    config.meteredOverride = metered
+                            ? WifiConfiguration.METERED_OVERRIDE_METERED
+                            : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+                    wm.updateNetwork(config);
+                    break;
+                }
             }
         }
 
@@ -2683,9 +2689,9 @@
                         ? NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL
                         : NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT;
                 writeIntAttribute(out, ATTR_SUBSCRIBER_ID_MATCH_RULE, subscriberIdMatchRule);
-                final String networkId = template.getNetworkId();
-                if (networkId != null) {
-                    out.attribute(null, ATTR_NETWORK_ID, networkId);
+                if (!template.getWifiNetworkKeys().isEmpty()) {
+                    out.attribute(null, ATTR_NETWORK_ID,
+                            template.getWifiNetworkKeys().iterator().next());
                 }
                 writeIntAttribute(out, ATTR_TEMPLATE_METERED,
                         template.getMeteredness());
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 08a7215..97c168d 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -1255,9 +1255,7 @@
                     info.diskImagePath = ai.modulePath;
                     info.versionCode = ai.versionCode;
                     info.versionName = ai.versionName;
-                    info.hasBootClassPathJars = ai.hasBootClassPathJars;
-                    info.hasDex2OatBootClassPathJars = ai.hasDex2OatBootClassPathJars;
-                    info.hasSystemServerClassPathJars = ai.hasSystemServerClassPathJars;
+                    info.hasClassPathJars = ai.hasClassPathJars;
                     return info;
                 }
             }
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 46cb720..186b2b5 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -435,7 +435,8 @@
                     || !pm.isGranted(Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
                             pkg, UserHandle.of(userId))
                     || !pm.isGranted(Manifest.permission.READ_PHONE_STATE, pkg,
-                            UserHandle.of(userId))) {
+                            UserHandle.of(userId))
+                    || pm.isSysComponentOrPersistentPlatformSignedPrivApp(pkg)) {
                 continue;
             }
 
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index eda05bf..b811e28 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -129,6 +129,7 @@
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.net.TelephonyNetworkSpecifier;
+import android.net.wifi.WifiInfo;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.INetworkManagementService;
@@ -226,13 +227,13 @@
 
     private static final long TEST_START = 1194220800000L;
     private static final String TEST_IFACE = "test0";
-    private static final String TEST_SSID = "AndroidAP";
+    private static final String TEST_WIFI_NETWORK_KEY = "TestWifiNetworkKey";
     private static final String TEST_IMSI = "310210";
     private static final int TEST_SUB_ID = 42;
     private static final Network TEST_NETWORK = mock(Network.class, CALLS_REAL_METHODS);
 
 
-    private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID);
+    private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_WIFI_NETWORK_KEY);
     private static NetworkTemplate sTemplateCarrierMetered =
             buildTemplateCarrierMetered(TEST_IMSI);
 
@@ -2096,10 +2097,13 @@
     }
 
     private static NetworkStateSnapshot buildWifi() {
+        WifiInfo mockWifiInfo = mock(WifiInfo.class);
+        when(mockWifiInfo.makeCopy(anyLong())).thenReturn(mockWifiInfo);
+        when(mockWifiInfo.getCurrentNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
         final NetworkCapabilities networkCapabilities = new NetworkCapabilities.Builder()
-                .addTransportType(TRANSPORT_WIFI).setSsid(TEST_SSID).build();
+                .addTransportType(TRANSPORT_WIFI).setTransportInfo(mockWifiInfo).build();
         return new NetworkStateSnapshot(TEST_NETWORK, networkCapabilities, prop,
                 null /*subscriberId*/, TYPE_WIFI);
     }
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 63a7acf..d6d6775 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -222,6 +222,11 @@
     private boolean mAreUiccApplicationsEnabled = true;
 
     /**
+     * The port index of the Uicc card.
+     */
+    private final int mPortIndex;
+
+    /**
      * Public copy constructor.
      * @hide
      */
@@ -274,6 +279,22 @@
             int carrierId, int profileClass, int subType, @Nullable String groupOwner,
             @Nullable UiccAccessRule[] carrierConfigAccessRules,
             boolean areUiccApplicationsEnabled) {
+        this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString,
+                cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierId, profileClass,
+                subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, 0);
+    }
+    /**
+     * @hide
+     */
+    public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+            CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+            Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
+            @Nullable UiccAccessRule[] nativeAccessRules, String cardString, int cardId,
+            boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled,
+            int carrierId, int profileClass, int subType, @Nullable String groupOwner,
+            @Nullable UiccAccessRule[] carrierConfigAccessRules,
+            boolean areUiccApplicationsEnabled, int portIndex) {
         this.mId = id;
         this.mIccId = iccId;
         this.mSimSlotIndex = simSlotIndex;
@@ -300,8 +321,8 @@
         this.mGroupOwner = groupOwner;
         this.mCarrierConfigAccessRules = carrierConfigAccessRules;
         this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
+        this.mPortIndex = portIndex;
     }
-
     /**
      * @return the subscription ID.
      */
@@ -737,6 +758,14 @@
     public int getCardId() {
         return this.mCardId;
     }
+    /**
+     * Returns the port index of the SIM card which contains the subscription.
+     *
+     * @return the portIndex
+     */
+    public int getPortIndex() {
+        return this.mPortIndex;
+    }
 
     /**
      * Set whether the subscription's group is disabled.
@@ -783,6 +812,7 @@
             UiccAccessRule[] nativeAccessRules = source.createTypedArray(UiccAccessRule.CREATOR);
             String cardString = source.readString();
             int cardId = source.readInt();
+            int portId = source.readInt();
             boolean isOpportunistic = source.readBoolean();
             String groupUUID = source.readString();
             boolean isGroupDisabled = source.readBoolean();
@@ -800,7 +830,7 @@
                     carrierName, nameSource, iconTint, number, dataRoaming, /* icon= */ null,
                     mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, cardId,
                     isOpportunistic, groupUUID, isGroupDisabled, carrierid, profileClass, subType,
-                    groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled);
+                    groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, portId);
             info.setAssociatedPlmns(ehplmns, hplmns);
             return info;
         }
@@ -830,6 +860,7 @@
         dest.writeTypedArray(mNativeAccessRules, flags);
         dest.writeString(mCardString);
         dest.writeInt(mCardId);
+        dest.writeInt(mPortIndex);
         dest.writeBoolean(mIsOpportunistic);
         dest.writeString(mGroupUUID == null ? null : mGroupUUID.toString());
         dest.writeBoolean(mIsGroupDisabled);
@@ -876,6 +907,7 @@
                 + " mnc=" + mMnc + " countryIso=" + mCountryIso + " isEmbedded=" + mIsEmbedded
                 + " nativeAccessRules=" + Arrays.toString(mNativeAccessRules)
                 + " cardString=" + cardStringToPrint + " cardId=" + mCardId
+                + " portIndex=" + mPortIndex
                 + " isOpportunistic=" + mIsOpportunistic + " groupUUID=" + mGroupUUID
                 + " isGroupDisabled=" + mIsGroupDisabled
                 + " profileClass=" + mProfileClass
@@ -892,7 +924,7 @@
         return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
                 mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso, mCardString,
                 mCardId, mDisplayName, mCarrierName, mNativeAccessRules, mIsGroupDisabled,
-                mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled);
+                mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex);
     }
 
     @Override
@@ -925,6 +957,7 @@
                 && Objects.equals(mCountryIso, toCompare.mCountryIso)
                 && Objects.equals(mCardString, toCompare.mCardString)
                 && Objects.equals(mCardId, toCompare.mCardId)
+                && mPortIndex == toCompare.mPortIndex
                 && Objects.equals(mGroupOwner, toCompare.mGroupOwner)
                 && TextUtils.equals(mDisplayName, toCompare.mDisplayName)
                 && TextUtils.equals(mCarrierName, toCompare.mCarrierName)
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 30cb8ca..fd9247c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -132,6 +132,8 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -145,6 +147,7 @@
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
+
 /**
  * Provides access to information about the telephony services on
  * the device. Applications can use the methods in this class to
@@ -3941,8 +3944,8 @@
      * <p>
      * If the caller has carrier priviliges on any active subscription, then they have permission to
      * get simple information like the card ID ({@link UiccCardInfo#getCardId()}), whether the card
-     * is an eUICC ({@link UiccCardInfo#isEuicc()}), and the slot index where the card is inserted
-     * ({@link UiccCardInfo#getSlotIndex()}).
+     * is an eUICC ({@link UiccCardInfo#isEuicc()}), and the physical slot index where the card is
+     * inserted ({@link UiccCardInfo#getPhysicalSlotIndex()}.
      * <p>
      * To get private information such as the EID ({@link UiccCardInfo#getEid()}) or ICCID
      * ({@link UiccCardInfo#getIccId()}), the caller must have carrier priviliges on that specific
@@ -3986,7 +3989,7 @@
             if (telephony == null) {
                 return null;
             }
-            return telephony.getUiccSlotsInfo();
+            return telephony.getUiccSlotsInfo(mContext.getOpPackageName());
         } catch (RemoteException e) {
             return null;
         }
@@ -4019,8 +4022,13 @@
      *        size should be same as {@link #getUiccSlotsInfo()}.
      * @return boolean Return true if the switch succeeds, false if the switch fails.
      * @hide
+     * @deprecated {@link #setSimSlotMapping(Collection, Executor, Consumer)}
      */
+     // TODO: once integrating the HAL changes we can  convert int[] to List<UiccSlotMapping> and
+     // converge API's in ITelephony.aidl and PhoneInterfaceManager
+
     @SystemApi
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean switchSlots(int[] physicalSlots) {
         try {
@@ -4035,6 +4043,109 @@
     }
 
     /**
+     * @param slotMapping Logical to physical slot and port mapping.
+     * @return {@code true} if slotMapping is valid.
+     * @return {@code false} if slotMapping is invalid.
+     *
+     * slotMapping is invalid if there are different entries (physical slot + port) mapping to the
+     * same logical slot or if there are same {physical slot + port} mapping to the different
+     * logical slot
+     * @hide
+     */
+    private static boolean isSlotMappingValid(@NonNull Collection<UiccSlotMapping> slotMapping) {
+        // Grouping the collection by logicalSlotIndex, finding different entries mapping to the
+        // same logical slot
+        Map<Integer, List<UiccSlotMapping>> slotMappingInfo = slotMapping.stream().collect(
+                Collectors.groupingBy(UiccSlotMapping::getLogicalSlotIndex));
+        for (Map.Entry<Integer, List<UiccSlotMapping>> entry : slotMappingInfo.entrySet()) {
+            List<UiccSlotMapping> logicalSlotMap = entry.getValue();
+            if (logicalSlotMap.size() > 1) {
+                // duplicate logicalSlotIndex found
+                return false;
+            }
+        }
+
+        // Grouping the collection by physical slot and port, finding same entries mapping to the
+        // different logical slot
+        Map<List<Integer>, List<UiccSlotMapping>> slotMapInfos = slotMapping.stream().collect(
+                Collectors.groupingBy(
+                        slot -> Arrays.asList(slot.getPhysicalSlotIndex(), slot.getPortIndex())));
+        for (Map.Entry<List<Integer>, List<UiccSlotMapping>> entry : slotMapInfos.entrySet()) {
+            List<UiccSlotMapping> portAndPhysicalSlotList = entry.getValue();
+            if (portAndPhysicalSlotList.size() > 1) {
+                // duplicate pair of portIndex and physicalSlotIndex found
+                return false;
+            }
+        }
+        return true;
+    }
+    /**
+     * Maps the logical slots to physical slots and ports. Mapping is specified from
+     * {@link UiccSlotMapping} which consist of both physical slot index and port index.
+     * Logical slot is the slot that is seen by modem. Physical slot is the actual physical slot.
+     * Port index is the index (enumerated value) for the associated port available on the SIM.
+     * Each physical slot can have multiple ports if multi-enabled profile(MEP) is supported.
+     *
+     * Example: no. of logical slots 1 and physical slots 2 do not support MEP, each physical slot
+     * has one port:
+     * The only logical slot (index 0) can be mapped to first physical slot (value 0), port(index
+     * 0) or
+     * second physical slot(value 1), port (index 0), while the other physical slot remains unmapped
+     * and inactive.
+     * slotMapping[0] = UiccSlotMapping{0 //logical slot, 0 //physical slot//, 0 //port//}
+     * slotMapping[0] = UiccSlotMapping{1 // logical slot, 1 //physical slot//, 0 //port//}
+     *
+     * Example no. of logical slots 2 and physical slots 2 supports MEP with 2 ports available:
+     * Each logical slot must be mapped to a port (physical slot and port combination).
+     * First logical slot (index 0) can be mapped to physical slot 1 and the second logical slot
+     * can be mapped to either port from physical slot 2.
+     *
+     * slotMapping[0] = UiccSlotMapping{0, 0, 0} and slotMapping[1] = UiccSlotMapping{1, 0, 0} or
+     * slotMapping[0] = UiccSlotMapping{0, 0, 0} and slotMapping[1] = UiccSlotMapping{1, 1, 1}
+     *
+     * or the other way around, the second logical slot(index 1) can be mapped to physical slot 1
+     * and the first logical slot can be mapped to either port from physical slot 2.
+     *
+     * slotMapping[1] = UiccSlotMapping{0, 0, 0} and slotMapping[0] = UiccSlotMapping{1, 0, 0} or
+     * slotMapping[1] = UiccSlotMapping{0, 0, 0} and slotMapping[0] = UiccSlotMapping{1, 1, 1}
+     *
+     * another possible mapping is each logical slot maps to each port of physical slot 2 and there
+     * is no active logical modem mapped to physical slot 1.
+     *
+     * slotMapping[0] = UiccSlotMapping{1, 0, 0} and slotMapping[1] = UiccSlotMapping{1, 1, 1} or
+     * slotMapping[0] = UiccSlotMapping{1, 1, 1} and slotMapping[1] = UiccSlotMapping{1, 0, 0}
+     *
+     * @param slotMapping Logical to physical slot and port mapping.
+     * @throws IllegalStateException if telephony service is null or slot mapping was sent when the
+     *         radio in middle of a silent restart or other invalid states to handle the command
+     * @throws IllegalArgumentException if the caller passes in an invalid collection of
+     *         UiccSlotMapping like duplicate data, etc
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setSimSlotMapping(@NonNull Collection<UiccSlotMapping> slotMapping) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (isSlotMappingValid(slotMapping)) {
+                    boolean result = telephony.setSimSlotMapping(new ArrayList(slotMapping));
+                    if (!result) {
+                        throw new IllegalStateException("setSimSlotMapping has failed");
+                    }
+                } else {
+                    throw new IllegalArgumentException("Duplicate UiccSlotMapping data found");
+                }
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
      * Get the mapping from logical slots to physical slots. The key of the map is the logical slot
      * id and the value is the physical slots id mapped to this logical slot id.
      *
@@ -4051,7 +4162,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                int[] slotMappingArray = telephony.getSlotsMapping();
+                int[] slotMappingArray = telephony.getSlotsMapping(mContext.getOpPackageName());
                 for (int i = 0; i < slotMappingArray.length; i++) {
                     slotMapping.put(i, slotMappingArray[i]);
                 }
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index b4389203..7dfe450 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -20,21 +20,27 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 /**
  * The UiccCardInfo represents information about a currently inserted UICC or embedded eUICC.
  */
 public final class UiccCardInfo implements Parcelable {
-
     private final boolean mIsEuicc;
     private final int mCardId;
     private final String mEid;
     private final String mIccId;
-    private final int mSlotIndex;
+    private final int mPhysicalSlotIndex;
     private final boolean mIsRemovable;
+    private final boolean mIsMultipleEnabledProfilesSupported;
+    private final List<UiccPortInfo> mPortList;
+    private boolean mIccIdAccessRestricted = false;
 
-    public static final @android.annotation.NonNull Creator<UiccCardInfo> CREATOR = new Creator<UiccCardInfo>() {
+    public static final @NonNull Creator<UiccCardInfo> CREATOR = new Creator<UiccCardInfo>() {
         @Override
         public UiccCardInfo createFromParcel(Parcel in) {
             return new UiccCardInfo(in);
@@ -47,22 +53,29 @@
     };
 
     private UiccCardInfo(Parcel in) {
-        mIsEuicc = in.readByte() != 0;
+        mIsEuicc = in.readBoolean();
         mCardId = in.readInt();
-        mEid = in.readString();
-        mIccId = in.readString();
-        mSlotIndex = in.readInt();
-        mIsRemovable = in.readByte() != 0;
+        mEid = in.readString8();
+        mIccId = in.readString8();
+        mPhysicalSlotIndex = in.readInt();
+        mIsRemovable = in.readBoolean();
+        mIsMultipleEnabledProfilesSupported = in.readBoolean();
+        mPortList = new ArrayList<UiccPortInfo>();
+        in.readTypedList(mPortList, UiccPortInfo.CREATOR);
+        mIccIdAccessRestricted = in.readBoolean();
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByte((byte) (mIsEuicc ? 1 : 0));
+        dest.writeBoolean(mIsEuicc);
         dest.writeInt(mCardId);
-        dest.writeString(mEid);
-        dest.writeString(mIccId);
-        dest.writeInt(mSlotIndex);
-        dest.writeByte((byte) (mIsRemovable ? 1 : 0));
+        dest.writeString8(mEid);
+        dest.writeString8(mIccId);
+        dest.writeInt(mPhysicalSlotIndex);
+        dest.writeBoolean(mIsRemovable);
+        dest.writeBoolean(mIsMultipleEnabledProfilesSupported);
+        dest.writeTypedList(mPortList, flags);
+        dest.writeBoolean(mIccIdAccessRestricted);
     }
 
     @Override
@@ -71,20 +84,34 @@
     }
 
     /**
+     * Construct a UiccCardInfo.
+     *
+     * @param isEuicc is a flag to check is eUICC or not
+     * @param cardId is unique ID used to identify a UiccCard.
+     * @param eid is unique eUICC Identifier
+     * @param physicalSlotIndex is unique index referring to a physical SIM slot.
+     * @param isRemovable is a flag to check is removable or embedded
+     * @param isMultipleEnabledProfilesSupported is a flag to check is MEP enabled or not
+     * @param portList has the information regarding port, ICCID and its active status
+     *
      * @hide
      */
-    public UiccCardInfo(boolean isEuicc, int cardId, String eid, String iccId, int slotIndex,
-            boolean isRemovable) {
+    public UiccCardInfo(boolean isEuicc, int cardId, String eid, int physicalSlotIndex,
+            boolean isRemovable, boolean isMultipleEnabledProfilesSupported,
+            @NonNull List<UiccPortInfo> portList) {
         this.mIsEuicc = isEuicc;
         this.mCardId = cardId;
         this.mEid = eid;
-        this.mIccId = iccId;
-        this.mSlotIndex = slotIndex;
+        this.mIccId = null;
+        this.mPhysicalSlotIndex = physicalSlotIndex;
         this.mIsRemovable = isRemovable;
+        this.mIsMultipleEnabledProfilesSupported = isMultipleEnabledProfilesSupported;
+        this.mPortList = portList;
     }
 
     /**
      * Return whether the UICC is an eUICC.
+     *
      * @return true if the UICC is an eUICC.
      */
     public boolean isEuicc() {
@@ -119,40 +146,83 @@
      * <p>
      * Note that this field may be omitted if the caller does not have the correct permissions
      * (see {@link TelephonyManager#getUiccCardsInfo()}).
+     *
+     * @deprecated with support for MEP(multiple enabled profile), a SIM card can have more than one
+     * ICCID active at the same time.Instead use {@link UiccPortInfo#getIccId()} to retrieve ICCID.
+     * To find {@link UiccPortInfo} use {@link UiccCardInfo#getPorts()}
+     *
+     * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond.
      */
     @Nullable
+    @Deprecated
     public String getIccId() {
-        return mIccId;
+        if (mIccIdAccessRestricted) {
+            throw new UnsupportedOperationException("getIccId from UiccPortInfo");
+        }
+        //always return ICCID from first port.
+        return getPorts().stream().findFirst().get().getIccId();
     }
 
     /**
      * Gets the slot index for the slot that the UICC is currently inserted in.
+     *
+     * @deprecated use {@link #getPhysicalSlotIndex()}
      */
+    @Deprecated
     public int getSlotIndex() {
-        return mSlotIndex;
+        return mPhysicalSlotIndex;
     }
 
     /**
-     * Returns a copy of the UiccCardinfo with the EID and ICCID set to null. These values are
-     * generally private and require carrier privileges to view.
-     *
-     * @hide
+     * Gets the physical slot index for the slot that the UICC is currently inserted in.
      */
-    @NonNull
-    public UiccCardInfo getUnprivileged() {
-        return new UiccCardInfo(mIsEuicc, mCardId, null, null, mSlotIndex, mIsRemovable);
+    public int getPhysicalSlotIndex() {
+        return mPhysicalSlotIndex;
     }
 
     /**
      * Return whether the UICC or eUICC is removable.
      * <p>
      * UICCs are generally removable, but eUICCs may be removable or built in to the device.
+     *
      * @return true if the UICC or eUICC is removable
      */
     public boolean isRemovable() {
         return mIsRemovable;
     }
 
+    /*
+     * Whether the UICC card supports multiple enable profile(MEP)
+     * UICCs are generally MEP disabled, there can be only one active profile on the physical
+     * sim card.
+     *
+     * @return {@code true} if the eUICC is supporting multiple enabled profile(MEP).
+     */
+    public boolean isMultipleEnabledProfilesSupported() {
+        return mIsMultipleEnabledProfilesSupported;
+    }
+
+    /**
+     * Get information regarding port, ICCID and its active status.
+     *
+     * @return Collection of {@link UiccPortInfo}
+     */
+    public @NonNull Collection<UiccPortInfo> getPorts() {
+        return Collections.unmodifiableList(mPortList);
+    }
+
+    /**
+     * if the flag is set to {@code true} the calling app is not allowed to access deprecated
+     * {@link #getIccId()}
+     * @param iccIdAccessRestricted is the flag to check if app is allowed to access ICCID
+     *
+     * @hide
+     */
+    public void setIccIdAccessRestricted(boolean iccIdAccessRestricted) {
+        this.mIccIdAccessRestricted = iccIdAccessRestricted;
+    }
+
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -167,13 +237,16 @@
                 && (mCardId == that.mCardId)
                 && (Objects.equals(mEid, that.mEid))
                 && (Objects.equals(mIccId, that.mIccId))
-                && (mSlotIndex == that.mSlotIndex)
-                && (mIsRemovable == that.mIsRemovable));
+                && (mPhysicalSlotIndex == that.mPhysicalSlotIndex)
+                && (mIsRemovable == that.mIsRemovable)
+                && (mIsMultipleEnabledProfilesSupported == that.mIsMultipleEnabledProfilesSupported)
+                && (Objects.equals(mPortList, that.mPortList)));
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mSlotIndex, mIsRemovable);
+        return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mPhysicalSlotIndex, mIsRemovable,
+                mIsMultipleEnabledProfilesSupported, mPortList);
     }
 
     @Override
@@ -185,11 +258,17 @@
                 + ", mEid="
                 + mEid
                 + ", mIccId="
-                + mIccId
-                + ", mSlotIndex="
-                + mSlotIndex
+                + SubscriptionInfo.givePrintableIccid(mIccId)
+                + ", mPhysicalSlotIndex="
+                + mPhysicalSlotIndex
                 + ", mIsRemovable="
                 + mIsRemovable
+                + ", mIsMultipleEnabledProfilesSupported="
+                + mIsMultipleEnabledProfilesSupported
+                + ", mPortList="
+                + mPortList
+                + ", mIccIdAccessRestricted="
+                + mIccIdAccessRestricted
                 + ")";
     }
-}
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/UiccPortInfo.aidl b/telephony/java/android/telephony/UiccPortInfo.aidl
new file mode 100644
index 0000000..7fff4ba
--- /dev/null
+++ b/telephony/java/android/telephony/UiccPortInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+parcelable UiccPortInfo;
diff --git a/telephony/java/android/telephony/UiccPortInfo.java b/telephony/java/android/telephony/UiccPortInfo.java
new file mode 100644
index 0000000..d1838c0
--- /dev/null
+++ b/telephony/java/android/telephony/UiccPortInfo.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * UiccPortInfo class represents information about a single port contained on {@link UiccCardInfo}.
+ * Per GSMA SGP.22 V3.0, a port is a logical entity to which an active UICC profile can be bound on
+ * a UICC card. If UICC supports 2 ports, then the port index is numbered 0,1.
+ * Each port index is unique within an UICC, but not necessarily unique across UICC’s.
+ * For UICC's does not support MEP(Multi-enabled profile), just return the default port index 0.
+ */
+public final class UiccPortInfo implements Parcelable{
+    private final String mIccId;
+    private final int mPortIndex;
+    private final int mLogicalSlotIndex;
+    private final boolean mIsActive;
+
+    /**
+     * A redacted String if caller does not have permission to read ICCID.
+     */
+    public static final String ICCID_REDACTED = "FFFFFFFFFFFFFFFFFFFF";
+
+    public static final @NonNull Creator<UiccPortInfo> CREATOR =
+            new Creator<UiccPortInfo>() {
+                @Override
+                public UiccPortInfo createFromParcel(Parcel in) {
+                    return new UiccPortInfo(in);
+                }
+                @Override
+                public UiccPortInfo[] newArray(int size) {
+                    return new UiccPortInfo[size];
+                }
+            };
+
+    private UiccPortInfo(Parcel in) {
+        mIccId = in.readString8();
+        mPortIndex = in.readInt();
+        mLogicalSlotIndex = in.readInt();
+        mIsActive = in.readBoolean();
+    }
+
+    @Override
+    public void writeToParcel(@Nullable Parcel dest, int flags) {
+        dest.writeString8(mIccId);
+        dest.writeInt(mPortIndex);
+        dest.writeInt(mLogicalSlotIndex);
+        dest.writeBoolean(mIsActive);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Construct a UiccPortInfo.
+     *
+     * @param iccId The ICCID of the profile.
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     * @param logicalSlotIndex is unique index referring to a logical SIM slot.
+     * @param isActive is flag to check if port was tied to a modem stack.
+     *
+     * @hide
+     */
+    public UiccPortInfo(String iccId, int portIndex, int logicalSlotIndex, boolean isActive) {
+        this.mIccId = iccId;
+        this.mPortIndex = portIndex;
+        this.mLogicalSlotIndex = logicalSlotIndex;
+        this.mIsActive = isActive;
+    }
+
+    /**
+     * Get the ICCID of the profile associated with this port.
+     * If this port is not {@link #isActive()}, returns {@code null}.
+     * If the caller does not have access to the ICCID for this port, it will be redacted and
+     * {@link #ICCID_REDACTED} will be returned.
+     */
+    public @Nullable String getIccId() {
+        return mIccId;
+    }
+
+    /**
+     * The port index is an enumeration of the ports available on the UICC.
+     * Example: if eUICC1 supports 2 ports, then the port index is numbered 0,1.
+     * Each port index is unique within an UICC, but not necessarily unique across UICC’s.
+     * For UICC's does not support MEP(Multi-enabled profile), just return the default port index 0.
+     */
+    @IntRange(from = 0)
+    public int getPortIndex() {
+        return mPortIndex;
+    }
+
+    /**
+     * @return {@code true} if port was tied to a modem stack.
+     */
+    public boolean isActive() {
+        return mIsActive;
+    }
+
+    /**
+     * Gets logical slot index for the slot that the UICC is currently attached.
+     * Logical slot index or ID: unique index referring to a logical SIM slot.
+     * Logical slot IDs start at 0 and go up depending on the number of supported active slots on
+     * a device.
+     * For example, a dual-SIM device typically has slot 0 and slot 1.
+     * If a device has multiple physical slots but only supports one active slot,
+     * it will have only the logical slot ID 0.
+     *
+     * @return the logical slot index for UICC port, if there is no logical slot index it returns
+     * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX}
+     */
+    @IntRange(from = 0)
+    public int getLogicalSlotIndex() {
+        return mLogicalSlotIndex;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        UiccPortInfo that = (UiccPortInfo) obj;
+        return (Objects.equals(mIccId, that.mIccId))
+                && (mPortIndex == that.mPortIndex)
+                && (mLogicalSlotIndex == that.mLogicalSlotIndex)
+                && (mIsActive == that.mIsActive);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIccId, mPortIndex, mLogicalSlotIndex, mIsActive);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "UiccPortInfo (isActive="
+                + mIsActive
+                + ", iccId="
+                + SubscriptionInfo.givePrintableIccid(mIccId)
+                + ", portIndex="
+                + mPortIndex
+                + ", mLogicalSlotIndex="
+                + mLogicalSlotIndex
+                + ")";
+    }
+}
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index a0e949a..2b1c8c8 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -24,6 +24,10 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -64,8 +68,10 @@
     private final int mLogicalSlotIdx;
     private final boolean mIsExtendedApduSupported;
     private final boolean mIsRemovable;
+    private final List<UiccPortInfo> mPortList;
+    private boolean mLogicalSlotAccessRestricted = false;
 
-    public static final @android.annotation.NonNull Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
+    public static final @NonNull Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
         @Override
         public UiccSlotInfo createFromParcel(Parcel in) {
             return new UiccSlotInfo(in);
@@ -78,24 +84,29 @@
     };
 
     private UiccSlotInfo(Parcel in) {
-        mIsActive = in.readByte() != 0;
-        mIsEuicc = in.readByte() != 0;
-        mCardId = in.readString();
+        mIsActive = in.readBoolean();
+        mIsEuicc = in.readBoolean();
+        mCardId = in.readString8();
         mCardStateInfo = in.readInt();
         mLogicalSlotIdx = in.readInt();
-        mIsExtendedApduSupported = in.readByte() != 0;
-        mIsRemovable = in.readByte() != 0;
+        mIsExtendedApduSupported = in.readBoolean();
+        mIsRemovable = in.readBoolean();
+        mPortList = new ArrayList<UiccPortInfo>();
+        in.readTypedList(mPortList, UiccPortInfo.CREATOR);
+        mLogicalSlotAccessRestricted = in.readBoolean();
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByte((byte) (mIsActive ? 1 : 0));
-        dest.writeByte((byte) (mIsEuicc ? 1 : 0));
-        dest.writeString(mCardId);
+        dest.writeBoolean(mIsActive);
+        dest.writeBoolean(mIsEuicc);
+        dest.writeString8(mCardId);
         dest.writeInt(mCardStateInfo);
         dest.writeInt(mLogicalSlotIdx);
-        dest.writeByte((byte) (mIsExtendedApduSupported ? 1 : 0));
-        dest.writeByte((byte) (mIsRemovable ? 1 : 0));
+        dest.writeBoolean(mIsExtendedApduSupported);
+        dest.writeBoolean(mIsRemovable);
+        dest.writeTypedList(mPortList, flags);
+        dest.writeBoolean(mLogicalSlotAccessRestricted);
     }
 
     @Override
@@ -117,25 +128,42 @@
         this.mLogicalSlotIdx = logicalSlotIdx;
         this.mIsExtendedApduSupported = isExtendedApduSupported;
         this.mIsRemovable = false;
+        this.mPortList = null;
     }
 
     /**
+     * Construct a UiccSlotInfo.
      * @hide
      */
-    public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
-            @CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported,
-            boolean isRemovable) {
-        this.mIsActive = isActive;
+    public UiccSlotInfo(boolean isEuicc, String cardId,
+            @CardStateInfo int cardStateInfo, boolean isExtendedApduSupported,
+            boolean isRemovable, @NonNull List<UiccPortInfo> portList) {
+        this.mIsActive = portList.get(0).isActive();
         this.mIsEuicc = isEuicc;
         this.mCardId = cardId;
         this.mCardStateInfo = cardStateInfo;
-        this.mLogicalSlotIdx = logicalSlotIdx;
+        this.mLogicalSlotIdx = portList.get(0).getLogicalSlotIndex();
         this.mIsExtendedApduSupported = isExtendedApduSupported;
         this.mIsRemovable = isRemovable;
+        this.mPortList = portList;
     }
 
+    /**
+     * @deprecated There is no longer isActive state for each slot because ports belonging
+     * to the physical slot could have different states
+     * we instead use {@link UiccPortInfo#isActive()}
+     * To get UiccPortInfo use {@link UiccSlotInfo#getPorts()}
+     *
+     * @return {@code true} if status is active.
+     * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond.
+     */
+    @Deprecated
     public boolean getIsActive() {
-        return mIsActive;
+        if (mLogicalSlotAccessRestricted) {
+            throw new UnsupportedOperationException("get port status from UiccPortInfo");
+        }
+        //always return status from first port.
+        return getPorts().stream().findFirst().get().isActive();
     }
 
     public boolean getIsEuicc() {
@@ -159,8 +187,21 @@
         return mCardStateInfo;
     }
 
+    /**
+     * @deprecated There is no longer getLogicalSlotIndex
+     * There is no longer getLogicalSlotIdx as each port belonging to this physical slot could have
+     * different logical slot index. Use {@link UiccPortInfo#getLogicalSlotIndex()} instead
+     *
+     * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond.
+     */
+    @Deprecated
     public int getLogicalSlotIdx() {
-        return mLogicalSlotIdx;
+        if (mLogicalSlotAccessRestricted) {
+            throw new UnsupportedOperationException("get logical slot index from UiccPortInfo");
+        }
+        //always return logical slot index from first port.
+        //portList always have at least one element.
+        return getPorts().stream().findFirst().get().getLogicalSlotIndex();
     }
 
     /**
@@ -170,16 +211,37 @@
         return mIsExtendedApduSupported;
     }
 
-   /**
+    /**
      * Return whether the UICC slot is for a removable UICC.
      * <p>
      * UICCs are generally removable, but eUICCs may be removable or built in to the device.
+     *
      * @return true if the slot is for removable UICCs
      */
     public boolean isRemovable() {
         return mIsRemovable;
     }
 
+    /**
+     * Get Information regarding port, iccid and its active status.
+     *
+     * @return Collection of {@link UiccPortInfo}
+     */
+    public @NonNull Collection<UiccPortInfo> getPorts() {
+        return Collections.unmodifiableList(mPortList);
+    }
+
+    /**
+     * Set the flag to check compatibility of the calling app's target SDK is T and beyond.
+     *
+     * @param logicalSlotAccessRestricted is the flag to check compatibility.
+     *
+     * @hide
+     */
+    public void setLogicalSlotAccessRestricted(boolean logicalSlotAccessRestricted) {
+        this.mLogicalSlotAccessRestricted = logicalSlotAccessRestricted;
+    }
+
     @Override
     public boolean equals(@Nullable Object obj) {
         if (this == obj) {
@@ -196,20 +258,14 @@
                 && (mCardStateInfo == that.mCardStateInfo)
                 && (mLogicalSlotIdx == that.mLogicalSlotIdx)
                 && (mIsExtendedApduSupported == that.mIsExtendedApduSupported)
-                && (mIsRemovable == that.mIsRemovable);
+                && (mIsRemovable == that.mIsRemovable)
+                && (Objects.equals(mPortList, that.mPortList));
     }
 
     @Override
     public int hashCode() {
-        int result = 1;
-        result = 31 * result + (mIsActive ? 1 : 0);
-        result = 31 * result + (mIsEuicc ? 1 : 0);
-        result = 31 * result + Objects.hashCode(mCardId);
-        result = 31 * result + mCardStateInfo;
-        result = 31 * result + mLogicalSlotIdx;
-        result = 31 * result + (mIsExtendedApduSupported ? 1 : 0);
-        result = 31 * result + (mIsRemovable ? 1 : 0);
-        return result;
+        return Objects.hash(mIsActive, mIsEuicc, mCardId, mCardStateInfo, mLogicalSlotIdx,
+                mIsExtendedApduSupported, mIsRemovable, mPortList);
     }
 
     @NonNull
@@ -229,6 +285,10 @@
                 + mIsExtendedApduSupported
                 + ", mIsRemovable="
                 + mIsRemovable
+                + ", mPortList="
+                + mPortList
+                + ", mLogicalSlotAccessRestricted="
+                + mLogicalSlotAccessRestricted
                 + ")";
     }
 }
diff --git a/telephony/java/android/telephony/UiccSlotMapping.aidl b/telephony/java/android/telephony/UiccSlotMapping.aidl
new file mode 100644
index 0000000..3b19499
--- /dev/null
+++ b/telephony/java/android/telephony/UiccSlotMapping.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+parcelable UiccSlotMapping;
diff --git a/telephony/java/android/telephony/UiccSlotMapping.java b/telephony/java/android/telephony/UiccSlotMapping.java
new file mode 100644
index 0000000..87e7acd
--- /dev/null
+++ b/telephony/java/android/telephony/UiccSlotMapping.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * <p>Provides information for a SIM slot mapping, which establishes a unique mapping between a
+ * logical SIM slot and a physical SIM slot and port index.  A logical SIM slot represents a
+ * potentially active SIM slot, where a physical SIM slot and port index represent a hardware SIM
+ * slot and port (capable of having an active profile) which can be mapped to a logical sim slot.
+ * <p>It contains the following parameters:
+ * <ul>
+ * <li>Port index: unique index referring to a port belonging to the physical SIM slot.
+ * If the SIM does not support multiple enabled profiles, the port index is default index 0.</li>
+ * <li>Physical slot index: unique index referring to a physical SIM slot. Physical slot IDs start
+ * at 0 and go up depending on the number of physical slots on the device.
+ * This differs from the number of logical slots a device has, which corresponds to the number of
+ * active slots a device is capable of using. For example, a device which switches between dual-SIM
+ * and single-SIM mode may always have two physical slots, but in single-SIM mode it will have only
+ * one logical slot.</li>
+ * <li>Logical slot index: unique index referring to a logical SIM slot, Logical slot IDs start at 0
+ * and go up depending on the number of supported active slots on a device.
+ * For example, a dual-SIM device typically has slot 0 and slot 1. If a device has multiple physical
+ * slots but only supports one active slot, it will have only the logical slot ID 0</li>
+ * </ul>
+ *
+ * <p> This configurations tells a specific logical slot is mapped to a port from an actual physical
+ * sim slot @see <a href="https://developer.android.com/guide/topics/connectivity/telecom/telephony-ids">the Android Developer Site</a>
+ * for more information.
+ * @hide
+ */
+@SystemApi
+public final class UiccSlotMapping implements Parcelable {
+    private final int mPortIndex;
+    private final int mPhysicalSlotIndex;
+    private final int mLogicalSlotIndex;
+
+    public static final @NonNull Creator<UiccSlotMapping> CREATOR =
+            new Creator<UiccSlotMapping>() {
+        @Override
+        public UiccSlotMapping createFromParcel(Parcel in) {
+            return new UiccSlotMapping(in);
+        }
+
+        @Override
+        public UiccSlotMapping[] newArray(int size) {
+            return new UiccSlotMapping[size];
+        }
+    };
+
+    private UiccSlotMapping(Parcel in) {
+        mPortIndex = in.readInt();
+        mPhysicalSlotIndex = in.readInt();
+        mLogicalSlotIndex = in.readInt();
+    }
+
+    @Override
+    public void writeToParcel(@Nullable Parcel dest, int flags) {
+        dest.writeInt(mPortIndex);
+        dest.writeInt(mPhysicalSlotIndex);
+        dest.writeInt(mLogicalSlotIndex);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     *
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     * @param physicalSlotIndex is unique index referring to a physical SIM slot.
+     * @param logicalSlotIndex is unique index referring to a logical SIM slot.
+     *
+     * @hide
+     */
+    public UiccSlotMapping(int portIndex, int physicalSlotIndex, int logicalSlotIndex) {
+        this.mPortIndex = portIndex;
+        this.mPhysicalSlotIndex = physicalSlotIndex;
+        this.mLogicalSlotIndex = logicalSlotIndex;
+    }
+
+    /**
+     * Port index is the unique index referring to a port belonging to the physical SIM slot.
+     * If the SIM does not support multiple enabled profiles, the port index is default index 0.
+     *
+     * @return port index.
+     */
+    @IntRange(from = 0)
+    public int getPortIndex() {
+        return mPortIndex;
+    }
+
+    /**
+     * Gets the physical slot index for the slot that the UICC is currently inserted in.
+     *
+     * @return physical slot index which is the index of actual physical UICC slot.
+     */
+    @IntRange(from = 0)
+    public int getPhysicalSlotIndex() {
+        return mPhysicalSlotIndex;
+    }
+
+    /**
+     * Gets logical slot index for the slot that the UICC is currently attached.
+     * Logical slot index is the unique index referring to a logical slot(logical modem stack).
+     *
+     * @return logical slot index;
+     */
+    @IntRange(from = 0)
+    public int getLogicalSlotIndex() {
+        return mLogicalSlotIndex;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        UiccSlotMapping that = (UiccSlotMapping) obj;
+        return (mPortIndex == that.mPortIndex)
+                && (mPhysicalSlotIndex == that.mPhysicalSlotIndex)
+                && (mLogicalSlotIndex == that.mLogicalSlotIndex);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPortIndex, mPhysicalSlotIndex, mLogicalSlotIndex);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "UiccSlotMapping (mPortIndex="
+                + mPortIndex
+                + ", mPhysicalSlotIndex="
+                + mPhysicalSlotIndex
+                + ", mLogicalSlotIndex="
+                + mLogicalSlotIndex
+                + ")";
+    }
+}
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 2f03475..892eb29 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -402,6 +402,21 @@
         }
 
         /**
+         * Notify the system that a given DataProfile was unthrottled.
+         *
+         * @param dataProfile DataProfile associated with an APN returned from the modem
+         */
+        public final void notifyDataProfileUnthrottled(@NonNull DataProfile dataProfile) {
+            synchronized (mApnUnthrottledCallbacks) {
+                for (IDataServiceCallback callback : mApnUnthrottledCallbacks) {
+                    mHandler.obtainMessage(DATA_SERVICE_INDICATION_APN_UNTHROTTLED,
+                            mSlotIndex, 0, new ApnUnthrottledIndication(dataProfile,
+                                    callback)).sendToTarget();
+                }
+            }
+        }
+
+        /**
          * Called when the instance of data service is destroyed (e.g. got unbind or binder died)
          * or when the data service provider is removed. The extended class should implement this
          * method to perform cleanup works.
@@ -496,13 +511,20 @@
     }
 
     private static final class ApnUnthrottledIndication {
+        public final DataProfile dataProfile;
         public final String apn;
         public final IDataServiceCallback callback;
         ApnUnthrottledIndication(String apn,
                 IDataServiceCallback callback) {
+            this.dataProfile = null;
             this.apn = apn;
             this.callback = callback;
         }
+        ApnUnthrottledIndication(DataProfile dataProfile, IDataServiceCallback callback) {
+            this.dataProfile = dataProfile;
+            this.apn = null;
+            this.callback = callback;
+        }
     }
 
     private class DataServiceHandler extends Handler {
@@ -636,8 +658,13 @@
                     ApnUnthrottledIndication apnUnthrottledIndication =
                             (ApnUnthrottledIndication) message.obj;
                     try {
-                        apnUnthrottledIndication.callback
-                                .onApnUnthrottled(apnUnthrottledIndication.apn);
+                        if (apnUnthrottledIndication.dataProfile != null) {
+                            apnUnthrottledIndication.callback
+                                    .onDataProfileUnthrottled(apnUnthrottledIndication.dataProfile);
+                        } else {
+                            apnUnthrottledIndication.callback
+                                    .onApnUnthrottled(apnUnthrottledIndication.apn);
+                        }
                     } catch (RemoteException e) {
                         loge("Failed to call onApnUnthrottled. " + e);
                     }
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index d082715..051d6c5 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -260,8 +260,8 @@
 
     /**
      * Unthrottles the APN on the current transport.  There is no matching "APN throttle" method.
-     * Instead, the APN is throttled for the time specified in
-     * {@link DataCallResponse#getRetryDurationMillis}.
+     * Instead, the APN is throttled when {@link IDataService#setupDataCall} fails within
+     * the time specified by {@link DataCallResponse#getRetryDurationMillis}.
      * <p/>
      * see: {@link DataCallResponse#getRetryDurationMillis}
      *
@@ -279,4 +279,27 @@
             Rlog.e(TAG, "onApnUnthrottled: callback is null!");
         }
     }
+
+    /**
+     * Unthrottles the DataProfile on the current transport.
+     * There is no matching "DataProfile throttle" method.
+     * Instead, the DataProfile is throttled when {@link IDataService#setupDataCall} fails within
+     * the time specified by {@link DataCallResponse#getRetryDurationMillis}.
+     * <p/>
+     * see: {@link DataCallResponse#getRetryDurationMillis}
+     *
+     * @param dataProfile DataProfile containing the APN to be throttled
+     */
+    public void onDataProfileUnthrottled(final @NonNull DataProfile dataProfile) {
+        if (mCallback != null) {
+            try {
+                if (DBG) Rlog.d(TAG, "onDataProfileUnthrottled");
+                mCallback.onDataProfileUnthrottled(dataProfile);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "onDataProfileUnthrottled: remote exception", e);
+            }
+        } else {
+            Rlog.e(TAG, "onDataProfileUnthrottled: callback is null!");
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
index 9cc2fea..8205b5e 100644
--- a/telephony/java/android/telephony/data/IDataServiceCallback.aidl
+++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
@@ -17,6 +17,7 @@
 package android.telephony.data;
 
 import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataProfile;
 
 /**
  * The call back interface
@@ -33,4 +34,5 @@
     void onHandoverStarted(int result);
     void onHandoverCancelled(int result);
     void onApnUnthrottled(in String apn);
+    void onDataProfileUnthrottled(in DataProfile dp);
 }
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index e1aec0a..ab35d77 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -17,6 +17,7 @@
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Context;
@@ -24,6 +25,7 @@
 import android.os.RemoteException;
 import android.service.euicc.EuiccProfileInfo;
 import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
@@ -122,7 +124,6 @@
 
     /** Result code indicating the caller is not the active LPA. */
     public static final int RESULT_CALLER_NOT_ALLOWED = -3;
-
     /**
      * Callback to receive the result of an eUICC card API.
      *
@@ -220,12 +221,48 @@
      * @param refresh Whether sending the REFRESH command to modem.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code.
+     *
+     * @deprecated instead use {@link #disableProfile(String, String, int, boolean, Executor,
+     *             ResultCallback)}
      */
+    @Deprecated
     public void disableProfile(String cardId, String iccid, boolean refresh,
             @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
         try {
             getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid,
-                    refresh, new IDisableProfileCallback.Stub() {
+                    TelephonyManager.DEFAULT_PORT_INDEX, refresh,
+                    new IDisableProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling disableProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+    /**
+     * Disables the profile of the given ICCID.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param iccid The iccid of the profile.
+     * @param portIndex the Port index is the unique index referring to a port.
+     * @param refresh Whether sending the REFRESH command to modem.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code.
+     */
+    public void disableProfile(@Nullable String cardId, @Nullable String iccid, int portIndex,
+            boolean refresh, @NonNull @CallbackExecutor Executor executor,
+            @NonNull ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid,
+                    portIndex, refresh, new IDisableProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
                             final long token = Binder.clearCallingIdentity();
@@ -251,12 +288,51 @@
      * @param refresh Whether sending the REFRESH command to modem.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+     *
+     * @deprecated instead use {@link #switchToProfile(String, String, int, boolean, Executor,
+     *             ResultCallback)}
      */
+    @Deprecated
     public void switchToProfile(String cardId, String iccid, boolean refresh,
             @CallbackExecutor Executor executor, ResultCallback<EuiccProfileInfo> callback) {
         try {
             getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid,
-                    refresh, new ISwitchToProfileCallback.Stub() {
+                    TelephonyManager.DEFAULT_PORT_INDEX, refresh,
+                    new ISwitchToProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo profile) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profile));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling switchToProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Switches from the current profile to another profile. The current profile will be disabled
+     * and the specified profile will be enabled. Here portIndex specifies on which port the
+     * profile is to be enabled.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param iccid The iccid of the profile to switch to.
+     * @param portIndex The Port index is the unique index referring to a port.
+     * @param refresh Whether sending the REFRESH command to modem.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+     */
+    public void switchToProfile(@Nullable String cardId, @Nullable String iccid, int portIndex,
+            boolean refresh, @NonNull @CallbackExecutor Executor executor,
+            @NonNull ResultCallback<EuiccProfileInfo> callback) {
+        try {
+            getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid,
+                    portIndex, refresh, new ISwitchToProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo profile) {
                             final long token = Binder.clearCallingIdentity();
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 2edb564c..45022a6 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -1418,4 +1418,22 @@
                         .getEuiccControllerService()
                         .get());
     }
+
+    /**
+     * Returns whether the passing portIndex is available.
+     * A port is available if it has no profiles enabled on it or calling app has carrier privilege
+     * over the profile installed on the selected port.
+     * Always returns false if the cardId is a physical card.
+     *
+     * @param portIndex is an enumeration of the ports available on the UICC.
+     * @return {@code true} if port is available
+     */
+    public boolean isSimPortAvailable(int portIndex) {
+        try {
+            return getIEuiccController().isSimPortAvailable(mCardId, portIndex,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 5577888..167aa07 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -77,6 +77,7 @@
 
 import android.telephony.UiccCardInfo;
 import android.telephony.UiccSlotInfo;
+import android.telephony.UiccSlotMapping;
 
 /**
  * Interface used to interact with the phone.  Mostly this is used by the
@@ -1742,17 +1743,35 @@
      * @return UiccSlotInfo array.
      * @hide
      */
-    UiccSlotInfo[] getUiccSlotsInfo();
+    UiccSlotInfo[] getUiccSlotsInfo(String callingPackage);
 
     /**
      * Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive.
      * @param physicalSlots Index i in the array representing physical slot for phone i. The array
      *        size should be same as getPhoneCount().
+     * @deprecated Use {@link #setSimSlotMapping(in List<UiccSlotMapping> slotMapping)} instead.
      * @return boolean Return true if the switch succeeds, false if the switch fails.
      */
     boolean switchSlots(in int[] physicalSlots);
 
     /**
+     * Maps the logical slots to the SlotPortMapping which consist of both physical slot index and
+     * port index. Logical slot is the slot that is seen by modem. Physical slot is the actual
+     * physical slot. Port index is the index (enumerated value) for the associated port available
+     * on the SIM. Each physical slot can have multiple ports which enables multi-enabled profile
+     * (MEP). If eUICC physical slot supports 2 ports, then the port index is numbered 0,1 and if
+     * eUICC2 supports 4 ports then the port index is numbered 0,1,2,3. Each portId is unique within
+     * a UICC physical slot but not necessarily unique across UICC’s. SEP(Single enabled profile)
+     * eUICC and non-eUICC will only have port Index 0.
+     *
+     * Logical slots that are already mapped to the requested SlotPortMapping are not impacted.
+     * @param slotMapping Index i in the list representing slot mapping for phone i.
+     *
+     * @return {@code true} if the switch succeeds, {@code false} if the switch fails.
+     */
+    boolean setSimSlotMapping(in List<UiccSlotMapping> slotMapping);
+
+    /**
      * Returns whether mobile data roaming is enabled on the subscription with id {@code subId}.
      *
      * @param subId the subscription id
@@ -2130,7 +2149,7 @@
     /**
      * Get the mapping from logical slots to physical slots.
      */
-    int[] getSlotsMapping();
+    int[] getSlotsMapping(String callingPackage);
 
     /**
      * Get the IRadio HAL Version encoded as 100 * MAJOR_VERSION + MINOR_VERSION or -1 if unknown
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 3a99f0e0..f650246 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -171,6 +171,8 @@
 
     public static final String SLOT_KEY  = "slot";
 
+    public static final String PORT_KEY = "port";
+
     // FIXME: This is used to pass a subId via intents, we need to look at its usage, which is
     // FIXME: extensive, and see if this should be an array of all active subId's or ...?
     /**
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
index e33f44c..c717c09 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
@@ -45,10 +45,10 @@
         in IGetAllProfilesCallback callback);
     oneway void getProfile(String callingPackage, String cardId, String iccid,
         in IGetProfileCallback callback);
-    oneway void disableProfile(String callingPackage, String cardId, String iccid, boolean refresh,
-        in IDisableProfileCallback callback);
-    oneway void switchToProfile(String callingPackage, String cardId, String iccid, boolean refresh,
-        in ISwitchToProfileCallback callback);
+    oneway void disableProfile(String callingPackage, String cardId, String iccid, int portIndex,
+            boolean refresh, in IDisableProfileCallback callback);
+    oneway void switchToProfile(String callingPackage, String cardId, String iccid, int portIndex,
+            boolean refresh, in ISwitchToProfileCallback callback);
     oneway void setNickname(String callingPackage, String cardId, String iccid, String nickname,
         in ISetNicknameCallback callback);
     oneway void deleteProfile(String callingPackage, String cardId, String iccid,
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 35e8a12..944ce348 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -51,4 +51,5 @@
     void setSupportedCountries(boolean isSupported, in List<String> countriesList);
     List<String> getSupportedCountries(boolean isSupported);
     boolean isSupportedCountry(String countryIso);
+    boolean isSimPortAvailable(int cardId, int portIndex, String callingPackage);
 }
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 5b44dba..21c3f76 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -23,6 +23,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.os.Build;
+import android.telephony.UiccPortInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet;
@@ -44,8 +45,7 @@
     static final int FPLMN_BYTE_SIZE = 3;
 
     // ICCID used for tests by some OEMs
-    // TODO(b/159354974): Replace the constant here with UiccPortInfo.ICCID_REDACTED once ready
-    private static final String TEST_ICCID = "FFFFFFFFFFFFFFFFFFFF";
+    public static final String TEST_ICCID = UiccPortInfo.ICCID_REDACTED;
 
     // A table mapping from a number to a hex character for fast encoding hex strings.
     private static final char[] HEX_CHARS = {
diff --git a/test-legacy/Android.mk b/test-legacy/Android.mk
index ce5e4cf..284008c 100644
--- a/test-legacy/Android.mk
+++ b/test-legacy/Android.mk
@@ -41,6 +41,6 @@
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 # Archive a copy of the classes.jar in SDK build.
-$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.legacy.jar)
+$(call dist-for-goals,sdk,$(full_classes_jar):android.test.legacy.jar)
 
 endif  # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index f0ab63e..06200cd 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -449,8 +449,7 @@
         // Query proper module name
         result = getPackageManagerNative().getStagedApexInfo(TEST_APEX_PACKAGE_NAME);
         assertThat(result.moduleName).isEqualTo(TEST_APEX_PACKAGE_NAME);
-        assertThat(result.hasBootClassPathJars).isTrue();
-        assertThat(result.hasSystemServerClassPathJars).isTrue();
+        assertThat(result.hasClassPathJars).isTrue();
         InstallUtils.openPackageInstallerSession(sessionId).abandon();
     }