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

Commit 8c8cd487 authored by Anton Kulakov's avatar Anton Kulakov
Browse files

Create UidState for SdkSandbox uids related to app.

To allow AppOps from sandbox each sandbox process uid
must have UidState.

Test: AppOpsServiceTest
Bug: 281676778
Change-Id: I667880728a634d13ea0d0db410e7abc970e690d3
parent 20690ec5
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -1180,6 +1180,8 @@ public class AppOpsService extends IAppOpsService.Stub {
                                    uidState.pkgOps.put(packageName,
                                            new Ops(packageName, uidState));
                                }

                                createSandboxUidStateIfNotExistsForAppLocked(uid);
                            }
                        }
                    }
@@ -1261,6 +1263,8 @@ public class AppOpsService extends IAppOpsService.Stub {
                ops.put(code, new Op(uidState, packageName, code, uid));
            }
        }

        createSandboxUidStateIfNotExistsForAppLocked(uid);
    }

    /**
@@ -4011,6 +4015,11 @@ public class AppOpsService extends IAppOpsService.Stub {
        return uidState;
    }

    private void createSandboxUidStateIfNotExistsForAppLocked(int uid) {
        final int sandboxUid = Process.toSdkSandboxUid(uid);
        getUidStateLocked(sandboxUid, true);
    }

    private void updateAppWidgetVisibility(SparseArray<String> uidPackageNames, boolean visible) {
        synchronized (this) {
            getUidStateTracker().updateAppWidgetVisibility(uidPackageNames, visible);
+62 −11
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ import static android.app.AppOpsManager.MODE_ERRORED;
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
import static android.app.AppOpsManager.OP_FLAGS_ALL;
import static android.app.AppOpsManager.OP_FLAG_SELF;
import static android.app.AppOpsManager.OP_READ_DEVICE_IDENTIFIERS;
import static android.app.AppOpsManager.OP_READ_SMS;
import static android.app.AppOpsManager.OP_TAKE_AUDIO_FOCUS;
import static android.app.AppOpsManager.OP_WIFI_SCAN;
import static android.app.AppOpsManager.OP_WRITE_SMS;
import static android.os.UserHandle.getAppId;
@@ -49,8 +51,10 @@ import static org.mockito.ArgumentMatchers.nullable;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
import android.app.SyncNotedAppOp;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Handler;
import android.os.HandlerThread;
@@ -58,6 +62,7 @@ import android.os.Process;
import android.permission.PermissionManager;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Log;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -79,7 +84,6 @@ import org.junit.runner.RunWith;
import org.mockito.quality.Strictness;

import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@@ -99,12 +103,15 @@ public class AppOpsServiceTest {

    private static final Context sContext = InstrumentationRegistry.getTargetContext();
    private static final String sMyPackageName = sContext.getOpPackageName();
    private static final String sSdkSandboxPackageName = sContext.getPackageManager()
            .getSdkSandboxPackageName();

    private File mStorageFile;
    private File mRecentAccessesFile;
    private Handler mHandler;
    private AppOpsService mAppOpsService;
    private int mMyUid;
    private int mSdkSandboxPackageUid;
    private long mTestStartMillis;
    private StaticMockitoSession mMockingSession;

@@ -132,6 +139,7 @@ public class AppOpsServiceTest {
        handlerThread.start();
        mHandler = new Handler(handlerThread.getLooper());
        mMyUid = Process.myUid();
        mSdkSandboxPackageUid = resolveSdkSandboxPackageUid();

        initializeStaticMocks();

@@ -152,6 +160,39 @@ public class AppOpsServiceTest {
        mMockingSession.finishMocking();
    }

    private static int resolveSdkSandboxPackageUid() {
        try {
            return sContext.getPackageManager().getPackageUid(
                    sSdkSandboxPackageName,
                    PackageManager.PackageInfoFlags.of(0)
            );
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Can't resolve sandbox package uid", e);
            return Process.INVALID_UID;
        }
    }

    private static void mockGetPackage(
            PackageManagerInternal managerMock,
            String packageName
    ) {
        AndroidPackage packageMock = mock(AndroidPackage.class);
        when(managerMock.getPackage(packageName)).thenReturn(packageMock);
    }

    private static void mockGetPackageStateInternal(
            PackageManagerInternal managerMock,
            String packageName,
            int uid
    ) {
        PackageStateInternal packageStateInternalMock = mock(PackageStateInternal.class);
        when(packageStateInternalMock.isPrivileged()).thenReturn(false);
        when(packageStateInternalMock.getAppId()).thenReturn(uid);
        when(packageStateInternalMock.getAndroidPackage()).thenReturn(mock(AndroidPackage.class));
        when(managerMock.getPackageStateInternal(packageName))
                .thenReturn(packageStateInternalMock);
    }

    private void initializeStaticMocks() {
        mMockingSession = mockitoSession()
                .strictness(Strictness.LENIENT)
@@ -163,16 +204,11 @@ public class AppOpsServiceTest {
        // Mock LocalServices.getService(PackageManagerInternal.class).getPackageStateInternal
        // and getPackage dependency needed by AppOpsService
        PackageManagerInternal mockPackageManagerInternal = mock(PackageManagerInternal.class);
        AndroidPackage mockMyPkg = mock(AndroidPackage.class);
        when(mockMyPkg.getAttributions()).thenReturn(Collections.emptyList());
        PackageStateInternal mockMyPSInternal = mock(PackageStateInternal.class);
        when(mockMyPSInternal.isPrivileged()).thenReturn(false);
        when(mockMyPSInternal.getAppId()).thenReturn(mMyUid);
        when(mockMyPSInternal.getAndroidPackage()).thenReturn(mockMyPkg);

        when(mockPackageManagerInternal.getPackageStateInternal(sMyPackageName))
                .thenReturn(mockMyPSInternal);
        when(mockPackageManagerInternal.getPackage(sMyPackageName)).thenReturn(mockMyPkg);
        mockGetPackage(mockPackageManagerInternal, sMyPackageName);
        mockGetPackageStateInternal(mockPackageManagerInternal, sMyPackageName, mMyUid);
        mockGetPackage(mockPackageManagerInternal, sSdkSandboxPackageName);
        mockGetPackageStateInternal(mockPackageManagerInternal, sSdkSandboxPackageName,
                mSdkSandboxPackageUid);
        when(mockPackageManagerInternal.getPackageUid(eq(sMyPackageName), anyLong(),
                eq(getUserId(mMyUid)))).thenReturn(mMyUid);
        doReturn(mockPackageManagerInternal).when(
@@ -233,6 +269,21 @@ public class AppOpsServiceTest {
        assertContainsOp(loggedOps, OP_WRITE_SMS, -1, mTestStartMillis, MODE_ERRORED);
    }

    @Test
    public void testNoteOperationFromSdkSandbox() {
        int sandboxUid = Process.toSdkSandboxUid(mMyUid);

        // Note an op that's allowed.
        SyncNotedAppOp allowedResult = mAppOpsService.noteOperation(OP_TAKE_AUDIO_FOCUS, sandboxUid,
                sSdkSandboxPackageName, null, false, null, false);
        assertThat(allowedResult.getOpMode()).isEqualTo(MODE_ALLOWED);

        // Note another op that's not allowed.
        SyncNotedAppOp erroredResult = mAppOpsService.noteOperation(OP_READ_DEVICE_IDENTIFIERS,
                sandboxUid, sSdkSandboxPackageName, null, false, null, false);
        assertThat(erroredResult.getOpMode()).isEqualTo(MODE_ERRORED);
    }

    /**
     * Tests the scenario where an operation's permission is controlled by another operation.
     * For example the results of a WIFI_SCAN can be used to infer the location of a user, so the