Loading src/java/com/android/internal/telephony/SubscriptionController.java +0 −1 Original line number Diff line number Diff line Loading @@ -2712,7 +2712,6 @@ public class SubscriptionController extends ISub.Stub { /** * @return the number of records cleared */ @Override public int clearSubInfo() { enforceModifyPhoneState("clearSubInfo"); Loading src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java +28 −0 Original line number Diff line number Diff line Loading @@ -769,6 +769,34 @@ public class SubscriptionDatabaseManager extends Handler { return subId; } /** * Remove a subscription record from the database. * * @param subId The subscription id of the subscription to be deleted. * * @throws IllegalArgumentException If {@code subId} is invalid. */ public void removeSubscriptionInfo(int subId) { if (!mAllSubscriptionInfoInternalCache.containsKey(subId)) { throw new IllegalArgumentException("subId " + subId + " is invalid."); } mReadWriteLock.writeLock().lock(); try { if (mContext.getContentResolver().delete(SimInfo.CONTENT_URI, SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?", new String[]{Integer.toString(subId)}) > 0) { mAllSubscriptionInfoInternalCache.remove(subId); } else { logel("Failed to remove subscription with subId=" + subId); } } finally { mReadWriteLock.writeLock().unlock(); } mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(subId)); } /** * Update a subscription in the database (synchronously or asynchronously). * Loading src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java +159 −23 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.stream.Collectors; import java.util.stream.IntStream; /** * The subscription manager service is the backend service of {@link SubscriptionManager}. Loading Loading @@ -830,8 +831,11 @@ public class SubscriptionManagerService extends ISub.Stub { public void markSubscriptionsInactive(int simSlotIndex) { mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(subInfo -> subInfo.getSimSlotIndex() == simSlotIndex) .forEach(subInfo -> mSubscriptionDatabaseManager.setSimSlotIndex( subInfo.getSubscriptionId(), SubscriptionManager.INVALID_SIM_SLOT_INDEX)); .forEach(subInfo -> { mSubscriptionDatabaseManager.setSimSlotIndex(subInfo.getSubscriptionId(), SubscriptionManager.INVALID_SIM_SLOT_INDEX); mSlotIndexToSubId.remove(simSlotIndex); }); } /** Loading Loading @@ -1364,8 +1368,9 @@ public class SubscriptionManagerService extends ISub.Stub { * @see SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh */ @Override // TODO: Remove this after SubscriptionController is removed. public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) { updateEmbeddedSubscriptions(List.of(cardId), null); } /** Loading Loading @@ -1430,17 +1435,40 @@ public class SubscriptionManagerService extends ISub.Stub { } /** * Remove subscription info record for the given device. * Remove subscription info record from the subscription database. * * @param uniqueId This is the unique identifier for the subscription within the specific * subscription type. * @param subscriptionType the type of subscription to be removed * @param subscriptionType the type of subscription to be removed. * * @return 0 if success, < 0 on error * // TODO: Remove this terrible return value once SubscriptionController is removed. * @return 0 if success, < 0 on error. * * @throws NullPointerException if {@code uniqueId} is {@code null}. * @throws SecurityException if callers do not hold the required permission. */ @Override @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public int removeSubInfo(@NonNull String uniqueId, int subscriptionType) { enforcePermissions("removeSubInfo", Manifest.permission.MODIFY_PHONE_STATE); final long identity = Binder.clearCallingIdentity(); try { SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager .getSubscriptionInfoInternalByIccId(uniqueId); if (subInfo == null) { loge("Cannot find subscription with uniqueId " + uniqueId); return -1; } if (subInfo.getSubscriptionType() != subscriptionType) { loge("The subscription type does not match."); return -1; } mSubscriptionDatabaseManager.removeSubscriptionInfo(subInfo.getSubscriptionId()); return 0; } finally { Binder.restoreCallingIdentity(identity); } } /** Loading Loading @@ -1631,6 +1659,10 @@ public class SubscriptionManagerService extends ISub.Stub { * @throws SecurityException if callers do not hold the required permission. */ @Override @RequiresPermission(anyOf = { Manifest.permission.MODIFY_PHONE_STATE, "carrier privileges", }) public int setOpportunistic(boolean opportunistic, int subId, @NonNull String callingPackage) { // Verify that the callingPackage belongs to the calling UID mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); Loading Loading @@ -1835,11 +1867,76 @@ public class SubscriptionManagerService extends ISub.Stub { } } /** * Remove a list of subscriptions from their subscription group. * * @param subIdList list of subId that need removing from their groups. * @param groupUuid The UUID of the subscription group. * @param callingPackage The package making the call. * * @throws SecurityException if the caller doesn't meet the requirements outlined above. * @throws IllegalArgumentException if the some subscriptions in the list doesn't belong the * specified group. * * @see SubscriptionManager#createSubscriptionGroup(List) */ @Override public void removeSubscriptionsFromGroup(int[] subIdList, @NonNull ParcelUuid groupUuid, @NonNull String callingPackage) { @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull int[] subIdList, @NonNull ParcelUuid groupUuid, @NonNull String callingPackage) { // Verify that the callingPackage belongs to the calling UID mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); // If it doesn't have modify phone state permission, or carrier privilege permission, // a SecurityException will be thrown. If it's due to invalid parameter or internal state, // it will return null. if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) != PackageManager.PERMISSION_GRANTED && !(checkCarrierPrivilegeOnSubList(subIdList, callingPackage) && canPackageManageGroup(groupUuid, callingPackage))) { throw new SecurityException("removeSubscriptionsFromGroup needs MODIFY_PHONE_STATE or" + " carrier privilege permission on all specified subscriptions."); } Objects.requireNonNull(subIdList); Objects.requireNonNull(groupUuid); if (subIdList.length == 0) { throw new IllegalArgumentException("subIdList is empty."); } long identity = Binder.clearCallingIdentity(); try { for (int subId : subIdList) { SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager .getSubscriptionInfoInternal(subId); if (subInfo == null) { throw new IllegalArgumentException("The provided sub id " + subId + " is not valid."); } if (!groupUuid.toString().equals(subInfo.getGroupUuid())) { throw new IllegalArgumentException("Subscription " + subInfo.getSubscriptionId() + " doesn't belong to group " + groupUuid); } } for (SubscriptionInfoInternal subInfo : mSubscriptionDatabaseManager.getAllSubscriptions()) { if (IntStream.of(subIdList).anyMatch( subId -> subId == subInfo.getSubscriptionId())) { mSubscriptionDatabaseManager.setGroupUuid(subInfo.getSubscriptionId(), ""); mSubscriptionDatabaseManager.setGroupOwner(subInfo.getSubscriptionId(), ""); } else if (subInfo.getGroupUuid().equals(groupUuid.toString())) { // Pre-T behavior. If there are still subscriptions having the same UUID, update // to the new owner. mSubscriptionDatabaseManager.setGroupOwner( subInfo.getSubscriptionId(), callingPackage); } } } finally { Binder.restoreCallingIdentity(identity); } } /** Loading @@ -1850,17 +1947,18 @@ public class SubscriptionManagerService extends ISub.Stub { * * @param subIdList list of subId that need adding into the group * @param groupUuid the groupUuid the subscriptions are being added to. * @param callingPackage The package making the call. * * @throws SecurityException if the caller doesn't meet the requirements outlined above. * @throws IllegalArgumentException if the some subscriptions in the list doesn't exist. * * @see SubscriptionManager#createSubscriptionGroup(List) */ @Override @RequiresPermission(anyOf = { Manifest.permission.MODIFY_PHONE_STATE, "carrier privileges", }) @Override public void addSubscriptionsIntoGroup(@NonNull int[] subIdList, @NonNull ParcelUuid groupUuid, @NonNull String callingPackage) { // Verify that the callingPackage belongs to the calling UID Loading Loading @@ -1975,7 +2073,7 @@ public class SubscriptionManagerService extends ISub.Stub { * * @param subId The subscription id. * * @return Logical slot indexx (i.e. phone id) as a positive integer or * @return Logical slot index (i.e. phone id) as a positive integer or * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if the supplied {@code subId} doesn't have * an associated slot index. */ Loading Loading @@ -2058,16 +2156,22 @@ public class SubscriptionManagerService extends ISub.Stub { } } /** * @return The default subscription id. */ @Override public int getDefaultSubId() { return mDefaultSubId.get(); } @Override public int clearSubInfo() { return 0; } /** * Get phone id from the subscription id. In the implementation, the logical SIM slot index * is equivalent to phone id. So this method is same as {@link #getSlotIndex(int)}. * * @param subId The subscription id. * * @return The phone id. */ @Override public int getPhoneId(int subId) { // slot index and phone id are equivalent in the current implementation. Loading Loading @@ -2123,6 +2227,9 @@ public class SubscriptionManagerService extends ISub.Stub { } } /** * @return The default subscription id for voice. */ @Override public int getDefaultVoiceSubId() { return mDefaultVoiceSubId.get(); Loading Loading @@ -2173,6 +2280,9 @@ public class SubscriptionManagerService extends ISub.Stub { } } /** * @return The default subscription id for SMS. */ @Override public int getDefaultSmsSubId() { return mDefaultSmsSubId.get(); Loading Loading @@ -2351,6 +2461,9 @@ public class SubscriptionManagerService extends ISub.Stub { @Override public boolean setSubscriptionEnabled(boolean enable, int subId) { enforcePermissions("setSubscriptionEnabled", Manifest.permission.MODIFY_PHONE_STATE); return true; } Loading @@ -2362,7 +2475,7 @@ public class SubscriptionManagerService extends ISub.Stub { * @return {@code true} if the subscription is active. * * @throws IllegalArgumentException if the provided slot index is invalid. * @throws SecurityException if callers do not hold the required permission. * * @throws SecurityException if callers do not hold the required permission. */ @Override @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) Loading Loading @@ -2621,7 +2734,7 @@ public class SubscriptionManagerService extends ISub.Stub { * * <p>Note the assumption is that one subscription (which usually means one SIM) has * only one phone number. The multiple sources backup each other so hopefully at least one * is availavle. For example, for a carrier that doesn't typically set phone numbers * is available. For example, for a carrier that doesn't typically set phone numbers * on {@link SubscriptionManager#PHONE_NUMBER_SOURCE_UICC UICC}, the source * {@link SubscriptionManager#PHONE_NUMBER_SOURCE_IMS IMS} may provide one. Or, a carrier may * decide to provide the phone number via source Loading Loading @@ -3010,15 +3123,38 @@ public class SubscriptionManagerService extends ISub.Stub { @NonNull String[] args) { IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); pw.println(SubscriptionManagerService.class.getSimpleName() + ":"); pw.println("Logical SIM slot sub id mapping:"); pw.increaseIndent(); mSlotIndexToSubId.forEach((slotIndex, subId) -> pw.println("Logical slot " + slotIndex + ": subId=" + subId)); pw.increaseIndent(); pw.println("defaultSubId=" + getDefaultSubId()); pw.println("defaultVoiceSubId=" + getDefaultVoiceSubId()); pw.println("defaultDataSubId=" + getDefaultDataSubId()); pw.println("activeDataSubId=" + getActiveDataSubscriptionId()); pw.println("defaultSmsSubId=" + getDefaultSmsSubId()); pw.println("Active subscriptions:"); pw.increaseIndent(); mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(SubscriptionInfoInternal::isActive).forEach(pw::println); pw.decreaseIndent(); pw.println("Embedded subscriptions:"); pw.increaseIndent(); mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(SubscriptionInfoInternal::isEmbedded).forEach(pw::println); pw.decreaseIndent(); pw.println("Opportunistic subscriptions:"); pw.increaseIndent(); mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(SubscriptionInfoInternal::isOpportunistic).forEach(pw::println); pw.decreaseIndent(); pw.println("All subscriptions:"); pw.increaseIndent(); mSubscriptionDatabaseManager.getAllSubscriptions().forEach(pw::println); pw.decreaseIndent(); pw.println("defaultSubId=" + getDefaultSubId()); pw.println("defaultVoiceSubId=" + getDefaultVoiceSubId()); pw.println("defaultDataSubId" + getDefaultDataSubId()); pw.println("activeDataSubId" + getActiveDataSubscriptionId()); pw.println("defaultSmsSubId" + getDefaultSmsSubId()); if (mEuiccManager != null) { pw.println("Euicc enabled=" + mEuiccManager.isEnabled()); } Loading tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java +76 −5 Original line number Diff line number Diff line Loading @@ -273,10 +273,17 @@ public class SubscriptionDatabaseManagerTest extends TelephonyTest { } int subId = Integer.parseInt(uri.getLastPathSegment()); assertThat(mDatabase.size()).isAtLeast(subId); ContentValues existingValues = mDatabase.get(subId - 1); logd("update: subId=" + subId + ", contentValues=" + values); ContentValues existingValues = mDatabase.stream() .filter(contentValues -> contentValues.get( SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID).equals(subId)) .findFirst() .orElse(null); if (existingValues == null) { throw new IllegalArgumentException("Invalid sub id " + subId); } for (Map.Entry<String, Object> entry : values.valueSet()) { String column = entry.getKey(); Object value = entry.getValue(); Loading @@ -290,7 +297,24 @@ public class SubscriptionDatabaseManagerTest extends TelephonyTest { @Override public int delete(Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException("delete is not supported uri=" + uri); if (!uri.isPathPrefixMatch(SimInfo.CONTENT_URI)) { throw new UnsupportedOperationException("Unsupported uri=" + uri); } logd("delete: uri=" + uri + ", selection=" + selection + ", selectionArgs=" + Arrays.toString(selectionArgs)); if (!selection.equals(SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?")) { throw new UnsupportedOperationException("Only support delete by sub id."); } int rowsRemoved = 0; for (String selectionArg : selectionArgs) { int subId = Integer.parseInt(selectionArg); // Clear it to null instead of removing it. rowsRemoved += mDatabase.removeIf(contentValues -> contentValues.get( SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID).equals(subId)) ? 1 : 0; } return rowsRemoved; } @Override Loading @@ -305,7 +329,14 @@ public class SubscriptionDatabaseManagerTest extends TelephonyTest { throw new IllegalArgumentException("Insert with unknown column " + column); } } int subId = mDatabase.size() + 1; // The last row's subId + 1 int subId; if (mDatabase.isEmpty()) { subId = 1; } else { subId = (int) mDatabase.get(mDatabase.size() - 1) .get(SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID) + 1; } values.put(SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, subId); mDatabase.add(values); return ContentUris.withAppendedId(SimInfo.CONTENT_URI, subId); Loading Loading @@ -1566,4 +1597,44 @@ public class SubscriptionDatabaseManagerTest extends TelephonyTest { assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2) .getIccId()).isEqualTo(FAKE_ICCID2); } @Test public void testRemoveSubscriptionInfo() throws Exception { insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1); insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO2); Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback); mDatabaseManagerUT.removeSubscriptionInfo(1); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)).isNull(); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)) .isEqualTo(FAKE_SUBSCRIPTION_INFO2); verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(1)); // Insert a new one. Should become sub 3. Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback); insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)).isNull(); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)) .isEqualTo(FAKE_SUBSCRIPTION_INFO2); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(3)) .isEqualTo(new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO1) .setId(3).build()); verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(3)); Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback); mDatabaseManagerUT.removeSubscriptionInfo(2); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)).isNull(); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)).isNull(); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(3)) .isEqualTo(new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO1) .setId(3).build()); verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(2)); Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback); mDatabaseManagerUT.removeSubscriptionInfo(3); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)).isNull(); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)).isNull(); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(3)).isNull(); verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(3)); } } tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java +19 −0 Original line number Diff line number Diff line Loading @@ -1633,4 +1633,23 @@ public class SubscriptionManagerServiceTest extends TelephonyTest { .getUserId()).isEqualTo(mSubscriptionManagerServiceUT .getSubscriptionInfoInternal(1).getUserId()); } @Test public void testRemoveSubInfo() { insertSubscription(FAKE_SUBSCRIPTION_INFO1); insertSubscription(FAKE_SUBSCRIPTION_INFO2); assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT .removeSubInfo(FAKE_ICCID1, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)); mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE); assertThat(mSubscriptionManagerServiceUT.removeSubInfo(FAKE_ICCID1, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)).isEqualTo(0); assertThat(mSubscriptionManagerServiceUT.removeSubInfo(FAKE_ICCID2, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)).isEqualTo(0); mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE); assertThat(mSubscriptionManagerServiceUT.getAllSubInfoList( CALLING_PACKAGE, CALLING_FEATURE).isEmpty()).isTrue(); } } Loading
src/java/com/android/internal/telephony/SubscriptionController.java +0 −1 Original line number Diff line number Diff line Loading @@ -2712,7 +2712,6 @@ public class SubscriptionController extends ISub.Stub { /** * @return the number of records cleared */ @Override public int clearSubInfo() { enforceModifyPhoneState("clearSubInfo"); Loading
src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java +28 −0 Original line number Diff line number Diff line Loading @@ -769,6 +769,34 @@ public class SubscriptionDatabaseManager extends Handler { return subId; } /** * Remove a subscription record from the database. * * @param subId The subscription id of the subscription to be deleted. * * @throws IllegalArgumentException If {@code subId} is invalid. */ public void removeSubscriptionInfo(int subId) { if (!mAllSubscriptionInfoInternalCache.containsKey(subId)) { throw new IllegalArgumentException("subId " + subId + " is invalid."); } mReadWriteLock.writeLock().lock(); try { if (mContext.getContentResolver().delete(SimInfo.CONTENT_URI, SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?", new String[]{Integer.toString(subId)}) > 0) { mAllSubscriptionInfoInternalCache.remove(subId); } else { logel("Failed to remove subscription with subId=" + subId); } } finally { mReadWriteLock.writeLock().unlock(); } mCallback.invokeFromExecutor(() -> mCallback.onSubscriptionChanged(subId)); } /** * Update a subscription in the database (synchronously or asynchronously). * Loading
src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java +159 −23 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.stream.Collectors; import java.util.stream.IntStream; /** * The subscription manager service is the backend service of {@link SubscriptionManager}. Loading Loading @@ -830,8 +831,11 @@ public class SubscriptionManagerService extends ISub.Stub { public void markSubscriptionsInactive(int simSlotIndex) { mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(subInfo -> subInfo.getSimSlotIndex() == simSlotIndex) .forEach(subInfo -> mSubscriptionDatabaseManager.setSimSlotIndex( subInfo.getSubscriptionId(), SubscriptionManager.INVALID_SIM_SLOT_INDEX)); .forEach(subInfo -> { mSubscriptionDatabaseManager.setSimSlotIndex(subInfo.getSubscriptionId(), SubscriptionManager.INVALID_SIM_SLOT_INDEX); mSlotIndexToSubId.remove(simSlotIndex); }); } /** Loading Loading @@ -1364,8 +1368,9 @@ public class SubscriptionManagerService extends ISub.Stub { * @see SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh */ @Override // TODO: Remove this after SubscriptionController is removed. public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) { updateEmbeddedSubscriptions(List.of(cardId), null); } /** Loading Loading @@ -1430,17 +1435,40 @@ public class SubscriptionManagerService extends ISub.Stub { } /** * Remove subscription info record for the given device. * Remove subscription info record from the subscription database. * * @param uniqueId This is the unique identifier for the subscription within the specific * subscription type. * @param subscriptionType the type of subscription to be removed * @param subscriptionType the type of subscription to be removed. * * @return 0 if success, < 0 on error * // TODO: Remove this terrible return value once SubscriptionController is removed. * @return 0 if success, < 0 on error. * * @throws NullPointerException if {@code uniqueId} is {@code null}. * @throws SecurityException if callers do not hold the required permission. */ @Override @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public int removeSubInfo(@NonNull String uniqueId, int subscriptionType) { enforcePermissions("removeSubInfo", Manifest.permission.MODIFY_PHONE_STATE); final long identity = Binder.clearCallingIdentity(); try { SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager .getSubscriptionInfoInternalByIccId(uniqueId); if (subInfo == null) { loge("Cannot find subscription with uniqueId " + uniqueId); return -1; } if (subInfo.getSubscriptionType() != subscriptionType) { loge("The subscription type does not match."); return -1; } mSubscriptionDatabaseManager.removeSubscriptionInfo(subInfo.getSubscriptionId()); return 0; } finally { Binder.restoreCallingIdentity(identity); } } /** Loading Loading @@ -1631,6 +1659,10 @@ public class SubscriptionManagerService extends ISub.Stub { * @throws SecurityException if callers do not hold the required permission. */ @Override @RequiresPermission(anyOf = { Manifest.permission.MODIFY_PHONE_STATE, "carrier privileges", }) public int setOpportunistic(boolean opportunistic, int subId, @NonNull String callingPackage) { // Verify that the callingPackage belongs to the calling UID mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); Loading Loading @@ -1835,11 +1867,76 @@ public class SubscriptionManagerService extends ISub.Stub { } } /** * Remove a list of subscriptions from their subscription group. * * @param subIdList list of subId that need removing from their groups. * @param groupUuid The UUID of the subscription group. * @param callingPackage The package making the call. * * @throws SecurityException if the caller doesn't meet the requirements outlined above. * @throws IllegalArgumentException if the some subscriptions in the list doesn't belong the * specified group. * * @see SubscriptionManager#createSubscriptionGroup(List) */ @Override public void removeSubscriptionsFromGroup(int[] subIdList, @NonNull ParcelUuid groupUuid, @NonNull String callingPackage) { @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull int[] subIdList, @NonNull ParcelUuid groupUuid, @NonNull String callingPackage) { // Verify that the callingPackage belongs to the calling UID mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); // If it doesn't have modify phone state permission, or carrier privilege permission, // a SecurityException will be thrown. If it's due to invalid parameter or internal state, // it will return null. if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) != PackageManager.PERMISSION_GRANTED && !(checkCarrierPrivilegeOnSubList(subIdList, callingPackage) && canPackageManageGroup(groupUuid, callingPackage))) { throw new SecurityException("removeSubscriptionsFromGroup needs MODIFY_PHONE_STATE or" + " carrier privilege permission on all specified subscriptions."); } Objects.requireNonNull(subIdList); Objects.requireNonNull(groupUuid); if (subIdList.length == 0) { throw new IllegalArgumentException("subIdList is empty."); } long identity = Binder.clearCallingIdentity(); try { for (int subId : subIdList) { SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager .getSubscriptionInfoInternal(subId); if (subInfo == null) { throw new IllegalArgumentException("The provided sub id " + subId + " is not valid."); } if (!groupUuid.toString().equals(subInfo.getGroupUuid())) { throw new IllegalArgumentException("Subscription " + subInfo.getSubscriptionId() + " doesn't belong to group " + groupUuid); } } for (SubscriptionInfoInternal subInfo : mSubscriptionDatabaseManager.getAllSubscriptions()) { if (IntStream.of(subIdList).anyMatch( subId -> subId == subInfo.getSubscriptionId())) { mSubscriptionDatabaseManager.setGroupUuid(subInfo.getSubscriptionId(), ""); mSubscriptionDatabaseManager.setGroupOwner(subInfo.getSubscriptionId(), ""); } else if (subInfo.getGroupUuid().equals(groupUuid.toString())) { // Pre-T behavior. If there are still subscriptions having the same UUID, update // to the new owner. mSubscriptionDatabaseManager.setGroupOwner( subInfo.getSubscriptionId(), callingPackage); } } } finally { Binder.restoreCallingIdentity(identity); } } /** Loading @@ -1850,17 +1947,18 @@ public class SubscriptionManagerService extends ISub.Stub { * * @param subIdList list of subId that need adding into the group * @param groupUuid the groupUuid the subscriptions are being added to. * @param callingPackage The package making the call. * * @throws SecurityException if the caller doesn't meet the requirements outlined above. * @throws IllegalArgumentException if the some subscriptions in the list doesn't exist. * * @see SubscriptionManager#createSubscriptionGroup(List) */ @Override @RequiresPermission(anyOf = { Manifest.permission.MODIFY_PHONE_STATE, "carrier privileges", }) @Override public void addSubscriptionsIntoGroup(@NonNull int[] subIdList, @NonNull ParcelUuid groupUuid, @NonNull String callingPackage) { // Verify that the callingPackage belongs to the calling UID Loading Loading @@ -1975,7 +2073,7 @@ public class SubscriptionManagerService extends ISub.Stub { * * @param subId The subscription id. * * @return Logical slot indexx (i.e. phone id) as a positive integer or * @return Logical slot index (i.e. phone id) as a positive integer or * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if the supplied {@code subId} doesn't have * an associated slot index. */ Loading Loading @@ -2058,16 +2156,22 @@ public class SubscriptionManagerService extends ISub.Stub { } } /** * @return The default subscription id. */ @Override public int getDefaultSubId() { return mDefaultSubId.get(); } @Override public int clearSubInfo() { return 0; } /** * Get phone id from the subscription id. In the implementation, the logical SIM slot index * is equivalent to phone id. So this method is same as {@link #getSlotIndex(int)}. * * @param subId The subscription id. * * @return The phone id. */ @Override public int getPhoneId(int subId) { // slot index and phone id are equivalent in the current implementation. Loading Loading @@ -2123,6 +2227,9 @@ public class SubscriptionManagerService extends ISub.Stub { } } /** * @return The default subscription id for voice. */ @Override public int getDefaultVoiceSubId() { return mDefaultVoiceSubId.get(); Loading Loading @@ -2173,6 +2280,9 @@ public class SubscriptionManagerService extends ISub.Stub { } } /** * @return The default subscription id for SMS. */ @Override public int getDefaultSmsSubId() { return mDefaultSmsSubId.get(); Loading Loading @@ -2351,6 +2461,9 @@ public class SubscriptionManagerService extends ISub.Stub { @Override public boolean setSubscriptionEnabled(boolean enable, int subId) { enforcePermissions("setSubscriptionEnabled", Manifest.permission.MODIFY_PHONE_STATE); return true; } Loading @@ -2362,7 +2475,7 @@ public class SubscriptionManagerService extends ISub.Stub { * @return {@code true} if the subscription is active. * * @throws IllegalArgumentException if the provided slot index is invalid. * @throws SecurityException if callers do not hold the required permission. * * @throws SecurityException if callers do not hold the required permission. */ @Override @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) Loading Loading @@ -2621,7 +2734,7 @@ public class SubscriptionManagerService extends ISub.Stub { * * <p>Note the assumption is that one subscription (which usually means one SIM) has * only one phone number. The multiple sources backup each other so hopefully at least one * is availavle. For example, for a carrier that doesn't typically set phone numbers * is available. For example, for a carrier that doesn't typically set phone numbers * on {@link SubscriptionManager#PHONE_NUMBER_SOURCE_UICC UICC}, the source * {@link SubscriptionManager#PHONE_NUMBER_SOURCE_IMS IMS} may provide one. Or, a carrier may * decide to provide the phone number via source Loading Loading @@ -3010,15 +3123,38 @@ public class SubscriptionManagerService extends ISub.Stub { @NonNull String[] args) { IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); pw.println(SubscriptionManagerService.class.getSimpleName() + ":"); pw.println("Logical SIM slot sub id mapping:"); pw.increaseIndent(); mSlotIndexToSubId.forEach((slotIndex, subId) -> pw.println("Logical slot " + slotIndex + ": subId=" + subId)); pw.increaseIndent(); pw.println("defaultSubId=" + getDefaultSubId()); pw.println("defaultVoiceSubId=" + getDefaultVoiceSubId()); pw.println("defaultDataSubId=" + getDefaultDataSubId()); pw.println("activeDataSubId=" + getActiveDataSubscriptionId()); pw.println("defaultSmsSubId=" + getDefaultSmsSubId()); pw.println("Active subscriptions:"); pw.increaseIndent(); mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(SubscriptionInfoInternal::isActive).forEach(pw::println); pw.decreaseIndent(); pw.println("Embedded subscriptions:"); pw.increaseIndent(); mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(SubscriptionInfoInternal::isEmbedded).forEach(pw::println); pw.decreaseIndent(); pw.println("Opportunistic subscriptions:"); pw.increaseIndent(); mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(SubscriptionInfoInternal::isOpportunistic).forEach(pw::println); pw.decreaseIndent(); pw.println("All subscriptions:"); pw.increaseIndent(); mSubscriptionDatabaseManager.getAllSubscriptions().forEach(pw::println); pw.decreaseIndent(); pw.println("defaultSubId=" + getDefaultSubId()); pw.println("defaultVoiceSubId=" + getDefaultVoiceSubId()); pw.println("defaultDataSubId" + getDefaultDataSubId()); pw.println("activeDataSubId" + getActiveDataSubscriptionId()); pw.println("defaultSmsSubId" + getDefaultSmsSubId()); if (mEuiccManager != null) { pw.println("Euicc enabled=" + mEuiccManager.isEnabled()); } Loading
tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java +76 −5 Original line number Diff line number Diff line Loading @@ -273,10 +273,17 @@ public class SubscriptionDatabaseManagerTest extends TelephonyTest { } int subId = Integer.parseInt(uri.getLastPathSegment()); assertThat(mDatabase.size()).isAtLeast(subId); ContentValues existingValues = mDatabase.get(subId - 1); logd("update: subId=" + subId + ", contentValues=" + values); ContentValues existingValues = mDatabase.stream() .filter(contentValues -> contentValues.get( SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID).equals(subId)) .findFirst() .orElse(null); if (existingValues == null) { throw new IllegalArgumentException("Invalid sub id " + subId); } for (Map.Entry<String, Object> entry : values.valueSet()) { String column = entry.getKey(); Object value = entry.getValue(); Loading @@ -290,7 +297,24 @@ public class SubscriptionDatabaseManagerTest extends TelephonyTest { @Override public int delete(Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException("delete is not supported uri=" + uri); if (!uri.isPathPrefixMatch(SimInfo.CONTENT_URI)) { throw new UnsupportedOperationException("Unsupported uri=" + uri); } logd("delete: uri=" + uri + ", selection=" + selection + ", selectionArgs=" + Arrays.toString(selectionArgs)); if (!selection.equals(SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=?")) { throw new UnsupportedOperationException("Only support delete by sub id."); } int rowsRemoved = 0; for (String selectionArg : selectionArgs) { int subId = Integer.parseInt(selectionArg); // Clear it to null instead of removing it. rowsRemoved += mDatabase.removeIf(contentValues -> contentValues.get( SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID).equals(subId)) ? 1 : 0; } return rowsRemoved; } @Override Loading @@ -305,7 +329,14 @@ public class SubscriptionDatabaseManagerTest extends TelephonyTest { throw new IllegalArgumentException("Insert with unknown column " + column); } } int subId = mDatabase.size() + 1; // The last row's subId + 1 int subId; if (mDatabase.isEmpty()) { subId = 1; } else { subId = (int) mDatabase.get(mDatabase.size() - 1) .get(SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID) + 1; } values.put(SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID, subId); mDatabase.add(values); return ContentUris.withAppendedId(SimInfo.CONTENT_URI, subId); Loading Loading @@ -1566,4 +1597,44 @@ public class SubscriptionDatabaseManagerTest extends TelephonyTest { assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2) .getIccId()).isEqualTo(FAKE_ICCID2); } @Test public void testRemoveSubscriptionInfo() throws Exception { insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1); insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO2); Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback); mDatabaseManagerUT.removeSubscriptionInfo(1); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)).isNull(); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)) .isEqualTo(FAKE_SUBSCRIPTION_INFO2); verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(1)); // Insert a new one. Should become sub 3. Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback); insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)).isNull(); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)) .isEqualTo(FAKE_SUBSCRIPTION_INFO2); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(3)) .isEqualTo(new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO1) .setId(3).build()); verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(3)); Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback); mDatabaseManagerUT.removeSubscriptionInfo(2); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)).isNull(); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)).isNull(); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(3)) .isEqualTo(new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO1) .setId(3).build()); verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(2)); Mockito.clearInvocations(mSubscriptionDatabaseManagerCallback); mDatabaseManagerUT.removeSubscriptionInfo(3); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(1)).isNull(); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(2)).isNull(); assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(3)).isNull(); verify(mSubscriptionDatabaseManagerCallback).onSubscriptionChanged(eq(3)); } }
tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java +19 −0 Original line number Diff line number Diff line Loading @@ -1633,4 +1633,23 @@ public class SubscriptionManagerServiceTest extends TelephonyTest { .getUserId()).isEqualTo(mSubscriptionManagerServiceUT .getSubscriptionInfoInternal(1).getUserId()); } @Test public void testRemoveSubInfo() { insertSubscription(FAKE_SUBSCRIPTION_INFO1); insertSubscription(FAKE_SUBSCRIPTION_INFO2); assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT .removeSubInfo(FAKE_ICCID1, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)); mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE); assertThat(mSubscriptionManagerServiceUT.removeSubInfo(FAKE_ICCID1, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)).isEqualTo(0); assertThat(mSubscriptionManagerServiceUT.removeSubInfo(FAKE_ICCID2, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM)).isEqualTo(0); mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE); assertThat(mSubscriptionManagerServiceUT.getAllSubInfoList( CALLING_PACKAGE, CALLING_FEATURE).isEmpty()).isTrue(); } }