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));
+ }
}
}