Merge "Change expected default value of setPreferredData"
diff --git a/api/current.txt b/api/current.txt
index 7c0311a..f2afa52 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -42457,9 +42457,9 @@
 
   public class MbmsGroupCallSession implements java.lang.AutoCloseable {
     method public void close();
-    method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsGroupCallSessionCallback);
+    method public static android.telephony.MbmsGroupCallSession create(android.content.Context, int, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
     method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
-    method public android.telephony.mbms.GroupCall startGroupCall(java.util.concurrent.Executor, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+    method public android.telephony.mbms.GroupCall startGroupCall(long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, java.util.concurrent.Executor, android.telephony.mbms.GroupCallCallback);
   }
 
   public class MbmsStreamingSession implements java.lang.AutoCloseable {
@@ -42811,6 +42811,7 @@
     method public java.lang.String getCountryIso();
     method public int getDataRoaming();
     method public java.lang.CharSequence getDisplayName();
+    method public java.lang.String getGroupUuid();
     method public java.lang.String getIccId();
     method public int getIconTint();
     method public deprecated int getMcc();
@@ -42818,7 +42819,6 @@
     method public deprecated int getMnc();
     method public java.lang.String getMncString();
     method public java.lang.String getNumber();
-    method public int getParentSubId();
     method public int getSimSlotIndex();
     method public int getSubscriptionId();
     method public boolean isEmbedded();
@@ -42852,6 +42852,7 @@
     method public static boolean isValidSubscriptionId(int);
     method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
     method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
+    method public java.lang.String setSubscriptionGroup(int[]);
     method public void setSubscriptionOverrideCongested(int, boolean, long);
     method public void setSubscriptionOverrideUnmetered(int, boolean, long);
     method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
@@ -43458,7 +43459,7 @@
   public class GroupCall implements java.lang.AutoCloseable {
     method public void close();
     method public long getTmgi();
-    method public void updateGroupCall(int[], int[]);
+    method public void updateGroupCall(java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
     field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
     field public static final int REASON_FREQUENCY_CONFLICT = 3; // 0x3
     field public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; // 0x6
@@ -43470,11 +43471,10 @@
     field public static final int STATE_STOPPED = 1; // 0x1
   }
 
-  public class GroupCallCallback {
-    ctor public GroupCallCallback();
-    method public void onBroadcastSignalStrengthUpdated(int);
-    method public void onError(int, java.lang.String);
-    method public void onGroupCallStateChanged(int, int);
+  public abstract interface GroupCallCallback {
+    method public abstract void onBroadcastSignalStrengthUpdated(int);
+    method public abstract void onError(int, java.lang.String);
+    method public abstract void onGroupCallStateChanged(int, int);
     field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
   }
 
@@ -43514,6 +43514,11 @@
     field public static final int ERROR_UNABLE_TO_READ_SIM = 206; // 0xce
   }
 
+  public static class MbmsErrors.GroupCallErrors {
+    field public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502; // 0x1f6
+    field public static final int ERROR_UNABLE_TO_START_SERVICE = 501; // 0x1f5
+  }
+
   public static class MbmsErrors.InitializationErrors {
     field public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102; // 0x66
     field public static final int ERROR_DUPLICATE_INITIALIZE = 101; // 0x65
@@ -43526,12 +43531,11 @@
     field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
   }
 
-  public class MbmsGroupCallSessionCallback {
-    ctor public MbmsGroupCallSessionCallback();
-    method public void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
-    method public void onError(int, java.lang.String);
-    method public void onMiddlewareReady();
-    method public void onServiceInterfaceAvailable(java.lang.String, int);
+  public abstract interface MbmsGroupCallSessionCallback {
+    method public abstract void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
+    method public abstract void onError(int, java.lang.String);
+    method public abstract void onMiddlewareReady();
+    method public abstract void onServiceInterfaceAvailable(java.lang.String, int);
   }
 
   public class MbmsStreamingSessionCallback {
diff --git a/api/system-current.txt b/api/system-current.txt
index b1f0bed..3dea80e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4462,6 +4462,16 @@
 
 }
 
+package android.service.carrier {
+
+  public abstract class ApnService extends android.app.Service {
+    ctor public ApnService();
+    method public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+}
+
 package android.service.euicc {
 
   public final class EuiccProfileInfo implements android.os.Parcelable {
@@ -6614,9 +6624,9 @@
     method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
     method public void onAppCallbackDied(int, int);
     method public android.os.IBinder onBind(android.content.Intent);
-    method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+    method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback);
     method public void stopGroupCall(int, long);
-    method public void updateGroupCall(int, long, int[], int[]);
+    method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
   }
 
   public class MbmsStreamingServiceBase extends android.os.Binder {
diff --git a/api/test-current.txt b/api/test-current.txt
index c09ff98..c86feb2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1050,9 +1050,9 @@
     method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
     method public void onAppCallbackDied(int, int);
     method public android.os.IBinder onBind(android.content.Intent);
-    method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+    method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback);
     method public void stopGroupCall(int, long);
-    method public void updateGroupCall(int, long, int[], int[]);
+    method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
   }
 
   public class MbmsStreamingServiceBase extends android.os.Binder {
diff --git a/core/java/android/service/carrier/ApnService.java b/core/java/android/service/carrier/ApnService.java
new file mode 100644
index 0000000..d53eb37
--- /dev/null
+++ b/core/java/android/service/carrier/ApnService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.carrier;
+
+import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
+import android.app.Service;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.telephony.IApnSourceService;
+
+import java.util.List;
+
+/**
+ * A service that the system can call to restore default APNs.
+ * <p>
+ * To extend this class, specify the full name of your implementation in the resource file
+ * {@code packages/providers/TelephonyProvider/res/values/config.xml} as the
+ * {@code apn_source_service}.
+ * </p>
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class ApnService extends Service {
+
+    private static final String LOG_TAG = "ApnService";
+
+    private final IApnSourceService.Stub mBinder = new IApnSourceService.Stub() {
+        /**
+         * Retreive APNs for the default slot index.
+         */
+        @Override
+        public ContentValues[] getApns(int subId) {
+            try {
+                List<ContentValues> apns = ApnService.this.onRestoreApns(subId);
+                return apns.toArray(new ContentValues[apns.size()]);
+            } catch (Exception e) {
+                Log.e(LOG_TAG, "Error in getApns for subId=" + subId + ": " + e.getMessage(), e);
+                return null;
+            }
+        }
+    };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    /**
+     * Override this method to restore default user APNs with a carrier service instead of the
+     * built in platform xml APNs list.
+     * <p>
+     * This method is called by the TelephonyProvider when the user requests restoring the default
+     * APNs. It should return a list of ContentValues representing the default APNs for the given
+     * subId.
+     */
+    @WorkerThread
+    public abstract List<ContentValues> onRestoreApns(int subId);
+}
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 717a858..21c4252 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -21,9 +21,9 @@
 import android.annotation.UnsupportedAppUsage;
 import android.os.SystemClock;
 
-import libcore.util.CountryTimeZones;
-import libcore.util.CountryTimeZones.TimeZoneMapping;
-import libcore.util.TimeZoneFinder;
+import libcore.timezone.CountryTimeZones;
+import libcore.timezone.CountryTimeZones.TimeZoneMapping;
+import libcore.timezone.TimeZoneFinder;
 import libcore.util.ZoneInfoDB;
 
 import java.io.PrintWriter;
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 955f64a..6157fec 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -33,7 +33,7 @@
 
 import com.android.settingslib.R;
 
-import libcore.util.TimeZoneFinder;
+import libcore.timezone.TimeZoneFinder;
 
 import org.xmlpull.v1.XmlPullParserException;
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 74c8023..564d35a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -894,10 +894,18 @@
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiverAsUser(
-                mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
         mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
                 new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
 
+        // Listen to package add and removal events for all users.
+        intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        mContext.registerReceiverAsUser(
+                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+
         try {
             mNMS.registerObserver(mTethering);
             mNMS.registerObserver(mDataActivityObserver);
@@ -4155,6 +4163,7 @@
     }
 
     private void onUserAdded(int userId) {
+        mPermissionMonitor.onUserAdded(userId);
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
@@ -4165,6 +4174,7 @@
     }
 
     private void onUserRemoved(int userId) {
+        mPermissionMonitor.onUserRemoved(userId);
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
@@ -4174,6 +4184,22 @@
         }
     }
 
+    private void onPackageAdded(String packageName, int uid) {
+        if (TextUtils.isEmpty(packageName) || uid < 0) {
+            Slog.wtf(TAG, "Invalid package in onPackageAdded: " + packageName + " | " + uid);
+            return;
+        }
+        mPermissionMonitor.onPackageAdded(packageName, uid);
+    }
+
+    private void onPackageRemoved(String packageName, int uid) {
+        if (TextUtils.isEmpty(packageName) || uid < 0) {
+            Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
+            return;
+        }
+        mPermissionMonitor.onPackageRemoved(uid);
+    }
+
     private void onUserUnlocked(int userId) {
         synchronized (mVpns) {
             // User present may be sent because of an unlock, which might mean an unlocked keystore.
@@ -4185,11 +4211,15 @@
         }
     }
 
-    private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+            final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+            final Uri packageData = intent.getData();
+            final String packageName =
+                    packageData != null ? packageData.getSchemeSpecificPart() : null;
             if (userId == UserHandle.USER_NULL) return;
 
             if (Intent.ACTION_USER_STARTED.equals(action)) {
@@ -4202,6 +4232,10 @@
                 onUserRemoved(userId);
             } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
                 onUserUnlocked(userId);
+            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                onPackageAdded(packageName, uid);
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                onPackageRemoved(packageName, uid);
             }
         }
     };
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 94c94a5..420b23e 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -27,10 +27,7 @@
 import static android.os.Process.SYSTEM_UID;
 
 import android.annotation.NonNull;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -42,7 +39,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -64,15 +60,14 @@
 public class PermissionMonitor {
     private static final String TAG = "PermissionMonitor";
     private static final boolean DBG = true;
-    private static final Boolean SYSTEM = Boolean.TRUE;
-    private static final Boolean NETWORK = Boolean.FALSE;
+    protected static final Boolean SYSTEM = Boolean.TRUE;
+    protected static final Boolean NETWORK = Boolean.FALSE;
     private static final int VERSION_Q = Build.VERSION_CODES.Q;
 
     private final Context mContext;
     private final PackageManager mPackageManager;
     private final UserManager mUserManager;
     private final INetworkManagementService mNetd;
-    private final BroadcastReceiver mIntentReceiver;
 
     // Values are User IDs.
     private final Set<Integer> mUsers = new HashSet<>();
@@ -85,26 +80,6 @@
         mPackageManager = context.getPackageManager();
         mUserManager = UserManager.get(context);
         mNetd = netd;
-        mIntentReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                int appUid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
-                Uri appData = intent.getData();
-                String appName = appData != null ? appData.getSchemeSpecificPart() : null;
-
-                if (Intent.ACTION_USER_ADDED.equals(action)) {
-                    onUserAdded(user);
-                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                    onUserRemoved(user);
-                } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-                    onAppAdded(appName, appUid);
-                } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                    onAppRemoved(appUid);
-                }
-            }
-        };
     }
 
     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -112,17 +87,6 @@
     public synchronized void startMonitoring() {
         log("Monitoring");
 
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_USER_ADDED);
-        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
-        mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
-        intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        intentFilter.addDataScheme("package");
-        mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
         List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
         if (apps == null) {
             loge("No apps");
@@ -260,7 +224,14 @@
         }
     }
 
