summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/AppOpsManager.java10
-rw-r--r--core/java/android/permission/PermissionUsageHelper.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java99
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java22
-rw-r--r--services/core/java/com/android/server/appop/DiscreteRegistry.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java76
-rw-r--r--services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java5
8 files changed, 175 insertions, 74 deletions
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 9bd6c750fb13..187b54a6c3fc 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -742,6 +742,13 @@ public class AppOpsManager {
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 {}
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 791764b4342f..03f94c549512 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -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;
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 534f93ec0e47..9676a57b2df9 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -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
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 78c67170d185..61a651234e0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -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());
@@ -321,6 +322,36 @@ public class AppOpsControllerTest extends SysuiTestCase {
}
@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
TestHandler testHandler = new TestHandler(mTestableLooper.getLooper());
@@ -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());
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index e001b059b85e..7789d604461c 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -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;
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 0439660f0c97..b9cc992b438e 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -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;
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 30eccca48ee9..fa4c985a1c44 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -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,
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
index 264ef87c97bf..f7950aacedc3 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -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);
}