aboutsummaryrefslogtreecommitdiff
path: root/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java')
-rw-r--r--src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java474
1 files changed, 363 insertions, 111 deletions
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index ba69d8a7c0..a8d05a334b 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -22,6 +22,7 @@ import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
@@ -44,9 +45,11 @@ import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.TelephonyServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Telephony.SimInfo;
import android.service.carrier.CarrierIdentifier;
@@ -95,6 +98,8 @@ import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.data.PhoneSwitcher;
import com.android.internal.telephony.euicc.EuiccController;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.subscription.SubscriptionDatabaseManager.SubscriptionDatabaseManagerCallback;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IccUtils;
@@ -122,6 +127,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
+import java.util.stream.Stream;
/**
* The subscription manager service is the backend service of {@link SubscriptionManager}.
@@ -129,6 +135,8 @@ import java.util.stream.IntStream;
*/
public class SubscriptionManagerService extends ISub.Stub {
private static final String LOG_TAG = "SMSVC";
+ private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
+ private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem";
/** Whether enabling verbose debugging message or not. */
private static final boolean VDBG = false;
@@ -173,7 +181,9 @@ public class SubscriptionManagerService extends ISub.Stub {
SimInfo.COLUMN_VOIMS_OPT_IN_STATUS,
SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS,
SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
- SimInfo.COLUMN_SATELLITE_ENABLED
+ SimInfo.COLUMN_SATELLITE_ENABLED,
+ SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
+ SimInfo.COLUMN_IS_NTN
);
/**
@@ -185,6 +195,18 @@ public class SubscriptionManagerService extends ISub.Stub {
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID = 213902861L;
+ /**
+ * Apps targeting on Android V and beyond can only see subscriptions accessible by them
+ * according to its user Id.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static final long FILTER_ACCESSIBLE_SUBS_BY_USER = 296076674L;
+
+ /** Wrap Binder methods for testing. */
+ @NonNull
+ private static final BinderWrapper BINDER_WRAPPER = new BinderWrapper();
+
/** Instance of subscription manager service. */
@NonNull
private static SubscriptionManagerService sInstance;
@@ -193,6 +215,10 @@ public class SubscriptionManagerService extends ISub.Stub {
@NonNull
private final Context mContext;
+ /** Feature flags */
+ @NonNull
+ private final FeatureFlags mFeatureFlags;
+
/** App Ops manager instance. */
@NonNull
private final AppOpsManager mAppOpsManager;
@@ -277,6 +303,12 @@ public class SubscriptionManagerService extends ISub.Stub {
private final int[] mSimState;
/**
+ * {@code true} if a user profile can only see the SIMs associated with it, unless it possesses
+ * no SIMs on the device.
+ */
+ private Map<Integer, List<Integer>> mUserIdToAvailableSubs = new ConcurrentHashMap<>();
+
+ /**
* Slot index/subscription map that automatically invalidate cache in
* {@link SubscriptionManager}.
*
@@ -349,6 +381,14 @@ public class SubscriptionManagerService extends ISub.Stub {
}
}
+ /** Binder Wrapper for test mocking. */
+ @VisibleForTesting
+ public static class BinderWrapper {
+ @NonNull public UserHandle getCallingUserHandle() {
+ return Binder.getCallingUserHandle();
+ }
+ }
+
/**
* This is the callback used for listening events from {@link SubscriptionManagerService}.
*/
@@ -405,10 +445,12 @@ public class SubscriptionManagerService extends ISub.Stub {
* @param context The context
* @param looper The looper for the handler.
*/
- public SubscriptionManagerService(@NonNull Context context, @NonNull Looper looper) {
+ public SubscriptionManagerService(@NonNull Context context, @NonNull Looper looper,
+ @NonNull FeatureFlags featureFlags) {
logl("Created SubscriptionManagerService");
sInstance = this;
mContext = context;
+ mFeatureFlags = featureFlags;
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
mEuiccManager = context.getSystemService(EuiccManager.class);
@@ -480,7 +522,8 @@ public class SubscriptionManagerService extends ISub.Stub {
HandlerThread handlerThread = new HandlerThread(LOG_TAG);
handlerThread.start();
mSubscriptionDatabaseManager = new SubscriptionDatabaseManager(context,
- handlerThread.getLooper(), new SubscriptionDatabaseManagerCallback(mHandler::post) {
+ handlerThread.getLooper(), mFeatureFlags,
+ new SubscriptionDatabaseManagerCallback(mHandler::post) {
/**
* Called when database has been loaded into the cache.
*/
@@ -500,6 +543,8 @@ public class SubscriptionManagerService extends ISub.Stub {
*/
@Override
public void onSubscriptionChanged(int subId) {
+ updateUserIdToAvailableSubs();
+
mSubscriptionManagerServiceCallbacks.forEach(
callback -> callback.invokeFromExecutor(
() -> callback.onSubscriptionChanged(subId)));
@@ -818,6 +863,26 @@ public class SubscriptionManagerService extends ISub.Stub {
}
/**
+ * Set whether the subscription ID supports oem satellite or not.
+ *
+ * @param subId The subscription ID.
+ * @param isNtn {@code true} Requested subscription ID supports oem satellite service,
+ * {@code false} otherwise.
+ */
+ public void setNtn(int subId, boolean isNtn) {
+ if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+ return;
+ }
+
+ // This can throw IllegalArgumentException if the subscription does not exist.
+ try {
+ mSubscriptionDatabaseManager.setNtn(subId, (isNtn ? 1 : 0));
+ } catch (IllegalArgumentException e) {
+ loge("setOnlyNonTerrestrialNetwork: invalid subId=" + subId);
+ }
+ }
+
+ /**
* Set ISO country code by subscription id.
*
* @param iso ISO country code associated with the subscription.
@@ -1096,7 +1161,16 @@ public class SubscriptionManagerService extends ISub.Stub {
builder.setDisplayName(nickName);
builder.setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER);
}
- builder.setProfileClass(embeddedProfile.getProfileClass());
+
+ if (android.os.Build.isDebuggable() &&
+ SystemProperties.getInt("telephony.test.bootstrap_cid", -2)
+ == carrierId) {
+ // Force set as provisioning profile for test purpose
+ log("Hardcording as bootstrap subscription for cid=" + carrierId);
+ builder.setProfileClass(SimInfo.PROFILE_CLASS_PROVISIONING);
+ } else {
+ builder.setProfileClass(embeddedProfile.getProfileClass());
+ }
builder.setPortIndex(getPortIndex(embeddedProfile.getIccid()));
CarrierIdentifier cid = embeddedProfile.getCarrierIdentifier();
@@ -1112,6 +1186,12 @@ public class SubscriptionManagerService extends ISub.Stub {
String mnc = cid.getMnc();
builder.setMcc(mcc);
builder.setMnc(mnc);
+ if (mFeatureFlags.oemEnabledSatelliteFlag()) {
+ builder.setOnlyNonTerrestrialNetwork(
+ isSatellitePlmn(mcc + mnc) ? 1 : 0);
+ } else {
+ log("updateEmbeddedSubscriptions: oemEnabledSatelliteFlag is disabled");
+ }
}
// If cardId = unsupported or un-initialized, we have no reason to update DB.
// Additionally, if the device does not support cardId for default eUICC, the
@@ -1383,6 +1463,7 @@ public class SubscriptionManagerService extends ISub.Stub {
MccTable.updateMccMncConfiguration(mContext, mccMnc);
}
setMccMnc(subId, mccMnc);
+ setNtn(subId, isSatellitePlmn(mccMnc));
} else {
loge("updateSubscription: mcc/mnc is empty");
}
@@ -1425,6 +1506,12 @@ public class SubscriptionManagerService extends ISub.Stub {
loge("updateSubscription: ICC card is not available.");
}
+ if (Flags.clearCachedImsPhoneNumberWhenDeviceLostImsRegistration()) {
+ // Clear the cached Ims phone number
+ // before proceeding with Ims Registration
+ setNumberFromIms(subId, new String(""));
+ }
+
// Attempt to restore SIM specific settings when SIM is loaded.
Bundle result = mContext.getContentResolver().call(
SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
@@ -1616,7 +1703,8 @@ public class SubscriptionManagerService extends ISub.Stub {
}
/**
- * Get all subscription info records from SIMs that are inserted now or previously inserted.
+ * Get all subscription info records from SIMs visible to the calling user that are inserted now
+ * or previously inserted.
*
* <p>
* If the caller does not have {@link Manifest.permission#READ_PHONE_NUMBERS} permission,
@@ -1656,8 +1744,7 @@ public class SubscriptionManagerService extends ISub.Stub {
throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or "
+ "carrier privilege");
}
-
- return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ return getSubscriptionInfoStreamAsUser(BINDER_WRAPPER.getCallingUserHandle())
// callers have READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE can get a full
// list. Carrier apps can only get the subscriptions they have privileged.
.filter(subInfo -> TelephonyPermissions.checkCallingOrSelfReadPhoneStateNoThrow(
@@ -1786,12 +1873,14 @@ public class SubscriptionManagerService extends ISub.Stub {
}
/**
- * Get the SubscriptionInfo(s) of the active subscriptions. The records will be sorted
- * by {@link SubscriptionInfo#getSimSlotIndex} then by
+ * Get the SubscriptionInfo(s) of the active subscriptions for calling user. The records will be
+ * sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
* {@link SubscriptionInfo#getSubscriptionId}.
*
* @param callingPackage The package making the call.
* @param callingFeatureId The feature in the package.
+ * @param isForAllProfiles whether the caller intends to see all subscriptions regardless
+ * association.
*
* @return Sorted list of the currently {@link SubscriptionInfo} records available on the
* device.
@@ -1804,13 +1893,13 @@ public class SubscriptionManagerService extends ISub.Stub {
"carrier privileges",
})
public List<SubscriptionInfo> getActiveSubscriptionInfoList(@NonNull String callingPackage,
- @Nullable String callingFeatureId) {
+ @Nullable String callingFeatureId, boolean isForAllProfiles) {
// Check if the caller has READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or carrier
// privilege on any active subscription. The carrier app will get full subscription infos
// on the subs it has carrier privilege.
if (!TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(mContext,
Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, callingFeatureId,
- "getAllSubInfoList")) {
+ "getActiveSubscriptionInfoList")) {
// Ideally we should avoid silent failure, but since this API has already been used by
// many apps and they do not expect the security exception, we return an empty list
// here so it's consistent with pre-U behavior.
@@ -1818,14 +1907,19 @@ public class SubscriptionManagerService extends ISub.Stub {
+ "permission. Returning empty list here.");
return Collections.emptyList();
}
-
- return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ if (isForAllProfiles && !hasAcrossAllUsersPermission()) {
+ //TODO(b/308809058 to determine whether the permission enforcement is needed)
+ loge("getActiveSubscriptionInfoList: "
+ + callingPackage + " has no appropriate permission.");
+ }
+ return getSubscriptionInfoStreamAsUser(isForAllProfiles
+ ? UserHandle.ALL : BINDER_WRAPPER.getCallingUserHandle())
.filter(SubscriptionInfoInternal::isActive)
// Remove the identifier if the caller does not have sufficient permission.
// carrier apps will get full subscription info on the subscriptions associated
// to them.
.map(subInfo -> conditionallyRemoveIdentifiers(subInfo.toSubscriptionInfo(),
- callingPackage, callingFeatureId, "getAllSubInfoList"))
+ callingPackage, callingFeatureId, "getActiveSubscriptionInfoList"))
.sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex)
.thenComparing(SubscriptionInfo::getSubscriptionId))
.collect(Collectors.toList());
@@ -1836,6 +1930,8 @@ public class SubscriptionManagerService extends ISub.Stub {
*
* @param callingPackage The package making the call.
* @param callingFeatureId The feature in the package.
+ * @param isForAllProfiles whether the caller intends to see all subscriptions regardless
+ * association.
*
* @return the number of active subscriptions.
*
@@ -1848,20 +1944,27 @@ public class SubscriptionManagerService extends ISub.Stub {
"carrier privileges",
})
public int getActiveSubInfoCount(@NonNull String callingPackage,
- @Nullable String callingFeatureId) {
+ @Nullable String callingFeatureId, boolean isForAllProfiles) {
if (!TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(mContext,
Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, callingFeatureId,
"getAllSubInfoList")) {
throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or "
+ "carrier privilege");
}
-
- final long token = Binder.clearCallingIdentity();
- try {
- return getActiveSubIdList(false).length;
- } finally {
- Binder.restoreCallingIdentity(token);
+ if (isForAllProfiles && !hasAcrossAllUsersPermission()) {
+ //TODO(b/308809058 to determine whether the permission enforcement is needed)
+ loge("getActiveSubInfoCount: "
+ + callingPackage + " has no appropriate permission.");
}
+ return getActiveSubIdListAsUser(false, isForAllProfiles
+ ? UserHandle.ALL : BINDER_WRAPPER.getCallingUserHandle()).length;
+ }
+
+ /**@return {@code true} if the caller is permitted to see all subscriptions. */
+ private boolean hasAcrossAllUsersPermission() {
+ return hasPermissions(Manifest.permission.INTERACT_ACROSS_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Manifest.permission.INTERACT_ACROSS_PROFILES);
}
/**
@@ -1892,27 +1995,42 @@ public class SubscriptionManagerService extends ISub.Stub {
@Nullable String callingFeatureId) {
enforcePermissions("getAvailableSubscriptionInfoList",
Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ return getAvailableSubscriptionsInternalStream()
+ .sorted(Comparator.comparing(SubscriptionInfoInternal::getSimSlotIndex)
+ .thenComparing(SubscriptionInfoInternal::getSubscriptionId))
+ .map(SubscriptionInfoInternal::toSubscriptionInfo)
+ .collect(Collectors.toList());
- // Now that all security checks pass, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- // Available eSIM profiles are reported by EuiccManager. However for physical SIMs if
- // they are in inactive slot or programmatically disabled, they are still considered
- // available. In this case we get their iccid from slot info and include their
- // subscriptionInfos.
- List<String> iccIds = getIccIdsOfInsertedPhysicalSims();
+ }
- return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
- .filter(subInfo -> subInfo.isActive() || iccIds.contains(subInfo.getIccId())
- || (mEuiccManager != null && mEuiccManager.isEnabled()
- && subInfo.isEmbedded()))
- .map(SubscriptionInfoInternal::toSubscriptionInfo)
- .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex)
- .thenComparing(SubscriptionInfo::getSubscriptionId))
- .collect(Collectors.toList());
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ /**
+ * @return all the subscriptions visible to user on the device.
+ */
+ private Stream<SubscriptionInfoInternal> getAvailableSubscriptionsInternalStream() {
+ // Available eSIM profiles are reported by EuiccManager. However for physical SIMs if
+ // they are in inactive slot or programmatically disabled, they are still considered
+ // available. In this case we get their iccid from slot info and include their
+ // subscriptionInfos.
+ List<String> iccIds = getIccIdsOfInsertedPhysicalSims();
+
+ return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .filter(subInfo -> subInfo.isActive() || iccIds.contains(subInfo.getIccId())
+ || (mEuiccManager != null && mEuiccManager.isEnabled()
+ && subInfo.isEmbedded()));
+ }
+
+ /**
+ * Tracks for each user Id, a list of subscriptions associated with it.
+ * A profile is barred from seeing unassociated subscriptions if it has its own subscription
+ * which is available to choose from the device.
+ */
+ private void updateUserIdToAvailableSubs() {
+ mUserIdToAvailableSubs = getAvailableSubscriptionsInternalStream()
+ .collect(Collectors.groupingBy(
+ SubscriptionInfoInternal::getUserId,
+ Collectors.mapping(SubscriptionInfoInternal::getSubscriptionId,
+ Collectors.toList())));
+ log("updateUserIdToAvailableSubs: " + mUserIdToAvailableSubs);
}
/**
@@ -1947,8 +2065,7 @@ public class SubscriptionManagerService extends ISub.Stub {
// Verify that the callingPackage belongs to the calling UID
mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
-
- return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ return getSubscriptionInfoStreamAsUser(BINDER_WRAPPER.getCallingUserHandle())
.map(SubscriptionInfoInternal::toSubscriptionInfo)
.filter(subInfo -> subInfo.isEmbedded()
&& mSubscriptionManager.canManageSubscription(subInfo, callingPackage))
@@ -2739,10 +2856,39 @@ public class SubscriptionManagerService extends ISub.Stub {
/**
* @return The default subscription id.
+ * @deprecated Use {@link #getDefaultSubIdAsUser}.
*/
@Override
public int getDefaultSubId() {
- return mDefaultSubId.get();
+ return getDefaultSubIdAsUser(BINDER_WRAPPER.getCallingUserHandle().getIdentifier());
+ }
+
+ /**
+ * @param userId The given user Id to check.
+ * @return The default subscription id.
+ */
+ @Override
+ public int getDefaultSubIdAsUser(@UserIdInt int userId) {
+ return getDefaultAsUser(userId, mDefaultSubId.get());
+ }
+
+ /**
+ * Get the default subscription visible to the caller.
+ * @param userId The calling user Id.
+ * @param defaultValue Useful if the user owns more than one subscription.
+ * @return The subscription Id default to use.
+ */
+ private int getDefaultAsUser(@UserIdInt int userId, int defaultValue) {
+ if (mFeatureFlags.workProfileApiSplit()) {
+ List<SubscriptionInfoInternal> subInfos =
+ getSubscriptionInfoStreamAsUser(UserHandle.of(userId))
+ .filter(SubscriptionInfoInternal::isActive)
+ .toList();
+ if (subInfos.size() == 1) {
+ return subInfos.get(0).getSubscriptionId();
+ }
+ }
+ return defaultValue;
}
/**
@@ -2834,10 +2980,20 @@ public class SubscriptionManagerService extends ISub.Stub {
/**
* @return The default subscription id for voice.
+ * @deprecated Use {@link #getDefaultVoiceSubIdAsUser}.
*/
@Override
public int getDefaultVoiceSubId() {
- return mDefaultVoiceSubId.get();
+ return getDefaultVoiceSubIdAsUser(BINDER_WRAPPER.getCallingUserHandle().getIdentifier());
+ }
+
+ /**
+ * @param userId The calling user Id.
+ * @return The default voice subscription id.
+ */
+ @Override
+ public int getDefaultVoiceSubIdAsUser(@UserIdInt int userId) {
+ return getDefaultAsUser(userId, mDefaultVoiceSubId.get());
}
/**
@@ -2880,10 +3036,24 @@ public class SubscriptionManagerService extends ISub.Stub {
/**
* @return The default subscription id for SMS.
+ * @deprecated Use {@link #getDefaultSmsSubIdAsUser}.
*/
@Override
public int getDefaultSmsSubId() {
- return mDefaultSmsSubId.get();
+ return getDefaultSmsSubIdAsUser(BINDER_WRAPPER.getCallingUserHandle().getIdentifier());
+ }
+
+ /**
+ * Get the default sms subscription id associated with the user. When a subscription is
+ * associated with personal profile or work profile, the default sms subscription id will be
+ * always the subscription it is associated with.
+ *
+ * @param userId The given user Id to check.
+ * @return The default voice id.
+ */
+ @Override
+ public int getDefaultSmsSubIdAsUser(@UserIdInt int userId) {
+ return getDefaultAsUser(userId, mDefaultSmsSubId.get());
}
/**
@@ -2940,19 +3110,30 @@ public class SubscriptionManagerService extends ISub.Stub {
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public int[] getActiveSubIdList(boolean visibleOnly) {
enforcePermissions("getActiveSubIdList", Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ // UserHandle.ALL because this API is exposed as system API.
+ return getActiveSubIdListAsUser(visibleOnly, UserHandle.ALL);
+ }
- final long token = Binder.clearCallingIdentity();
- try {
- return mSlotIndexToSubId.values().stream()
- .filter(subId -> {
- SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
- .getSubscriptionInfoInternal(subId);
- return subInfo != null && (!visibleOnly || subInfo.isVisible()); })
- .mapToInt(x -> x)
- .toArray();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ /**
+ * Get the active subscription id list as user.
+ * Must be used before clear Binder identity.
+ *
+ * @param visibleOnly {@code true} if only includes user visible subscription's sub id.
+ * @param user If {@code null}, uses the calling user handle to judge which subscriptions are
+ * accessible to the caller.
+ * @return List of the active subscription id.
+ */
+ private int[] getActiveSubIdListAsUser(boolean visibleOnly, @NonNull final UserHandle user) {
+ return mSlotIndexToSubId.values().stream()
+ .filter(subId -> {
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subId);
+ return subInfo != null && (!visibleOnly || subInfo.isVisible())
+ && isSubscriptionAssociatedWithUserInternal(
+ subInfo, user.getIdentifier());
+ })
+ .mapToInt(x -> x)
+ .toArray();
}
/**
@@ -3590,7 +3771,6 @@ public class SubscriptionManagerService extends ISub.Stub {
}
UserHandle userHandle = UserHandle.of(subInfo.getUserId());
- logv("getSubscriptionUserHandle subId = " + subId + " userHandle = " + userHandle);
if (userHandle.getIdentifier() == UserHandle.USER_NULL) {
return null;
}
@@ -3610,40 +3790,35 @@ public class SubscriptionManagerService extends ISub.Stub {
* else {@code false} if subscription is not associated with user.
*
* @throws SecurityException if the caller doesn't have permissions required.
- *
+ * @throws IllegalArgumentException if the subscription has no records on device.
*/
@Override
public boolean isSubscriptionAssociatedWithUser(int subscriptionId,
@NonNull UserHandle userHandle) {
enforcePermissions("isSubscriptionAssociatedWithUser",
Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+ SubscriptionInfoInternal subInfoInternal = mSubscriptionDatabaseManager
+ .getSubscriptionInfoInternal(subscriptionId);
+ // Throw IAE if no record of the sub's association state.
+ if (subInfoInternal == null) {
+ throw new IllegalArgumentException(
+ "[isSubscriptionAssociatedWithUser]: Subscription doesn't exist: "
+ + subscriptionId);
+ }
+
+ if (mFeatureFlags.workProfileApiSplit()) {
+ return isSubscriptionAssociatedWithUserInternal(
+ subInfoInternal, userHandle.getIdentifier());
+ }
long token = Binder.clearCallingIdentity();
try {
- // Return true if there are no subscriptions on the device.
- List<SubscriptionInfo> subInfoList = getAllSubInfoList(
- mContext.getOpPackageName(), mContext.getAttributionTag());
- if (subInfoList == null || subInfoList.isEmpty()) {
- return true;
- }
-
- List<Integer> subIdList = subInfoList.stream().map(SubscriptionInfo::getSubscriptionId)
- .collect(Collectors.toList());
- if (!subIdList.contains(subscriptionId)) {
- // Return true as this subscription is not available on the device.
- return true;
- }
-
// Get list of subscriptions associated with this user.
List<SubscriptionInfo> associatedSubscriptionsList =
getSubscriptionInfoListAssociatedWithUser(userHandle);
- if (associatedSubscriptionsList.isEmpty()) {
- return false;
- }
-
// Return true if required subscription is present in associated subscriptions list.
for (SubscriptionInfo subInfo: associatedSubscriptionsList) {
- if (subInfo.getSubscriptionId() == subscriptionId){
+ if (subInfo.getSubscriptionId() == subscriptionId) {
return true;
}
}
@@ -3654,6 +3829,25 @@ public class SubscriptionManagerService extends ISub.Stub {
}
/**
+ * @param subInfo The subscription info to check.
+ * @param userId The caller user Id.
+ * @return {@code true} if the given user Id is allowed to access to the given subscription.
+ */
+ private boolean isSubscriptionAssociatedWithUserInternal(
+ @NonNull SubscriptionInfoInternal subInfo, @UserIdInt int userId) {
+ if (!mFeatureFlags.workProfileApiSplit()
+ || !CompatChanges.isChangeEnabled(FILTER_ACCESSIBLE_SUBS_BY_USER,
+ Binder.getCallingUid())) {
+ return true;
+ }
+ return subInfo.getUserId() == userId
+ // Can access the unassociated sub if the user doesn't have its own.
+ || (subInfo.getUserId() == UserHandle.USER_NULL
+ && mUserIdToAvailableSubs.get(userId) == null)
+ || userId == UserHandle.USER_ALL;
+ }
+
+ /**
* Get list of subscriptions associated with user.
*
* If user handle is associated with some subscriptions, return subscriptionsAssociatedWithUser
@@ -3666,50 +3860,70 @@ public class SubscriptionManagerService extends ISub.Stub {
*
*/
@Override
- public @NonNull List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser(
+ @NonNull
+ public List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser(
@NonNull UserHandle userHandle) {
enforcePermissions("getSubscriptionInfoListAssociatedWithUser",
Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+ if (mFeatureFlags.workProfileApiSplit()) {
+ return getSubscriptionInfoStreamAsUser(userHandle)
+ .map(SubscriptionInfoInternal::toSubscriptionInfo)
+ .collect(Collectors.toList());
+ }
+
long token = Binder.clearCallingIdentity();
try {
- List<SubscriptionInfo> subInfoList = getAllSubInfoList(
- mContext.getOpPackageName(), mContext.getAttributionTag());
- if (subInfoList == null || subInfoList.isEmpty()) {
+ List<SubscriptionInfoInternal> subInfoList = mSubscriptionDatabaseManager
+ .getAllSubscriptions();
+ if (subInfoList.isEmpty()) {
return new ArrayList<>();
}
List<SubscriptionInfo> subscriptionsAssociatedWithUser = new ArrayList<>();
List<SubscriptionInfo> subscriptionsWithNoAssociation = new ArrayList<>();
- for (SubscriptionInfo subInfo : subInfoList) {
- int subId = subInfo.getSubscriptionId();
- UserHandle subIdUserHandle = getSubscriptionUserHandle(subId);
- if (userHandle.equals(subIdUserHandle)) {
+ for (SubscriptionInfoInternal subInfo : subInfoList) {
+ if (subInfo.getUserId() == userHandle.getIdentifier()) {
// Store subscriptions whose user handle matches with required user handle.
- subscriptionsAssociatedWithUser.add(subInfo);
- } else if (subIdUserHandle == null) {
+ subscriptionsAssociatedWithUser.add(subInfo.toSubscriptionInfo());
+ } else if (subInfo.getUserId() == UserHandle.USER_NULL) {
// Store subscriptions whose user handle is set to null.
- subscriptionsWithNoAssociation.add(subInfo);
+ subscriptionsWithNoAssociation.add(subInfo.toSubscriptionInfo());
}
}
UserManager userManager = mContext.getSystemService(UserManager.class);
if ((userManager != null)
&& (userManager.isManagedProfile(userHandle.getIdentifier()))) {
- // For work profile, return subscriptions associated only with work profile
+ // For work profile, return subscriptions associated only with work profile even
+ // if it's empty.
return subscriptionsAssociatedWithUser;
}
- // For all other profiles, if subscriptionsAssociatedWithUser is empty return all the
- // subscriptionsWithNoAssociation.
- return subscriptionsAssociatedWithUser.isEmpty() ?
- subscriptionsWithNoAssociation : subscriptionsAssociatedWithUser;
+ // For all other profiles, if subscriptionsAssociatedWithUser is empty return all
+ // the subscriptionsWithNoAssociation.
+ return subscriptionsAssociatedWithUser.isEmpty()
+ ? subscriptionsWithNoAssociation : subscriptionsAssociatedWithUser;
} finally {
Binder.restoreCallingIdentity(token);
}
}
/**
+ * Get subscriptions accessible to the caller user.
+ *
+ * @param user The user to check.
+ * @return a stream of accessible internal subscriptions.
+ */
+ @NonNull
+ private Stream<SubscriptionInfoInternal> getSubscriptionInfoStreamAsUser(
+ @NonNull final UserHandle user) {
+ return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
+ .filter(info -> isSubscriptionAssociatedWithUserInternal(
+ info, user.getIdentifier()));
+ }
+
+ /**
* Called during setup wizard restore flow to attempt to restore the backed up sim-specific
* configs to device for all existing SIMs in the subscription database {@link SimInfo}.
* Internally, it will store the backup data in an internal file. This file will persist on
@@ -3776,14 +3990,26 @@ public class SubscriptionManagerService extends ISub.Stub {
* @throws SecurityException if the caller does not have any permissions.
*/
private void enforcePermissions(@Nullable String message, @NonNull String ...permissions) {
+ if (!hasPermissions(permissions)) {
+ throw new SecurityException(
+ message + ". Does not have any of the following permissions. "
+ + Arrays.toString(permissions));
+ }
+ }
+
+ /**
+ * Check have any of the permissions
+ * @param permissions The permissions to check.
+ * @return {@code true} if the caller has one of the given permissions.
+ */
+ private boolean hasPermissions(@NonNull String ...permissions) {
for (String permission : permissions) {
if (mContext.checkCallingOrSelfPermission(permission)
== PackageManager.PERMISSION_GRANTED) {
- return;
+ return true;
}
}
- throw new SecurityException(message + ". Does not have any of the following permissions. "
- + Arrays.toString(permissions));
+ return false;
}
/**
@@ -3807,9 +4033,8 @@ public class SubscriptionManagerService extends ISub.Stub {
*/
@Nullable
public SubscriptionInfo getSubscriptionInfo(int subId) {
- SubscriptionInfoInternal subscriptionInfoInternal = getSubscriptionInfoInternal(subId);
- return subscriptionInfoInternal != null
- ? subscriptionInfoInternal.toSubscriptionInfo() : null;
+ SubscriptionInfoInternal infoInternal = getSubscriptionInfoInternal(subId);
+ return infoInternal != null ? infoInternal.toSubscriptionInfo() : null;
}
/**
@@ -3926,8 +4151,10 @@ public class SubscriptionManagerService extends ISub.Stub {
*/
@VisibleForTesting
public void updateGroupDisabled() {
- List<SubscriptionInfo> activeSubscriptions = getActiveSubscriptionInfoList(
- mContext.getOpPackageName(), mContext.getFeatureId());
+ List<SubscriptionInfoInternal> activeSubscriptions = mSubscriptionDatabaseManager
+ .getAllSubscriptions().stream()
+ .filter(SubscriptionInfoInternal::isActive)
+ .collect(Collectors.toList());
for (SubscriptionInfo oppSubInfo : getOpportunisticSubscriptions(
mContext.getOpPackageName(), mContext.getFeatureId())) {
boolean groupDisabled = activeSubscriptions.stream()
@@ -3949,6 +4176,39 @@ public class SubscriptionManagerService extends ISub.Stub {
}
/**
+ * @param mccMnc MccMnc value to check whether it supports non-terrestrial network or not.
+ * @return {@code true} if MCC/MNC is matched with in the device overlay key
+ * "config_satellite_esim_identifier", {@code false} otherwise.
+ */
+ private boolean isSatellitePlmn(@NonNull String mccMnc) {
+ if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+ log("isSatellitePlmn: oemEnabledSatelliteFlag is disabled");
+ return false;
+ }
+
+ final int id = R.string.config_satellite_sim_identifier;
+ String overlayMccMnc = null;
+ try {
+ overlayMccMnc = mContext.getResources().getString(id);
+ } catch (Resources.NotFoundException ex) {
+ loge("isSatellitePlmn: id= " + id + ", ex=" + ex);
+ }
+ if (TextUtils.isEmpty(overlayMccMnc) && isMockModemAllowed()) {
+ log("isSatellitePlmn: Read config_satellite_sim_identifier from device config");
+ overlayMccMnc = DeviceConfig.getString(DeviceConfig.NAMESPACE_TELEPHONY,
+ "config_satellite_sim_identifier", "");
+ }
+ log("isSatellitePlmn: overlayMccMnc=" + overlayMccMnc + ", mccMnc=" + mccMnc);
+ return TextUtils.equals(mccMnc, overlayMccMnc);
+ }
+
+ private boolean isMockModemAllowed() {
+ boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false);
+ return (SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false)
+ || SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false));
+ }
+
+ /**
* Log debug messages.
*
* @param s debug messages
@@ -3977,15 +4237,6 @@ public class SubscriptionManagerService extends ISub.Stub {
}
/**
- * Log verbose messages.
- *
- * @param s verbose messages
- */
- private void logv(@NonNull String s) {
- Rlog.v(LOG_TAG, s);
- }
-
- /**
* Dump the state of {@link SubscriptionManagerService}.
*
* @param fd File descriptor
@@ -4019,6 +4270,7 @@ public class SubscriptionManagerService extends ISub.Stub {
pw.println("activeDataSubId=" + getActiveDataSubscriptionId());
pw.println("defaultSmsSubId=" + getDefaultSmsSubId());
pw.println("areAllSubscriptionsLoaded=" + areAllSubscriptionsLoaded());
+ pw.println("mUserIdToAvailableSubs=" + mUserIdToAvailableSubs);
pw.println();
for (int i = 0; i < mSimState.length; i++) {
pw.println("mSimState[" + i + "]="