Merge "Create "trusted chain" mechanism for AttributionSource" into sc-dev

This commit is contained in:
Nate Myren
2021-07-02 20:03:33 +00:00
committed by Android (Google) Code Review
8 changed files with 175 additions and 74 deletions

View File

@@ -741,6 +741,13 @@ public class AppOpsManager {
@TestApi
public static final int ATTRIBUTION_FLAG_RECEIVER = 0x4;
/**
* Attribution chain flag: Specifies that all attribution sources in the chain were trusted.
* Must only be set by system server.
* @hide
*/
public static final int ATTRIBUTION_FLAG_TRUSTED = 0x8;
/**
* No attribution flags.
* @hide
@@ -760,7 +767,8 @@ public class AppOpsManager {
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
ATTRIBUTION_FLAG_ACCESSOR,
ATTRIBUTION_FLAG_INTERMEDIARY,
ATTRIBUTION_FLAG_RECEIVER
ATTRIBUTION_FLAG_RECEIVER,
ATTRIBUTION_FLAG_TRUSTED
})
public @interface AttributionFlags {}

View File

@@ -19,9 +19,11 @@ package android.permission;
import static android.Manifest.permission_group.CAMERA;
import static android.Manifest.permission_group.LOCATION;
import static android.Manifest.permission_group.MICROPHONE;
import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
import static android.app.AppOpsManager.ATTRIBUTION_FLAGS_NONE;
import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
import static android.app.AppOpsManager.ATTRIBUTION_FLAG_TRUSTED;
import static android.app.AppOpsManager.AttributionFlags;
import static android.app.AppOpsManager.OPSTR_CAMERA;
import static android.app.AppOpsManager.OPSTR_COARSE_LOCATION;
@@ -180,7 +182,10 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
public void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
@Nullable String attributionTag, boolean active, @AttributionFlags int attributionFlags,
int attributionChainId) {
if ((attributionFlags & ATTRIBUTION_FLAGS_NONE) != 0) {
if (attributionChainId == ATTRIBUTION_CHAIN_ID_NONE
|| attributionFlags == ATTRIBUTION_FLAGS_NONE
|| (attributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) {
// If this is not a chain, or it is untrusted, return
return;
}

View File

@@ -64,7 +64,7 @@ import javax.inject.Inject;
*/
@SysUISingleton
public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsController,
AppOpsManager.OnOpActiveChangedInternalListener,
AppOpsManager.OnOpActiveChangedListener,
AppOpsManager.OnOpNotedListener, IndividualSensorPrivacyController.Callback,
Dumpable {
@@ -359,11 +359,29 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
mBGHandler.post(() -> notifySuscribersWorker(code, uid, packageName, active));
}
/**
* Required to override, delegate to other. Should not be called.
*/
public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
onOpActiveChanged(op, uid, packageName, null, active,
AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
}
// Get active app ops, and check if their attributions are trusted
@Override
public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
public void onOpActiveChanged(String op, int uid, String packageName, String attributionTag,
boolean active, int attributionFlags, int attributionChainId) {
int code = AppOpsManager.strOpToOp(op);
if (DEBUG) {
Log.w(TAG, String.format("onActiveChanged(%d,%d,%s,%s", code, uid, packageName,
Boolean.toString(active)));
Log.w(TAG, String.format("onActiveChanged(%d,%d,%s,%s,%d,%d)", code, uid, packageName,
Boolean.toString(active), attributionChainId, attributionFlags));
}
if (attributionChainId != AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE
&& attributionFlags != AppOpsManager.ATTRIBUTION_FLAGS_NONE
&& (attributionFlags & AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR) == 0
&& (attributionFlags & AppOpsManager.ATTRIBUTION_FLAG_TRUSTED) == 0) {
// if this attribution chain isn't trusted, and this isn't the accessor, do not show it.
return;
}
boolean activeChanged = updateActives(code, uid, packageName, active);
if (!activeChanged) return; // early return

View File

@@ -77,6 +77,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
private static final int TEST_UID = UserHandle.getUid(0, 0);
private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
private static final int TEST_CHAIN_ID = 1;
@Mock
private AppOpsManager mAppOpsManager;
@@ -162,7 +163,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
mCallback);
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
mTestableLooper.processAllMessages();
@@ -174,7 +175,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
public void addCallback_notIncludedCode() {
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
verify(mCallback, never()).onActiveStateChanged(
anyInt(), anyInt(), anyString(), anyBoolean());
@@ -185,7 +186,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
mController.removeCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
verify(mCallback, never()).onActiveStateChanged(
anyInt(), anyInt(), anyString(), anyBoolean());
@@ -196,7 +197,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback);
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
TEST_UID, TEST_PACKAGE_NAME, true);
@@ -204,18 +205,18 @@ public class AppOpsControllerTest extends SysuiTestCase {
@Test
public void getActiveItems_sameDetails() {
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO,
TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO,
TEST_UID, TEST_PACKAGE_NAME, true);
assertEquals(1, mController.getActiveAppOps().size());
}
@Test
public void getActiveItems_differentDetails() {
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO,
TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
mController.onOpActiveChanged(AppOpsManager.OPSTR_CAMERA,
TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION,
TEST_UID, TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME,
@@ -225,9 +226,9 @@ public class AppOpsControllerTest extends SysuiTestCase {
@Test
public void getActiveItemsForUser() {
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO,
TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpActiveChanged(AppOpsManager.OP_CAMERA,
mController.onOpActiveChanged(AppOpsManager.OPSTR_CAMERA,
TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION,
TEST_UID, TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME,
@@ -242,11 +243,11 @@ public class AppOpsControllerTest extends SysuiTestCase {
public void systemAndExemptedRolesAreIgnored() {
assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals(""));
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO,
TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
assertEquals(0, mController.getActiveAppOpsForUser(
UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE), false).size());
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO,
TEST_UID_NON_USER_SENSITIVE, SYSTEM_PKG, true);
assertEquals(0, mController.getActiveAppOpsForUser(
UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE), false).size());
@@ -257,7 +258,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals(""));
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO,
TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
mTestableLooper.processAllMessages();
@@ -279,8 +280,8 @@ public class AppOpsControllerTest extends SysuiTestCase {
mController.setBGHandler(mMockHandler);
mController.setListening(true);
mController.onOpActiveChanged(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
true);
mController.onOpActiveChanged(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID,
TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
assertFalse(mController.getActiveAppOps().isEmpty());
@@ -320,6 +321,36 @@ public class AppOpsControllerTest extends SysuiTestCase {
verify(mMockHandler, times(2)).scheduleRemoval(any(), anyLong());
}
@Test
public void testUntrustedChainUsagesDiscarded() {
assertTrue(mController.getActiveAppOps().isEmpty());
mController.setBGHandler(mMockHandler);
//untrusted receiver access
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID,
TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true,
AppOpsManager.ATTRIBUTION_FLAG_RECEIVER, TEST_CHAIN_ID);
//untrusted intermediary access
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID,
TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true,
AppOpsManager.ATTRIBUTION_FLAG_INTERMEDIARY, TEST_CHAIN_ID);
assertTrue(mController.getActiveAppOps().isEmpty());
}
@Test
public void testTrustedChainUsagesKept() {
//untrusted accessor access
mController.onOpActiveChanged(AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID,
TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true,
AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR, TEST_CHAIN_ID);
//trusted access
mController.onOpActiveChanged(AppOpsManager.OPSTR_CAMERA, TEST_UID,
TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, true,
AppOpsManager.ATTRIBUTION_FLAG_RECEIVER | AppOpsManager.ATTRIBUTION_FLAG_TRUSTED,
TEST_CHAIN_ID);
assertEquals(2, mController.getActiveAppOps().size());
}
@Test
public void testActiveOpNotRemovedAfterNoted() throws InterruptedException {
// Replaces the timeout delay with 5 ms
@@ -329,7 +360,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mController.setBGHandler(testHandler);
mController.onOpActiveChanged(
AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
@@ -364,7 +395,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
mController.onOpActiveChanged(
AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
List<AppOpItem> list = mController.getActiveAppOps();
@@ -375,7 +406,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
assertEquals(2, list.size());
mController.onOpActiveChanged(
AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false);
AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false);
mTestableLooper.processAllMessages();
@@ -393,7 +424,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
mController.onOpActiveChanged(
AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
verify(mCallback).onActiveStateChanged(
@@ -405,7 +436,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
mController.onOpActiveChanged(
AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
@@ -421,7 +452,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
verify(mCallback, never())
@@ -434,7 +465,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
assertTrue(mController.getActiveAppOps().isEmpty());
@@ -446,7 +477,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OP_PHONE_CALL_MICROPHONE, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
assertTrue(mController.getActiveAppOps().isEmpty());
@@ -461,7 +492,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
verify(mCallback).onActiveStateChanged(
@@ -477,7 +508,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
mRecordingCallback.onRecordingConfigChanged(Collections.emptyList());
@@ -493,7 +524,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
AudioRecordingConfiguration mockARC = mock(AudioRecordingConfiguration.class);
@@ -516,7 +547,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mCallback);
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
List<AppOpItem> list = mController.getActiveAppOps();
assertEquals(1, list.size());
@@ -526,7 +557,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
// Add a camera op, and disable the microphone. The camera op should be the only op returned
mController.onSensorBlockedChanged(MICROPHONE, true);
mController.onOpActiveChanged(
AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
list = mController.getActiveAppOps();
assertEquals(1, list.size());
@@ -550,7 +581,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mCallback);
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OP_PHONE_CALL_MICROPHONE, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
List<AppOpItem> list = mController.getActiveAppOps();
assertEquals(1, list.size());
@@ -560,7 +591,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
// Add a camera op, and disable the microphone. The camera op should be the only op returned
mController.onSensorBlockedChanged(MICROPHONE, true);
mController.onOpActiveChanged(
AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
list = mController.getActiveAppOps();
assertEquals(1, list.size());
@@ -583,7 +614,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mCallback);
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
List<AppOpItem> list = mController.getActiveAppOps();
assertEquals(1, list.size());
@@ -593,7 +624,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
// Add an audio op, and disable the camera. The audio op should be the only op returned
mController.onSensorBlockedChanged(CAMERA, true);
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
list = mController.getActiveAppOps();
assertEquals(1, list.size());
@@ -616,7 +647,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
mCallback);
mTestableLooper.processAllMessages();
mController.onOpActiveChanged(
AppOpsManager.OP_PHONE_CALL_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_PHONE_CALL_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
List<AppOpItem> list = mController.getActiveAppOps();
assertEquals(1, list.size());
@@ -626,7 +657,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
// Add an audio op, and disable the camera. The audio op should be the only op returned
mController.onSensorBlockedChanged(CAMERA, true);
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
list = mController.getActiveAppOps();
assertEquals(1, list.size());

View File

@@ -20,6 +20,7 @@ import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
import static android.app.AppOpsManager.ATTRIBUTION_FLAG_TRUSTED;
import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP;
import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
@@ -3332,7 +3333,8 @@ public class AppOpsService extends IAppOpsService.Stub {
verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
skipProxyOperation = resolveSkipProxyOperation(skipProxyOperation, attributionSource);
skipProxyOperation = skipProxyOperation
&& isCallerAndAttributionTrusted(attributionSource);
String resolveProxyPackageName = AppOpsManager.resolvePackageName(proxyUid,
proxyPackageName);
@@ -3849,7 +3851,8 @@ public class AppOpsService extends IAppOpsService.Stub {
verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
skipProxyOperation = resolveSkipProxyOperation(skipProxyOperation, attributionSource);
boolean isCallerTrusted = isCallerAndAttributionTrusted(attributionSource);
skipProxyOperation = isCallerTrusted && skipProxyOperation;
String resolvedProxyPackageName = AppOpsManager.resolvePackageName(proxyUid,
proxyPackageName);
@@ -3858,11 +3861,15 @@ public class AppOpsService extends IAppOpsService.Stub {
proxiedPackageName);
}
final boolean isChainTrusted = isCallerTrusted
&& attributionChainId != ATTRIBUTION_CHAIN_ID_NONE
&& ((proxyAttributionFlags & ATTRIBUTION_FLAG_TRUSTED) != 0
|| (proxiedAttributionFlags & ATTRIBUTION_FLAG_TRUSTED) != 0);
final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
final boolean isProxyTrusted = mContext.checkPermission(
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
== PackageManager.PERMISSION_GRANTED || isSelfBlame
|| attributionChainId != ATTRIBUTION_CHAIN_ID_NONE;
|| isChainTrusted;
String resolvedProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
proxiedPackageName);
@@ -4051,7 +4058,8 @@ public class AppOpsService extends IAppOpsService.Stub {
final String proxiedAttributionTag = attributionSource.getNextAttributionTag();
final IBinder proxiedToken = attributionSource.getNextToken();
skipProxyOperation = resolveSkipProxyOperation(skipProxyOperation, attributionSource);
skipProxyOperation = skipProxyOperation
&& isCallerAndAttributionTrusted(attributionSource);
verifyIncomingProxyUid(attributionSource);
verifyIncomingOp(code);
@@ -4334,11 +4342,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
private boolean resolveSkipProxyOperation(boolean requestsSkipProxyOperation,
@NonNull AttributionSource attributionSource) {
if (!requestsSkipProxyOperation) {
return false;
}
private boolean isCallerAndAttributionTrusted(@NonNull AttributionSource attributionSource) {
if (attributionSource.getUid() != Binder.getCallingUid()
&& attributionSource.isTrusted(mContext)) {
return true;

View File

@@ -19,6 +19,7 @@ package com.android.server.appop;
import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
import static android.app.AppOpsManager.ATTRIBUTION_FLAG_TRUSTED;
import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
@@ -359,7 +360,8 @@ final class DiscreteRegistry {
for (int opEventNum = 0; opEventNum < nOpEvents; opEventNum++) {
DiscreteOpEvent event = opEvents.get(opEventNum);
if (event == null
|| event.mAttributionChainId == ATTRIBUTION_CHAIN_ID_NONE) {
|| event.mAttributionChainId == ATTRIBUTION_CHAIN_ID_NONE
|| (event.mAttributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) {
continue;
}

View File

@@ -20,7 +20,10 @@ import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY;
import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.RECORD_AUDIO;
import static android.Manifest.permission.UPDATE_APP_OPS_STATS;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
import static android.app.AppOpsManager.ATTRIBUTION_FLAGS_NONE;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_ERRORED;
import static android.app.AppOpsManager.MODE_IGNORED;
@@ -5601,13 +5604,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@PermissionCheckerManager.PermissionResult
public int checkOp(int op, AttributionSourceState attributionSource,
String message, boolean forDataDelivery, boolean startDataDelivery) {
int result = checkOp(mContext, op, new AttributionSource(attributionSource), message,
forDataDelivery, startDataDelivery);
int result = checkOp(mContext, op, mPermissionManagerServiceInternal,
new AttributionSource(attributionSource), message, forDataDelivery,
startDataDelivery);
if (result != PermissionChecker.PERMISSION_GRANTED && startDataDelivery) {
// Finish any started op if some step in the attribution chain failed.
finishDataDelivery(op, attributionSource, /*fromDatasource*/ false);
}
return result;
return result;
}
@PermissionCheckerManager.PermissionResult
@@ -5731,8 +5735,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final int op = AppOpsManager.permissionToOpCode(permission);
final int attributionChainId =
getAttributionChainId(startDataDelivery, attributionSource);
final boolean hasChain = attributionChainId != ATTRIBUTION_CHAIN_ID_NONE;
AttributionSource current = attributionSource;
AttributionSource next = null;
// We consider the chain trusted if the start node has UPDATE_APP_OPS_STATS, and
// every attributionSource in the chain is registered with the system.
final boolean isChainStartTrusted = !hasChain || checkPermission(context,
permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current.getUid(),
current.getRenouncedPermissions());
while (true) {
final boolean skipCurrentChecks = (fromDatasource || next != null);
@@ -5777,13 +5787,17 @@ public class PermissionManagerService extends IPermissionManager.Stub {
&& current.equals(attributionSource)
&& next != null && next.getNext() == null);
final boolean selfAccess = singleReceiverFromDatasource || next == null;
final boolean isLinkTrusted = isChainStartTrusted
&& (current.isTrusted(context) || current.equals(attributionSource))
&& (next == null || next.isTrusted(context));
final int proxyAttributionFlags = (!skipCurrentChecks)
final int proxyAttributionFlags = (!skipCurrentChecks && hasChain)
? resolveProxyAttributionFlags(attributionSource, current, fromDatasource,
startDataDelivery, selfAccess)
: AppOpsManager.ATTRIBUTION_FLAGS_NONE;
final int proxiedAttributionFlags = resolveProxiedAttributionFlags(
attributionSource, next, fromDatasource, startDataDelivery, selfAccess);
startDataDelivery, selfAccess, isLinkTrusted)
: ATTRIBUTION_FLAGS_NONE;
final int proxiedAttributionFlags = hasChain ? resolveProxiedAttributionFlags(
attributionSource, next, fromDatasource, startDataDelivery, selfAccess,
isLinkTrusted) : ATTRIBUTION_FLAGS_NONE;
final int opMode = performOpTransaction(context, op, current, message,
forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
@@ -5838,48 +5852,52 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private static @AttributionFlags int resolveProxyAttributionFlags(
@NonNull AttributionSource attributionChain,
@NonNull AttributionSource current, boolean fromDatasource,
boolean startDataDelivery, boolean selfAccess) {
boolean startDataDelivery, boolean selfAccess, boolean isTrusted) {
return resolveAttributionFlags(attributionChain, current, fromDatasource,
startDataDelivery, selfAccess, /*flagsForProxy*/ true);
startDataDelivery, selfAccess, isTrusted, /*flagsForProxy*/ true);
}
private static @AttributionFlags int resolveProxiedAttributionFlags(
@NonNull AttributionSource attributionChain,
@NonNull AttributionSource current, boolean fromDatasource,
boolean startDataDelivery, boolean selfAccess) {
boolean startDataDelivery, boolean selfAccess, boolean isTrusted) {
return resolveAttributionFlags(attributionChain, current, fromDatasource,
startDataDelivery, selfAccess, /*flagsForProxy*/ false);
startDataDelivery, selfAccess, isTrusted, /*flagsForProxy*/ false);
}
private static @AttributionFlags int resolveAttributionFlags(
@NonNull AttributionSource attributionChain,
@NonNull AttributionSource current, boolean fromDatasource,
boolean startDataDelivery, boolean selfAccess, boolean flagsForProxy) {
boolean startDataDelivery, boolean selfAccess, boolean isTrusted,
boolean flagsForProxy) {
if (current == null || !startDataDelivery) {
return AppOpsManager.ATTRIBUTION_FLAGS_NONE;
}
int trustedFlag = isTrusted
? AppOpsManager.ATTRIBUTION_FLAG_TRUSTED : AppOpsManager.ATTRIBUTION_FLAGS_NONE;
if (flagsForProxy) {
if (selfAccess) {
return AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
return trustedFlag | AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
} else if (!fromDatasource && current.equals(attributionChain)) {
return AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
return trustedFlag | AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
}
} else {
if (selfAccess) {
return AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
return trustedFlag | AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
} else if (fromDatasource && current.equals(attributionChain.getNext())) {
return AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
return trustedFlag | AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
} else if (current.getNext() == null) {
return AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
return trustedFlag | AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
}
}
if (fromDatasource && current.equals(attributionChain)) {
return AppOpsManager.ATTRIBUTION_FLAGS_NONE;
}
return AppOpsManager.ATTRIBUTION_FLAG_INTERMEDIARY;
return trustedFlag | AppOpsManager.ATTRIBUTION_FLAG_INTERMEDIARY;
}
private static int checkOp(@NonNull Context context, @NonNull int op,
@NonNull PermissionManagerServiceInternal permissionManagerServiceInt,
@NonNull AttributionSource attributionSource, @Nullable String message,
boolean forDataDelivery, boolean startDataDelivery) {
if (op < 0 || attributionSource.getPackageName() == null) {
@@ -5888,10 +5906,17 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final int attributionChainId =
getAttributionChainId(startDataDelivery, attributionSource);
final boolean hasChain = attributionChainId != ATTRIBUTION_CHAIN_ID_NONE;
AttributionSource current = attributionSource;
AttributionSource next = null;
// We consider the chain trusted if the start node has UPDATE_APP_OPS_STATS, and
// every attributionSource in the chain is registered with the system.
final boolean isChainStartTrusted = !hasChain || checkPermission(context,
permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current.getUid(),
current.getRenouncedPermissions());
while (true) {
final boolean skipCurrentChecks = (next != null);
next = current.getNext();
@@ -5904,14 +5929,17 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// The access is for oneself if this is the single attribution source in the chain.
final boolean selfAccess = (next == null);
final boolean isLinkTrusted = isChainStartTrusted
&& (current.isTrusted(context) || current.equals(attributionSource))
&& (next == null || next.isTrusted(context));
final int proxyAttributionFlags = (!skipCurrentChecks)
final int proxyAttributionFlags = (!skipCurrentChecks && hasChain)
? resolveProxyAttributionFlags(attributionSource, current,
/*fromDatasource*/ false, startDataDelivery, selfAccess)
: AppOpsManager.ATTRIBUTION_FLAGS_NONE;
final int proxiedAttributionFlags = resolveProxiedAttributionFlags(
/*fromDatasource*/ false, startDataDelivery, selfAccess,
isLinkTrusted) : ATTRIBUTION_FLAGS_NONE;
final int proxiedAttributionFlags = hasChain ? resolveProxiedAttributionFlags(
attributionSource, next, /*fromDatasource*/ false, startDataDelivery,
selfAccess);
selfAccess, isLinkTrusted) : ATTRIBUTION_FLAGS_NONE;
final int opMode = performOpTransaction(context, op, current, message,
forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,

View File

@@ -29,6 +29,7 @@ import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.permission.PermissionManager;
import android.speech.IRecognitionListener;
import android.speech.IRecognitionService;
import android.speech.IRecognitionServiceManagerCallback;
@@ -139,6 +140,10 @@ final class SpeechRecognitionManagerServiceImpl extends
@NonNull AttributionSource attributionSource)
throws RemoteException {
attributionSource.enforceCallingUid();
if (!attributionSource.isTrusted(mMaster.getContext())) {
mMaster.getContext().getSystemService(PermissionManager.class)
.registerAttributionSource(attributionSource);
}
service.startListening(recognizerIntent, listener, attributionSource);
}