-    private synchronized void onUserAdded(int user) {
+    /**
+     * Called when a user is added. See {link #ACTION_USER_ADDED}.
+     *
+     * @param user The integer userHandle of the added user. See {@link #EXTRA_USER_HANDLE}.
+     *
+     * @hide
+     */
+    public synchronized void onUserAdded(int user) {
         if (user < 0) {
             loge("Invalid user in onUserAdded: " + user);
             return;
@@ -272,7 +243,14 @@
         update(users, mApps, true);
     }
 
-    private synchronized void onUserRemoved(int user) {
+    /**
+     * Called when an user is removed. See {link #ACTION_USER_REMOVED}.
+     *
+     * @param user The integer userHandle of the removed user. See {@link #EXTRA_USER_HANDLE}.
+     *
+     * @hide
+     */
+    public synchronized void onUserRemoved(int user) {
         if (user < 0) {
             loge("Invalid user in onUserRemoved: " + user);
             return;
@@ -284,8 +262,8 @@
         update(users, mApps, false);
     }
 
-
-    private Boolean highestPermissionForUid(Boolean currentPermission, String name) {
+    @VisibleForTesting
+    protected Boolean highestPermissionForUid(Boolean currentPermission, String name) {
         if (currentPermission == SYSTEM) {
             return currentPermission;
         }
@@ -303,33 +281,39 @@
         return currentPermission;
     }
 
-    private synchronized void onAppAdded(String appName, int appUid) {
-        if (TextUtils.isEmpty(appName) || appUid < 0) {
-            loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
-            return;
-        }
-
+    /**
+     * Called when a package is added. See {link #ACTION_PACKAGE_ADDED}.
+     *
+     * @param packageName The name of the new package.
+     * @param uid The uid of the new package.
+     *
+     * @hide
+     */
+    public synchronized void onPackageAdded(String packageName, int uid) {
         // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
         // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
-        final Boolean permission = highestPermissionForUid(mApps.get(appUid), appName);
-        if (permission != mApps.get(appUid)) {
-            mApps.put(appUid, permission);
+        final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName);
+        if (permission != mApps.get(uid)) {
+            mApps.put(uid, permission);
 
             Map<Integer, Boolean> apps = new HashMap<>();
-            apps.put(appUid, permission);
+            apps.put(uid, permission);
             update(mUsers, apps, true);
         }
     }
 
-    private synchronized void onAppRemoved(int appUid) {
-        if (appUid < 0) {
-            loge("Invalid app in onAppRemoved: " + appUid);
-            return;
-        }
+    /**
+     * Called when a package is removed. See {link #ACTION_PACKAGE_REMOVED}.
+     *
+     * @param uid containing the integer uid previously assigned to the package.
+     *
+     * @hide
+     */
+    public synchronized void onPackageRemoved(int uid) {
         Map<Integer, Boolean> apps = new HashMap<>();
 
         Boolean permission = null;
-        String[] packages = mPackageManager.getPackagesForUid(appUid);
+        String[] packages = mPackageManager.getPackagesForUid(uid);
         if (packages != null && packages.length > 0) {
             for (String name : packages) {
                 permission = highestPermissionForUid(permission, name);
@@ -341,16 +325,16 @@
                 }
             }
         }
-        if (permission == mApps.get(appUid)) {
+        if (permission == mApps.get(uid)) {
             // The permissions of this UID have not changed. Nothing to do.
             return;
         } else if (permission != null) {
-            mApps.put(appUid, permission);
-            apps.put(appUid, permission);
+            mApps.put(uid, permission);
+            apps.put(uid, permission);
             update(mUsers, apps, true);
         } else {
-            mApps.remove(appUid);
-            apps.put(appUid, NETWORK);  // doesn't matter which permission we pick here
+            mApps.remove(uid);
+            apps.put(uid, NETWORK);  // doesn't matter which permission we pick here
             update(mUsers, apps, false);
         }
     }
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index c4d2851..da0a794 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -48,7 +48,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import libcore.icu.ICU;
 import libcore.timezone.TzDataSetVersion;
-import libcore.util.TimeZoneFinder;
+import libcore.timezone.TimeZoneFinder;
 import libcore.util.ZoneInfoDB;
 
 import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java
index e373797..269cda1 100644
--- a/telephony/java/android/telephony/MbmsGroupCallSession.java
+++ b/telephony/java/android/telephony/MbmsGroupCallSession.java
@@ -37,6 +37,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -107,14 +108,14 @@
      * {@link MbmsGroupCallSession} that you received before calling this method again.
      *
      * @param context The {@link Context} to use.
-     * @param executor The executor on which you wish to execute callbacks.
      * @param subscriptionId The subscription ID to use.
+     * @param executor The executor on which you wish to execute callbacks.
      * @param callback A callback object on which you wish to receive results of asynchronous
      *                 operations.
      * @return An instance of {@link MbmsGroupCallSession}, or null if an error occurred.
      */
     public static @Nullable MbmsGroupCallSession create(@NonNull Context context,
-            @NonNull Executor executor, int subscriptionId,
+            int subscriptionId, @NonNull Executor executor,
             final @NonNull MbmsGroupCallSessionCallback callback) {
         if (!sIsInitialized.compareAndSet(false, true)) {
             throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession");
@@ -138,11 +139,11 @@
 
     /**
      * Create a new {@link MbmsGroupCallSession} using the system default data subscription ID.
-     * See {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+     * See {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
      */
     public static MbmsGroupCallSession create(@NonNull Context context,
             @NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) {
-        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
+        return create(context, SubscriptionManager.getDefaultSubscriptionId(), executor, callback);
     }
 
     /**
@@ -153,7 +154,7 @@
      * instance of {@link MbmsGroupCallSessionCallback}, but callbacks that have already been
      * enqueued will still be delivered.
      *
-     * It is safe to call {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)} to
+     * It is safe to call {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)} to
      * obtain another instance of {@link MbmsGroupCallSession} immediately after this method
      * returns.
      *
@@ -189,18 +190,19 @@
      * Asynchronous errors through the callback include any of the errors in
      * {@link MbmsErrors.GeneralErrors}.
      *
-     * @param executor The executor on which you wish to execute callbacks for this stream.
      * @param tmgi The TMGI, an identifier for the group call you want to join.
-     * @param saiArray An array of SAIs for the group call that should be negotiated separately with
+     * @param saiList A list of SAIs for the group call that should be negotiated separately with
      *                the carrier.
-     * @param frequencyArray An array of frequencies for the group call that should be negotiated
+     * @param frequencyList A lost of frequencies for the group call that should be negotiated
      *                separately with the carrier.
+     * @param executor The executor on which you wish to execute callbacks for this stream.
      * @param callback The callback that you want to receive information about the call on.
      * @return An instance of {@link GroupCall} through which the call can be controlled.
      *         May be {@code null} if an error occurred.
      */
-    public @Nullable GroupCall startGroupCall(@NonNull Executor executor, long tmgi, int[] saiArray,
-            int[] frequencyArray, @NonNull GroupCallCallback callback) {
+    public @Nullable GroupCall startGroupCall(long tmgi, @NonNull List<Integer> saiList,
+            @NonNull List<Integer> frequencyList, @NonNull Executor executor,
+            @NonNull GroupCallCallback callback) {
         IMbmsGroupCallService groupCallService = mService.get();
         if (groupCallService == null) {
             throw new IllegalStateException("Middleware not yet bound");
@@ -215,7 +217,7 @@
 
         try {
             int returnCode = groupCallService.startGroupCall(
-                    mSubscriptionId, tmgi, saiArray, frequencyArray, serviceCallback);
+                    mSubscriptionId, tmgi, saiList, frequencyList, serviceCallback);
             if (returnCode == MbmsErrors.UNKNOWN) {
                 // Unbind and throw an obvious error
                 close();
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 22c1e58..b41e14e 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -143,9 +143,11 @@
     private boolean mIsOpportunistic;
 
     /**
-     * SubId of the parent subscription, if there is one.
+     * A UUID assigned to the subscription group. It returns
+     * null if not assigned.
      */
-    private int mParentSubId;
+    @Nullable
+    private String mGroupUUID;
 
     /**
      * @hide
@@ -156,7 +158,7 @@
             @Nullable UiccAccessRule[] accessRules, String cardId) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
                 roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
-                false, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+                false, null);
     }
 
     /**
@@ -166,7 +168,7 @@
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
             @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
-            int parentSubId) {
+            @Nullable String groupUUID) {
         this.mId = id;
         this.mIccId = iccId;
         this.mSimSlotIndex = simSlotIndex;
@@ -184,7 +186,7 @@
         this.mAccessRules = accessRules;
         this.mCardId = cardId;
         this.mIsOpportunistic = isOpportunistic;
-        this.mParentSubId = parentSubId;
+        this.mGroupUUID = groupUUID;
     }
 
     /**
@@ -388,16 +390,16 @@
     }
 
     /**
-     * Used in scenarios where a child subscription is bundled with a primary parent subscription.
-     * The child subscription will typically be opportunistic (see {@link #isOpportunistic()})
-     * and will be used to provide data services where available, with the parent being the primary
-     * fallback subscription.
+     * Used in scenarios where different subscriptions are bundled as a group.
+     * It's typically a primary and an opportunistic subscription. (see {@link #isOpportunistic()})
+     * Such that those subscriptions will have some affiliated behaviors such as opportunistic
+     * subscription may be invisible to the user.
      *
-     * @return subId of parent subscription if it’s bundled with a primary subscription.
-     * If there isn't one, {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
+     * @return group UUID a String of group UUID if it belongs to a group. Otherwise
+     * it will return null.
      */
-    public int getParentSubId() {
-        return mParentSubId;
+    public String getGroupUuid() {
+        return mGroupUUID;
     }
 
     /**
@@ -493,11 +495,11 @@
             UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
             String cardId = source.readString();
             boolean isOpportunistic = source.readBoolean();
-            int parentSubId = source.readInt();
+            String groupUUID = source.readString();
 
             return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
                     nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
-                    isEmbedded, accessRules, cardId, isOpportunistic, parentSubId);
+                    isEmbedded, accessRules, cardId, isOpportunistic, groupUUID);
         }
 
         @Override
@@ -525,7 +527,7 @@
         dest.writeTypedArray(mAccessRules, flags);
         dest.writeString(mCardId);
         dest.writeBoolean(mIsOpportunistic);
-        dest.writeInt(mParentSubId);
+        dest.writeString(mGroupUUID);
     }
 
     @Override
@@ -559,13 +561,13 @@
                 + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
                 + " accessRules " + Arrays.toString(mAccessRules)
                 + " cardId=" + cardIdToPrint + " isOpportunistic " + mIsOpportunistic
-                + " parentSubId=" + mParentSubId + "}";
+                + " mGroupUUID=" + mGroupUUID + "}";
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
-                mIsOpportunistic, mParentSubId, mIccId, mNumber, mMcc, mMnc, mCountryIso,
+                mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso,
                 mCardId, mDisplayName, mCarrierName, mAccessRules);
     }
 
@@ -588,7 +590,7 @@
                 && mDataRoaming == toCompare.mDataRoaming
                 && mIsEmbedded == toCompare.mIsEmbedded
                 && mIsOpportunistic == toCompare.mIsOpportunistic
-                && mParentSubId == toCompare.mParentSubId
+                && Objects.equals(mGroupUUID, toCompare.mGroupUUID)
                 && Objects.equals(mIccId, toCompare.mIccId)
                 && Objects.equals(mNumber, toCompare.mNumber)
                 && Objects.equals(mMcc, toCompare.mMcc)
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 3e0a273..876633c 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -43,6 +43,7 @@
 import android.net.INetworkPolicyManager;
 import android.net.NetworkCapabilities;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
@@ -118,7 +119,6 @@
     @UnsupportedAppUsage
     public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo");
 
-
     /**
      * Generates a content {@link Uri} used to receive updates on simInfo change
      * on the given subscriptionId
@@ -576,6 +576,15 @@
     public static final String PARENT_SUB_ID = "parent_sub_id";
 
     /**
+     * TelephonyProvider column name for group ID. Subscriptions with same group ID
+     * are considered bundled together, and should behave as a single subscription at
+     * certain scenarios.
+     *
+     * @hide
+     */
+    public static final String GROUP_UUID = "group_uuid";
+
+    /**
      * Broadcast Action: The user has changed one of the default subs related to
      * data, phone calls, or sms</p>
      *
@@ -850,8 +859,13 @@
         IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
             @Override
             public void onSubscriptionsChanged() {
-                if (DBG) log("onOpportunisticSubscriptionsChanged callback received.");
-                mExecutor.execute(() -> onOpportunisticSubscriptionsChanged());
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    if (DBG) log("onOpportunisticSubscriptionsChanged callback received.");
+                    mExecutor.execute(() -> onOpportunisticSubscriptionsChanged());
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
             }
         };
 
@@ -2355,19 +2369,40 @@
     }
 
     /**
-     * Set parent subId by simInfo index
+     * Inform SubscriptionManager that subscriptions in the list are bundled
+     * as a group. Typically it's a primary subscription and an opportunistic
+     * subscription. It should only affect multi-SIM scenarios where primary
+     * and opportunistic subscriptions can be activated together.
+     * Being in the same group means they might be activated or deactivated
+     * together, some of them may be invisible to the users, etc.
      *
-     * @param parentSubId subId of its parent subscription.
-     * @param subId the unique SubscriptionInfo index in database
-     * @return the number of records updated
-     * @hide
+     * Caller will either have {@link android.Manifest.permission.MODIFY_PHONE_STATE}
+     * permission or can manage all subscriptions in the list, according to their
+     * acess rules.
+     *
+     * @param subIdList list of subId that will be in the same group
+     * @return groupUUID a UUID assigned to the subscription group. It returns
+     * null if fails.
      *
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public int setParentSubId(int parentSubId, int subId) {
-        if (VDBG) logd("[setParentSubId]+ parentSubId:" + parentSubId + " subId:" + subId);
-        return setSubscriptionPropertyHelper(subId, "parentSubId",
-                (iSub)-> iSub.setParentSubId(parentSubId, subId));
+    public String setSubscriptionGroup(int[] subIdList) {
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        if (VDBG) {
+            logd("[setSubscriptionGroup]+ subIdList:" + Arrays.toString(subIdList));
+        }
+
+        String groupUUID = null;
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            if (iSub != null) {
+                groupUUID = iSub.setSubscriptionGroup(subIdList, pkgForDebug);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return groupUUID;
     }
 
     private interface CallISubMethodHelper {
diff --git a/telephony/java/android/telephony/mbms/GroupCall.java b/telephony/java/android/telephony/mbms/GroupCall.java
index 9aca18e..25e274e 100644
--- a/telephony/java/android/telephony/mbms/GroupCall.java
+++ b/telephony/java/android/telephony/mbms/GroupCall.java
@@ -17,6 +17,7 @@
 package android.telephony.mbms;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.os.RemoteException;
 import android.telephony.MbmsGroupCallSession;
 import android.telephony.mbms.vendor.IMbmsGroupCallService;
@@ -24,6 +25,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 
 /**
  * Class used to represent a single MBMS group call. After a call has been started with
@@ -41,8 +43,26 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
     public @interface GroupCallState {}
+
+    /**
+     * Indicates that the group call is in a stopped state
+     *
+     * This can be reported after network action or after calling {@link #close}.
+     */
     public static final int STATE_STOPPED = 1;
+
+    /**
+     * Indicates that the group call is started.
+     *
+     * Data can be transmitted and received in this state.
+     */
     public static final int STATE_STARTED = 2;
+
+    /**
+     * Indicates that the group call is stalled.
+     *
+     * This may be due to a network issue or the device being temporarily out of range.
+     */
     public static final int STATE_STALLED = 3;
 
     /**
@@ -122,16 +142,17 @@
      * Send an update to the middleware when the SAI (Service Area Identifier) list and frequency
      * information of the group call has * changed. Callers must obtain this information from the
      * wireless carrier independently.
-     * @param saiArray New array of SAIs that the call is available on.
-     * @param frequencyArray New array of frequencies that the call is available on.
+     * @param saiList New list of SAIs that the call is available on.
+     * @param frequencyList New list of frequencies that the call is available on.
      */
-    public void updateGroupCall(int[] saiArray, int[] frequencyArray) {
+    public void updateGroupCall(@NonNull List<Integer> saiList,
+            @NonNull List<Integer> frequencyList) {
         if (mService == null) {
             throw new IllegalStateException("No group call service attached");
         }
 
         try {
-            mService.updateGroupCall(mSubscriptionId, mTmgi, saiArray, frequencyArray);
+            mService.updateGroupCall(mSubscriptionId, mTmgi, saiList, frequencyList);
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService = null;
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
index 001bb02..77e36bb 100644
--- a/telephony/java/android/telephony/mbms/GroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.mbms;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.Nullable;
 
 import java.lang.annotation.Retention;
@@ -26,7 +27,7 @@
  * A callback class for use when the application is in a group call. The middleware
  * will provide updates on the status of the call via this callback.
  */
-public class GroupCallCallback {
+public interface GroupCallCallback {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {
@@ -40,7 +41,7 @@
             MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
             MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
             MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
-    private @interface GroupCallError{}
+    @interface GroupCallError{}
 
     /**
      * Indicates broadcast signal strength is not available for this call.
@@ -48,7 +49,7 @@
      * This may be due to the call no longer being available due to geography
      * or timing (end of service)
      */
-    public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
+    int SIGNAL_STRENGTH_UNAVAILABLE = -1;
 
     /**
      * Called by the middleware when it has detected an error condition in this group call. The
@@ -56,9 +57,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(@GroupCallError int errorCode, @Nullable String message) {
-        // default implementation empty
-    }
+    void onError(@GroupCallError int errorCode, @Nullable String message);
 
     /**
      * Called to indicate this call has changed state.
@@ -66,10 +65,8 @@
      * See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
      * and {@link GroupCall#STATE_STALLED}.
      */
-    public void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
-            @GroupCall.GroupCallStateChangeReason int reason) {
-        // default implementation empty
-    }
+    void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+            @GroupCall.GroupCallStateChangeReason int reason);
 
     /**
      * Broadcast Signal Strength updated.
@@ -81,7 +78,5 @@
      * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
      * for this call due to timing, geography or popularity.
      */
-    public void onBroadcastSignalStrengthUpdated(int signalStrength) {
-        // default implementation empty
-    }
+    void onBroadcastSignalStrengthUpdated(@IntRange(from = -1, to = 4) int signalStrength);
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index 7c4321b..52e4d33 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -140,5 +140,21 @@
         public static final int ERROR_UNKNOWN_FILE_INFO = 403;
     }
 
+    /**
+     * Indicates the errors that are applicable only to the group call use-case.
+     */
+    public static class GroupCallErrors {
+        private GroupCallErrors() { }
+        /** Indicates that the middleware was unable to start the group call. */
+        public static final int ERROR_UNABLE_TO_START_SERVICE = 501;
+
+        /**
+         * Indicates that the app called
+         * {@link android.telephony.MbmsGroupCallSession#startGroupCall} more than once for the
+         * same {@code tmgi}.
+         */
+        public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502;
+    }
+
     private MbmsErrors() {}
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
index 7da734e..04e7ba1 100644
--- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.mbms;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.telephony.MbmsGroupCallSession;
@@ -29,9 +30,9 @@
 /**
  * A callback class that is used to receive information from the middleware on MBMS group-call
  * services. An instance of this object should be passed into
- * {@link MbmsGroupCallSession#create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ * {@link MbmsGroupCallSession#create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
  */
-public class MbmsGroupCallSessionCallback {
+public interface MbmsGroupCallSessionCallback {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {
@@ -48,7 +49,7 @@
             MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
             MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
             MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
-    private @interface GroupCallError{}
+    @interface GroupCallError{}
 
     /**
      * Called by the middleware when it has detected an error condition. The possible error codes
@@ -56,8 +57,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(@GroupCallError int errorCode, @Nullable String message) {
-    }
+    void onError(@GroupCallError int errorCode, @Nullable String message);
 
     /**
      * Indicates that the list of currently available SAIs has been updated. The app may use this
@@ -70,21 +70,22 @@
      * @param availableSais A list of lists of available SAIS in neighboring cells, where each list
      *                      contains the available SAIs in an individual cell.
      */
-    public void onAvailableSaisUpdated(List<Integer> currentSais,
-            List<List<Integer>> availableSais) {
-    }
+    void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
+            @NonNull List<List<Integer>> availableSais);
 
     /**
      * Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
-     * via this callback may be used to establish a data-link interface with the modem before the
-     * middleware is ready.
-     * Note that this method may be called before {@link #onMiddlewareReady()}.
+     * via this callback may be used to establish a data-link interface with the modem.
+     *
+     * In order to establish the data-link interface, the multicast IP and port must be obtained
+     * out-of-band from the carrier. A {@link java.net.MulticastSocket} may then be constructed
+     * using a {@link java.net.NetworkInterface} with the name and interface supplied by this
+     * callback.
      *
      * @param interfaceName The interface name for the data link.
      * @param index The index for the data link.
      */
-    public void onServiceInterfaceAvailable(String interfaceName, int index) {
-    }
+    void onServiceInterfaceAvailable(@NonNull String interfaceName, int index);
 
     /**
      * Called to indicate that the middleware has been initialized and is ready.
@@ -94,6 +95,5 @@
      * delivered via {@link #onError(int, String)} with error code
      * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
      */
-    public void onMiddlewareReady() {
-    }
+    void onMiddlewareReady();
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
index 721256a..44cc24a 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
@@ -29,11 +29,11 @@
 
     void stopGroupCall(int subId, long tmgi);
 
-    void updateGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
-        in int[] frequencyArray);
+    void updateGroupCall(int subscriptionId, long tmgi, in List saiList,
+        in List frequencyList);
 
-    int startGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
-        in int[] frequencyArray, IGroupCallCallback callback);
+    int startGroupCall(int subscriptionId, long tmgi, in List saiList,
+        in List frequencyList, IGroupCallCallback callback);
 
     void dispose(int subId);
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
index 3734ca7..e86a47d 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
@@ -115,15 +115,16 @@
         }
 
         @Override
-        public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
-                int[] frequencyArray) {
+        public void updateGroupCall(int subscriptionId, long tmgi, List saiList,
+                List frequencyList) {
             MbmsGroupCallServiceBase.this.updateGroupCall(
-                    subscriptionId, tmgi, saiArray, frequencyArray);
+                    subscriptionId, tmgi, saiList, frequencyList);
         }
 
         @Override
-        public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray,
-                final int[] frequencyArray, final IGroupCallCallback callback)
+        public int startGroupCall(final int subscriptionId, final long tmgi,
+                final List saiList,
+                final List frequencyList, final IGroupCallCallback callback)
                 throws RemoteException {
             if (callback == null) {
                 throw new NullPointerException("Callback must not be null");
@@ -132,7 +133,7 @@
             final int uid = Binder.getCallingUid();
 
             int result = MbmsGroupCallServiceBase.this.startGroupCall(
-                    subscriptionId, tmgi, saiArray, frequencyArray, new GroupCallCallback() {
+                    subscriptionId, tmgi, saiList, frequencyList, new GroupCallCallback() {
                         @Override
                         public void onError(final int errorCode, final String message) {
                             try {
@@ -209,13 +210,13 @@
      *
      * @param subscriptionId The subscription id to use.
      * @param tmgi The TMGI, an identifier for the group call.
-     * @param saiArray An array of SAIs for the group call.
-     * @param frequencyArray An array of frequencies for the group call.
+     * @param saiList A list of SAIs for the group call.
+     * @param frequencyList A list of frequencies for the group call.
      * @param callback The callback object on which the app wishes to receive updates.
      * @return Any error in {@link MbmsErrors.GeneralErrors}
      */
-    public int startGroupCall(int subscriptionId, long tmgi, int[] saiArray, int[] frequencyArray,
-            GroupCallCallback callback) {
+    public int startGroupCall(int subscriptionId, long tmgi, List<Integer> saiList,
+            List<Integer> frequencyList, GroupCallCallback callback) {
         throw new UnsupportedOperationException("Not implemented");
     }
 
@@ -237,11 +238,11 @@
     /**
      * Called when the app receives new SAI and frequency information for the group call identified
      * by {@code tmgi}.
-     * @param saiArray New array of SAIs that the call is available on.
-     * @param frequencyArray New array of frequencies that the call is available on.
+     * @param saiList New list of SAIs that the call is available on.
+     * @param frequencyList New list of frequencies that the call is available on.
      */
-    public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
-            int[] frequencyArray) {
+    public void updateGroupCall(int subscriptionId, long tmgi, List<Integer> saiList,
+            List<Integer> frequencyList) {
         throw new UnsupportedOperationException("Not implemented");
     }
 
diff --git a/telephony/java/com/android/internal/telephony/IApnSourceService.aidl b/telephony/java/com/android/internal/telephony/IApnSourceService.aidl
index 07bb18b..34c9067 100644
--- a/telephony/java/com/android/internal/telephony/IApnSourceService.aidl
+++ b/telephony/java/com/android/internal/telephony/IApnSourceService.aidl
@@ -20,5 +20,5 @@
 
 interface IApnSourceService {
     /** Retreive APNs. */
-    ContentValues[] getApns();
+    ContentValues[] getApns(int subId);
 }
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 85b4941..3aa7cbc 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -165,13 +165,23 @@
     int setOpportunistic(boolean opportunistic, int subId);
 
     /**
-     * Set parent subId by simInfo index
+     * Inform SubscriptionManager that subscriptions in the list are bundled
+     * as a group. Typically it's a primary subscription and an opportunistic
+     * subscription. It should only affect multi-SIM scenarios where primary
+     * and opportunistic subscriptions can be activated together.
+     * Being in the same group means they might be activated or deactivated
+     * together, some of them may be invisible to the users, etc.
      *
-     * @param parentSubId: subId of its parent subscription.
-     * @param subId the unique SubscriptionInfo index in database
-     * @return the number of records updated
+     * Caller will either have {@link android.Manifest.permission.MODIFY_PHONE_STATE}
+     * permission or can manage all subscriptions in the list, according to their
+     * acess rules.
+     *
+     * @param subIdList list of subId that will be in the same group
+     * @return groupUUID a UUID assigned to the subscription group. It returns
+     * null if fails.
+     *
      */
-    int setParentSubId(int parentSubId, int subId);
+    String setSubscriptionGroup(in int[] subIdList, String callingPackage);
 
     /**
      * Set which subscription is preferred for cellular data. It's
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index af7123b..f2bd770 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -27,9 +27,17 @@
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.os.Process.SYSTEM_UID;
 
+import static com.android.server.connectivity.PermissionMonitor.NETWORK;
+import static com.android.server.connectivity.PermissionMonitor.SYSTEM;
+
+import static junit.framework.Assert.fail;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.spy;
@@ -40,6 +48,8 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.Build;
+import android.os.INetworkManagementService;
+import android.os.UserHandle;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -48,12 +58,19 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.HashMap;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PermissionMonitorTest {
-    private static final int MOCK_UID = 10001;
-    private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" };
+    private static final int MOCK_USER1 = 0;
+    private static final int MOCK_USER2 = 1;
+    private static final int MOCK_UID1 = 10001;
+    private static final String MOCK_PACKAGE1 = "appName1";
+    private static final String SYSTEM_PACKAGE1 = "sysName1";
+    private static final String SYSTEM_PACKAGE2 = "sysName2";
     private static final String PARTITION_SYSTEM = "system";
     private static final String PARTITION_OEM = "oem";
     private static final String PARTITION_PRODUCT = "product";
@@ -63,6 +80,7 @@
 
     @Mock private Context mContext;
     @Mock private PackageManager mPackageManager;
+    @Mock private INetworkManagementService mNMS;
 
     private PermissionMonitor mPermissionMonitor;
 
@@ -70,8 +88,7 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(MOCK_PACKAGE_NAMES);
-        mPermissionMonitor = spy(new PermissionMonitor(mContext, null));
+        mPermissionMonitor = spy(new PermissionMonitor(mContext, mNMS));
     }
 
     private boolean hasBgPermission(String partition, int targetSdkVersion, int uid,
@@ -80,7 +97,8 @@
         packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion;
         packageInfo.applicationInfo.uid = uid;
         when(mPackageManager.getPackageInfoAsUser(
-                eq(MOCK_PACKAGE_NAMES[0]), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+                eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[] {MOCK_PACKAGE1});
         return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid);
     }
 
@@ -143,16 +161,16 @@
 
     @Test
     public void testHasUseBackgroundNetworksPermission() throws Exception {
-        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID));
-        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
-        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, NETWORK_STACK));
-        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
-        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID,
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS));
-        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
 
-        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID));
-        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
     }
 
     @Test
@@ -172,15 +190,150 @@
 
     @Test
     public void testHasUseBackgroundNetworksPermissionVendorApp() throws Exception {
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, NETWORK_STACK));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID,
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
 
-        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID));
-        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
+    }
+
+    private class NMSMonitor {
+        private final HashMap<Integer, Boolean> mApps = new HashMap<>();
+
+        NMSMonitor(INetworkManagementService mockNMS) throws Exception {
+            // Add hook to verify and track result of setPermission.
+            doAnswer((InvocationOnMock invocation) -> {
+                final Object[] args = invocation.getArguments();
+                final Boolean isSystem = args[0].equals("SYSTEM");
+                for (final int uid : (int[]) args[1]) {
+                    // TODO: Currently, permission monitor will send duplicate commands for each uid
+                    // corresponding to each user. Need to fix that and uncomment below test.
+                    // if (mApps.containsKey(uid) && mApps.get(uid) == isSystem) {
+                    //     fail("uid " + uid + " is already set to " + isSystem);
+                    // }
+                    mApps.put(uid, isSystem);
+                }
+                return null;
+            }).when(mockNMS).setPermission(anyString(), any(int[].class));
+
+            // Add hook to verify and track result of clearPermission.
+            doAnswer((InvocationOnMock invocation) -> {
+                final Object[] args = invocation.getArguments();
+                for (final int uid : (int[]) args[0]) {
+                    // TODO: Currently, permission monitor will send duplicate commands for each uid
+                    // corresponding to each user. Need to fix that and uncomment below test.
+                    // if (!mApps.containsKey(uid)) {
+                    //     fail("uid " + uid + " does not exist.");
+                    // }
+                    mApps.remove(uid);
+                }
+                return null;
+            }).when(mockNMS).clearPermission(any(int[].class));
+        }
+
+        public void expectPermission(Boolean permission, int[] users, int[] apps) {
+            for (final int user : users) {
+                for (final int app : apps) {
+                    final int uid = UserHandle.getUid(user, app);
+                    if (!mApps.containsKey(uid)) {
+                        fail("uid " + uid + " does not exist.");
+                    }
+                    if (mApps.get(uid) != permission) {
+                        fail("uid " + uid + " has wrong permission: " +  permission);
+                    }
+                }
+            }
+        }
+
+        public void expectNoPermission(int[] users, int[] apps) {
+            for (final int user : users) {
+                for (final int app : apps) {
+                    final int uid = UserHandle.getUid(user, app);
+                    if (mApps.containsKey(uid)) {
+                        fail("uid " + uid + " has listed permissions, expected none.");
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testUserAndPackageAddRemove() throws Exception {
+        final NMSMonitor mNMSMonitor = new NMSMonitor(mNMS);
+
+        // MOCK_UID1: MOCK_PACKAGE1 only has network permission.
+        // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission.
+        // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission.
+        doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString());
+        doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(),
+                eq(SYSTEM_PACKAGE1));
+        doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+                eq(SYSTEM_PACKAGE2));
+        doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+                eq(MOCK_PACKAGE1));
+
+        // Add SYSTEM_PACKAGE2, expect only have network permission.
+        mPermissionMonitor.onUserAdded(MOCK_USER1);
+        addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID);
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+        // Add SYSTEM_PACKAGE1, expect permission escalate.
+        addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID);
+        mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+        mPermissionMonitor.onUserAdded(MOCK_USER2);
+        mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID});
+
+        addPackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
+        mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID});
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{MOCK_UID1});
+
+        // Remove MOCK_UID1, expect no permission left for all user.
+        mPermissionMonitor.onPackageRemoved(MOCK_UID1);
+        removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1);
+        mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1});
+
+        // Remove SYSTEM_PACKAGE1, expect permission downgrade.
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2});
+        removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID);
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID});
+
+        mPermissionMonitor.onUserRemoved(MOCK_USER1);
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER2}, new int[]{SYSTEM_UID});
+
+        // Remove all packages, expect no permission left.
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{});
+        removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_UID);
+        mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID, MOCK_UID1});
+
+        // Remove last user, expect no redundant clearPermission is invoked.
+        mPermissionMonitor.onUserRemoved(MOCK_USER2);
+        mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID, MOCK_UID1});
+    }
+
+    // Normal package add/remove operations will trigger multiple intent for uids corresponding to
+    // each user. To simulate generic package operations, the onPackageAdded/Removed will need to be
+    // called multiple times with the uid corresponding to each user.
+    private void addPackageForUsers(int[] users, String packageName, int uid) {
+        for (final int user : users) {
+            mPermissionMonitor.onPackageAdded(packageName, UserHandle.getUid(user, uid));
+        }
+    }
+
+    private void removePackageForUsers(int[] users, int uid) {
+        for (final int user : users) {
+            mPermissionMonitor.onPackageRemoved(UserHandle.getUid(user, uid));
+        }
     }
 }