Loading services/core/java/com/android/server/AppOpsService.java +50 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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( Loading Loading @@ -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; Loading Loading @@ -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( Loading services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java +50 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(); Loading Loading
services/core/java/com/android/server/AppOpsService.java +50 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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( Loading Loading @@ -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; Loading Loading @@ -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( Loading
services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java +50 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(); Loading