Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eae3ba62 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Added remove subscription support" am: 7a3d4427

parents 3fbb156e 7a3d4427
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -2712,7 +2712,6 @@ public class SubscriptionController extends ISub.Stub {
    /**
     * @return the number of records cleared
     */
    @Override
    public int clearSubInfo() {
        enforceModifyPhoneState("clearSubInfo");

+28 −0
Original line number Diff line number Diff line
@@ -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).
     *
+159 −23
Original line number Diff line number Diff line
@@ -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}.
@@ -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);
                });
    }

    /**
@@ -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);
    }

    /**
@@ -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);
        }
    }

    /**
@@ -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);
@@ -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);
        }
    }

    /**
@@ -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
@@ -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.
     */
@@ -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.
@@ -2123,6 +2227,9 @@ public class SubscriptionManagerService extends ISub.Stub {
        }
    }

    /**
     * @return The default subscription id for voice.
     */
    @Override
    public int getDefaultVoiceSubId() {
        return mDefaultVoiceSubId.get();
@@ -2173,6 +2280,9 @@ public class SubscriptionManagerService extends ISub.Stub {
        }
    }

    /**
     * @return The default subscription id for SMS.
     */
    @Override
    public int getDefaultSmsSubId() {
        return mDefaultSmsSubId.get();
@@ -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;
    }

@@ -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)
@@ -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
@@ -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());
        }
+76 −5
Original line number Diff line number Diff line
@@ -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();
@@ -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
@@ -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);
@@ -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));
    }
}
+19 −0
Original line number Diff line number Diff line
@@ -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();
    }
}