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

Commit 73f76641 authored by hyosun's avatar hyosun
Browse files

To read the group UUID, the calling app either needs carrier privileges or the...

To read the group UUID, the calling app either needs carrier privileges or the READ_PHONE_STATE permission and access to device identifiers.

If the app has only the READ_PHONE_STATE permission, it can no longer read the group UUID.
When SubscriptionManager#getSubscriptionsInGroup is called, If the calling app has carrier permission or READ_PHONE_STATE permission and access to device identifiers, then returns a list.
If not, it returns an empty list.

Bug: 213902861
Test: atest SubscriptionManagerTest
Test: atest SubscriptionControllerTest
Test: manual (b/213902861#comment54)
Change-Id: Idaa1b97f7ae2d0ce0c0027b0499d4a930fcce981
Merged-In: Idaa1b97f7ae2d0ce0c0027b0499d4a930fcce981
parent 4c5c285d
Loading
Loading
Loading
Loading
+39 −2
Original line number Original line Diff line number Diff line
@@ -28,6 +28,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.ContentValues;
@@ -162,6 +165,15 @@ public class SubscriptionController extends ISub.Stub {
    protected TelephonyManager mTelephonyManager;
    protected TelephonyManager mTelephonyManager;
    protected UiccController mUiccController;
    protected UiccController mUiccController;


    /**
     * Apps targeting on Android T and beyond will get an empty list if there is no access to device
     * identifiers nor has carrier privileges when calling
     * SubscriptionManager#getSubscriptionsInGroup.
     */
    @ChangeId
    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
    public static final long REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID = 213902861L;

    private AppOpsManager mAppOps;
    private AppOpsManager mAppOps;


    // Allows test mocks to avoid SELinux failures on invalidate calls.
    // Allows test mocks to avoid SELinux failures on invalidate calls.
@@ -3965,10 +3977,20 @@ public class SubscriptionController extends ISub.Stub {
     * Get subscriptionInfo list of subscriptions that are in the same group of given subId.
     * Get subscriptionInfo list of subscriptions that are in the same group of given subId.
     * See {@link #createSubscriptionGroup(int[], String)} for more details.
     * See {@link #createSubscriptionGroup(int[], String)} for more details.
     *
     *
     * Caller will either have {@link android.Manifest.permission#READ_PHONE_STATE}
     * Caller must have {@link android.Manifest.permission#READ_PHONE_STATE}
     * permission or had carrier privilege permission on the subscription.
     * or carrier privilege permission on the subscription.
     * {@link TelephonyManager#hasCarrierPrivileges(int)}
     * {@link TelephonyManager#hasCarrierPrivileges(int)}
     *
     *
     * <p>Starting with API level 33, the caller needs READ_PHONE_STATE and access to device
     * identifiers to get the list of subscriptions associated with a group UUID.
     * This method can be invoked if one of the following requirements is met:
     * <ul>
     *     <li>If the app has carrier privilege permission.
     *     {@link TelephonyManager#hasCarrierPrivileges()}
     *     <li>If the app has {@link android.Manifest.permission#READ_PHONE_STATE} and
     *     access to device identifiers.
     * </ul>
     *
     * @throws SecurityException if the caller doesn't meet the requirements
     * @throws SecurityException if the caller doesn't meet the requirements
     *             outlined above.
     *             outlined above.
     *
     *
@@ -3995,6 +4017,21 @@ public class SubscriptionController extends ISub.Stub {
            Binder.restoreCallingIdentity(identity);
            Binder.restoreCallingIdentity(identity);
        }
        }


        // If the calling app neither has carrier privileges nor READ_PHONE_STATE and access to
        // device identifiers, it will return an empty list.
        if (CompatChanges.isChangeEnabled(
                REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID, Binder.getCallingUid())) {
            try {
                if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext,
                        callingPackage, callingFeatureId, "getSubscriptionsInGroup")) {
                    EventLog.writeEvent(0x534e4554, "213902861", Binder.getCallingUid());
                    return new ArrayList<>();
                }
            } catch (SecurityException e) {
                EventLog.writeEvent(0x534e4554, "213902861", Binder.getCallingUid());
                return new ArrayList<>();
            }
        }
        return subInfoList.stream().filter(info -> {
        return subInfoList.stream().filter(info -> {
            if (!groupUuid.equals(info.getGroupUuid())) return false;
            if (!groupUuid.equals(info.getGroupUuid())) return false;
            int subId = info.getSubscriptionId();
            int subId = info.getSubscriptionId();
+81 −14
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.internal.telephony;


import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION;
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION;


import static com.android.internal.telephony.SubscriptionController.REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID;
import static com.android.internal.telephony.uicc.IccCardStatus.CardState.CARDSTATE_PRESENT;
import static com.android.internal.telephony.uicc.IccCardStatus.CardState.CARDSTATE_PRESENT;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
@@ -33,7 +34,9 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.times;
@@ -41,6 +44,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import android.Manifest;
import android.Manifest;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.ContentResolver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.ContentValues;
import android.content.Intent;
import android.content.Intent;
@@ -68,9 +72,14 @@ import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UiccSlot;
import com.android.internal.telephony.uicc.UiccSlot;


import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;

import org.junit.After;
import org.junit.After;
import org.junit.Before;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentCaptor;


import java.util.ArrayList;
import java.util.ArrayList;
@@ -108,6 +117,9 @@ public class SubscriptionControllerTest extends TelephonyTest {
    private static final String DISPLAY_NUMBER = "123456";
    private static final String DISPLAY_NUMBER = "123456";
    private static final String DISPLAY_NAME = "testing_display_name";
    private static final String DISPLAY_NAME = "testing_display_name";


    @Rule
    public TestRule mCompatChangeRule = new PlatformCompatChangeRule();

    @Before
    @Before
    public void setUp() throws Exception {
    public void setUp() throws Exception {
        super.setUp(getClass().getSimpleName());
        super.setUp(getClass().getSimpleName());
@@ -1099,6 +1111,7 @@ public class SubscriptionControllerTest extends TelephonyTest {


    @Test
    @Test
    @SmallTest
    @SmallTest
    @EnableCompatChanges({REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID})
    public void testAddSubscriptionIntoGroupWithCarrierPrivilegePermission() throws Exception {
    public void testAddSubscriptionIntoGroupWithCarrierPrivilegePermission() throws Exception {
        testInsertSim();
        testInsertSim();
        // Adding a second profile and mark as embedded.
        // Adding a second profile and mark as embedded.
@@ -1115,6 +1128,7 @@ public class SubscriptionControllerTest extends TelephonyTest {


        // Create group for sub 1.
        // Create group for sub 1.
        int[] subIdList = new int[] {1};
        int[] subIdList = new int[] {1};
        doReturn(subIdList).when(mSubscriptionManager).getCompleteActiveSubscriptionIdList();
        doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(1);
        doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(1);
        ParcelUuid groupId = mSubscriptionControllerUT.createSubscriptionGroup(
        ParcelUuid groupId = mSubscriptionControllerUT.createSubscriptionGroup(
                subIdList, "packageName1");
                subIdList, "packageName1");
@@ -1150,6 +1164,7 @@ public class SubscriptionControllerTest extends TelephonyTest {


    @Test
    @Test
    @SmallTest
    @SmallTest
    @EnableCompatChanges({REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID})
    public void testUpdateSubscriptionGroupWithCarrierPrivilegePermission() throws Exception {
    public void testUpdateSubscriptionGroupWithCarrierPrivilegePermission() throws Exception {
        testInsertSim();
        testInsertSim();
        // Adding a second profile and mark as embedded.
        // Adding a second profile and mark as embedded.
@@ -1166,6 +1181,7 @@ public class SubscriptionControllerTest extends TelephonyTest {


        int[] subIdList = new int[] {1};
        int[] subIdList = new int[] {1};


        doReturn(subIdList).when(mSubscriptionManager).getCompleteActiveSubscriptionIdList();
        doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(1);
        doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(1);
        doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(2);
        doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(2);


@@ -1271,6 +1287,7 @@ public class SubscriptionControllerTest extends TelephonyTest {


    @Test
    @Test
    @SmallTest
    @SmallTest
    @EnableCompatChanges({REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID})
    public void testSetSubscriptionGroup() throws Exception {
    public void testSetSubscriptionGroup() throws Exception {
        testInsertSim();
        testInsertSim();
        // Adding a second profile and mark as embedded.
        // Adding a second profile and mark as embedded.
@@ -1293,6 +1310,7 @@ public class SubscriptionControllerTest extends TelephonyTest {
        assertNotEquals(null, groupUuid);
        assertNotEquals(null, groupUuid);


        // Sub 1 and sub 2 should be in same group.
        // Sub 1 and sub 2 should be in same group.
        doReturn(subIdList).when(mSubscriptionManager).getCompleteActiveSubscriptionIdList();
        List<SubscriptionInfo> infoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
        List<SubscriptionInfo> infoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
                groupUuid, mContext.getOpPackageName(), mContext.getAttributionTag());
                groupUuid, mContext.getOpPackageName(), mContext.getAttributionTag());
        assertNotEquals(null, infoList);
        assertNotEquals(null, infoList);
@@ -1705,42 +1723,88 @@ public class SubscriptionControllerTest extends TelephonyTest {
    }
    }


    @Test
    @Test
    public void testGetSubscriptionsInGroupWithNoPermission() throws Exception {
    @DisableCompatChanges({REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID})
        // If the calling package does not have the READ_PHONE_STATE permission or carrier
    public void testGetSubscriptionsInGroupWithReadPhoneState() throws Exception {
        // privileges then getSubscriptionsInGroup should throw a SecurityException when the
        // For backward compatibility test
        // READ_PHONE_STATE permission check is performed.
        ParcelUuid groupUuid = setupGetSubscriptionsInGroupTest();
        ParcelUuid groupUuid = setupGetSubscriptionsInGroupTest();
        mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
        setupReadPhoneNumbersTest();
        setIdentifierAccess(false);

        List<SubscriptionInfo> subInfoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
                groupUuid, mCallingPackage, mCallingFeature);

        assertTrue(subInfoList.size() > 0);
        for (SubscriptionInfo info : subInfoList) {
            assertEquals(UNAVAILABLE_ICCID, info.getIccId());
            assertEquals(UNAVAILABLE_ICCID, info.getCardString());
            assertEquals(UNAVAILABLE_NUMBER, info.getNumber());
        }
    }


    @Test
    @EnableCompatChanges({REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID})
    public void testGetSubscriptionsInGroupWithoutAppropriatePermission() throws Exception {
        ParcelUuid groupUuid = setupGetSubscriptionsInGroupTest();

        // no permission
        setNoPermission();
        try {
        try {
            mSubscriptionControllerUT.getSubscriptionsInGroup(groupUuid, mCallingPackage,
            mSubscriptionControllerUT.getSubscriptionsInGroup(groupUuid, mCallingPackage,
                    mCallingFeature);
                    mCallingFeature);
            fail("getSubscriptionsInGroup should fail when invoked with no permissions");
            fail("getSubscriptionsInGroup should fail when invoked with no permissions");
        } catch (SecurityException expected) {
        } catch (SecurityException expected) {
        }
        }

        // only has the device identifiers permission
        setIdentifierAccess(true);
        try {
            mSubscriptionControllerUT.getSubscriptionsInGroup(groupUuid, mCallingPackage,
                    mCallingFeature);
            fail("getSubscriptionsInGroup should fail when invoked with no"
                    + "READ_PHONE_STATE permissions");
        } catch (SecurityException expected) {
        }

        // only has the READ_PHONE_STATE permission
        setIdentifierAccess(false);
        setReadPhoneState();
        List<SubscriptionInfo> subInfoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
                groupUuid, mCallingPackage, mCallingFeature);
        assertNotNull(subInfoList);
        assertTrue(subInfoList.isEmpty());
    }
    }


    @Test
    @Test
    public void testGetSubscriptionsInGroupWithReadPhoneState() throws Exception {
    @EnableCompatChanges({REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID})
        // If the calling package only has the READ_PHONE_STATE permission then
    public void testGetSubscriptionsInGroupWithReadDeviceIdentifier() throws Exception {
        // getSubscriptionsInGroup should still return the list of SubscriptionInfo objects
        // but the ICC ID should not be available via getIccId or getCardString.
        ParcelUuid groupUuid = setupGetSubscriptionsInGroupTest();
        ParcelUuid groupUuid = setupGetSubscriptionsInGroupTest();
        setupReadPhoneNumbersTest();
        setNoPermission();
        setIdentifierAccess(false);
        setCarrierPrivileges(false);
        setIdentifierAccess(true);
        setReadPhoneState();


        List<SubscriptionInfo> subInfoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
        List<SubscriptionInfo> subInfoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
                groupUuid, mCallingPackage, mCallingFeature);
                groupUuid, mCallingPackage, mCallingFeature);


        assertTrue(subInfoList.size() > 0);
        assertTrue(subInfoList.size() > 0);
        for (SubscriptionInfo info : subInfoList) {
        for (SubscriptionInfo info : subInfoList) {
            assertEquals(UNAVAILABLE_ICCID, info.getIccId());
            assertTrue(info.getIccId().length() > 0);
            assertEquals(UNAVAILABLE_ICCID, info.getCardString());
            assertTrue(info.getCardString().length() > 0);
            assertEquals(UNAVAILABLE_NUMBER, info.getNumber());
        }
    }
    }

    private void setNoPermission() {
        doThrow(new SecurityException()).when(mContext)
                .enforcePermission(anyString(), anyInt(), anyInt(), anyString());
    }

    private void setReadPhoneState() {
        doNothing().when(mContext).enforcePermission(
                eq(android.Manifest.permission.READ_PHONE_STATE), anyInt(), anyInt(), anyString());
    }
    }


    @Test
    @Test
    @EnableCompatChanges({REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID})
    public void testGetSubscriptionInGroupWithPhoneNumberAccess() throws Exception {
    public void testGetSubscriptionInGroupWithPhoneNumberAccess() throws Exception {
        // If the calling package meets any of the requirements for the
        // If the calling package meets any of the requirements for the
        // LegacyPermissionManager#checkPhoneNumberAccess test then the number should be available
        // LegacyPermissionManager#checkPhoneNumberAccess test then the number should be available
@@ -1758,6 +1822,7 @@ public class SubscriptionControllerTest extends TelephonyTest {
    }
    }


    @Test
    @Test
    @EnableCompatChanges({REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID})
    public void testGetSubscriptionsInGroupWithCarrierPrivileges() throws Exception {
    public void testGetSubscriptionsInGroupWithCarrierPrivileges() throws Exception {
        // If the calling package has the READ_PRIVILEGED_PHONE_STATE permission or carrier
        // If the calling package has the READ_PRIVILEGED_PHONE_STATE permission or carrier
        // privileges the ICC ID should be available in the SubscriptionInfo objects in the List.
        // privileges the ICC ID should be available in the SubscriptionInfo objects in the List.
@@ -1775,6 +1840,7 @@ public class SubscriptionControllerTest extends TelephonyTest {
    }
    }


    @Test
    @Test
    @EnableCompatChanges({REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID})
    public void testGetSubscriptionsInGroupWithPrivilegedPermission() throws Exception {
    public void testGetSubscriptionsInGroupWithPrivilegedPermission() throws Exception {
        // If the calling package has the READ_PRIVILEGED_PHONE_STATE permission or carrier
        // If the calling package has the READ_PRIVILEGED_PHONE_STATE permission or carrier
        // privileges the ICC ID should be available in the SubscriptionInfo objects in the List.
        // privileges the ICC ID should be available in the SubscriptionInfo objects in the List.
@@ -1796,6 +1862,7 @@ public class SubscriptionControllerTest extends TelephonyTest {
        ParcelUuid groupUuid = mSubscriptionControllerUT.createSubscriptionGroup(subIdList,
        ParcelUuid groupUuid = mSubscriptionControllerUT.createSubscriptionGroup(subIdList,
                mCallingPackage);
                mCallingPackage);
        assertNotNull(groupUuid);
        assertNotNull(groupUuid);
        doReturn(subIdList).when(mSubscriptionManager).getCompleteActiveSubscriptionIdList();
        return groupUuid;
        return groupUuid;
    }
    }