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 atest GtsSuspendAppsTestCases Bug: 138636979 Change-Id: Ie95555856afdd56728125f7e60b6a78cf9fc0e58 Merged-In: Ic5fb1807deceabfd956b666fa76f8bcc94020ac3
This commit is contained in:
@@ -18,9 +18,11 @@ package com.android.server.appop;
|
|||||||
|
|
||||||
import static android.app.AppOpsManager.MAX_PRIORITY_UID_STATE;
|
import static android.app.AppOpsManager.MAX_PRIORITY_UID_STATE;
|
||||||
import static android.app.AppOpsManager.MIN_PRIORITY_UID_STATE;
|
import static android.app.AppOpsManager.MIN_PRIORITY_UID_STATE;
|
||||||
|
import static android.app.AppOpsManager.OP_CAMERA;
|
||||||
import static android.app.AppOpsManager.OP_FLAGS_ALL;
|
import static android.app.AppOpsManager.OP_FLAGS_ALL;
|
||||||
import static android.app.AppOpsManager.OP_NONE;
|
import static android.app.AppOpsManager.OP_NONE;
|
||||||
import static android.app.AppOpsManager.OP_PLAY_AUDIO;
|
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_BACKGROUND;
|
||||||
import static android.app.AppOpsManager.UID_STATE_CACHED;
|
import static android.app.AppOpsManager.UID_STATE_CACHED;
|
||||||
import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
|
import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
|
||||||
@@ -173,6 +175,12 @@ public class AppOpsService extends IAppOpsService.Stub {
|
|||||||
UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_NONEXISTENT
|
UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_NONEXISTENT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final int[] OPS_RESTRICTED_ON_SUSPEND = {
|
||||||
|
OP_PLAY_AUDIO,
|
||||||
|
OP_RECORD_AUDIO,
|
||||||
|
OP_CAMERA,
|
||||||
|
};
|
||||||
|
|
||||||
Context mContext;
|
Context mContext;
|
||||||
final AtomicFile mFile;
|
final AtomicFile mFile;
|
||||||
final Handler mHandler;
|
final Handler mHandler;
|
||||||
@@ -784,11 +792,12 @@ public class AppOpsService extends IAppOpsService.Stub {
|
|||||||
final int[] changedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
|
final int[] changedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
|
||||||
final String[] changedPkgs = intent.getStringArrayExtra(
|
final String[] changedPkgs = intent.getStringArrayExtra(
|
||||||
Intent.EXTRA_CHANGED_PACKAGE_LIST);
|
Intent.EXTRA_CHANGED_PACKAGE_LIST);
|
||||||
|
for (int code : OPS_RESTRICTED_ON_SUSPEND) {
|
||||||
ArraySet<ModeCallback> callbacks;
|
ArraySet<ModeCallback> callbacks;
|
||||||
synchronized (AppOpsService.this) {
|
synchronized (AppOpsService.this) {
|
||||||
callbacks = mOpModeWatchers.get(OP_PLAY_AUDIO);
|
callbacks = mOpModeWatchers.get(code);
|
||||||
if (callbacks == null) {
|
if (callbacks == null) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
callbacks = new ArraySet<>(callbacks);
|
callbacks = new ArraySet<>(callbacks);
|
||||||
}
|
}
|
||||||
@@ -797,7 +806,8 @@ public class AppOpsService extends IAppOpsService.Stub {
|
|||||||
final String changedPkg = changedPkgs[i];
|
final String changedPkg = changedPkgs[i];
|
||||||
// We trust packagemanager to insert matching uid and packageNames in the
|
// We trust packagemanager to insert matching uid and packageNames in the
|
||||||
// extras
|
// extras
|
||||||
notifyOpChanged(callbacks, OP_PLAY_AUDIO, changedUid, changedPkg);
|
notifyOpChanged(callbacks, code, changedUid, changedPkg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, packageSuspendFilter);
|
}, packageSuspendFilter);
|
||||||
@@ -1800,6 +1810,9 @@ public class AppOpsService extends IAppOpsService.Stub {
|
|||||||
*/
|
*/
|
||||||
private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName,
|
private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName,
|
||||||
boolean raw, boolean verify) {
|
boolean raw, boolean verify) {
|
||||||
|
if (isOpRestrictedDueToSuspend(code, packageName, uid)) {
|
||||||
|
return AppOpsManager.MODE_IGNORED;
|
||||||
|
}
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (verify) {
|
if (verify) {
|
||||||
checkPackage(uid, packageName);
|
checkPackage(uid, packageName);
|
||||||
@@ -2654,6 +2667,14 @@ public class AppOpsService extends IAppOpsService.Stub {
|
|||||||
return op;
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 isOpRestrictedLocked(int uid, int code, String packageName) {
|
private boolean isOpRestrictedLocked(int uid, int code, String packageName) {
|
||||||
int userHandle = UserHandle.getUserId(uid);
|
int userHandle = UserHandle.getUserId(uid);
|
||||||
final int restrictionSetCount = mOpUserRestrictions.size();
|
final int restrictionSetCount = mOpUserRestrictions.size();
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ package com.android.server.pm;
|
|||||||
|
|
||||||
import static android.app.AppOpsManager.MODE_ALLOWED;
|
import static android.app.AppOpsManager.MODE_ALLOWED;
|
||||||
import static android.app.AppOpsManager.MODE_IGNORED;
|
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_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.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -39,7 +42,6 @@ import android.content.pm.LauncherApps;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.SuspendDialogInfo;
|
import android.content.pm.SuspendDialogInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.media.AudioAttributes;
|
|
||||||
import android.os.BaseBundle;
|
import android.os.BaseBundle;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -551,28 +553,42 @@ public class SuspendPackagesTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAudioOpBlockedOnSuspend() throws Exception {
|
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(
|
final IAppOpsService iAppOps = IAppOpsService.Stub.asInterface(
|
||||||
ServiceManager.getService(Context.APP_OPS_SERVICE));
|
ServiceManager.getService(Context.APP_OPS_SERVICE));
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
final IAppOpsCallback watcher = new IAppOpsCallback.Stub() {
|
final IAppOpsCallback watcher = new IAppOpsCallback.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void opChanged(int op, int uid, String packageName) {
|
public void opChanged(int op, int uid, String packageName) {
|
||||||
if (op == OP_PLAY_AUDIO && packageName.equals(TEST_APP_PACKAGE_NAME)) {
|
if (op == code && packageName.equals(TEST_APP_PACKAGE_NAME)) {
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
iAppOps.startWatchingMode(OP_PLAY_AUDIO, TEST_APP_PACKAGE_NAME, watcher);
|
iAppOps.startWatchingMode(code, TEST_APP_PACKAGE_NAME, watcher);
|
||||||
final int testPackageUid = mPackageManager.getPackageUid(TEST_APP_PACKAGE_NAME, 0);
|
final int testPackageUid = mPackageManager.getPackageUid(TEST_APP_PACKAGE_NAME, 0);
|
||||||
int audioOpMode = iAppOps.checkAudioOperation(OP_PLAY_AUDIO,
|
int opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME);
|
||||||
AudioAttributes.USAGE_UNKNOWN, testPackageUid, TEST_APP_PACKAGE_NAME);
|
assertEquals("Op " + opToName(code) + " disallowed for unsuspended package", MODE_ALLOWED,
|
||||||
assertEquals("Audio muted for unsuspended package", MODE_ALLOWED, audioOpMode);
|
opMode);
|
||||||
suspendTestPackage(null, null, null);
|
suspendTestPackage(null, null, null);
|
||||||
assertTrue("AppOpsWatcher did not callback", latch.await(5, TimeUnit.SECONDS));
|
assertTrue("AppOpsWatcher did not callback", latch.await(5, TimeUnit.SECONDS));
|
||||||
audioOpMode = iAppOps.checkAudioOperation(OP_PLAY_AUDIO,
|
opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME);
|
||||||
AudioAttributes.USAGE_UNKNOWN, testPackageUid, TEST_APP_PACKAGE_NAME);
|
assertEquals("Op " + opToName(code) + " allowed for suspended package", MODE_IGNORED,
|
||||||
assertEquals("Audio not muted for suspended package", MODE_IGNORED, audioOpMode);
|
opMode);
|
||||||
iAppOps.stopWatchingMode(watcher);
|
iAppOps.stopWatchingMode(watcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user