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

Commit ed5edb77 authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

DO NOT MERGE revoke certain app-ops on suspend

Revoking an apps authorizations to use camera and record or play audio
while suspended. Appops watchers will also be notified of this change to
re-evaluate privileges at the time of suspension.

Test: atest FrameworksServicesTests:SuspendPackagesTest

Bug: 138636979
Change-Id: Ie95555856afdd56728125f7e60b6a78cf9fc0e58
Merged-In: Ie95555856afdd56728125f7e60b6a78cf9fc0e58
Merged-In: Ic5fb1807deceabfd956b666fa76f8bcc94020ac3
parent 870474af
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
@@ -22,8 +22,11 @@ import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -96,6 +99,9 @@ import java.util.List;
import java.util.Map;

import static android.app.AppOpsManager._NUM_UID_STATE;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_PLAY_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
import static android.app.AppOpsManager.UID_STATE_CACHED;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
@@ -170,6 +176,12 @@ public class AppOpsService extends IAppOpsService.Stub {
            "rc",       // UID_STATE_CACHED
    };

    private static final int[] OPS_RESTRICTED_ON_SUSPEND = {
            OP_PLAY_AUDIO,
            OP_RECORD_AUDIO,
            OP_CAMERA,
    };

    Context mContext;
    final AtomicFile mFile;
    final Handler mHandler;
@@ -635,6 +647,33 @@ public class AppOpsService extends IAppOpsService.Stub {
            }
        }

        final IntentFilter packageSuspendFilter = new IntentFilter();
        packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
        packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                final String[] changedPkgs = intent.getStringArrayExtra(
                    Intent.EXTRA_CHANGED_PACKAGE_LIST);
                for (int code : OPS_RESTRICTED_ON_SUSPEND) {
                    ArraySet<ModeCallback> callbacks;
                    synchronized (AppOpsService.this) {
                        callbacks = mOpModeWatchers.get(code);
                        if (callbacks == null) {
                            continue;
                        }
                        callbacks = new ArraySet<>(callbacks);
                    }
                    for (int i = 0; i < changedPkgs.length; i++) {
                        final String changedPkg = changedPkgs[i];
                        // We trust packagemanager to insert matching uid and packageNames in the
                        // extras
                        notifyOpChanged(callbacks, code, -1, changedPkg);
                    }
                }
            }
        }, packageSuspendFilter);

        PackageManagerInternal packageManagerInternal = LocalServices.getService(
                PackageManagerInternal.class);
        packageManagerInternal.setExternalSourcesPolicy(
@@ -1419,6 +1458,9 @@ public class AppOpsService extends IAppOpsService.Stub {
        if (resolvedPackageName == null) {
            return AppOpsManager.MODE_IGNORED;
        }
        if (isOpRestrictedDueToSuspend(code, packageName, uid)) {
            return AppOpsManager.MODE_IGNORED;
        }
        synchronized (this) {
            if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
                return AppOpsManager.MODE_IGNORED;
@@ -1461,6 +1503,14 @@ public class AppOpsService extends IAppOpsService.Stub {
        return checkOperation(code, uid, packageName);
    }

    private boolean isOpRestrictedDueToSuspend(int code, String packageName, int uid) {
        if (!ArrayUtils.contains(OPS_RESTRICTED_ON_SUSPEND, code)) {
            return false;
        }
        final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
        return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid));
    }

    private boolean isPackageSuspendedForUser(String pkg, int uid) {
        try {
            return AppGlobals.getPackageManager().isPackageSuspendedForUser(
+50 −0
Original line number Diff line number Diff line
@@ -16,6 +16,13 @@

package com.android.server.pm;

import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_PLAY_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.opToName;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -40,6 +47,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
@@ -52,6 +60,8 @@ import android.util.Log;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;

import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.servicestests.apps.suspendtestapp.SuspendTestActivity;
import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver;

@@ -546,6 +556,46 @@ public class SuspendPackagesTest {
        assertEquals(ACTION_REPORT_MY_PACKAGE_UNSUSPENDED, intentFromApp.getAction());
    }

    @Test
    public void testCameraBlockedOnSuspend() throws Exception {
        assertOpBlockedOnSuspend(OP_CAMERA);
    }

    @Test
    public void testPlayAudioBlockedOnSuspend() throws Exception {
        assertOpBlockedOnSuspend(OP_PLAY_AUDIO);
    }

    @Test
    public void testRecordAudioBlockedOnSuspend() throws Exception {
        assertOpBlockedOnSuspend(OP_RECORD_AUDIO);
    }

    private void assertOpBlockedOnSuspend(int code) throws Exception {
        final IAppOpsService iAppOps = IAppOpsService.Stub.asInterface(
                ServiceManager.getService(Context.APP_OPS_SERVICE));
        final CountDownLatch latch = new CountDownLatch(1);
        final IAppOpsCallback watcher = new IAppOpsCallback.Stub() {
            @Override
            public void opChanged(int op, int uid, String packageName) {
                if (op == code && packageName.equals(TEST_APP_PACKAGE_NAME)) {
                    latch.countDown();
                }
            }
        };
        iAppOps.startWatchingMode(code, TEST_APP_PACKAGE_NAME, watcher);
        final int testPackageUid = mPackageManager.getPackageUid(TEST_APP_PACKAGE_NAME, 0);
        int opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME);
        assertEquals("Op " + opToName(code) + " disallowed for unsuspended package", MODE_ALLOWED,
                opMode);
        suspendTestPackage(null, null, null);
        assertTrue("AppOpsWatcher did not callback", latch.await(5, TimeUnit.SECONDS));
        opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME);
        assertEquals("Op " + opToName(code) + " allowed for suspended package", MODE_IGNORED,
                opMode);
        iAppOps.stopWatchingMode(watcher);
    }

    @After
    public void tearDown() throws IOException {
        mAppCommsReceiver.unregister();