summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/content/src/com/android/commands/content/Content.java24
-rw-r--r--cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java19
-rw-r--r--core/api/current.txt29
-rw-r--r--core/api/system-current.txt14
-rw-r--r--core/api/test-current.txt6
-rw-r--r--core/java/android/app/Activity.java29
-rw-r--r--core/java/android/app/AppOpsManager.java245
-rw-r--r--core/java/android/app/AppOpsManagerInternal.java54
-rw-r--r--core/java/android/app/ContextImpl.java96
-rw-r--r--core/java/android/app/IActivityManager.aidl2
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java1
-rw-r--r--core/java/android/content/AttributionSource.aidl19
-rw-r--r--core/java/android/content/AttributionSource.java612
-rw-r--r--core/java/android/content/ContentProvider.java430
-rw-r--r--core/java/android/content/ContentProviderClient.java34
-rw-r--r--core/java/android/content/ContentProviderNative.java181
-rw-r--r--core/java/android/content/ContentResolver.java44
-rw-r--r--core/java/android/content/Context.java15
-rw-r--r--core/java/android/content/ContextParams.java102
-rw-r--r--core/java/android/content/ContextWrapper.java6
-rw-r--r--core/java/android/content/IContentProvider.java68
-rw-r--r--core/java/android/content/PermissionChecker.java764
-rw-r--r--core/java/android/permission/IPermissionManager.aidl5
-rw-r--r--core/java/android/permission/PermissionManager.java43
-rw-r--r--core/java/android/provider/DocumentsProvider.java42
-rw-r--r--core/java/android/provider/Settings.java22
-rw-r--r--core/java/android/speech/IRecognitionService.aidl14
-rw-r--r--core/java/android/speech/RecognitionService.java131
-rw-r--r--core/java/android/speech/SpeechRecognizer.java15
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl22
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--core/tests/coretests/src/android/content/ContentResolverTest.java2
-rw-r--r--core/tests/coretests/src/android/provider/NameValueCacheTest.java30
-rw-r--r--core/tests/coretests/src/android/provider/TestDocumentsProvider.java8
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java47
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java14
-rw-r--r--packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java7
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java21
-rw-r--r--packages/Shell/AndroidManifest.xml5
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java145
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java18
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java375
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java86
-rw-r--r--services/core/java/com/android/server/policy/AppOpsPolicy.java64
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java33
-rw-r--r--services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java87
-rw-r--r--services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java2
-rw-r--r--services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java52
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java45
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java6
-rw-r--r--services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java8
-rw-r--r--test-mock/src/android/test/mock/MockContentProvider.java45
-rw-r--r--test-mock/src/android/test/mock/MockIContentProvider.java44
54 files changed, 3131 insertions, 1107 deletions
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index ca1d598ee7d7..f0ac53014bd3 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -19,6 +19,7 @@ package com.android.commands.content;
import android.app.ActivityManager;
import android.app.ContentProviderHolder;
import android.app.IActivityManager;
+import android.content.AttributionSource;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.IContentProvider;
@@ -562,7 +563,8 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.insert(resolveCallingPackage(), null, mUri, mContentValues, mExtras);
+ provider.insert(new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), mUri, mContentValues, mExtras);
}
}
@@ -576,7 +578,8 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.delete(resolveCallingPackage(), null, mUri, mExtras);
+ provider.delete(new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), mUri, mExtras);
}
}
@@ -593,7 +596,8 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Bundle result = provider.call(null, null, mUri.getAuthority(), mMethod, mArg, mExtras);
+ Bundle result = provider.call(new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), mUri.getAuthority(), mMethod, mArg, mExtras);
if (result != null) {
result.size(); // unpack
}
@@ -620,7 +624,9 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- try (ParcelFileDescriptor fd = provider.openFile(null, null, mUri, "r", null, null)) {
+ try (ParcelFileDescriptor fd = provider.openFile(
+ new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), mUri, "r", null)) {
FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out);
}
}
@@ -633,7 +639,8 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- try (ParcelFileDescriptor fd = provider.openFile(null, null, mUri, "w", null, null)) {
+ try (ParcelFileDescriptor fd = provider.openFile(new AttributionSource(
+ Binder.getCallingUid(), resolveCallingPackage(), null), mUri, "w", null)) {
FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor());
}
}
@@ -651,8 +658,8 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Cursor cursor = provider.query(resolveCallingPackage(), null, mUri, mProjection,
- mExtras, null);
+ Cursor cursor = provider.query(new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), mUri, mProjection, mExtras, null);
if (cursor == null) {
System.out.println("No result found.");
return;
@@ -716,7 +723,8 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.update(resolveCallingPackage(), null, mUri, mValues, mExtras);
+ provider.update(new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), mUri, mValues, mExtras);
}
}
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index b23bf5da5c8d..bc95986c083f 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -20,6 +20,7 @@ import android.app.ActivityManager;
import android.app.ContentProviderHolder;
import android.app.IActivityManager;
import android.app.UiAutomation;
+import android.content.AttributionSource;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentProvider;
@@ -28,6 +29,7 @@ import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -67,7 +69,8 @@ public class ShellUiAutomatorBridge extends UiAutomatorBridge {
throw new IllegalStateException("Could not find provider: " + providerName);
}
provider = holder.provider;
- cursor = provider.query(null, null, Settings.Secure.CONTENT_URI,
+ cursor = provider.query(new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), Settings.Secure.CONTENT_URI,
new String[] {
Settings.Secure.VALUE
},
@@ -123,4 +126,18 @@ public class ShellUiAutomatorBridge extends UiAutomatorBridge {
}
return ret;
}
+
+ private static String resolveCallingPackage() {
+ switch (Binder.getCallingUid()) {
+ case Process.ROOT_UID: {
+ return "root";
+ }
+ case Process.SHELL_UID: {
+ return "com.android.shell";
+ }
+ default: {
+ return null;
+ }
+ }
+ }
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 9b8ea6b52382..eb5f648c7190 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9907,6 +9907,27 @@ package android.content {
method @Deprecated public void setUpdateThrottle(long);
}
+ public final class AttributionSource implements android.os.Parcelable {
+ method public boolean checkCallingUid();
+ method public int describeContents();
+ method public void enforceCallingUid();
+ method @Nullable public String getAttributionTag();
+ method @Nullable public android.content.AttributionSource getNext();
+ method @Nullable public String getPackageName();
+ method public int getUid();
+ method public boolean isTrusted(@NonNull android.content.Context);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.AttributionSource> CREATOR;
+ }
+
+ public static final class AttributionSource.Builder {
+ ctor public AttributionSource.Builder(int);
+ method @NonNull public android.content.AttributionSource build();
+ method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@NonNull String);
+ method @NonNull public android.content.AttributionSource.Builder setNext(@NonNull android.content.AttributionSource);
+ method @NonNull public android.content.AttributionSource.Builder setPackageName(@NonNull String);
+ }
+
public abstract class BroadcastReceiver {
ctor public BroadcastReceiver();
method public final void abortBroadcast();
@@ -10076,6 +10097,7 @@ package android.content {
method public abstract int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]);
method public int delete(@NonNull android.net.Uri, @Nullable android.os.Bundle);
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
+ method @Nullable public final android.content.AttributionSource getCallingAttributionSource();
method @Nullable public final String getCallingAttributionTag();
method @Nullable public final String getCallingPackage();
method @Nullable public final String getCallingPackageUnchecked();
@@ -10438,6 +10460,7 @@ package android.content {
method public abstract android.content.Context getApplicationContext();
method public abstract android.content.pm.ApplicationInfo getApplicationInfo();
method public abstract android.content.res.AssetManager getAssets();
+ method @NonNull public android.content.AttributionSource getAttributionSource();
method @Nullable public String getAttributionTag();
method public abstract java.io.File getCacheDir();
method public abstract ClassLoader getClassLoader();
@@ -10643,8 +10666,7 @@ package android.content {
public final class ContextParams {
method @Nullable public String getAttributionTag();
- method @Nullable public String getReceiverAttributionTag();
- method @Nullable public String getReceiverPackage();
+ method @Nullable public android.content.AttributionSource getNextAttributionSource();
}
public static final class ContextParams.Builder {
@@ -10652,7 +10674,7 @@ package android.content {
ctor public ContextParams.Builder(@NonNull android.content.ContextParams);
method @NonNull public android.content.ContextParams build();
method @NonNull public android.content.ContextParams.Builder setAttributionTag(@Nullable String);
- method @NonNull public android.content.ContextParams.Builder setReceiverPackage(@Nullable String, @Nullable String);
+ method @NonNull public android.content.ContextParams.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
}
public class ContextWrapper extends android.content.Context {
@@ -39082,6 +39104,7 @@ package android.speech {
method public void bufferReceived(byte[]) throws android.os.RemoteException;
method public void endOfSpeech() throws android.os.RemoteException;
method public void error(int) throws android.os.RemoteException;
+ method @NonNull public android.content.AttributionSource getCallingAttributionSource();
method public int getCallingUid();
method public void partialResults(android.os.Bundle) throws android.os.RemoteException;
method public void readyForSpeech(android.os.Bundle) throws android.os.RemoteException;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 92c21f3ef3fc..3f26e140c8e6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -596,7 +596,7 @@ package android.app {
public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
method public int describeContents();
- method @Nullable public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOps(@NonNull String);
+ method @Nullable public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOps(@Nullable String);
method @NonNull public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOpsAt(@IntRange(from=0) int);
method @IntRange(from=0) public int getAttributedOpsCount();
method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
@@ -2217,6 +2217,14 @@ package android.content {
method @NonNull public java.io.File getDeviceProtectedDataDirForUser(@NonNull android.os.UserHandle);
}
+ public final class AttributionSource implements android.os.Parcelable {
+ method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public java.util.Set<java.lang.String> getRenouncedPermissions();
+ }
+
+ public static final class AttributionSource.Builder {
+ method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public android.content.AttributionSource.Builder setRenouncedPermissions(@NonNull java.util.Set<java.lang.String>);
+ }
+
public abstract class BroadcastReceiver {
method @NonNull public final android.os.UserHandle getSendingUser();
}
@@ -2291,11 +2299,11 @@ package android.content {
}
public final class ContextParams {
- method @Nullable @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public java.util.Set<java.lang.String> getRenouncedPermissions();
+ method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public java.util.Set<java.lang.String> getRenouncedPermissions();
}
public static final class ContextParams.Builder {
- method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public android.content.ContextParams.Builder setRenouncedPermissions(@Nullable java.util.Set<java.lang.String>);
+ method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public android.content.ContextParams.Builder setRenouncedPermissions(@NonNull java.util.Set<java.lang.String>);
}
public class ContextWrapper extends android.content.Context {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 21771df3c6a3..7cfe7826377a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -220,6 +220,7 @@ package android.app {
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void rebootHistory(long);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void reloadNonHistoricalState();
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void resetHistoryParameters();
+ method @RequiresPermission("android.permission.MANAGE_APPOPS") public void resetPackageOpsNoHistory(@NonNull String);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void setHistoryParameters(int, long, int);
method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(int, int, String, int);
method public static int strOpToOp(@NonNull String);
@@ -654,6 +655,11 @@ package android.bluetooth {
package android.content {
+ public final class AttributionSource implements android.os.Parcelable {
+ ctor public AttributionSource(int, @Nullable String, @Nullable String);
+ ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable android.content.AttributionSource);
+ }
+
public final class AutofillOptions implements android.os.Parcelable {
ctor public AutofillOptions(int, boolean);
method public int describeContents();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 47843cc113a2..0f38b5fdb5c3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5246,13 +5246,40 @@ public class Activity extends ContextThemeWrapper
if (requestCode < 0) {
throw new IllegalArgumentException("requestCode should be >= 0");
}
+
if (mHasCurrentPermissionsRequest) {
Log.w(TAG, "Can request only one set of permissions at a time");
// Dispatch the callback with empty arrays which means a cancellation.
onRequestPermissionsResult(requestCode, new String[0], new int[0]);
return;
}
- Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
+
+ List<String> filteredPermissions = null;
+
+ if (!getAttributionSource().getRenouncedPermissions().isEmpty()) {
+ final int permissionCount = permissions.length;
+ for (int i = 0; i < permissionCount; i++) {
+ if (getAttributionSource().getRenouncedPermissions().contains(permissions[i])) {
+ if (filteredPermissions == null) {
+ filteredPermissions = new ArrayList<>(i);
+ for (int j = 0; j < i; j++) {
+ filteredPermissions.add(permissions[i]);
+ }
+ }
+ } else if (filteredPermissions != null) {
+ filteredPermissions.add(permissions[i]);
+ }
+ }
+ }
+
+ final Intent intent;
+ if (filteredPermissions == null) {
+ intent = getPackageManager().buildRequestPermissionsIntent(permissions);
+ } else {
+ intent = getPackageManager().buildRequestPermissionsIntent(
+ filteredPermissions.toArray(new String[0]));
+ }
+
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 7e4af1ad7952..f76e1c0e50fb 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -50,6 +50,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
+import android.content.AttributionSource;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -5955,7 +5956,7 @@ public class AppOpsManager {
*
* @return The historical ops for the attribution.
*/
- public @Nullable AttributedHistoricalOps getAttributedOps(@NonNull String attributionTag) {
+ public @Nullable AttributedHistoricalOps getAttributedOps(@Nullable String attributionTag) {
if (mAttributedHistoricalOps == null) {
return null;
}
@@ -6480,7 +6481,7 @@ public class AppOpsManager {
* Gets number of discrete historical app ops.
*
* @return The number historical app ops.
- * @see #getOpAt(int)
+ * @see #getDiscreteAccessAt(int)
*/
public @IntRange(from = 0) int getDiscreteAccessCount() {
if (mDiscreteAccesses == null) {
@@ -6494,7 +6495,7 @@ public class AppOpsManager {
*
* @param index The index to lookup.
* @return The op at the given index.
- * @see #getOpCount()
+ * @see #getDiscreteAccessCount()
*/
public @NonNull AttributedOpEntry getDiscreteAccessAt(@IntRange(from = 0) int index) {
if (mDiscreteAccesses == null) {
@@ -7979,7 +7980,7 @@ public class AppOpsManager {
try {
collectNoteOpCallsForValidation(op);
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
- boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID ? true : false;
+ boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID;
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
// Set stack trace as default message
@@ -8033,14 +8034,9 @@ public class AppOpsManager {
*/
public int noteProxyOp(int op, @Nullable String proxiedPackageName, int proxiedUid,
@Nullable String proxiedAttributionTag, @Nullable String message) {
- int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, proxiedAttributionTag,
- message);
- if (mode == MODE_ERRORED) {
- throw new SecurityException("Proxy package " + mContext.getOpPackageName()
- + " from uid " + Process.myUid() + " or calling package " + proxiedPackageName
- + " from uid " + proxiedUid + " not allowed to perform " + sOpNames[op]);
- }
- return mode;
+ return noteProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
+ new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag)),
+ message, /*skipProxyOperation*/ false);
}
/**
@@ -8069,6 +8065,36 @@ public class AppOpsManager {
}
/**
+ * Make note of an application performing an operation on behalf of another application(s).
+ *
+ * @param op The operation to note. One of the OPSTR_* constants.
+ * @param attributionSource The permission identity for which to note.
+ * @param message A message describing the reason the op was noted
+ * @param skipProxyOperation Whether to skip the proxy note.
+ *
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
+ * if it is not allowed and should be silently ignored (without causing the app to crash).
+ *
+ * @throws SecurityException If the any proxying operations in the permission identityf
+ * chain fails.
+ *
+ * @hide
+ */
+ public int noteProxyOp(@NonNull int op, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean skipProxyOperation) {
+ final int mode = noteProxyOpNoThrow(op, attributionSource, message, skipProxyOperation);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException("Proxy package "
+ + attributionSource.getPackageName() + " from uid "
+ + attributionSource.getUid() + " or calling package "
+ + attributionSource.getNextPackageName() + " from uid "
+ + attributionSource.getNextUid() + " not allowed to perform "
+ + sOpNames[op]);
+ }
+ return mode;
+ }
+
+ /**
* @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String, String)} instead
*/
@Deprecated
@@ -8093,24 +8119,36 @@ public class AppOpsManager {
*/
public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
int proxiedUid, @Nullable String proxiedAttributionTag, @Nullable String message) {
- return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid,
- proxiedAttributionTag, message);
+ return noteProxyOpNoThrow(strOpToOp(op), new AttributionSource(
+ mContext.getAttributionSource(), new AttributionSource(proxiedUid,
+ proxiedPackageName, proxiedAttributionTag)), message,
+ /*skipProxyOperation*/ false);
}
/**
- * @see #noteProxyOpNoThrow(String, String, int, String, String)
+ * Make note of an application performing an operation on behalf of another application(s).
+ *
+ * @param op The operation to note. One of the OPSTR_* constants.
+ * @param attributionSource The permission identity for which to note.
+ * @param message A message describing the reason the op was noted
+ * @param skipProxyOperation Whether to note op for the proxy
+ *
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
+ * if it is not allowed and should be silently ignored (without causing the app to crash).
*
* @hide
*/
@SuppressWarnings("AndroidFrameworkClientSidePermissionCheck")
- public int noteProxyOpNoThrow(int op, @Nullable String proxiedPackageName, int proxiedUid,
- @Nullable String proxiedAttributionTag, @Nullable String message) {
+ public int noteProxyOpNoThrow(int op, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean skipProxyOperation) {
int myUid = Process.myUid();
try {
collectNoteOpCallsForValidation(op);
- int collectionMode = getNotedOpCollectionMode(proxiedUid, proxiedPackageName, op);
- boolean shouldCollectMessage = myUid == Process.SYSTEM_UID ? true : false;
+ int collectionMode = getNotedOpCollectionMode(
+ attributionSource.getNextUid(),
+ attributionSource.getNextAttributionTag(), op);
+ boolean shouldCollectMessage = (myUid == Process.SYSTEM_UID);
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
// Set stack trace as default message
@@ -8119,20 +8157,19 @@ public class AppOpsManager {
}
}
- int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName,
- proxiedAttributionTag, myUid, mContext.getOpPackageName(),
- mContext.getAttributionTag(), collectionMode == COLLECT_ASYNC, message,
- shouldCollectMessage);
+ int mode = mService.noteProxyOperation(op, attributionSource,
+ collectionMode == COLLECT_ASYNC, message,
+ shouldCollectMessage, skipProxyOperation);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
- collectNotedOpForSelf(op, proxiedAttributionTag);
+ collectNotedOpForSelf(op, attributionSource.getNextAttributionTag());
} else if (collectionMode == COLLECT_SYNC
// Only collect app-ops when the proxy is trusted
&& (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
myUid) == PackageManager.PERMISSION_GRANTED ||
- Binder.getCallingUid() == proxiedUid)) {
- collectNotedOpSync(op, proxiedAttributionTag);
+ Binder.getCallingUid() == attributionSource.getNextUid())) {
+ collectNotedOpSync(op, attributionSource.getNextAttributionTag());
}
}
@@ -8424,7 +8461,7 @@ public class AppOpsManager {
try {
collectNoteOpCallsForValidation(op);
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
- boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID ? true : false;
+ boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID;
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
// Set stack trace as default message
@@ -8450,6 +8487,7 @@ public class AppOpsManager {
throw e.rethrowFromSystemServer();
}
}
+
/**
* Report that an application has started executing a long-running operation on behalf of
* another application when handling an IPC. This function will verify that the calling uid and
@@ -8470,19 +8508,45 @@ public class AppOpsManager {
*/
public int startProxyOp(@NonNull String op, int proxiedUid, @NonNull String proxiedPackageName,
@Nullable String proxiedAttributionTag, @Nullable String message) {
- final int mode = startProxyOpNoThrow(op, proxiedUid, proxiedPackageName,
- proxiedAttributionTag, message);
+ return startProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
+ new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag)),
+ message, /*skipProxyOperation*/ false);
+ }
+
+ /**
+ * Report that an application has started executing a long-running operation on behalf of
+ * another application for the attribution chain specified by the {@link AttributionSource}}.
+ *
+ * @param op The op to note
+ * @param attributionSource The permission identity for which to check
+ * @param message A message describing the reason the op was noted
+ * @param skipProxyOperation Whether to skip the proxy start.
+ *
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
+ * if it is not allowed and should be silently ignored (without causing the app to crash).
+ *
+ * @throws SecurityException If the any proxying operations in the permission identity
+ * chain fails.
+ *
+ * @hide
+ */
+ public int startProxyOp(@NonNull String op, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean skipProxyOperation) {
+ final int mode = startProxyOpNoThrow(AppOpsManager.strOpToOp(op), attributionSource,
+ message, skipProxyOperation);
if (mode == MODE_ERRORED) {
- throw new SecurityException("Proxy package " + mContext.getOpPackageName()
- + " from uid " + Process.myUid() + " or calling package " + proxiedPackageName
- + " from uid " + proxiedUid + " not allowed to perform "
- + sOpNames[strOpToOp(op)]);
+ throw new SecurityException("Proxy package "
+ + attributionSource.getPackageName() + " from uid "
+ + attributionSource.getUid() + " or calling package "
+ + attributionSource.getNextPackageName() + " from uid "
+ + attributionSource.getNextUid() + " not allowed to perform "
+ + op);
}
return mode;
}
/**
- *Like {@link #startProxyOp(String, int, String, String, String)} but instead
+ * Like {@link #startProxyOp(String, int, String, String, String)} but instead
* of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
*
* @see #startProxyOp(String, int, String, String, String)
@@ -8490,11 +8554,28 @@ public class AppOpsManager {
public int startProxyOpNoThrow(@NonNull String op, int proxiedUid,
@NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag,
@Nullable String message) {
- try {
- int opInt = strOpToOp(op);
+ return startProxyOpNoThrow(AppOpsManager.strOpToOp(op), new AttributionSource(
+ mContext.getAttributionSource(), new AttributionSource(proxiedUid,
+ proxiedPackageName, proxiedAttributionTag)), message,
+ /*skipProxyOperation*/ false);
+ }
- collectNoteOpCallsForValidation(opInt);
- int collectionMode = getNotedOpCollectionMode(proxiedUid, proxiedPackageName, opInt);
+ /**
+ * Like {@link #startProxyOp(String, AttributionSource, String)} but instead
+ * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED} and
+ * the checks is for the attribution chain specified by the {@link AttributionSource}.
+ *
+ * @see #startProxyOp(String, AttributionSource, String)
+ *
+ * @hide
+ */
+ public int startProxyOpNoThrow(int op, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean skipProxyOperation) {
+ try {
+ collectNoteOpCallsForValidation(op);
+ int collectionMode = getNotedOpCollectionMode(
+ attributionSource.getNextUid(),
+ attributionSource.getNextPackageName(), op);
boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID;
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
@@ -8504,24 +8585,23 @@ public class AppOpsManager {
}
}
- int mode = mService.startProxyOperation(getClientId(), opInt, proxiedUid,
- proxiedPackageName, proxiedAttributionTag, Process.myUid(),
- mContext.getOpPackageName(), mContext.getAttributionTag(), false,
- collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
+ int mode = mService.startProxyOperation(getClientId(), op,
+ attributionSource, false, collectionMode == COLLECT_ASYNC, message,
+ shouldCollectMessage, skipProxyOperation);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
- collectNotedOpForSelf(opInt, proxiedAttributionTag);
+ collectNotedOpForSelf(op,
+ attributionSource.getNextAttributionTag());
} else if (collectionMode == COLLECT_SYNC
// Only collect app-ops when the proxy is trusted
&& (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
Process.myUid()) == PackageManager.PERMISSION_GRANTED
- || Binder.getCallingUid() == proxiedUid)) {
- collectNotedOpSync(opInt, proxiedAttributionTag);
+ || Binder.getCallingUid() == attributionSource.getNextUid())) {
+ collectNotedOpSync(op, attributionSource.getNextAttributionTag());
}
}
-
return mode;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -8580,22 +8660,37 @@ public class AppOpsManager {
}
/**
- * Report that an application is no longer performing an operation that had previously
+ * Report that an application is no longer performing an operation that had previously
* been started with {@link #startProxyOp(String, int, String, String, String)}. There is no
* validation of input or result; the parameters supplied here must be the exact same ones
* previously passed in when starting the operation.
+ *
* @param op The operation which was started
- * @param proxiedUid The uid the op was started on behalf of
- * @param proxiedPackageName The package the op was started on behalf of
- * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
- * attribution tag} or {@code null} for default attribution
+ * @param proxiedUid The proxied appp's UID
+ * @param proxiedPackageName The proxied appp's package name
+ * @param proxiedAttributionTag The proxied appp's attribution tag or
+ * {@code null} for default attribution
*/
public void finishProxyOp(@NonNull String op, int proxiedUid,
@NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag) {
+ finishProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
+ new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag)));
+ }
+
+ /**
+ * Report that an application is no longer performing an operation that had previously
+ * been started with {@link #startProxyOp(String, AttributionSource, String)}. There is no
+ * validation of input or result; the parameters supplied here must be the exact same ones
+ * previously passed in when starting the operation.
+ *
+ * @param op The operation which was started
+ * @param attributionSource The permission identity for which to finish
+ *
+ * @hide
+ */
+ public void finishProxyOp(@NonNull String op, @NonNull AttributionSource attributionSource) {
try {
- mService.finishProxyOperation(getClientId(), strOpToOp(op), proxiedUid,
- proxiedPackageName, proxiedAttributionTag, Process.myUid(),
- mContext.getOpPackageName(), mContext.getAttributionTag());
+ mService.finishProxyOperation(getClientId(), strOpToOp(op), attributionSource);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -8616,6 +8711,46 @@ public class AppOpsManager {
}
/**
+ * Get whether you are currently proxying to another package. That applies only
+ * for long running operations like {@link #OP_RECORD_AUDIO}.
+ *
+ * @param op The op.
+ * @param proxyAttributionTag Your attribution tag to query for.
+ * @param proxiedUid The proxied UID to query for.
+ * @param proxiedPackageName The proxied package to query for.
+ * @return Whether you are currently proxying to this target.
+ *
+ * @hide
+ */
+ public boolean isProxying(int op, @NonNull String proxyAttributionTag, int proxiedUid,
+ @NonNull String proxiedPackageName) {
+ try {
+ return mService.isProxying(op, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), proxiedUid, proxiedPackageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Clears the op state (last accesses + op modes) for a package but not
+ * the historical state.
+ *
+ * @param packageName The package to reset.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MANAGE_APPOPS)
+ public void resetPackageOpsNoHistory(@NonNull String packageName) {
+ try {
+ mService.resetPackageOpsNoHistory(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Start collection of noted appops on this thread.
*
* <p>Called at the beginning of a two way binder transaction.
@@ -8771,7 +8906,7 @@ public class AppOpsManager {
packageName = "android";
}
- // check it the appops needs to be collected and cache result
+ // check if the appops needs to be collected and cache result
if (sAppOpsToNote[op] == SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED) {
boolean shouldCollectNotes;
try {
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 5e032f00a3a0..a3d0cf2e972f 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -18,12 +18,17 @@ package android.app;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.AttributionSource;
+import android.os.IBinder;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.TriFunction;
/**
* App ops service local interface.
@@ -76,6 +81,55 @@ public abstract class AppOpsManagerInternal {
@Nullable String message, boolean shouldCollectMessage,
@NonNull HeptFunction<Integer, Integer, String, String, Boolean, String, Boolean,
Integer> superImpl);
+
+ /**
+ * Allows overriding note proxy operation behavior.
+ *
+ * @param code The op code to note.
+ * @param attributionSource The permission identity of the caller.
+ * @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
+ * @param message The message in the async noted op
+ * @param shouldCollectMessage whether to collect messages
+ * @param skipProxyOperation Whether to skip the proxy portion of the operation
+ * @param superImpl The super implementation.
+ * @return The app op note result.
+ */
+ int noteProxyOperation(int code, @NonNull AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage, boolean skipProxyOperation,
+ @NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean,
+ Boolean, Integer> superImpl);
+
+ /**
+ * Allows overriding start proxy operation behavior.
+ *
+ * @param code The op code to start.
+ * @param attributionSource The permission identity of the caller.
+ * @param startIfModeDefault Whether to start the op of the mode is default.
+ * @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
+ * @param message The message in the async noted op
+ * @param shouldCollectMessage whether to collect messages
+ * @param skipProxyOperation Whether to skip the proxy portion of the operation
+ * @param superImpl The super implementation.
+ * @return The app op note result.
+ */
+ int startProxyOperation(IBinder token, int code,
+ @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation, @NonNull OctFunction<IBinder, Integer,
+ AttributionSource, Boolean, Boolean, String, Boolean, Boolean,
+ Integer> superImpl);
+
+ /**
+ * Allows overriding finish proxy op.
+ *
+ * @param clientId Client state token.
+ * @param code The op code to finish.
+ * @param attributionSource The permission identity of the caller.
+ */
+ void finishProxyOperation(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource,
+ @NonNull TriFunction<IBinder, Integer, AttributionSource, Void> superImpl);
}
/**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 03e95fc3b6b9..f8165e9a7cf6 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -60,6 +60,7 @@ import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.content.AttributionSource;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -224,8 +225,8 @@ class ContextImpl extends Context {
private final String mBasePackageName;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mOpPackageName;
-
private final @NonNull ContextParams mParams;
+ private @NonNull AttributionSource mAttributionSource;
private final @NonNull ResourcesManager mResourcesManager;
@UnsupportedAppUsage
@@ -467,13 +468,13 @@ class ContextImpl extends Context {
/** @hide */
@Override
public String getOpPackageName() {
- return mOpPackageName != null ? mOpPackageName : getBasePackageName();
+ return mAttributionSource.getPackageName();
}
/** @hide */
@Override
public @Nullable String getAttributionTag() {
- return mParams.getAttributionTag();
+ return mAttributionSource.getAttributionTag();
}
@Override
@@ -482,6 +483,11 @@ class ContextImpl extends Context {
}
@Override
+ public @NonNull AttributionSource getAttributionSource() {
+ return mAttributionSource;
+ }
+
+ @Override
public ApplicationInfo getApplicationInfo() {
if (mPackageInfo != null) {
return mPackageInfo.getApplicationInfo();
@@ -2074,13 +2080,7 @@ class ContextImpl extends Context {
Log.v(TAG, "Treating renounced permission " + permission + " as denied");
return PERMISSION_DENIED;
}
-
- try {
- return ActivityManager.getService().checkPermissionWithToken(
- permission, pid, uid, callerToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return checkPermission(permission, pid, uid);
}
@Override
@@ -2415,8 +2415,10 @@ class ContextImpl extends Context {
LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE);
if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, ContextParams.EMPTY, null,
- mToken, new UserHandle(UserHandle.getUserId(application.uid)),
+ ContextImpl c = new ContextImpl(this, mMainThread, pi, ContextParams.EMPTY,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
+ null, mToken, new UserHandle(UserHandle.getUserId(application.uid)),
flags, null, null);
final int displayId = getDisplayId();
@@ -2446,15 +2448,19 @@ class ContextImpl extends Context {
if (packageName.equals("system") || packageName.equals("android")) {
// The system resources are loaded in every application, so we can safely copy
// the context without reloading Resources.
- return new ContextImpl(this, mMainThread, mPackageInfo, mParams, null,
- mToken, user, flags, null, null);
+ return new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
+ null, mToken, user, flags, null, null);
}
LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, mParams, null,
- mToken, user, flags, null, null);
+ ContextImpl c = new ContextImpl(this, mMainThread, pi, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
+ null, mToken, user, flags, null, null);
final int displayId = getDisplayId();
final Integer overrideDisplayId = mForceDisplayOverrideInResources
@@ -2491,8 +2497,10 @@ class ContextImpl extends Context {
final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName);
final String[] paths = mPackageInfo.getSplitPaths(splitName);
- final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
- mParams, splitName, mToken, mUser, mFlags, classLoader, null);
+ final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
+ splitName, mToken, mUser, mFlags, classLoader, null);
context.setResources(ResourcesManager.getInstance().getResources(
mToken,
@@ -2526,6 +2534,8 @@ class ContextImpl extends Context {
}
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
mSplitName, mToken, mUser, mFlags, mClassLoader, null);
final int displayId = getDisplayId();
@@ -2544,6 +2554,8 @@ class ContextImpl extends Context {
}
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
mSplitName, mToken, mUser, mFlags, mClassLoader, null);
final int displayId = display.getDisplayId();
@@ -2650,6 +2662,8 @@ class ContextImpl extends Context {
@UiContext
ContextImpl createWindowContextBase(@NonNull IBinder token, @NonNull Display display) {
ContextImpl baseContext = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
mSplitName, token, mUser, mFlags, mClassLoader, null);
// Window contexts receive configurations directly from the server and as such do not
// need to override their display in ResourcesManager.
@@ -2695,9 +2709,10 @@ class ContextImpl extends Context {
@NonNull
@Override
- public Context createContext(@NonNull ContextParams params) {
- return new ContextImpl(this, mMainThread, mPackageInfo, params, mSplitName,
- mToken, mUser, mFlags, mClassLoader, null);
+ public Context createContext(@NonNull ContextParams contextParams) {
+ return new ContextImpl(this, mMainThread, mPackageInfo, contextParams,
+ contextParams.getAttributionTag(), contextParams.getNextAttributionSource(),
+ mSplitName, mToken, mUser, mFlags, mClassLoader, null);
}
@Override
@@ -2710,16 +2725,20 @@ class ContextImpl extends Context {
public Context createDeviceProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
| Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mParams, mSplitName,
- mToken, mUser, flags, mClassLoader, null);
+ return new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
+ mSplitName, mToken, mUser, flags, mClassLoader, null);
}
@Override
public Context createCredentialProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
| Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mParams, mSplitName,
- mToken, mUser, flags, mClassLoader, null);
+ return new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
+ mSplitName, mToken, mUser, flags, mClassLoader, null);
}
@Override
@@ -2893,7 +2912,7 @@ class ContextImpl extends Context {
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
- ContextParams.EMPTY, null, null, null, 0, null, null);
+ ContextParams.EMPTY, null, null, null, null, null, 0, null, null);
context.setResources(packageInfo.getResources());
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
context.mResourcesManager.getDisplayMetrics());
@@ -2911,7 +2930,7 @@ class ContextImpl extends Context {
static ContextImpl createSystemUiContext(ContextImpl systemContext, int displayId) {
final LoadedApk packageInfo = systemContext.mPackageInfo;
ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo,
- ContextParams.EMPTY, null, null, null, 0, null, null);
+ ContextParams.EMPTY, null, null, null, null, null, 0, null, null);
context.setResources(createResources(null, packageInfo, null, displayId, null,
packageInfo.getCompatibilityInfo(), null));
context.updateDisplay(displayId);
@@ -2936,7 +2955,7 @@ class ContextImpl extends Context {
String opPackageName) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
- ContextParams.EMPTY, null, null, null, 0, null, opPackageName);
+ ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName);
context.setResources(packageInfo.getResources());
context.mContextType = isSystemOrSystemUI(context) ? CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
: CONTEXT_TYPE_NON_UI;
@@ -2966,7 +2985,7 @@ class ContextImpl extends Context {
}
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, ContextParams.EMPTY,
- activityInfo.splitName, activityToken, null, 0, classLoader, null);
+ null, null, activityInfo.splitName, activityToken, null, 0, classLoader, null);
context.mContextType = CONTEXT_TYPE_ACTIVITY;
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
@@ -2999,6 +3018,7 @@ class ContextImpl extends Context {
private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
@NonNull LoadedApk packageInfo, @NonNull ContextParams params,
+ @Nullable String attributionTag, @Nullable AttributionSource nextAttributionSource,
@Nullable String splitName, @Nullable IBinder token, @Nullable UserHandle user,
int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
mOuterContext = this;
@@ -3054,9 +3074,27 @@ class ContextImpl extends Context {
mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName;
mParams = Objects.requireNonNull(params);
+ initializeAttributionSource(attributionTag, nextAttributionSource);
mContentResolver = new ApplicationContentResolver(this, mainThread);
}
+ private void initializeAttributionSource(@Nullable String attributionTag,
+ @Nullable AttributionSource nextAttributionSource) {
+ mAttributionSource = new AttributionSource(Process.myUid(), mOpPackageName,
+ attributionTag, nextAttributionSource);
+ // If we want to access protected data on behalf of another app we need to
+ // tell the OS that we opt in to participate in the attribution chain.
+ if (nextAttributionSource != null) {
+ // If an app happened to stub the internal OS for testing the registration method
+ // can return null. In this case we keep the current untrusted attribution source.
+ final AttributionSource attributionSource = getSystemService(PermissionManager.class)
+ .registerAttributionSource(mAttributionSource);
+ if (attributionSource != null) {
+ mAttributionSource = attributionSource;
+ }
+ }
+ }
+
void setResources(Resources r) {
if (r instanceof CompatResources) {
((CompatResources) r).setContext(this);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 4c2433c04771..81e5e1d96294 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -475,8 +475,6 @@ interface IActivityManager {
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
boolean isTopOfTask(in IBinder token);
void bootAnimationComplete();
- int checkPermissionWithToken(in String permission, int pid, int uid,
- in IBinder callerToken);
@UnsupportedAppUsage
void registerTaskStackListener(in ITaskStackListener listener);
void unregisterTaskStackListener(in ITaskStackListener listener);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index a3d19ca6425c..0be7b732b4bd 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -745,7 +745,6 @@ public final class BluetoothAdapter {
* Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
*/
BluetoothAdapter(IBluetoothManager managerService) {
-
if (managerService == null) {
throw new IllegalArgumentException("bluetooth manager service is null");
}
diff --git a/core/java/android/content/AttributionSource.aidl b/core/java/android/content/AttributionSource.aidl
new file mode 100644
index 000000000000..10d5c274ae91
--- /dev/null
+++ b/core/java/android/content/AttributionSource.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+parcelable AttributionSource;
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
new file mode 100644
index 000000000000..053bfc1a3253
--- /dev/null
+++ b/core/java/android/content/AttributionSource.java
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.AppGlobals;
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.permission.PermissionManager;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.Immutable;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This class represents a source to which access to permission protected data should be
+ * attributed. Attribution sources can be chained to represent cases where the protected
+ * data would flow through several applications. For example, app A may ask app B for
+ * contacts and in turn app B may ask app C for contacts. In this case, the attribution
+ * chain would be A -> B -> C and the data flow would be C -> B -> A. There are two
+ * main benefits of using the attribution source mechanism: avoid doing explicit permission
+ * checks on behalf of the calling app if you are accessing private data on their behalf
+ * to send back; avoid double data access blaming which happens as you check the calling
+ * app's permissions and when you access the data behind these permissions (for runtime
+ * permissions). Also if not explicitly blaming the caller the data access would be
+ * counted towards your app vs to the previous app where yours was just a proxy.
+ * <p>
+ * Every {@link Context} has an attribution source and you can get it via {@link
+ * Context#getAttributionSource()} representing itself, which is a chain of one. You
+ * can attribute work to another app, or more precisely to a chain of apps, through
+ * which the data you would be accessing would flow, via {@link Context#createContext(
+ * ContextParams)} plus specifying an attribution source for the next app to receive
+ * the protected data you are accessing via {@link AttributionSource.Builder#setNext(
+ * AttributionSource)}. Creating this attribution chain ensures that the datasource would
+ * check whether every app in the attribution chain has permission to access the data
+ * before releasing it. The datasource will also record appropriately that this data was
+ * accessed by the apps in the sequence if the data is behind a sensitive permission
+ * (e.g. dangerous). Again, this is useful if you are accessing the data on behalf of another
+ * app, for example a speech recognizer using the mic so it can provide recognition to
+ * a calling app.
+ * <p>
+ * You can create an attribution chain of you and any other app without any verification
+ * as this is something already available via the {@link android.app.AppOpsManager} APIs.
+ * This is supported to handle cases where you don't have access to the caller's attribution
+ * source and you can directly use the {@link AttributionSource.Builder} APIs. However,
+ * if the data flows through more than two apps (more than you access the data for the
+ * caller - which you cannot know ahead of time) you need to have a handle to the {@link
+ * AttributionSource} for the calling app's context in order to create an attribution context.
+ * This means you either need to have an API for the other app to send you its attribution
+ * source or use a platform API that pipes the callers attribution source.
+ * <p>
+ * You cannot forge an attribution chain without the participation of every app in the
+ * attribution chain (aside of the special case mentioned above). To create an attribution
+ * source that is trusted you need to create an attribution context that points to an
+ * attribution source that was explicitly created by the app that it refers to, recursively.
+ * <p>
+ * Since creating an attribution context leads to all permissions for apps in the attribution
+ * chain being checked, you need to expect getting a security exception when accessing
+ * permission protected APIs since some app in the chain may not have the permission.
+ */
+@Immutable
+// TODO: Codegen doesn't properly verify the class if the parcelling is inner class
+// TODO: Codegen doesn't allow overriding the constructor to change its visibility
+// TODO: Codegen applies method level annotations to argument vs the generated member (@SystemApi)
+// TODO: Codegen doesn't properly read/write IBinder members
+// TODO: Codegen doesn't properly handle Set arguments
+// @DataClass(genEqualsHashCode = true, genConstructor = false, genBuilder = true)
+public final class AttributionSource implements Parcelable {
+ /**
+ * @hide
+ */
+ static class RenouncedPermissionsParcelling implements Parcelling<Set<String>> {
+
+ @Override
+ public void parcel(Set<String> item, Parcel dest, int parcelFlags) {
+ if (item == null) {
+ dest.writeInt(-1);
+ } else {
+ dest.writeInt(item.size());
+ for (String permission : item) {
+ dest.writeString8(permission);
+ }
+ }
+ }
+
+ @Override
+ public Set<String> unparcel(Parcel source) {
+ final int size = source.readInt();
+ if (size < 0) {
+ return null;
+ }
+ final ArraySet<String> result = new ArraySet<>(size);
+ for (int i = 0; i < size; i++) {
+ result.add(source.readString8());
+ }
+ return result;
+ }
+ }
+
+ /**
+ * The UID that is accessing the permission protected data.
+ */
+ private final int mUid;
+
+ /**
+ * The package that is accessing the permission protected data.
+ */
+ private @Nullable String mPackageName = null;
+
+ /**
+ * The attribution tag of the app accessing the permission protected data.
+ */
+ private @Nullable String mAttributionTag = null;
+
+ /**
+ * Unique token for that source.
+ *
+ * @hide
+ */
+ private @Nullable IBinder mToken = null;
+
+ /**
+ * Permissions that should be considered revoked regardless if granted.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
+ @DataClass.ParcelWith(RenouncedPermissionsParcelling.class)
+ private @Nullable Set<String> mRenouncedPermissions = null;
+
+ /**
+ * The next app to receive the permission protected data.
+ */
+ private @Nullable AttributionSource mNext = null;
+
+ /** @hide */
+ @TestApi
+ public AttributionSource(int uid, @Nullable String packageName,
+ @Nullable String attributionTag) {
+ this(uid, packageName, attributionTag, /*next*/ null);
+ }
+
+ /** @hide */
+ @TestApi
+ public AttributionSource(int uid, @Nullable String packageName,
+ @Nullable String attributionTag, @Nullable AttributionSource next) {
+ this(uid, packageName, attributionTag, /*token*/ null,
+ /*renouncedPermissions*/ null, next);
+ }
+
+ /** @hide */
+ public AttributionSource(@NonNull AttributionSource current,
+ @Nullable AttributionSource next) {
+ this(current.getUid(), current.getPackageName(), current.getAttributionTag(),
+ /*token*/ null, /*renouncedPermissions*/ null, next);
+ }
+
+ /** @hide */
+ public AttributionSource withNextAttributionSource(@Nullable AttributionSource next) {
+ return new AttributionSource(mUid, mPackageName, mAttributionTag, mToken,
+ mRenouncedPermissions, next);
+ }
+
+ /** @hide */
+ public AttributionSource withToken(@Nullable IBinder token) {
+ return new AttributionSource(mUid, mPackageName, mAttributionTag, token,
+ mRenouncedPermissions, mNext);
+ }
+
+ /**
+ * If you are handling an IPC and you don't trust the caller you need to validate
+ * whether the attribution source is one for the calling app to prevent the caller
+ * to pass you a source from another app without including themselves in the
+ * attribution chain.
+ *
+ * @throws SecurityException if the attribution source cannot be trusted to be
+ * from the caller.
+ */
+ public void enforceCallingUid() {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID && callingUid != mUid) {
+ throw new SecurityException("Calling uid: " + callingUid
+ + " doesn't match source uid: " + mUid);
+ }
+ // No need to check package as app ops manager does it already.
+ }
+
+ /**
+ * If you are handling an IPC and you don't trust the caller you need to validate
+ * whether the attribution source is one for the calling app to prevent the caller
+ * to pass you a source from another app without including themselves in the
+ * attribution chain.
+ *f
+ * @return if the attribution source cannot be trusted to be from the caller.
+ */
+ public boolean checkCallingUid() {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID && callingUid != mUid) {
+ return false;
+ }
+ // No need to check package as app ops manager does it already.
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ if (Build.IS_DEBUGGABLE) {
+ return "AttributionSource { " +
+ "uid = " + mUid + ", " +
+ "packageName = " + mPackageName + ", " +
+ "attributionTag = " + mAttributionTag + ", " +
+ "token = " + mToken + ", " +
+ "next = " + mNext +
+ " }";
+ }
+ return super.toString();
+ }
+
+ /**
+ * @return The next UID that would receive the permission protected data.
+ *
+ * @hide
+ */
+ public int getNextUid() {
+ if (mNext != null) {
+ return mNext.getUid();
+ }
+ return Process.INVALID_UID;
+ }
+
+ /**
+ * @return The next package that would receive the permission protected data.
+ *
+ * @hide
+ */
+ public @Nullable String getNextPackageName() {
+ if (mNext != null) {
+ return mNext.getPackageName();
+ }
+ return null;
+ }
+
+ /**
+ * @return The nexxt package's attribution tag that would receive
+ * the permission protected data.
+ *
+ * @hide
+ */
+ public @Nullable String getNextAttributionTag() {
+ if (mNext != null) {
+ return mNext.getAttributionTag();
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether this attribution source can be trusted. That is whether
+ * the app it refers to created it and provided to the attribution chain.
+ *
+ * @param context Context handle.
+ * @return Whether this is a trusted source.
+ */
+ public boolean isTrusted(@NonNull Context context) {
+ return mToken != null && context.getSystemService(PermissionManager.class)
+ .isRegisteredAttributionSource(this);
+ }
+
+ /**
+ * Permissions that should be considered revoked regardless if granted.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
+ @NonNull
+ public Set<String> getRenouncedPermissions() {
+ return CollectionUtils.emptyIfNull(mRenouncedPermissions);
+ }
+
+ @DataClass.Suppress({"setUid", "setToken"})
+ static class BaseBuilder {}
+
+
+
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/AttributionSource.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /* package-private */ AttributionSource(
+ int uid,
+ @Nullable String packageName,
+ @Nullable String attributionTag,
+ @Nullable IBinder token,
+ @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) @Nullable Set<String> renouncedPermissions,
+ @Nullable AttributionSource next) {
+ this.mUid = uid;
+ this.mPackageName = packageName;
+ this.mAttributionTag = attributionTag;
+ this.mToken = token;
+ this.mRenouncedPermissions = renouncedPermissions;
+ com.android.internal.util.AnnotationValidations.validate(
+ SystemApi.class, null, mRenouncedPermissions);
+ com.android.internal.util.AnnotationValidations.validate(
+ RequiresPermission.class, null, mRenouncedPermissions,
+ "value", android.Manifest.permission.RENOUNCE_PERMISSIONS);
+ this.mNext = next;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The UID that is accessing the permission protected data.
+ */
+ public int getUid() {
+ return mUid;
+ }
+
+ /**
+ * The package that is accessing the permission protected data.
+ */
+ public @Nullable String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * The attribution tag of the app accessing the permission protected data.
+ */
+ public @Nullable String getAttributionTag() {
+ return mAttributionTag;
+ }
+
+ /**
+ * Unique token for that source.
+ *
+ * @hide
+ */
+ public @Nullable IBinder getToken() {
+ return mToken;
+ }
+
+ /**
+ * The next app to receive the permission protected data.
+ */
+ public @Nullable AttributionSource getNext() {
+ return mNext;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(AttributionSource other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ AttributionSource that = (AttributionSource) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mUid == that.mUid
+ && Objects.equals(mPackageName, that.mPackageName)
+ && Objects.equals(mAttributionTag, that.mAttributionTag)
+ && Objects.equals(mToken, that.mToken)
+ && Objects.equals(mRenouncedPermissions, that.mRenouncedPermissions)
+ && Objects.equals(mNext, that.mNext);
+ }
+
+ @Override
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mUid;
+ _hash = 31 * _hash + Objects.hashCode(mPackageName);
+ _hash = 31 * _hash + Objects.hashCode(mAttributionTag);
+ _hash = 31 * _hash + Objects.hashCode(mToken);
+ _hash = 31 * _hash + Objects.hashCode(mRenouncedPermissions);
+ _hash = 31 * _hash + Objects.hashCode(mNext);
+ return _hash;
+ }
+
+ static Parcelling<Set<String>> sParcellingForRenouncedPermissions =
+ Parcelling.Cache.get(
+ RenouncedPermissionsParcelling.class);
+ static {
+ if (sParcellingForRenouncedPermissions == null) {
+ sParcellingForRenouncedPermissions = Parcelling.Cache.put(
+ new RenouncedPermissionsParcelling());
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mPackageName != null) flg |= 0x2;
+ if (mAttributionTag != null) flg |= 0x4;
+ if (mToken != null) flg |= 0x8;
+ if (mRenouncedPermissions != null) flg |= 0x10;
+ if (mNext != null) flg |= 0x20;
+ dest.writeByte(flg);
+ dest.writeInt(mUid);
+ if (mPackageName != null) dest.writeString(mPackageName);
+ if (mAttributionTag != null) dest.writeString(mAttributionTag);
+ if (mToken != null) dest.writeStrongBinder(mToken);
+ sParcellingForRenouncedPermissions.parcel(mRenouncedPermissions, dest, flags);
+ if (mNext != null) dest.writeTypedObject(mNext, flags);
+ }
+
+ @Override
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ /* package-private */ AttributionSource(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ int uid = in.readInt();
+ String packageName = (flg & 0x2) == 0 ? null : in.readString();
+ String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
+ IBinder token = (flg & 0x8) == 0 ? null : in.readStrongBinder();
+ Set<String> renouncedPermissions = sParcellingForRenouncedPermissions.unparcel(in);
+ AttributionSource next = (flg & 0x20) == 0 ? null : (AttributionSource) in.readTypedObject(AttributionSource.CREATOR);
+
+ this.mUid = uid;
+ this.mPackageName = packageName;
+ this.mAttributionTag = attributionTag;
+ this.mToken = token;
+ this.mRenouncedPermissions = renouncedPermissions;
+ com.android.internal.util.AnnotationValidations.validate(
+ SystemApi.class, null, mRenouncedPermissions);
+ com.android.internal.util.AnnotationValidations.validate(
+ RequiresPermission.class, null, mRenouncedPermissions,
+ "value", android.Manifest.permission.RENOUNCE_PERMISSIONS);
+ this.mNext = next;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ public static final @NonNull Parcelable.Creator<AttributionSource> CREATOR
+ = new Parcelable.Creator<AttributionSource>() {
+ @Override
+ public AttributionSource[] newArray(int size) {
+ return new AttributionSource[size];
+ }
+
+ @Override
+ public AttributionSource createFromParcel(@NonNull Parcel in) {
+ return new AttributionSource(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AttributionSource}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder extends BaseBuilder {
+
+ private int mUid;
+ private @Nullable String mPackageName;
+ private @Nullable String mAttributionTag;
+ private @Nullable IBinder mToken;
+ private @SystemApi @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) @Nullable Set<String> mRenouncedPermissions;
+ private @Nullable AttributionSource mNext;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param uid
+ * The UID that is accessing the permission protected data.
+ */
+ public Builder(
+ int uid) {
+ mUid = uid;
+ }
+
+ /**
+ * The package that is accessing the permission protected data.
+ */
+ public @NonNull Builder setPackageName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mPackageName = value;
+ return this;
+ }
+
+ /**
+ * The attribution tag of the app accessing the permission protected data.
+ */
+ public @NonNull Builder setAttributionTag(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mAttributionTag = value;
+ return this;
+ }
+
+ /**
+ * Permissions that should be considered revoked regardless if granted.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
+ public @NonNull Builder setRenouncedPermissions(@NonNull Set<String> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mRenouncedPermissions = value;
+ return this;
+ }
+
+ /**
+ * The next app to receive the permission protected data.
+ */
+ public @NonNull Builder setNext(@NonNull AttributionSource value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mNext = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull AttributionSource build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mPackageName = null;
+ }
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mAttributionTag = null;
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mToken = null;
+ }
+ if ((mBuilderFieldsSet & 0x10) == 0) {
+ mRenouncedPermissions = null;
+ }
+ if ((mBuilderFieldsSet & 0x20) == 0) {
+ mNext = null;
+ }
+ AttributionSource o = new AttributionSource(
+ mUid,
+ mPackageName,
+ mAttributionTag,
+ mToken,
+ mRenouncedPermissions,
+ mNext);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x40) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 73b4f62a23e4..82842039c310 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -18,11 +18,6 @@ package android.content;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.MODE_ERRORED;
-import static android.app.AppOpsManager.MODE_IGNORED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Trace.TRACE_TAG_DATABASE;
import android.annotation.NonNull;
@@ -45,7 +40,6 @@ import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
-import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
@@ -57,7 +51,6 @@ import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
@@ -141,7 +134,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
private boolean mNoPerms;
private boolean mSingleUser;
- private ThreadLocal<Pair<String, String>> mCallingPackage;
+ private ThreadLocal<AttributionSource> mCallingAttributionSource;
private Transport mTransport = new Transport();
@@ -231,13 +224,13 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
@Override
- public Cursor query(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public Cursor query(@NonNull AttributionSource attributionSource, Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable ICancellationSignal cancellationSignal) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
// The caller has no access to the data, so return an empty cursor with
// the columns in the requested order. The caller may ask for an invalid
// column and we would not catch that but this is not a problem in practice.
@@ -253,8 +246,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
// we have to execute the query as if allowed to get a cursor with the
// columns. We then use the column names to return an empty cursor.
Cursor cursor;
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
cursor = mInterface.query(
uri, projection, queryArgs,
@@ -262,7 +255,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
}
if (cursor == null) {
return null;
@@ -272,8 +265,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
return new MatrixCursor(cursor.getColumnNames(), 0);
}
Trace.traceBegin(TRACE_TAG_DATABASE, "query");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.query(
uri, projection, queryArgs,
@@ -281,7 +274,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -314,60 +307,59 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
@Override
- public Uri insert(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public Uri insert(@NonNull AttributionSource attributionSource, Uri uri,
ContentValues initialValues, Bundle extras) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ if (enforceWritePermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return rejectInsert(uri, initialValues);
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
}
}
Trace.traceBegin(TRACE_TAG_DATABASE, "insert");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return maybeAddUserId(mInterface.insert(uri, initialValues, extras), userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public int bulkInsert(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public int bulkInsert(@NonNull AttributionSource attributionSource, Uri uri,
ContentValues[] initialValues) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "bulkInsert");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.bulkInsert(uri, initialValues);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public ContentProviderResult[] applyBatch(String callingPkg,
- @Nullable String attributionTag, String authority,
- ArrayList<ContentProviderOperation> operations)
+ public ContentProviderResult[] applyBatch(@NonNull AttributionSource attributionSource,
+ String authority, ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
validateIncomingAuthority(authority);
int numOperations = operations.size();
@@ -383,22 +375,24 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
operation = new ContentProviderOperation(operation, uri);
operations.set(i, operation);
}
+ final AttributionSource accessAttributionSource =
+ attributionSource;
if (operation.isReadOperation()) {
- if (enforceReadPermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(accessAttributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
throw new OperationApplicationException("App op not allowed", 0);
}
}
if (operation.isWriteOperation()) {
- if (enforceWritePermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(accessAttributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
throw new OperationApplicationException("App op not allowed", 0);
}
}
}
Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
ContentProviderResult[] results = mInterface.applyBatch(authority,
operations);
@@ -414,111 +408,111 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public int delete(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public int delete(@NonNull AttributionSource attributionSource, Uri uri,
Bundle extras) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "delete");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.delete(uri, extras);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public int update(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public int update(@NonNull AttributionSource attributionSource, Uri uri,
ContentValues values, Bundle extras) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "update");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.update(uri, values, extras);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public ParcelFileDescriptor openFile(String callingPkg, @Nullable String attributionTag,
- Uri uri, String mode, ICancellationSignal cancellationSignal, IBinder callerToken)
+ public ParcelFileDescriptor openFile(@NonNull AttributionSource attributionSource,
+ Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, attributionTag, uri, mode, callerToken);
+ enforceFilePermission(attributionSource, uri, mode);
Trace.traceBegin(TRACE_TAG_DATABASE, "openFile");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.openFile(
uri, mode, CancellationSignal.fromTransport(cancellationSignal));
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String attributionTag,
+ public AssetFileDescriptor openAssetFile(@NonNull AttributionSource attributionSource,
Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, attributionTag, uri, mode, null);
+ enforceFilePermission(attributionSource, uri, mode);
Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.openAssetFile(
uri, mode, CancellationSignal.fromTransport(cancellationSignal));
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public Bundle call(String callingPkg, @Nullable String attributionTag, String authority,
+ public Bundle call(@NonNull AttributionSource attributionSource, String authority,
String method, @Nullable String arg, @Nullable Bundle extras) {
validateIncomingAuthority(authority);
Bundle.setDefusable(extras, true);
Trace.traceBegin(TRACE_TAG_DATABASE, "call");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.call(authority, method, arg, extras);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -539,23 +533,23 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPkg,
- @Nullable String attributionTag, Uri uri, String mimeType, Bundle opts,
- ICancellationSignal cancellationSignal) throws FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(
+ @NonNull AttributionSource attributionSource, Uri uri, String mimeType,
+ Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
Bundle.setDefusable(opts, true);
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, attributionTag, uri, "r", null);
+ enforceFilePermission(attributionSource, uri, "r");
Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.openTypedAssetFile(
uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -566,34 +560,34 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
@Override
- public Uri canonicalize(String callingPkg, @Nullable String attributionTag, Uri uri) {
+ public Uri canonicalize(@NonNull AttributionSource attributionSource, Uri uri) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
return null;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "canonicalize");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return maybeAddUserId(mInterface.canonicalize(uri), userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public void canonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback callback) {
final Bundle result = new Bundle();
try {
result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- canonicalize(callingPkg, attributionTag, uri));
+ canonicalize(attributionSource, uri));
} catch (Exception e) {
result.putParcelable(ContentResolver.REMOTE_CALLBACK_ERROR,
new ParcelableException(e));
@@ -602,34 +596,34 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
@Override
- public Uri uncanonicalize(String callingPkg, String attributionTag, Uri uri) {
+ public Uri uncanonicalize(@NonNull AttributionSource attributionSource, Uri uri) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
return null;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "uncanonicalize");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return maybeAddUserId(mInterface.uncanonicalize(uri), userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public void uncanonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public void uncanonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback callback) {
final Bundle result = new Bundle();
try {
result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- uncanonicalize(callingPkg, attributionTag, uri));
+ uncanonicalize(attributionSource, uri));
} catch (Exception e) {
result.putParcelable(ContentResolver.REMOTE_CALLBACK_ERROR,
new ParcelableException(e));
@@ -638,92 +632,95 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
@Override
- public boolean refresh(String callingPkg, String attributionTag, Uri uri, Bundle extras,
- ICancellationSignal cancellationSignal) throws RemoteException {
+ public boolean refresh(@NonNull AttributionSource attributionSource, Uri uri,
+ Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException {
uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
return false;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "refresh");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.refresh(uri, extras,
CancellationSignal.fromTransport(cancellationSignal));
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public int checkUriPermission(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public int checkUriPermission(@NonNull AttributionSource attributionSource, Uri uri,
int uid, int modeFlags) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
Trace.traceBegin(TRACE_TAG_DATABASE, "checkUriPermission");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.checkUriPermission(uri, uid, modeFlags);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
- private void enforceFilePermission(String callingPkg, @Nullable String attributionTag,
- Uri uri, String mode, IBinder callerToken)
+ @PermissionChecker.PermissionResult
+ private void enforceFilePermission(@NonNull AttributionSource attributionSource,
+ Uri uri, String mode)
throws FileNotFoundException, SecurityException {
if (mode != null && mode.indexOf('w') != -1) {
- if (enforceWritePermission(callingPkg, attributionTag, uri, callerToken)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
throw new FileNotFoundException("App op not allowed");
}
} else {
- if (enforceReadPermission(callingPkg, attributionTag, uri, callerToken)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
throw new FileNotFoundException("App op not allowed");
}
}
}
- private int enforceReadPermission(String callingPkg, @Nullable String attributionTag,
- Uri uri, IBinder callerToken)
+ @PermissionChecker.PermissionResult
+ private int enforceReadPermission(@NonNull AttributionSource attributionSource, Uri uri)
throws SecurityException {
- final int mode = enforceReadPermissionInner(uri, callingPkg, attributionTag,
- callerToken);
- if (mode != MODE_ALLOWED) {
- return mode;
+ final int result = enforceReadPermissionInner(uri, attributionSource);
+ if (result != PermissionChecker.PERMISSION_GRANTED) {
+ return result;
}
-
- return noteProxyOp(callingPkg, attributionTag, mReadOp);
+ // Only check the read op if it differs from the one for the permission
+ // we already checked above to avoid double attribution for every access.
+ if (mTransport.mReadOp != AppOpsManager.OP_NONE
+ && mTransport.mReadOp != AppOpsManager.permissionToOpCode(mReadPermission)) {
+ return PermissionChecker.checkOpForDataDelivery(getContext(),
+ AppOpsManager.opToPublicName(mTransport.mReadOp),
+ attributionSource, /*message*/ null);
+ }
+ return PermissionChecker.PERMISSION_GRANTED;
}
- private int enforceWritePermission(String callingPkg, String attributionTag, Uri uri,
- IBinder callerToken)
+ @PermissionChecker.PermissionResult
+ private int enforceWritePermission(@NonNull AttributionSource attributionSource, Uri uri)
throws SecurityException {
- final int mode = enforceWritePermissionInner(uri, callingPkg, attributionTag,
- callerToken);
- if (mode != MODE_ALLOWED) {
- return mode;
+ final int result = enforceWritePermissionInner(uri, attributionSource);
+ if (result != PermissionChecker.PERMISSION_GRANTED) {
+ return result;
}
-
- return noteProxyOp(callingPkg, attributionTag, mWriteOp);
- }
-
- private int noteProxyOp(String callingPkg, String attributionTag, int op) {
- if (op != AppOpsManager.OP_NONE) {
- int mode = mAppOpsManager.noteProxyOp(op, callingPkg, Binder.getCallingUid(),
- attributionTag, null);
- return mode == MODE_DEFAULT ? MODE_IGNORED : mode;
+ // Only check the write op if it differs from the one for the permission
+ // we already checked above to avoid double attribution for every access.
+ if (mTransport.mWriteOp != AppOpsManager.OP_NONE
+ && mTransport.mWriteOp != AppOpsManager.permissionToOpCode(mWritePermission)) {
+ return PermissionChecker.checkOpForDataDelivery(getContext(),
+ AppOpsManager.opToPublicName(mTransport.mWriteOp),
+ attributionSource, /*message*/ null);
}
-
- return AppOpsManager.MODE_ALLOWED;
+ return PermissionChecker.PERMISSION_GRANTED;
}
}
@@ -731,49 +728,53 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
if (UserHandle.getUserId(uid) == context.getUserId() || mSingleUser) {
return true;
}
- return context.checkPermission(INTERACT_ACROSS_USERS, pid, uid) == PERMISSION_GRANTED
+ return context.checkPermission(INTERACT_ACROSS_USERS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED
|| context.checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid)
- == PERMISSION_GRANTED;
+ == PackageManager.PERMISSION_GRANTED;
}
/**
* Verify that calling app holds both the given permission and any app-op
* associated with that permission.
*/
- private int checkPermissionAndAppOp(String permission, String callingPkg,
- @Nullable String attributionTag, IBinder callerToken) {
- if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(),
- callerToken) != PERMISSION_GRANTED) {
- return MODE_ERRORED;
+ @PermissionChecker.PermissionResult
+ private int checkPermission(String permission,
+ @NonNull AttributionSource attributionSource) {
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return PermissionChecker.PERMISSION_GRANTED;
}
-
- return mTransport.noteProxyOp(callingPkg, attributionTag,
- AppOpsManager.permissionToOpCode(permission));
+ if (!attributionSource.checkCallingUid()) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ return PermissionChecker.checkPermissionForDataDeliveryFromDataSource(getContext(),
+ permission, -1, new AttributionSource(getContext().getAttributionSource(),
+ attributionSource), /*message*/ null);
}
/** {@hide} */
- protected int enforceReadPermissionInner(Uri uri, String callingPkg,
- @Nullable String attributionTag, IBinder callerToken) throws SecurityException {
+ @PermissionChecker.PermissionResult
+ protected int enforceReadPermissionInner(Uri uri,
+ @NonNull AttributionSource attributionSource) throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
String missingPerm = null;
- int strongestMode = MODE_ALLOWED;
+ int strongestResult = PermissionChecker.PERMISSION_GRANTED;
if (UserHandle.isSameApp(uid, mMyUid)) {
- return MODE_ALLOWED;
+ return PermissionChecker.PERMISSION_GRANTED;
}
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getReadPermission();
if (componentPerm != null) {
- final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, attributionTag,
- callerToken);
- if (mode == MODE_ALLOWED) {
- return MODE_ALLOWED;
+ final int result = checkPermission(componentPerm, attributionSource);
+ if (result == PermissionChecker.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_GRANTED;
} else {
missingPerm = componentPerm;
- strongestMode = Math.max(strongestMode, mode);
+ strongestResult = Math.max(strongestResult, result);
}
}
@@ -787,16 +788,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
for (PathPermission pp : pps) {
final String pathPerm = pp.getReadPermission();
if (pathPerm != null && pp.match(path)) {
- final int mode = checkPermissionAndAppOp(pathPerm, callingPkg,
- attributionTag, callerToken);
- if (mode == MODE_ALLOWED) {
- return MODE_ALLOWED;
+ final int result = checkPermission(pathPerm, attributionSource);
+ if (result == PermissionChecker.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_GRANTED;
} else {
// any denied <path-permission> means we lose
// default <provider> access.
allowDefaultRead = false;
missingPerm = pathPerm;
- strongestMode = Math.max(strongestMode, mode);
+ strongestResult = Math.max(strongestResult, result);
}
}
}
@@ -804,22 +804,22 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
// if we passed <path-permission> checks above, and no default
// <provider> permission, then allow access.
- if (allowDefaultRead) return MODE_ALLOWED;
+ if (allowDefaultRead) return PermissionChecker.PERMISSION_GRANTED;
}
// last chance, check against any uri grants
final int callingUserId = UserHandle.getUserId(uid);
final Uri userUri = (mSingleUser && !UserHandle.isSameUser(mMyUid, uid))
? maybeAddUserId(uri, callingUserId) : uri;
- if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION,
- callerToken) == PERMISSION_GRANTED) {
- return MODE_ALLOWED;
+ if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_GRANTED;
}
// If the worst denial we found above was ignored, then pass that
// ignored through; otherwise we assume it should be a real error below.
- if (strongestMode == MODE_IGNORED) {
- return MODE_IGNORED;
+ if (strongestResult == PermissionChecker.PERMISSION_SOFT_DENIED) {
+ return PermissionChecker.PERMISSION_SOFT_DENIED;
}
final String suffix;
@@ -836,28 +836,28 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
/** {@hide} */
- protected int enforceWritePermissionInner(Uri uri, String callingPkg,
- @Nullable String attributionTag, IBinder callerToken) throws SecurityException {
+ @PermissionChecker.PermissionResult
+ protected int enforceWritePermissionInner(Uri uri,
+ @NonNull AttributionSource attributionSource) throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
String missingPerm = null;
- int strongestMode = MODE_ALLOWED;
+ int strongestResult = PermissionChecker.PERMISSION_GRANTED;
if (UserHandle.isSameApp(uid, mMyUid)) {
- return MODE_ALLOWED;
+ return PermissionChecker.PERMISSION_GRANTED;
}
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getWritePermission();
if (componentPerm != null) {
- final int mode = checkPermissionAndAppOp(componentPerm, callingPkg,
- attributionTag, callerToken);
- if (mode == MODE_ALLOWED) {
- return MODE_ALLOWED;
+ final int mode = checkPermission(componentPerm, attributionSource);
+ if (mode == PermissionChecker.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_GRANTED;
} else {
missingPerm = componentPerm;
- strongestMode = Math.max(strongestMode, mode);
+ strongestResult = Math.max(strongestResult, mode);
}
}
@@ -871,16 +871,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
for (PathPermission pp : pps) {
final String pathPerm = pp.getWritePermission();
if (pathPerm != null && pp.match(path)) {
- final int mode = checkPermissionAndAppOp(pathPerm, callingPkg,
- attributionTag, callerToken);
- if (mode == MODE_ALLOWED) {
- return MODE_ALLOWED;
+ final int mode = checkPermission(pathPerm, attributionSource);
+ if (mode == PermissionChecker.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_GRANTED;
} else {
// any denied <path-permission> means we lose
// default <provider> access.
allowDefaultWrite = false;
missingPerm = pathPerm;
- strongestMode = Math.max(strongestMode, mode);
+ strongestResult = Math.max(strongestResult, mode);
}
}
}
@@ -888,19 +887,19 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
// if we passed <path-permission> checks above, and no default
// <provider> permission, then allow access.
- if (allowDefaultWrite) return MODE_ALLOWED;
+ if (allowDefaultWrite) return PermissionChecker.PERMISSION_GRANTED;
}
// last chance, check against any uri grants
- if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- callerToken) == PERMISSION_GRANTED) {
- return MODE_ALLOWED;
+ if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_GRANTED;
}
// If the worst denial we found above was ignored, then pass that
// ignored through; otherwise we assume it should be a real error below.
- if (strongestMode == MODE_IGNORED) {
- return MODE_IGNORED;
+ if (strongestResult == PermissionChecker.PERMISSION_SOFT_DENIED) {
+ return PermissionChecker.PERMISSION_SOFT_DENIED;
}
final String failReason = mExported
@@ -941,9 +940,10 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
* Set the calling package/feature, returning the current value (or {@code null})
* which can be used later to restore the previous state.
*/
- private Pair<String, String> setCallingPackage(Pair<String, String> callingPackage) {
- final Pair<String, String> original = mCallingPackage.get();
- mCallingPackage.set(callingPackage);
+ private @Nullable AttributionSource setCallingAttributionSource(
+ @Nullable AttributionSource attributionSource) {
+ final AttributionSource original = mCallingAttributionSource.get();
+ mCallingAttributionSource.set(attributionSource);
onCallingPackageChanged();
return original;
}
@@ -963,13 +963,30 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
* calling UID.
*/
public final @Nullable String getCallingPackage() {
- final Pair<String, String> pkg = mCallingPackage.get();
- if (pkg != null) {
- mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg.first);
- return pkg.first;
- }
+ final AttributionSource callingAttributionSource = getCallingAttributionSource();
+ return (callingAttributionSource != null)
+ ? callingAttributionSource.getPackageName() : null;
+ }
- return null;
+ /**
+ * Gets the attribution source of the calling app. If you want to attribute
+ * the data access to the calling app you can create an attribution context
+ * via {@link android.content.Context#createContext(ContextParams)} and passing
+ * this identity to {@link ContextParams.Builder#setNextAttributionSource(
+ * AttributionSource)}.
+ *
+ * @return The identity of the caller for permission purposes.
+ *
+ * @see ContextParams.Builder#setNextAttributionSource(AttributionSource)
+ * @see AttributionSource
+ */
+ public final @Nullable AttributionSource getCallingAttributionSource() {
+ final AttributionSource attributionSource = mCallingAttributionSource.get();
+ if (attributionSource != null) {
+ mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(),
+ attributionSource.getPackageName());
+ }
+ return attributionSource;
}
/**
@@ -983,11 +1000,10 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
* @see #getCallingPackage
*/
public final @Nullable String getCallingAttributionTag() {
- final Pair<String, String> pkg = mCallingPackage.get();
- if (pkg != null) {
- return pkg.second;
+ final AttributionSource attributionSource = mCallingAttributionSource.get();
+ if (attributionSource != null) {
+ return attributionSource.getAttributionTag();
}
-
return null;
}
@@ -1012,11 +1028,10 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
* @see Context#grantUriPermission(String, Uri, int)
*/
public final @Nullable String getCallingPackageUnchecked() {
- final Pair<String, String> pkg = mCallingPackage.get();
- if (pkg != null) {
- return pkg.first;
+ final AttributionSource attributionSource = mCallingAttributionSource.get();
+ if (attributionSource != null) {
+ return attributionSource.getPackageName();
}
-
return null;
}
@@ -1038,12 +1053,12 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
/** {@hide} */
public final long binderToken;
/** {@hide} */
- public final Pair<String, String> callingPackage;
+ public final @Nullable AttributionSource callingAttributionSource;
/** {@hide} */
- public CallingIdentity(long binderToken, Pair<String, String> callingPackage) {
+ public CallingIdentity(long binderToken, @Nullable AttributionSource attributionSource) {
this.binderToken = binderToken;
- this.callingPackage = callingPackage;
+ this.callingAttributionSource = attributionSource;
}
}
@@ -1059,7 +1074,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
*/
@SuppressWarnings("AndroidFrameworkBinderIdentity")
public final @NonNull CallingIdentity clearCallingIdentity() {
- return new CallingIdentity(Binder.clearCallingIdentity(), setCallingPackage(null));
+ return new CallingIdentity(Binder.clearCallingIdentity(),
+ setCallingAttributionSource(null));
}
/**
@@ -1071,7 +1087,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
*/
public final void restoreCallingIdentity(@NonNull CallingIdentity identity) {
Binder.restoreCallingIdentity(identity.binderToken);
- mCallingPackage.set(identity.callingPackage);
+ mCallingAttributionSource.set(identity.callingAttributionSource);
}
/**
@@ -2374,7 +2390,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
- mCallingPackage = new ThreadLocal<>();
+ mCallingAttributionSource = new ThreadLocal<>();
/*
* Only allow it to be set once, so after the content service gives
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 5af7861e1a20..518e7534d512 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -79,7 +79,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
private final IContentProvider mContentProvider;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mPackageName;
- private final @Nullable String mAttributionTag;
+ private final @NonNull AttributionSource mAttributionSource;
+
private final String mAuthority;
private final boolean mStable;
@@ -103,7 +104,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
mContentResolver = contentResolver;
mContentProvider = contentProvider;
mPackageName = contentResolver.mPackageName;
- mAttributionTag = contentResolver.mAttributionTag;
+ mAttributionSource = contentResolver.getAttributionSource();
mAuthority = authority;
mStable = stable;
@@ -193,7 +194,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
cancellationSignal.setRemote(remoteCancellationSignal);
}
final Cursor cursor = mContentProvider.query(
- mPackageName, mAttributionTag, uri, projection, queryArgs,
+ mAttributionSource, uri, projection, queryArgs,
remoteCancellationSignal);
if (cursor == null) {
return null;
@@ -254,7 +255,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
beforeRemote();
try {
- return mContentProvider.canonicalize(mPackageName, mAttributionTag, url);
+ return mContentProvider.canonicalize(mAttributionSource, url);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -272,7 +273,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
beforeRemote();
try {
- return mContentProvider.uncanonicalize(mPackageName, mAttributionTag, url);
+ return mContentProvider.uncanonicalize(mAttributionSource, url);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -297,7 +298,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
remoteCancellationSignal = mContentProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return mContentProvider.refresh(mPackageName, mAttributionTag, url, extras,
+ return mContentProvider.refresh(mAttributionSource, url, extras,
remoteCancellationSignal);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -317,7 +318,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
beforeRemote();
try {
- return mContentProvider.checkUriPermission(mPackageName, mAttributionTag, uri, uid,
+ return mContentProvider.checkUriPermission(mAttributionSource, uri, uid,
modeFlags);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -343,7 +344,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
beforeRemote();
try {
- return mContentProvider.insert(mPackageName, mAttributionTag, url, initialValues,
+ return mContentProvider.insert(mAttributionSource, url, initialValues,
extras);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -364,7 +365,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
beforeRemote();
try {
- return mContentProvider.bulkInsert(mPackageName, mAttributionTag, url, initialValues);
+ return mContentProvider.bulkInsert(mAttributionSource, url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -388,7 +389,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
beforeRemote();
try {
- return mContentProvider.delete(mPackageName, mAttributionTag, url, extras);
+ return mContentProvider.delete(mAttributionSource, url, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -413,7 +414,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
beforeRemote();
try {
- return mContentProvider.update(mPackageName, mAttributionTag, url, values, extras);
+ return mContentProvider.update(mAttributionSource, url, values, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -457,8 +458,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
remoteSignal = mContentProvider.createCancellationSignal();
signal.setRemote(remoteSignal);
}
- return mContentProvider.openFile(mPackageName, mAttributionTag, url, mode,
- remoteSignal, null);
+ return mContentProvider.openFile(mAttributionSource, url, mode, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -502,7 +502,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
remoteSignal = mContentProvider.createCancellationSignal();
signal.setRemote(remoteSignal);
}
- return mContentProvider.openAssetFile(mPackageName, mAttributionTag, url, mode,
+ return mContentProvider.openAssetFile(mAttributionSource, url, mode,
remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -544,7 +544,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
signal.setRemote(remoteSignal);
}
return mContentProvider.openTypedAssetFile(
- mPackageName, mAttributionTag, uri, mimeTypeFilter, opts, remoteSignal);
+ mAttributionSource, uri, mimeTypeFilter, opts, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -571,7 +571,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
beforeRemote();
try {
- return mContentProvider.applyBatch(mPackageName, mAttributionTag, authority,
+ return mContentProvider.applyBatch(mAttributionSource, authority,
operations);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -598,7 +598,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable {
beforeRemote();
try {
- return mContentProvider.call(mPackageName, mAttributionTag, authority, method, arg,
+ return mContentProvider.call(mAttributionSource, authority, method, arg,
extras);
} catch (DeadObjectException e) {
if (!mStable) {
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 7d121d56c86d..47c966990861 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -16,6 +16,7 @@
package android.content;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetFileDescriptor;
@@ -83,8 +84,8 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String callingFeatureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
// String[] projection
@@ -103,7 +104,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
- Cursor cursor = query(callingPkg, callingFeatureId, url, projection, queryArgs,
+ Cursor cursor = query(attributionSource, url, projection, queryArgs,
cancellationSignal);
if (cursor != null) {
CursorToBulkCursorAdaptor adaptor = null;
@@ -158,13 +159,13 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case INSERT_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues values = ContentValues.CREATOR.createFromParcel(data);
Bundle extras = data.readBundle();
- Uri out = insert(callingPkg, featureId, url, values, extras);
+ Uri out = insert(attributionSource, url, values, extras);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -173,12 +174,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case BULK_INSERT_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues[] values = data.createTypedArray(ContentValues.CREATOR);
- int count = bulkInsert(callingPkg, featureId, url, values);
+ int count = bulkInsert(attributionSource, url, values);
reply.writeNoException();
reply.writeInt(count);
return true;
@@ -187,8 +188,8 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case APPLY_BATCH_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
String authority = data.readString();
final int numOperations = data.readInt();
final ArrayList<ContentProviderOperation> operations =
@@ -196,7 +197,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
for (int i = 0; i < numOperations; i++) {
operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
}
- final ContentProviderResult[] results = applyBatch(callingPkg, featureId,
+ final ContentProviderResult[] results = applyBatch(attributionSource,
authority, operations);
reply.writeNoException();
reply.writeTypedArray(results, 0);
@@ -206,12 +207,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case DELETE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
Bundle extras = data.readBundle();
- int count = delete(callingPkg, featureId, url, extras);
+ int count = delete(attributionSource, url, extras);
reply.writeNoException();
reply.writeInt(count);
@@ -221,13 +222,13 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case UPDATE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues values = ContentValues.CREATOR.createFromParcel(data);
Bundle extras = data.readBundle();
- int count = update(callingPkg, featureId, url, values, extras);
+ int count = update(attributionSource, url, values, extras);
reply.writeNoException();
reply.writeInt(count);
@@ -237,16 +238,15 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case OPEN_FILE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
- IBinder callerToken = data.readStrongBinder();
ParcelFileDescriptor fd;
- fd = openFile(callingPkg, featureId, url, mode, signal, callerToken);
+ fd = openFile(attributionSource, url, mode, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -261,15 +261,15 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case OPEN_ASSET_FILE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
AssetFileDescriptor fd;
- fd = openAssetFile(callingPkg, featureId, url, mode, signal);
+ fd = openAssetFile(attributionSource, url, mode, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -285,14 +285,14 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
String authority = data.readString();
String method = data.readString();
String stringArg = data.readString();
Bundle extras = data.readBundle();
- Bundle responseBundle = call(callingPkg, featureId, authority, method,
+ Bundle responseBundle = call(attributionSource, authority, method,
stringArg, extras);
reply.writeNoException();
@@ -315,8 +315,8 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case OPEN_TYPED_ASSET_FILE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
String mimeType = data.readString();
Bundle opts = data.readBundle();
@@ -324,7 +324,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
data.readStrongBinder());
AssetFileDescriptor fd;
- fd = openTypedAssetFile(callingPkg, featureId, url, mimeType, opts, signal);
+ fd = openTypedAssetFile(attributionSource, url, mimeType, opts, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -349,11 +349,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case CANONICALIZE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
- Uri out = canonicalize(callingPkg, featureId, url);
+ Uri out = canonicalize(attributionSource, url);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -361,22 +361,22 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case CANONICALIZE_ASYNC_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri uri = Uri.CREATOR.createFromParcel(data);
RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
- canonicalizeAsync(callingPkg, featureId, uri, callback);
+ canonicalizeAsync(attributionSource, uri, callback);
return true;
}
case UNCANONICALIZE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
- Uri out = uncanonicalize(callingPkg, featureId, url);
+ Uri out = uncanonicalize(attributionSource, url);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -384,24 +384,24 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case UNCANONICALIZE_ASYNC_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri uri = Uri.CREATOR.createFromParcel(data);
RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
- uncanonicalizeAsync(callingPkg, featureId, uri, callback);
+ uncanonicalizeAsync(attributionSource, uri, callback);
return true;
}
case REFRESH_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
Bundle extras = data.readBundle();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
- boolean out = refresh(callingPkg, featureId, url, extras, signal);
+ boolean out = refresh(attributionSource, url, extras, signal);
reply.writeNoException();
reply.writeInt(out ? 0 : -1);
return true;
@@ -409,13 +409,13 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case CHECK_URI_PERMISSION_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri uri = Uri.CREATOR.createFromParcel(data);
int uid = data.readInt();
int modeFlags = data.readInt();
- int out = checkUriPermission(callingPkg, featureId, uri, uid, modeFlags);
+ int out = checkUriPermission(attributionSource, uri, uid, modeFlags);
reply.writeNoException();
reply.writeInt(out);
return true;
@@ -451,7 +451,7 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public Cursor query(String callingPkg, @Nullable String featureId, Uri url,
+ public Cursor query(@NonNull AttributionSource attributionSource, Uri url,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable ICancellationSignal cancellationSignal)
throws RemoteException {
@@ -461,8 +461,7 @@ final class ContentProviderProxy implements IContentProvider
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
int length = 0;
if (projection != null) {
@@ -540,7 +539,7 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public Uri insert(String callingPkg, @Nullable String featureId, Uri url,
+ public Uri insert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues values, Bundle extras) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -548,8 +547,7 @@ final class ContentProviderProxy implements IContentProvider
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
values.writeToParcel(data, 0);
data.writeBundle(extras);
@@ -566,15 +564,14 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public int bulkInsert(String callingPkg, @Nullable String featureId, Uri url,
+ public int bulkInsert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues[] values) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeTypedArray(values, 0);
@@ -590,15 +587,14 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
+ public ContentProviderResult[] applyBatch(@NonNull AttributionSource attributionSource,
String authority, ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
data.writeString(authority);
data.writeInt(operations.size());
for (ContentProviderOperation operation : operations) {
@@ -617,15 +613,14 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public int delete(String callingPkg, @Nullable String featureId, Uri url, Bundle extras)
+ public int delete(@NonNull AttributionSource attributionSource, Uri url, Bundle extras)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeBundle(extras);
@@ -641,15 +636,14 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public int update(String callingPkg, @Nullable String featureId, Uri url,
+ public int update(@NonNull AttributionSource attributionSource, Uri url,
ContentValues values, Bundle extras) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
values.writeToParcel(data, 0);
data.writeBundle(extras);
@@ -666,20 +660,18 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url,
- String mode, ICancellationSignal signal, IBinder token)
+ public ParcelFileDescriptor openFile(@NonNull AttributionSource attributionSource, Uri url,
+ String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeString(mode);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
- data.writeStrongBinder(token);
mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0);
@@ -695,7 +687,7 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+ public AssetFileDescriptor openAssetFile(@NonNull AttributionSource attributionSource,
Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
@@ -703,8 +695,7 @@ final class ContentProviderProxy implements IContentProvider
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeString(mode);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
@@ -723,15 +714,14 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+ public Bundle call(@NonNull AttributionSource attributionSource, String authority,
String method, String request, Bundle extras) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
data.writeString(authority);
data.writeString(method);
data.writeString(request);
@@ -771,7 +761,7 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPkg, @Nullable String featureId,
+ public AssetFileDescriptor openTypedAssetFile(@NonNull AttributionSource attributionSource,
Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
@@ -779,8 +769,7 @@ final class ContentProviderProxy implements IContentProvider
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeString(mimeType);
data.writeBundle(opts);
@@ -820,15 +809,14 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri url)
+ public Uri canonicalize(@NonNull AttributionSource attributionSource, Uri url)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
mRemote.transact(IContentProvider.CANONICALIZE_TRANSACTION, data, reply, 0);
@@ -843,14 +831,13 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- /* oneway */ public void canonicalizeAsync(String callingPkg, @Nullable String featureId,
+ /* oneway */ public void canonicalizeAsync(@NonNull AttributionSource attributionSource,
Uri uri, RemoteCallback callback) throws RemoteException {
Parcel data = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
uri.writeToParcel(data, 0);
callback.writeToParcel(data, 0);
@@ -862,15 +849,14 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri url)
+ public Uri uncanonicalize(@NonNull AttributionSource attributionSource, Uri url)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
mRemote.transact(IContentProvider.UNCANONICALIZE_TRANSACTION, data, reply, 0);
@@ -885,14 +871,13 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- /* oneway */ public void uncanonicalizeAsync(String callingPkg, @Nullable String featureId,
+ /* oneway */ public void uncanonicalizeAsync(@NonNull AttributionSource attributionSource,
Uri uri, RemoteCallback callback) throws RemoteException {
Parcel data = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
uri.writeToParcel(data, 0);
callback.writeToParcel(data, 0);
@@ -904,15 +889,14 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle extras,
+ public boolean refresh(@NonNull AttributionSource attributionSource, Uri url, Bundle extras,
ICancellationSignal signal) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeBundle(extras);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
@@ -929,15 +913,14 @@ final class ContentProviderProxy implements IContentProvider
}
@Override
- public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri url, int uid,
+ public int checkUriPermission(@NonNull AttributionSource attributionSource, Uri url, int uid,
int modeFlags) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeInt(uid);
data.writeInt(modeFlags);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 8ea417f900db..14b2a65c4da6 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -816,7 +816,6 @@ public abstract class ContentResolver implements ContentInterface {
public ContentResolver(@Nullable Context context, @Nullable ContentInterface wrapped) {
mContext = context != null ? context : ActivityThread.currentApplication();
mPackageName = mContext.getOpPackageName();
- mAttributionTag = mContext.getAttributionTag();
mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
mWrapped = wrapped;
}
@@ -1217,7 +1216,7 @@ public abstract class ContentResolver implements ContentInterface {
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
- qCursor = unstableProvider.query(mPackageName, mAttributionTag, uri, projection,
+ qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
@@ -1228,7 +1227,7 @@ public abstract class ContentResolver implements ContentInterface {
if (stableProvider == null) {
return null;
}
- qCursor = stableProvider.query(mPackageName, mAttributionTag, uri, projection,
+ qCursor = stableProvider.query(mContext.getAttributionSource(), uri, projection,
queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
@@ -1320,7 +1319,7 @@ public abstract class ContentResolver implements ContentInterface {
try {
final UriResultListener resultListener = new UriResultListener();
- provider.canonicalizeAsync(mPackageName, mAttributionTag, url,
+ provider.canonicalizeAsync(mContext.getAttributionSource(), url,
new RemoteCallback(resultListener));
resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
if (resultListener.exception != null) {
@@ -1371,7 +1370,7 @@ public abstract class ContentResolver implements ContentInterface {
try {
final UriResultListener resultListener = new UriResultListener();
- provider.uncanonicalizeAsync(mPackageName, mAttributionTag, url,
+ provider.uncanonicalizeAsync(mContext.getAttributionSource(), url,
new RemoteCallback(resultListener));
resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
if (resultListener.exception != null) {
@@ -1429,7 +1428,7 @@ public abstract class ContentResolver implements ContentInterface {
remoteCancellationSignal = provider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return provider.refresh(mPackageName, mAttributionTag, url, extras,
+ return provider.refresh(mContext.getAttributionSource(), url, extras,
remoteCancellationSignal);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
@@ -1858,7 +1857,7 @@ public abstract class ContentResolver implements ContentInterface {
try {
fd = unstableProvider.openAssetFile(
- mPackageName, mAttributionTag, uri, mode,
+ mContext.getAttributionSource(), uri, mode,
remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
@@ -1873,8 +1872,8 @@ public abstract class ContentResolver implements ContentInterface {
if (stableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
- fd = stableProvider.openAssetFile(
- mPackageName, mAttributionTag, uri, mode, remoteCancellationSignal);
+ fd = stableProvider.openAssetFile(mContext.getAttributionSource(),
+ uri, mode, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -2025,7 +2024,7 @@ public abstract class ContentResolver implements ContentInterface {
try {
fd = unstableProvider.openTypedAssetFile(
- mPackageName, mAttributionTag, uri, mimeType, opts,
+ mContext.getAttributionSource(), uri, mimeType, opts,
remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
@@ -2041,7 +2040,7 @@ public abstract class ContentResolver implements ContentInterface {
throw new FileNotFoundException("No content provider: " + uri);
}
fd = stableProvider.openTypedAssetFile(
- mPackageName, mAttributionTag, uri, mimeType, opts,
+ mContext.getAttributionSource(), uri, mimeType, opts,
remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
@@ -2190,7 +2189,7 @@ public abstract class ContentResolver implements ContentInterface {
}
try {
long startTime = SystemClock.uptimeMillis();
- Uri createdRow = provider.insert(mPackageName, mAttributionTag, url, values, extras);
+ Uri createdRow = provider.insert(mContext.getAttributionSource(), url, values, extras);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
return createdRow;
@@ -2271,7 +2270,7 @@ public abstract class ContentResolver implements ContentInterface {
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsCreated = provider.bulkInsert(mPackageName, mAttributionTag, url, values);
+ int rowsCreated = provider.bulkInsert(mContext.getAttributionSource(), url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
return rowsCreated;
@@ -2330,7 +2329,7 @@ public abstract class ContentResolver implements ContentInterface {
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsDeleted = provider.delete(mPackageName, mAttributionTag, url, extras);
+ int rowsDeleted = provider.delete(mContext.getAttributionSource(), url, extras);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "delete", null);
return rowsDeleted;
@@ -2397,7 +2396,8 @@ public abstract class ContentResolver implements ContentInterface {
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsUpdated = provider.update(mPackageName, mAttributionTag, uri, values, extras);
+ int rowsUpdated = provider.update(mContext.getAttributionSource(),
+ uri, values, extras);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, uri, "update", null);
return rowsUpdated;
@@ -2446,8 +2446,8 @@ public abstract class ContentResolver implements ContentInterface {
throw new IllegalArgumentException("Unknown authority " + authority);
}
try {
- final Bundle res = provider.call(mPackageName, mAttributionTag, authority, method, arg,
- extras);
+ final Bundle res = provider.call(mContext.getAttributionSource(),
+ authority, method, arg, extras);
Bundle.setDefusable(res, true);
return res;
} catch (RemoteException e) {
@@ -3866,12 +3866,17 @@ public abstract class ContentResolver implements ContentInterface {
/** @hide */
@UnsupportedAppUsage
public String getPackageName() {
- return mPackageName;
+ return mContext.getOpPackageName();
}
/** @hide */
public @Nullable String getAttributionTag() {
- return mAttributionTag;
+ return mContext.getAttributionTag();
+ }
+
+ /** @hide */
+ public @NonNull AttributionSource getAttributionSource() {
+ return mContext.getAttributionSource();
}
@UnsupportedAppUsage
@@ -3881,7 +3886,6 @@ public abstract class ContentResolver implements ContentInterface {
@UnsupportedAppUsage
final String mPackageName;
- final @Nullable String mAttributionTag;
final int mTargetSdkVersion;
final ContentInterface mWrapped;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6ff296c9799f..8531d341ee9b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -889,6 +889,15 @@ public abstract class Context {
return null;
}
+ /**
+ * @return The identity of this context for permission purposes.
+ *
+ * @see AttributionSource
+ */
+ public @NonNull AttributionSource getAttributionSource() {
+ return null;
+ }
+
// TODO moltmann: Remove
/**
* @removed
@@ -6465,8 +6474,10 @@ public abstract class Context {
* @removed
*/
@Deprecated
- public @NonNull Context createFeatureContext(@Nullable String featureId) {
- return createAttributionContext(featureId);
+ public @NonNull Context createFeatureContext(@Nullable String attributionTag) {
+ return createContext(new ContextParams.Builder()
+ .setAttributionTag(attributionTag)
+ .build());
}
/**
diff --git a/core/java/android/content/ContextParams.java b/core/java/android/content/ContextParams.java
index fad905bfac13..2b2db8fca2ca 100644
--- a/core/java/android/content/ContextParams.java
+++ b/core/java/android/content/ContextParams.java
@@ -19,7 +19,6 @@ package android.content;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import java.util.Collections;
@@ -38,36 +37,26 @@ import java.util.Set;
* is an arbitrary string your app specifies for the purposes of tracking permission
* accesses from a given portion of your app; against another package and optionally
* its attribution tag if you are accessing the data on behalf of another app and
- * you will be passing that data to this app. Both attributions are not mutually
- * exclusive.
- *
- * <p>For example if you have a feature "foo" in your app which accesses
- * permissions on behalf of app "foo.bar.baz" with feature "bar" you need to
- * create a context like this:
- *
- * <pre class="prettyprint">
- * context.createContext(new ContextParams.Builder()
- * .setAttributionTag("foo")
- * .setReceiverPackage("foo.bar.baz", "bar")
- * .build())
- * </pre>
+ * you will be passing that data to this app, recursively. Both attributions are
+ * not mutually exclusive.
*
* @see Context#createContext(ContextParams)
+ * @see AttributionSource
*/
public final class ContextParams {
- private final String mAttributionTag;
- private final String mReceiverPackage;
- private final String mReceiverAttributionTag;
- private final Set<String> mRenouncedPermissions;
+ private final @Nullable String mAttributionTag;
+ private final @Nullable AttributionSource mNext;
+ private final @NonNull Set<String> mRenouncedPermissions;
/** {@hide} */
public static final ContextParams EMPTY = new ContextParams.Builder().build();
- private ContextParams(@NonNull ContextParams.Builder builder) {
- mAttributionTag = builder.mAttributionTag;
- mReceiverPackage = builder.mReceiverPackage;
- mReceiverAttributionTag = builder.mReceiverAttributionTag;
- mRenouncedPermissions = builder.mRenouncedPermissions;
+ private ContextParams(@Nullable String attributionTag,
+ @Nullable AttributionSource next,
+ @NonNull Set<String> renouncedPermissions) {
+ mAttributionTag = attributionTag;
+ mNext = next;
+ mRenouncedPermissions = renouncedPermissions;
}
/**
@@ -79,45 +68,35 @@ public final class ContextParams {
}
/**
- * @return The receiving package.
- */
- @Nullable
- public String getReceiverPackage() {
- return mReceiverPackage;
- }
-
- /**
- * @return The receiving package's attribution tag.
- */
- @Nullable
- public String getReceiverAttributionTag() {
- return mReceiverAttributionTag;
- }
-
- /**
* @return The set of permissions to treat as renounced.
* @hide
*/
@SystemApi
- @SuppressLint("NullableCollection")
@RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
- public @Nullable Set<String> getRenouncedPermissions() {
+ public @NonNull Set<String> getRenouncedPermissions() {
return mRenouncedPermissions;
}
/** @hide */
public boolean isRenouncedPermission(@NonNull String permission) {
- return mRenouncedPermissions != null && mRenouncedPermissions.contains(permission);
+ return mRenouncedPermissions.contains(permission);
+ }
+
+ /**
+ * @return The receiving attribution source.
+ */
+ @Nullable
+ public AttributionSource getNextAttributionSource() {
+ return mNext;
}
/**
* Builder for creating a {@link ContextParams}.
*/
public static final class Builder {
- private String mAttributionTag;
- private String mReceiverPackage;
- private String mReceiverAttributionTag;
- private Set<String> mRenouncedPermissions;
+ private @Nullable String mAttributionTag;
+ private @NonNull Set<String> mRenouncedPermissions = Collections.emptySet();
+ private @Nullable AttributionSource mNext;
/**
* Create a new builder.
@@ -145,9 +124,8 @@ public final class ContextParams {
public Builder(@NonNull ContextParams params) {
Objects.requireNonNull(params);
mAttributionTag = params.mAttributionTag;
- mReceiverPackage = params.mReceiverPackage;
- mReceiverAttributionTag = params.mReceiverAttributionTag;
mRenouncedPermissions = params.mRenouncedPermissions;
+ mNext = params.mNext;
}
/**
@@ -163,18 +141,16 @@ public final class ContextParams {
}
/**
- * Sets the package and its optional attribution tag that would be receiving
- * the permission protected data.
+ * Sets the attribution source for the app on whose behalf you are doing the work.
*
- * @param packageName The package name receiving the permission protected data.
- * @param attributionTag An attribution tag of the receiving package.
+ * @param next The permission identity of the receiving app.
* @return This builder.
+ *
+ * @see AttributionSource
*/
@NonNull
- public Builder setReceiverPackage(@Nullable String packageName,
- @Nullable String attributionTag) {
- mReceiverPackage = packageName;
- mReceiverAttributionTag = attributionTag;
+ public Builder setNextAttributionSource(@NonNull AttributionSource next) {
+ mNext = Objects.requireNonNull(next);
return this;
}
@@ -194,19 +170,16 @@ public final class ContextParams {
* permissions are supported by this mechanism.
*
* @param renouncedPermissions The set of permissions to treat as
- * renounced.
+ * renounced, which is as if not granted.
* @return This builder.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
public @NonNull Builder setRenouncedPermissions(
- @Nullable Set<String> renouncedPermissions) {
- if (renouncedPermissions != null) {
- mRenouncedPermissions = Collections.unmodifiableSet(renouncedPermissions);
- } else {
- mRenouncedPermissions = null;
- }
+ @NonNull Set<String> renouncedPermissions) {
+ mRenouncedPermissions = Collections.unmodifiableSet(
+ Objects.requireNonNull(renouncedPermissions));
return this;
}
@@ -217,7 +190,8 @@ public final class ContextParams {
*/
@NonNull
public ContextParams build() {
- return new ContextParams(this);
+ return new ContextParams(mAttributionTag, mNext,
+ mRenouncedPermissions);
}
}
}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 609f417a8008..de0d65fec1fb 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1060,6 +1060,12 @@ public class ContextWrapper extends Context {
return mBase.createAttributionContext(attributionTag);
}
+ @NonNull
+ @Override
+ public AttributionSource getAttributionSource() {
+ return mBase.getAttributionSource();
+ }
+
@Override
public boolean isRestricted() {
return mBase.isRestricted();
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 9210b132c75a..e0315a3e171b 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -16,11 +16,13 @@
package android.content;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
@@ -38,11 +40,11 @@ import java.util.ArrayList;
* @hide
*/
public interface IContentProvider extends IInterface {
- public Cursor query(String callingPkg, @Nullable String attributionTag, Uri url,
+ Cursor query(@NonNull AttributionSource attributionSource, Uri url,
@Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
throws RemoteException;
- public String getType(Uri url) throws RemoteException;
+ String getType(Uri url) throws RemoteException;
/**
* A oneway version of getType. The functionality is exactly the same, except that the
@@ -55,54 +57,56 @@ public interface IContentProvider extends IInterface {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ "ContentProviderClient#insert(android.net.Uri, android.content.ContentValues)} "
+ "instead")
- public default Uri insert(String callingPkg, Uri url, ContentValues initialValues)
+ default Uri insert(String callingPkg, Uri url, ContentValues initialValues)
throws RemoteException {
- return insert(callingPkg, null, url, initialValues, null);
+ return insert(new AttributionSource(Binder.getCallingUid(), callingPkg, null),
+ url, initialValues, null);
}
- public Uri insert(String callingPkg, String attributionTag, Uri url,
+ Uri insert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues initialValues, Bundle extras) throws RemoteException;
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ "ContentProviderClient#bulkInsert(android.net.Uri, android.content.ContentValues[])"
+ "} instead")
- public default int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
+ default int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
throws RemoteException {
- return bulkInsert(callingPkg, null, url, initialValues);
+ return bulkInsert(new AttributionSource(Binder.getCallingUid(), callingPkg, null),
+ url, initialValues);
}
- public int bulkInsert(String callingPkg, String attributionTag, Uri url,
+ int bulkInsert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues[] initialValues) throws RemoteException;
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ "ContentProviderClient#delete(android.net.Uri, java.lang.String, java.lang"
+ ".String[])} instead")
- public default int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
+ default int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
throws RemoteException {
- return delete(callingPkg, null, url,
- ContentResolver.createSqlQueryBundle(selection, selectionArgs));
+ return delete(new AttributionSource(Binder.getCallingUid(), callingPkg, null),
+ url, ContentResolver.createSqlQueryBundle(selection, selectionArgs));
}
- public int delete(String callingPkg, String attributionTag, Uri url, Bundle extras)
+ int delete(@NonNull AttributionSource attributionSource, Uri url, Bundle extras)
throws RemoteException;
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ "ContentProviderClient#update(android.net.Uri, android.content.ContentValues, java"
+ ".lang.String, java.lang.String[])} instead")
- public default int update(String callingPkg, Uri url, ContentValues values, String selection,
+ default int update(String callingPkg, Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException {
- return update(callingPkg, null, url, values,
- ContentResolver.createSqlQueryBundle(selection, selectionArgs));
+ return update(new AttributionSource(Binder.getCallingUid(), callingPkg, null),
+ url, values, ContentResolver.createSqlQueryBundle(selection, selectionArgs));
}
- public int update(String callingPkg, String attributionTag, Uri url, ContentValues values,
+ int update(@NonNull AttributionSource attributionSource, Uri url, ContentValues values,
Bundle extras) throws RemoteException;
- public ParcelFileDescriptor openFile(String callingPkg, @Nullable String attributionTag,
- Uri url, String mode, ICancellationSignal signal, IBinder callerToken)
+ ParcelFileDescriptor openFile(@NonNull AttributionSource attributionSource,
+ Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException;
- public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String attributionTag,
+ AssetFileDescriptor openAssetFile(@NonNull AttributionSource attributionSource,
Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException;
- public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String attributionTag,
+ ContentProviderResult[] applyBatch(@NonNull AttributionSource attributionSource,
String authority, ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException;
@@ -112,18 +116,19 @@ public interface IContentProvider extends IInterface {
+ "instead")
public default Bundle call(String callingPkg, String method,
@Nullable String arg, @Nullable Bundle extras) throws RemoteException {
- return call(callingPkg, null, "unknown", method, arg, extras);
+ return call(new AttributionSource(Binder.getCallingUid(), callingPkg, null),
+ "unknown", method, arg, extras);
}
- public Bundle call(String callingPkg, @Nullable String attributionTag, String authority,
+ Bundle call(@NonNull AttributionSource attributionSource, String authority,
String method, @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
- public int checkUriPermission(String callingPkg, @Nullable String attributionTag, Uri uri,
+ int checkUriPermission(@NonNull AttributionSource attributionSource, Uri uri,
int uid, int modeFlags) throws RemoteException;
- public ICancellationSignal createCancellationSignal() throws RemoteException;
+ ICancellationSignal createCancellationSignal() throws RemoteException;
- public Uri canonicalize(String callingPkg, @Nullable String attributionTag, Uri uri)
+ Uri canonicalize(@NonNull AttributionSource attributionSource, Uri uri)
throws RemoteException;
/**
@@ -131,10 +136,10 @@ public interface IContentProvider extends IInterface {
* call returns immediately, and the resulting type is returned when available via
* a binder callback.
*/
- void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
+ void canonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback callback) throws RemoteException;
- public Uri uncanonicalize(String callingPkg, @Nullable String attributionTag, Uri uri)
+ Uri uncanonicalize(@NonNull AttributionSource attributionSource, Uri uri)
throws RemoteException;
/**
@@ -142,18 +147,17 @@ public interface IContentProvider extends IInterface {
* call returns immediately, and the resulting type is returned when available via
* a binder callback.
*/
- void uncanonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
+ void uncanonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback callback) throws RemoteException;
- public boolean refresh(String callingPkg, @Nullable String attributionTag, Uri url,
+ public boolean refresh(@NonNull AttributionSource attributionSource, Uri url,
@Nullable Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException;
// Data interchange.
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
- public AssetFileDescriptor openTypedAssetFile(String callingPkg,
- @Nullable String attributionTag, Uri url, String mimeType, Bundle opts,
- ICancellationSignal signal)
+ public AssetFileDescriptor openTypedAssetFile(@NonNull AttributionSource attributionSource,
+ Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
throws RemoteException, FileNotFoundException;
/* IPC constants */
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 159db92c79c9..08eac5aff655 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -27,6 +27,8 @@ import android.os.Process;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
* This class provides permission check APIs that verify both the
@@ -68,8 +70,14 @@ import java.lang.annotation.RetentionPolicy;
* @hide
*/
public final class PermissionChecker {
+ private static final String PLATFORM_PACKAGE_NAME = "android";
+
/** The permission is granted. */
- public static final int PERMISSION_GRANTED = PackageManager.PERMISSION_GRANTED;
+ public static final int PERMISSION_GRANTED = AppOpsManager.MODE_ALLOWED;
+
+ /** Only for runtime permissions, its returned when the runtime permission
+ * is granted, but the corresponding app op is denied. */
+ public static final int PERMISSION_SOFT_DENIED = AppOpsManager.MODE_IGNORED;
/** Returned when:
* <ul>
@@ -79,15 +87,14 @@ public final class PermissionChecker {
* </ul>
*
*/
- public static final int PERMISSION_HARD_DENIED = PackageManager.PERMISSION_DENIED;
-
- /** Only for runtime permissions, its returned when the runtime permission
- * is granted, but the corresponding app op is denied. */
- public static final int PERMISSION_SOFT_DENIED = PackageManager.PERMISSION_DENIED - 1;
+ public static final int PERMISSION_HARD_DENIED = AppOpsManager.MODE_ERRORED;
/** Constant when the PID for which we check permissions is unknown. */
public static final int PID_UNKNOWN = -1;
+ private static final ConcurrentHashMap<String, PermissionInfo> sPlatformPermissions
+ = new ConcurrentHashMap<>();
+
/** @hide */
@IntDef({PERMISSION_GRANTED,
PERMISSION_SOFT_DENIED,
@@ -131,6 +138,50 @@ public final class PermissionChecker {
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
* @param message A message describing the reason the permission was checked
+ * @param startDataDelivery Whether this is the start of data delivery.
+ *
+ * @see #checkPermissionForPreflight(Context, String, int, int, String)
+ */
+ @PermissionResult
+ public static int checkPermissionForDataDelivery(@NonNull Context context,
+ @NonNull String permission, int pid, int uid, @Nullable String packageName,
+ @Nullable String attributionTag, @Nullable String message, boolean startDataDelivery) {
+ return checkPermissionForDataDelivery(context, permission, pid, new AttributionSource(uid,
+ packageName, attributionTag), message, startDataDelivery);
+ }
+
+ /**
+ * Checks whether a given package in a UID and PID has a given permission
+ * and whether the app op that corresponds to this permission is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(Context, String, int, int, String)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * <p>For more details how to determine the {@code packageName}, {@code attributionTag}, and
+ * {@code message}, please check the description in
+ * {@link AppOpsManager#noteOp(String, int, String, String, String)}
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
+ * is not known.
+ * @param uid The uid for which to check.
+ * @param packageName The package name for which to check. If null the
+ * the first package for the calling UID will be used.
+ * @param attributionTag attribution tag
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ * @param message A message describing the reason the permission was checked
*
* @see #checkPermissionForPreflight(Context, String, int, int, String)
*/
@@ -138,8 +189,303 @@ public final class PermissionChecker {
public static int checkPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, int pid, int uid, @Nullable String packageName,
@Nullable String attributionTag, @Nullable String message) {
- return checkPermissionCommon(context, permission, pid, uid, packageName, attributionTag,
- message, true /*forDataDelivery*/);
+ return checkPermissionForDataDelivery(context, permission, pid, uid,
+ packageName, attributionTag, message, false /*startDataDelivery*/);
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed. Call this method if you are the datasource which would not blame you for
+ * access to the data since you are the data.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(Context, String, int, int, String)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
+ * is not known.
+ * @param attributionSource the permission identity
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ * @param message A message describing the reason the permission was checked
+ *
+ * @see #checkPermissionForPreflight(Context, String, AttributionSource)
+ */
+ @PermissionResult
+ public static int checkPermissionForDataDeliveryFromDataSource(@NonNull Context context,
+ @NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ return checkPermissionForDataDeliveryCommon(context, permission, pid, attributionSource,
+ message, false /*startDataDelivery*/, /*fromDatasource*/ true);
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(Context, String, AttributionSource)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
+ * is not known.
+ * @param attributionSource the permission identity
+ * @param message A message describing the reason the permission was checked
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #checkPermissionForPreflight(Context, String, AttributionSource)
+ */
+ @PermissionResult
+ public static int checkPermissionForDataDelivery(@NonNull Context context,
+ @NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ return checkPermissionForDataDelivery(context, permission, pid, attributionSource,
+ message, false /*startDataDelivery*/);
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a data listener it should have the required
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(Context, String,
+ * AttributionSource)}
+ * to determine if the app has or may have permission and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
+ * is not known.
+ * @param attributionSource The identity for which to check the permission.
+ * @param message A message describing the reason the permission was checked
+ * @param startDataDelivery Whether this is the start of data delivery.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #checkPermissionForPreflight(Context, String, AttributionSource)
+ */
+ @PermissionResult
+ public static int checkPermissionForDataDelivery(@NonNull Context context,
+ @NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean startDataDelivery) {
+ return checkPermissionForDataDeliveryCommon(context, permission, pid, attributionSource,
+ message, startDataDelivery, /*fromDatasource*/ false);
+ }
+
+ private static int checkPermissionForDataDeliveryCommon(@NonNull Context context,
+ @NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean startDataDelivery, boolean fromDatasource) {
+ // If the check failed in the middle of the chain, finish any started op.
+ final int result = checkPermissionCommon(context, permission, attributionSource,
+ message, true /*forDataDelivery*/, startDataDelivery, fromDatasource);
+ if (startDataDelivery && result != PERMISSION_GRANTED) {
+ finishDataDelivery(context, AppOpsManager.permissionToOp(permission),
+ attributionSource);
+ }
+ return result;
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed. The app ops area also marked as started. This is useful for long running
+ * permissions like camera.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a data listener it should have the required
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(Context, String,
+ * AttributionSource)}
+ * to determine if the app has or may have permission and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param attributionSource The identity for which to check the permission.
+ * @param message A message describing the reason the permission was checked
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #checkPermissionForPreflight(Context, String, AttributionSource)
+ */
+ @PermissionResult
+ public static int checkPermissionAndStartDataDelivery(@NonNull Context context,
+ @NonNull String permission, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ return checkPermissionCommon(context, permission, attributionSource,
+ message, true /*forDataDelivery*/, /*startDataDelivery*/ true,
+ /*fromDatasource*/ false);
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link
+ * AttributionSource} has a given app op allowed and marks the op as started.
+ *
+ * <strong>NOTE:</strong> Use this method only for app op checks at the
+ * point where you will deliver the protected data to clients.
+ *
+ * <p>For example, if an app registers a data listener it should have the data
+ * op but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkOpForPreflight(Context, String, AttributionSource, String)}
+ * to determine if the app has or may have op access and this check will not
+ * leave a trace that op protected data was delivered. When you are about to
+ * deliver the data to a registered listener you should use this method which
+ * will evaluate the op access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param opName THe op to start.
+ * @param attributionSource The identity for which to check the permission.
+ * @param message A message describing the reason the permission was checked
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #finishDataDelivery(Context, String, AttributionSource)
+ */
+ @PermissionResult
+ public static int startOpForDataDelivery(@NonNull Context context,
+ @NonNull String opName, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ final int result = checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource,
+ message, true /*forDataDelivery*/, true /*startDataDelivery*/);
+ // It is important to finish any started op if some step in the attribution chain failed.
+ if (result != PERMISSION_GRANTED) {
+ finishDataDelivery(context, opName, attributionSource);
+ }
+ return result;
+ }
+
+ /**
+ * Finishes an ongoing op for data access chain described by the given {@link
+ * AttributionSource}.
+ *
+ * @param context Context for accessing resources.
+ * @param op The op to finish.
+ * @param attributionSource The identity for which finish op.
+ *
+ * @see #startOpForDataDelivery(Context, String, AttributionSource, String)
+ * @see #checkPermissionAndStartDataDelivery(Context, String, AttributionSource, String)
+ */
+ public static void finishDataDelivery(@NonNull Context context, @NonNull String op,
+ @NonNull AttributionSource attributionSource) {
+ if (op == null || attributionSource.getPackageName() == null) {
+ return;
+ }
+
+ final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ appOpsManager.finishProxyOp(op, attributionSource);
+
+ if (attributionSource.getNext() != null) {
+ finishDataDelivery(context, op, attributionSource.getNext());
+ }
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link
+ * AttributionSource} has a given app op allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for op checks at the
+ * preflight point where you will not deliver the protected data
+ * to clients but schedule a data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a data listener it should have the op
+ * but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have data
+ * access and this check will not leave a trace that protected data
+ * was delivered. When you are about to deliver the data to a registered
+ * listener you should use {@link #checkOpForDataDelivery(Context, String,
+ * AttributionSource, String)} which will evaluate the op access based
+ * on the current fg/bg state of the app and leave a record that the data was
+ * accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param opName The op to check.
+ * @param attributionSource The identity for which to check the permission.
+ * @param message A message describing the reason the permission was checked
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #checkOpForDataDelivery(Context, String, AttributionSource, String)
+ */
+ @PermissionResult
+ public static int checkOpForPreflight(@NonNull Context context,
+ @NonNull String opName, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ return checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource,
+ message, false /*forDataDelivery*/, false /*startDataDelivery*/);
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has an allowed app op.
+ *
+ * <strong>NOTE:</strong> Use this method only for op checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a data listener it should have the data
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkOpForPreflight(Context, String, AttributionSource, String)}
+ * to determine if the app has or may have data access and this check will not
+ * leave a trace that op protected data was delivered. When you are about to
+ * deliver the data to a registered listener you should use this method which
+ * will evaluate the op access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param opName The op to check.
+ * @param attributionSource The identity for which to check the op.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ * @param message A message describing the reason the permission was checked
+ *
+ * @see #checkOpForPreflight(Context, String, AttributionSource, String)
+ */
+ @PermissionResult
+ public static int checkOpForDataDelivery(@NonNull Context context,
+ @NonNull String opName, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ return checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource,
+ message, true /*forDataDelivery*/, false /*startDataDelivery*/);
}
/**
@@ -158,8 +504,8 @@ public final class PermissionChecker {
* fg/gb state) and this check will not leave a trace that permission protected data
* was delivered. When you are about to deliver the location data to a registered
* listener you should use {@link #checkPermissionForDataDelivery(Context, String,
- * int, int, String, String)} which will evaluate the permission access based on the current
- * fg/bg state of the app and leave a record that the data was accessed.
+ * int, int, String, String, String)} which will evaluate the permission access based
+ * on the currentfg/bg state of the app and leave a record that the data was accessed.
*
* @param context Context for accessing resources.
* @param permission The permission to check.
@@ -170,13 +516,49 @@ public final class PermissionChecker {
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
*
- * @see #checkPermissionForDataDelivery(Context, String, int, int, String, String)
+ * @see #checkPermissionForDataDelivery(Context, String, int, int, String, String, String)
*/
@PermissionResult
public static int checkPermissionForPreflight(@NonNull Context context,
@NonNull String permission, int pid, int uid, @Nullable String packageName) {
- return checkPermissionCommon(context, permission, pid, uid, packageName,
- null /*attributionTag*/, null /*message*/, false /*forDataDelivery*/);
+ return checkPermissionForPreflight(context, permission, new AttributionSource(
+ uid, packageName, null /*attributionTag*/));
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * preflight point where you will not deliver the permission protected data
+ * to clients but schedule permission data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a data listener it should have the required
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have the
+ * permission and this check will not leave a trace that permission protected data
+ * was delivered. When you are about to deliver the protected data to a registered
+ * listener you should use {@link #checkPermissionForDataDelivery(Context, String,
+ * int, AttributionSource, String, boolean)} which will evaluate the permission access based
+ * on the current fg/bg state of the app and leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param attributionSource The identity for which to check the permission.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #checkPermissionForDataDelivery(Context, String, int, AttributionSource,
+ * String, boolean)
+ */
+ @PermissionResult
+ public static int checkPermissionForPreflight(@NonNull Context context,
+ @NonNull String permission, @NonNull AttributionSource attributionSource) {
+ return checkPermissionCommon(context, permission, attributionSource,
+ null /*message*/, false /*forDataDelivery*/, /*startDataDelivery*/ false,
+ /*fromDatasource*/ false);
}
/**
@@ -211,7 +593,8 @@ public final class PermissionChecker {
public static int checkSelfPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, @Nullable String message) {
return checkPermissionForDataDelivery(context, permission, Process.myPid(),
- Process.myUid(), context.getPackageName(), context.getAttributionTag(), message);
+ Process.myUid(), context.getPackageName(), context.getAttributionTag(), message,
+ /*startDataDelivery*/ false);
}
/**
@@ -289,7 +672,8 @@ public final class PermissionChecker {
return PERMISSION_HARD_DENIED;
}
return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
- Binder.getCallingUid(), callingPackageName, callingAttributionTag, message);
+ Binder.getCallingUid(), callingPackageName, callingAttributionTag, message,
+ /*startDataDelivery*/ false);
}
/**
@@ -308,8 +692,8 @@ public final class PermissionChecker {
* fg/gb state) and this check will not leave a trace that permission protected data
* was delivered. When you are about to deliver the location data to a registered
* listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context,
- * String, String)} which will evaluate the permission access based on the current fg/bg state
- * of the app and leave a record that the data was accessed.
+ * String, String, String, String)} which will evaluate the permission access based on the
+ * current fg/bg stateof the app and leave a record that the data was accessed.
*
* @param context Context for accessing resources.
* @param permission The permission to check.
@@ -318,7 +702,7 @@ public final class PermissionChecker {
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
*
- * @see #checkCallingPermissionForDataDelivery(Context, String, String, String)
+ * @see #checkCallingPermissionForDataDelivery(Context, String, String, String, String)
*/
@PermissionResult
public static int checkCallingPermissionForPreflight(@NonNull Context context,
@@ -370,7 +754,8 @@ public final class PermissionChecker {
callingAttributionTag = (Binder.getCallingPid() == Process.myPid())
? context.getAttributionTag() : callingAttributionTag;
return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
- Binder.getCallingUid(), callingPackageName, callingAttributionTag, message);
+ Binder.getCallingUid(), callingPackageName, callingAttributionTag, message,
+ /*startDataDelivery*/ false);
}
/**
@@ -408,88 +793,325 @@ public final class PermissionChecker {
Binder.getCallingUid(), packageName);
}
- static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
- @Nullable String message, boolean forDataDelivery) {
- final PermissionInfo permissionInfo;
- try {
- // TODO(b/147869157): Cache platform defined app op and runtime permissions to avoid
- // calling into the package manager every time.
- permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0);
- } catch (PackageManager.NameNotFoundException ignored) {
- return PERMISSION_HARD_DENIED;
- }
+ @PermissionResult
+ private static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
+ @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean forDataDelivery, boolean startDataDelivery,
+ boolean fromDatasource) {
+ PermissionInfo permissionInfo = sPlatformPermissions.get(permission);
- if (packageName == null) {
- String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
- if (packageNames != null && packageNames.length > 0) {
- packageName = packageNames[0];
+ if (permissionInfo == null) {
+ try {
+ permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0);
+ if (PLATFORM_PACKAGE_NAME.equals(permissionInfo.packageName)) {
+ // Double addition due to concurrency is fine - the backing store is concurrent.
+ sPlatformPermissions.put(permission, permissionInfo);
+ }
+ } catch (PackageManager.NameNotFoundException ignored) {
+ return PERMISSION_HARD_DENIED;
}
}
if (permissionInfo.isAppOp()) {
- return checkAppOpPermission(context, permission, pid, uid, packageName, attributionTag,
- message, forDataDelivery);
+ return checkAppOpPermission(context, permission, attributionSource, message,
+ forDataDelivery, fromDatasource);
}
if (permissionInfo.isRuntime()) {
- return checkRuntimePermission(context, permission, pid, uid, packageName,
- attributionTag, message, forDataDelivery);
+ return checkRuntimePermission(context, permission, attributionSource, message,
+ forDataDelivery, startDataDelivery, fromDatasource);
+ }
+
+ if (!fromDatasource && !checkPermission(context, permission, attributionSource.getUid(),
+ attributionSource.getRenouncedPermissions())) {
+ return PERMISSION_HARD_DENIED;
}
- return context.checkPermission(permission, pid, uid);
+
+ if (attributionSource.getNext() != null) {
+ return checkPermissionCommon(context, permission,
+ attributionSource.getNext(), message, forDataDelivery,
+ startDataDelivery, /*fromDatasource*/ false);
+ }
+
+ return PERMISSION_GRANTED;
}
+ @PermissionResult
private static int checkAppOpPermission(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
- @Nullable String message, boolean forDataDelivery) {
- final String op = AppOpsManager.permissionToOp(permission);
- if (op == null || packageName == null) {
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean fromDatasource) {
+ final int op = AppOpsManager.permissionToOpCode(permission);
+ if (op < 0 || attributionSource.getPackageName() == null) {
return PERMISSION_HARD_DENIED;
}
final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- final int opMode = (forDataDelivery)
- ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
- : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
- switch (opMode) {
- case AppOpsManager.MODE_ALLOWED:
- case AppOpsManager.MODE_FOREGROUND: {
+ AttributionSource current = attributionSource;
+ AttributionSource next = null;
+
+ while (true) {
+ final boolean skipCurrentChecks = (fromDatasource || next != null);
+
+ next = current.getNext();
+
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if ((!fromDatasource || current != attributionSource)
+ && next != null && !current.isTrusted(context)) {
+ return PERMISSION_HARD_DENIED;
+ }
+
+ int opMode;
+ if (forDataDelivery) {
+ if (next == null) {
+ opMode = appOpsManager.noteOpNoThrow(op, current.getUid(),
+ current.getPackageName(), current.getAttributionTag(), message);
+ } else {
+ opMode = appOpsManager.noteProxyOpNoThrow(op, current, message,
+ skipCurrentChecks);
+ }
+ } else {
+ opMode = appOpsManager.unsafeCheckOpRawNoThrow(op, current.getUid(),
+ current.getPackageName());
+ if (next != null && opMode == AppOpsManager.MODE_ALLOWED) {
+ opMode = appOpsManager.unsafeCheckOpRawNoThrow(op, next.getUid(),
+ next.getPackageName());
+ }
+ }
+
+ switch (opMode) {
+ case AppOpsManager.MODE_IGNORED:
+ case AppOpsManager.MODE_ERRORED: {
+ return PERMISSION_HARD_DENIED;
+ }
+ case AppOpsManager.MODE_DEFAULT: {
+ if (!skipCurrentChecks && !checkPermission(context, permission,
+ attributionSource.getUid(), attributionSource
+ .getRenouncedPermissions())) {
+ return PERMISSION_HARD_DENIED;
+ }
+ if (next != null && !checkPermission(context, permission,
+ next.getUid(), next.getRenouncedPermissions())) {
+ return PERMISSION_HARD_DENIED;
+ }
+ }
+ }
+
+ if (next == null || next.getNext() == null) {
return PERMISSION_GRANTED;
}
- case AppOpsManager.MODE_DEFAULT: {
- return context.checkPermission(permission, pid, uid)
- == PackageManager.PERMISSION_GRANTED
- ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED;
+
+ current = next;
+ }
+ }
+
+ private static int checkRuntimePermission(@NonNull Context context, @NonNull String permission,
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) {
+ // Now let's check the identity chain...
+ final int op = AppOpsManager.permissionToOpCode(permission);
+
+ AttributionSource current = attributionSource;
+ AttributionSource next = null;
+
+ while (true) {
+ final boolean skipCurrentChecks = (fromDatasource || next != null);
+ next = current.getNext();
+
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if ((!fromDatasource || current != attributionSource)
+ && next != null && !current.isTrusted(context)) {
+ return PERMISSION_HARD_DENIED;
}
- default: {
+
+ // If we already checked the permission for this one, skip the work
+ if (!skipCurrentChecks && !checkPermission(context, permission,
+ current.getUid(), current.getRenouncedPermissions())) {
+ return PERMISSION_HARD_DENIED;
+ }
+
+ if (next != null && !checkPermission(context, permission,
+ next.getUid(), next.getRenouncedPermissions())) {
return PERMISSION_HARD_DENIED;
}
+
+ if (op < 0) {
+ continue;
+ }
+
+ // The access is for oneself if this is the single receiver of data
+ // after the data source or if this is the single attribution source
+ // in the chain if not from a datasource.
+ final boolean singleReceiverFromDatasource = (fromDatasource
+ && current == attributionSource && next != null && next.getNext() == null);
+ final boolean selfAccess = singleReceiverFromDatasource || next == null;
+
+ final int opMode = performOpTransaction(context, op, current, message,
+ forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
+ singleReceiverFromDatasource);
+
+ switch (opMode) {
+ case AppOpsManager.MODE_ERRORED: {
+ return PERMISSION_HARD_DENIED;
+ }
+ case AppOpsManager.MODE_IGNORED: {
+ return PERMISSION_SOFT_DENIED;
+ }
+ }
+
+ if (next == null || next.getNext() == null) {
+ return PERMISSION_GRANTED;
+ }
+
+ current = next;
}
}
- private static int checkRuntimePermission(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
- @Nullable String message, boolean forDataDelivery) {
- if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
- return PERMISSION_HARD_DENIED;
+ private static boolean checkPermission(@NonNull Context context, @NonNull String permission,
+ int uid, @NonNull Set<String> renouncedPermissions) {
+ final boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1,
+ uid) == PackageManager.PERMISSION_GRANTED;
+ if (permissionGranted && renouncedPermissions.contains(permission)) {
+ return false;
}
+ return permissionGranted;
+ }
- final String op = AppOpsManager.permissionToOp(permission);
- if (op == null || packageName == null) {
- return PERMISSION_GRANTED;
+ private static int checkOp(@NonNull Context context, @NonNull int op,
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery) {
+ if (op < 0 || attributionSource.getPackageName() == null) {
+ return PERMISSION_HARD_DENIED;
}
- final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- final int opMode = (forDataDelivery)
- ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
- : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
+ AttributionSource current = attributionSource;
+ AttributionSource next = null;
+
+ while (true) {
+ final boolean skipCurrentChecks = (next != null);
+ next = current.getNext();
+
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if (next != null && !current.isTrusted(context)) {
+ return PERMISSION_HARD_DENIED;
+ }
+
+ // The access is for oneself if this is the single attribution source in the chain.
+ final boolean selfAccess = (next == null);
+
+ final int opMode = performOpTransaction(context, op, current, message,
+ forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
+ /*fromDatasource*/ false);
+
+ switch (opMode) {
+ case AppOpsManager.MODE_ERRORED: {
+ return PERMISSION_HARD_DENIED;
+ }
+ case AppOpsManager.MODE_IGNORED: {
+ return PERMISSION_SOFT_DENIED;
+ }
+ }
- switch (opMode) {
- case AppOpsManager.MODE_ALLOWED:
- case AppOpsManager.MODE_FOREGROUND:
+ if (next == null || next.getNext() == null) {
return PERMISSION_GRANTED;
- default:
- return PERMISSION_SOFT_DENIED;
+ }
+
+ current = next;
+ }
+ }
+ // If from data source and there is next app after that we need to note SELF of (noteOp) for the app vs proxy
+ private static int performOpTransaction(@NonNull Context context, int op,
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery, boolean skipProxyOperation,
+ boolean selfAccess, boolean singleReceiverFromDatasource) {
+ // We cannot perform app ops transactions without a package name. In all relevant
+ // places we pass the package name but just in case there is a bug somewhere we
+ // do a best effort to resolve the package from the UID (pick first without a loss
+ // of generality - they are in the same security sandbox).
+ final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ if (!forDataDelivery) {
+ final String resolvedPackageName = resolvePackageName(context, attributionSource);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ final int opMode = appOpsManager.unsafeCheckOpRawNoThrow(op,
+ attributionSource.getUid(), resolvedPackageName);
+ final AttributionSource previous = attributionSource.getNext();
+ if (opMode == AppOpsManager.MODE_ALLOWED && previous != null) {
+ final String resolvedPreviousPackageName = resolvePackageName(context,
+ previous);
+ if (resolvedPreviousPackageName == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ return appOpsManager.unsafeCheckOpRawNoThrow(op, previous.getUid(),
+ resolvedPreviousPackageName);
+ }
+ return opMode;
+ } else if (startDataDelivery) {
+ final AttributionSource accessorSource = (!singleReceiverFromDatasource)
+ ? attributionSource : attributionSource.getNext();
+ final AttributionSource resolvedAttributionSource = resolveAttributionSource(
+ context, accessorSource);
+ if (resolvedAttributionSource.getPackageName() == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ if (selfAccess) {
+ return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(),
+ resolvedAttributionSource.getPackageName(),
+ /*startIfModeDefault*/ false,
+ resolvedAttributionSource.getAttributionTag(),
+ message);
+ } else {
+ return appOpsManager.startProxyOpNoThrow(op, resolvedAttributionSource, message,
+ skipProxyOperation);
+ }
+ } else {
+ final AttributionSource accessorSource = (!singleReceiverFromDatasource)
+ ? attributionSource : attributionSource.getNext();
+ final AttributionSource resolvedAttributionSource = resolveAttributionSource(
+ context, accessorSource);
+ if (resolvedAttributionSource.getPackageName() == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ if (selfAccess) {
+ return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(),
+ resolvedAttributionSource.getPackageName(),
+ resolvedAttributionSource.getAttributionTag(),
+ message);
+ } else {
+ return appOpsManager.noteProxyOpNoThrow(op, resolvedAttributionSource, message,
+ skipProxyOperation);
+ }
+ }
+ }
+
+ private static @Nullable String resolvePackageName(@NonNull Context context,
+ @NonNull AttributionSource attributionSource) {
+ if (attributionSource.getPackageName() != null) {
+ return attributionSource.getPackageName();
+ }
+ final String[] packageNames = context.getPackageManager().getPackagesForUid(
+ attributionSource.getUid());
+ if (packageNames != null) {
+ // This is best effort if the caller doesn't pass a package. The security
+ // sandbox is UID, therefore we pick an arbitrary package.
+ return packageNames[0];
+ }
+ return null;
+ }
+
+ private static @NonNull AttributionSource resolveAttributionSource(
+ @NonNull Context context, @NonNull AttributionSource attributionSource) {
+ if (attributionSource.getPackageName() != null) {
+ return attributionSource;
}
+ return new AttributionSource(attributionSource.getUid(),
+ resolvePackageName(context, attributionSource),
+ attributionSource.getAttributionTag(),
+ attributionSource.getToken(),
+ attributionSource.getRenouncedPermissions(),
+ attributionSource.getNext());
}
}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 8c105be9fbb7..ef075e1efbff 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -16,6 +16,7 @@
package android.permission;
+import android.content.AttributionSource;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -85,4 +86,8 @@ interface IPermissionManager {
boolean setAutoRevokeExempted(String packageName, boolean exempted, int userId);
boolean isAutoRevokeExempted(String packageName, int userId);
+
+ AttributionSource registerAttributionSource(in AttributionSource source);
+
+ boolean isRegisteredAttributionSource(in AttributionSource source);
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index baa25f07f514..936cbfc70708 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -44,6 +44,7 @@ import android.content.pm.PermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.location.LocationManager;
import android.media.AudioManager;
+import android.content.AttributionSource;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -1099,6 +1100,48 @@ public final class PermissionManager {
callingFeatureId, pid, uid);
}
+ /**
+ * Registers an attribution source with the OS. An app can only register an attribution
+ * source for itself. Once an attribution source has been registered another app can
+ * check whether this registration exists and thus trust the payload in the source
+ * object. This is important for permission checking and specifically for app op blaming
+ * since a malicious app should not be able to force the OS to blame another app
+ * that doesn't participate in an attribution chain.
+ *
+ * @param source The attribution source to register.
+ *
+ * @see #isRegisteredAttributionSource(AttributionSource)
+ *
+ * @hide
+ */
+ public @NonNull AttributionSource registerAttributionSource(@NonNull AttributionSource source) {
+ try {
+ return mPermissionManager.registerAttributionSource(source);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether an attribution source is registered.
+ *
+ * @param source The attribution source to check.
+ * @return Whether this is a registered source.
+ *
+ * @see #registerAttributionSource(AttributionSource)
+ *
+ * @hide
+ */
+ public boolean isRegisteredAttributionSource(@NonNull AttributionSource source) {
+ try {
+ return mPermissionManager.isRegisteredAttributionSource(source);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
/* @hide */
private static int checkPermissionUncached(@Nullable String permission, int pid, int uid) {
final IActivityManager am = ActivityManager.getService();
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index b704d66d8e41..a5a24c0f1013 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -1102,8 +1102,7 @@ public abstract class DocumentsProvider extends ContentProvider {
// signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for
// MANAGE_DOCUMENTS or associated URI permission here instead
final Uri rootUri = extraUri;
- enforceWritePermissionInner(rootUri, getCallingPackage(), getCallingAttributionTag(),
- null);
+ enforceWritePermissionInner(rootUri, getCallingAttributionSource());
final String rootId = DocumentsContract.getRootId(rootUri);
ejectRoot(rootId);
@@ -1121,8 +1120,7 @@ public abstract class DocumentsProvider extends ContentProvider {
}
if (METHOD_IS_CHILD_DOCUMENT.equals(method)) {
- enforceReadPermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceReadPermissionInner(documentUri, getCallingAttributionSource());
final Uri childUri = extraTargetUri;
final String childAuthority = childUri.getAuthority();
@@ -1134,8 +1132,7 @@ public abstract class DocumentsProvider extends ContentProvider {
&& isChildDocument(documentId, childId));
} else if (METHOD_CREATE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceWritePermissionInner(documentUri, getCallingAttributionSource());
final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
@@ -1149,8 +1146,7 @@ public abstract class DocumentsProvider extends ContentProvider {
out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
} else if (METHOD_CREATE_WEB_LINK_INTENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceWritePermissionInner(documentUri, getCallingAttributionSource());
final Bundle options = extras.getBundle(DocumentsContract.EXTRA_OPTIONS);
final IntentSender intentSender = createWebLinkIntent(documentId, options);
@@ -1158,8 +1154,7 @@ public abstract class DocumentsProvider extends ContentProvider {
out.putParcelable(DocumentsContract.EXTRA_RESULT, intentSender);
} else if (METHOD_RENAME_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceWritePermissionInner(documentUri, getCallingAttributionSource());
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
final String newDocumentId = renameDocument(documentId, displayName);
@@ -1183,8 +1178,7 @@ public abstract class DocumentsProvider extends ContentProvider {
}
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceWritePermissionInner(documentUri, getCallingAttributionSource());
deleteDocument(documentId);
// Document no longer exists, clean up any grants.
@@ -1194,10 +1188,8 @@ public abstract class DocumentsProvider extends ContentProvider {
final Uri targetUri = extraTargetUri;
final String targetId = DocumentsContract.getDocumentId(targetUri);
- enforceReadPermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
- enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingAttributionTag(),
- null);
+ enforceReadPermissionInner(documentUri, getCallingAttributionSource());
+ enforceWritePermissionInner(targetUri, getCallingAttributionSource());
final String newDocumentId = copyDocument(documentId, targetId);
@@ -1220,12 +1212,9 @@ public abstract class DocumentsProvider extends ContentProvider {
final Uri targetUri = extraTargetUri;
final String targetId = DocumentsContract.getDocumentId(targetUri);
- enforceWritePermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
- enforceReadPermissionInner(parentSourceUri, getCallingPackage(),
- getCallingAttributionTag(), null);
- enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingAttributionTag(),
- null);
+ enforceWritePermissionInner(documentUri, getCallingAttributionSource());
+ enforceReadPermissionInner(parentSourceUri, getCallingAttributionSource());
+ enforceWritePermissionInner(targetUri, getCallingAttributionSource());
final String newDocumentId = moveDocument(documentId, parentSourceId, targetId);
@@ -1246,10 +1235,8 @@ public abstract class DocumentsProvider extends ContentProvider {
final Uri parentSourceUri = extraParentUri;
final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri);
- enforceReadPermissionInner(parentSourceUri, getCallingPackage(),
- getCallingAttributionTag(), null);
- enforceWritePermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceReadPermissionInner(parentSourceUri, getCallingAttributionSource());
+ enforceWritePermissionInner(documentUri, getCallingAttributionSource());
removeDocument(documentId, parentSourceId);
// It's responsibility of the provider to revoke any grants, as the document may be
@@ -1258,8 +1245,7 @@ public abstract class DocumentsProvider extends ContentProvider {
final boolean isTreeUri = isTreeUri(documentUri);
if (isTreeUri) {
- enforceReadPermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceReadPermissionInner(documentUri, getCallingAttributionSource());
} else {
getContext().enforceCallingPermission(Manifest.permission.MANAGE_DOCUMENTS, null);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5d924cc88706..12ff6405b8f2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2754,7 +2754,7 @@ public final class Settings {
arg.putBoolean(CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY, true);
}
IContentProvider cp = mProviderHolder.getProvider(cr);
- cp.call(cr.getPackageName(), cr.getAttributionTag(),
+ cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
@@ -2774,7 +2774,7 @@ public final class Settings {
args.putString(CALL_METHOD_PREFIX_KEY, prefix);
args.putSerializable(CALL_METHOD_FLAGS_KEY, keyValues);
IContentProvider cp = mProviderHolder.getProvider(cr);
- Bundle bundle = cp.call(cr.getPackageName(), cr.getAttributionTag(),
+ Bundle bundle = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(),
mCallSetAllCommand, null, args);
return bundle.getBoolean(KEY_CONFIG_SET_RETURN);
@@ -2862,14 +2862,14 @@ public final class Settings {
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
- b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
+ b = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallGetCommand, name,
args);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
- b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
+ b = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, args);
}
if (b != null) {
@@ -2939,13 +2939,13 @@ public final class Settings {
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
- c = cp.query(cr.getPackageName(), cr.getAttributionTag(), mUri,
+ c = cp.query(cr.getAttributionSource(), mUri,
SELECT_VALUE_PROJECTION, queryArgs, null);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
- c = cp.query(cr.getPackageName(), cr.getAttributionTag(), mUri,
+ c = cp.query(cr.getAttributionSource(), mUri,
SELECT_VALUE_PROJECTION, queryArgs, null);
}
if (c == null) {
@@ -3051,7 +3051,7 @@ public final class Settings {
}
// Fetch all flags for the namespace at once for caching purposes
- Bundle b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
+ Bundle b = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
if (b == null) {
// Invalid response, return an empty map
@@ -5877,7 +5877,7 @@ public final class Settings {
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
+ cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_SECURE, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
@@ -14914,7 +14914,7 @@ public final class Settings {
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
+ cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_GLOBAL, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
@@ -16053,7 +16053,7 @@ public final class Settings {
arg.putString(Settings.CALL_METHOD_PREFIX_KEY, createPrefix(namespace));
}
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
+ cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_CONFIG, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset to defaults for " + DeviceConfig.CONTENT_URI, e);
@@ -16082,7 +16082,7 @@ public final class Settings {
arg.putInt(CALL_METHOD_USER_KEY, userHandle);
arg.putParcelable(CALL_METHOD_MONITOR_CALLBACK_KEY, callback);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
+ cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(),
CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG, null, arg);
} catch (RemoteException e) {
diff --git a/core/java/android/speech/IRecognitionService.aidl b/core/java/android/speech/IRecognitionService.aidl
index cc1cdedd0f96..9a5e534a68cf 100644
--- a/core/java/android/speech/IRecognitionService.aidl
+++ b/core/java/android/speech/IRecognitionService.aidl
@@ -17,6 +17,7 @@
package android.speech;
import android.os.Bundle;
+import android.content.AttributionSource;
import android.content.Intent;
import android.speech.IRecognitionListener;
@@ -39,11 +40,10 @@ oneway interface IRecognitionService {
* this intent can contain extra parameters to manipulate the behavior of the recognition
* client. For more information see {@link RecognizerIntent}.
* @param listener to receive callbacks, note that this must be non-null
- * @param packageName the package name calling this API
- * @param featureId The feature in the package
+ * @param attributionSource The attribution source of the caller.
*/
void startListening(in Intent recognizerIntent, in IRecognitionListener listener,
- String packageName, String featureId, int callingUid);
+ in AttributionSource attributionSource);
/**
* Stops listening for speech. Speech captured so far will be recognized as
@@ -51,18 +51,14 @@ oneway interface IRecognitionService {
* is called during the speech capturing.
*
* @param listener to receive callbacks, note that this must be non-null
- * @param packageName the package name calling this API
- * @param featureId The feature in the package
*/
- void stopListening(in IRecognitionListener listener, String packageName, String featureId);
+ void stopListening(in IRecognitionListener listener);
/**
* Cancels the speech recognition.
*
* @param listener to receive callbacks, note that this must be non-null
* @param packageName the package name calling this API
- * @param featureId The feature in the package
- * @param isShutdown Whether the cancellation is caused by a client calling #shutdown
*/
- void cancel(in IRecognitionListener listener, String packageName, String featureId, boolean isShutdown);
+ void cancel(in IRecognitionListener listener, boolean isShutdown);
}
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index fd584f191743..4afa94735d62 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -16,11 +16,16 @@
package android.speech;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
+import android.app.AppOpsManager;
import android.app.Service;
+import android.content.Context;
+import android.content.ContextParams;
import android.content.Intent;
import android.content.PermissionChecker;
import android.os.Binder;
@@ -28,13 +33,13 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.content.AttributionSource;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
-import com.android.internal.util.Preconditions;
-
import java.lang.ref.WeakReference;
+import java.util.Objects;
/**
* This class provides a base class for recognition service implementations. This class should be
@@ -86,13 +91,13 @@ public abstract class RecognitionService extends Service {
switch (msg.what) {
case MSG_START_LISTENING:
StartListeningArgs args = (StartListeningArgs) msg.obj;
- dispatchStartListening(args.mIntent, args.mListener, args.mCallingUid);
+ dispatchStartListening(args.mIntent, args.mListener, args.mAttributionSource);
break;
case MSG_STOP_LISTENING:
dispatchStopListening((IRecognitionListener) msg.obj);
break;
case MSG_CANCEL:
- dispatchCancel((IRecognitionListener) msg.obj);
+ dispatchCancel((IRecognitionListener) msg.obj, msg.arg1 == 1);
break;
case MSG_RESET:
dispatchClearCallback();
@@ -102,10 +107,11 @@ public abstract class RecognitionService extends Service {
};
private void dispatchStartListening(Intent intent, final IRecognitionListener listener,
- int callingUid) {
+ @NonNull AttributionSource attributionSource) {
if (mCurrentCallback == null) {
if (DBG) Log.d(TAG, "created new mCurrentCallback, listener = " + listener.asBinder());
- mCurrentCallback = new Callback(listener, callingUid);
+ mCurrentCallback = new Callback(listener, attributionSource);
+
RecognitionService.this.onStartListening(intent, mCurrentCallback);
} else {
try {
@@ -133,13 +139,16 @@ public abstract class RecognitionService extends Service {
}
}
- private void dispatchCancel(IRecognitionListener listener) {
+ private void dispatchCancel(IRecognitionListener listener, boolean shutDown) {
if (mCurrentCallback == null) {
if (DBG) Log.d(TAG, "cancel called with no preceding startListening - ignoring");
} else if (mCurrentCallback.mListener.asBinder() != listener.asBinder()) {
Log.w(TAG, "cancel called by client who did not call startListening - ignoring");
} else { // the correct state
RecognitionService.this.onCancel(mCurrentCallback);
+ if (shutDown) {
+ mCurrentCallback.finishRecordAudioOpAttributionToCallerIfNeeded();
+ }
mCurrentCallback = null;
if (DBG) Log.d(TAG, "canceling - setting mCurrentCallback to null");
}
@@ -153,12 +162,13 @@ public abstract class RecognitionService extends Service {
public final Intent mIntent;
public final IRecognitionListener mListener;
- public final int mCallingUid;
+ public final @NonNull AttributionSource mAttributionSource;
- public StartListeningArgs(Intent intent, IRecognitionListener listener, int callingUid) {
+ public StartListeningArgs(Intent intent, IRecognitionListener listener,
+ @NonNull AttributionSource attributionSource) {
this.mIntent = intent;
this.mListener = listener;
- this.mCallingUid = callingUid;
+ this.mAttributionSource = attributionSource;
}
}
@@ -247,18 +257,19 @@ public abstract class RecognitionService extends Service {
*/
public class Callback {
private final IRecognitionListener mListener;
- private final int mCallingUid;
+ private final @NonNull AttributionSource mCallingAttributionSource;
+ private @Nullable Context mAttributionContext;
- private Callback(IRecognitionListener listener, int callingUid) {
+ private Callback(IRecognitionListener listener,
+ @NonNull AttributionSource attributionSource) {
mListener = listener;
- mCallingUid = callingUid;
+ mCallingAttributionSource = attributionSource;
}
/**
* The service should call this method when the user has started to speak.
*/
public void beginningOfSpeech() throws RemoteException {
- if (DBG) Log.d(TAG, "beginningOfSpeech");
mListener.onBeginningOfSpeech();
}
@@ -270,6 +281,7 @@ public abstract class RecognitionService extends Service {
* single channel audio stream. The sample rate is implementation dependent.
*/
public void bufferReceived(byte[] buffer) throws RemoteException {
+ startRecordAudioOpAttributionToCallerIfNeeded();
mListener.onBufferReceived(buffer);
}
@@ -302,6 +314,7 @@ public abstract class RecognitionService extends Service {
* {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter
*/
public void partialResults(Bundle partialResults) throws RemoteException {
+ startRecordAudioOpAttributionToCallerIfNeeded();
mListener.onPartialResults(partialResults);
}
@@ -323,6 +336,7 @@ public abstract class RecognitionService extends Service {
* {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter
*/
public void results(Bundle results) throws RemoteException {
+ startRecordAudioOpAttributionToCallerIfNeeded();
Message.obtain(mHandler, MSG_RESET).sendToTarget();
mListener.onResults(results);
}
@@ -342,7 +356,65 @@ public abstract class RecognitionService extends Service {
* is being processed. This is obtained from {@link Binder#getCallingUid()}.
*/
public int getCallingUid() {
- return mCallingUid;
+ return mCallingAttributionSource.getUid();
+ }
+
+ /**
+ * Gets the permission identity of the calling app. If you want to attribute
+ * the mic access to the calling app you can create an attribution context
+ * via {@link android.content.Context#createContext(android.content.ContextParams)}
+ * and passing this identity to {@link
+ * android.content.ContextParams.Builder#setNextAttributionSource(AttributionSource)}.
+ *
+ *
+ *
+ *
+ * @return The permission identity of the calling app.
+ *
+ * @see android.content.ContextParams.Builder#setNextAttributionSource(
+ * AttributionSource)
+ */
+ @SuppressLint("CallbackMethodName")
+ public @NonNull AttributionSource getCallingAttributionSource() {
+ return mCallingAttributionSource;
+ }
+
+ private void startRecordAudioOpAttributionToCallerIfNeeded() throws RemoteException {
+ if (!isProxyingRecordAudioToCaller()) {
+ final int result = PermissionChecker.checkPermissionAndStartDataDelivery(
+ RecognitionService.this, Manifest.permission.RECORD_AUDIO,
+ getAttributionContextForCaller().getAttributionSource(),
+ /*message*/ null);
+ if (result == PermissionChecker.PERMISSION_GRANTED) {
+ return;
+ }
+ error(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
+ }
+ }
+
+ private @NonNull Context getAttributionContextForCaller() {
+ if (mAttributionContext == null) {
+ mAttributionContext = createContext(new ContextParams.Builder()
+ .setNextAttributionSource(mCallingAttributionSource)
+ .build());
+ }
+ return mAttributionContext;
+ }
+
+ void finishRecordAudioOpAttributionToCallerIfNeeded() {
+ if (isProxyingRecordAudioToCaller()) {
+ final String op = AppOpsManager.permissionToOp(Manifest.permission.RECORD_AUDIO);
+ PermissionChecker.finishDataDelivery(RecognitionService.this,
+ op, getAttributionContextForCaller().getAttributionSource());
+ }
+ }
+
+ private boolean isProxyingRecordAudioToCaller() {
+ final int op = AppOpsManager.permissionToOpCode(Manifest.permission.RECORD_AUDIO);
+ final AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
+ return appOpsManager.isProxying(op, getAttributionTag(),
+ mCallingAttributionSource.getUid(),
+ mCallingAttributionSource.getPackageName());
}
}
@@ -356,44 +428,35 @@ public abstract class RecognitionService extends Service {
@Override
public void startListening(Intent recognizerIntent, IRecognitionListener listener,
- String packageName, String featureId, int callingUid) {
- Preconditions.checkNotNull(packageName);
-
+ @NonNull AttributionSource attributionSource) {
+ Objects.requireNonNull(attributionSource);
+ attributionSource.enforceCallingUid();
if (DBG) Log.d(TAG, "startListening called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener, true /*forDataDelivery*/,
- packageName, featureId)) {
+ if (service != null) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_START_LISTENING, service.new StartListeningArgs(
- recognizerIntent, listener, callingUid)));
+ recognizerIntent, listener, attributionSource)));
}
}
@Override
- public void stopListening(IRecognitionListener listener, String packageName,
- String featureId) {
- Preconditions.checkNotNull(packageName);
-
+ public void stopListening(IRecognitionListener listener) {
if (DBG) Log.d(TAG, "stopListening called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener, false /*forDataDelivery*/,
- packageName, featureId)) {
+ if (service != null) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_STOP_LISTENING, listener));
}
}
@Override
- public void cancel(IRecognitionListener listener, String packageName,
- String featureId, boolean isShutdown) {
- Preconditions.checkNotNull(packageName);
-
+ public void cancel(IRecognitionListener listener, boolean isShutdown) {
if (DBG) Log.d(TAG, "cancel called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener, false /*forDataDelivery*/,
- packageName, featureId)) {
+ if (service != null) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
- MSG_CANCEL, listener));
+ MSG_CANCEL, isShutdown ? 1 : 0, 0, listener));
}
}
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 9b93a64e48a3..7aa5ee51b606 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -386,8 +386,7 @@ public class SpeechRecognizer {
return;
}
try {
- mService.startListening(recognizerIntent, mListener, mContext.getOpPackageName(),
- mContext.getAttributionTag(), android.os.Process.myUid());
+ mService.startListening(recognizerIntent, mListener, mContext.getAttributionSource());
if (DBG) Log.d(TAG, "service start listening command succeded");
} catch (final RemoteException e) {
Log.e(TAG, "startListening() failed", e);
@@ -401,8 +400,7 @@ public class SpeechRecognizer {
return;
}
try {
- mService.stopListening(mListener, mContext.getOpPackageName(),
- mContext.getAttributionTag());
+ mService.stopListening(mListener);
if (DBG) Log.d(TAG, "service stop listening command succeded");
} catch (final RemoteException e) {
Log.e(TAG, "stopListening() failed", e);
@@ -416,11 +414,7 @@ public class SpeechRecognizer {
return;
}
try {
- mService.cancel(
- mListener,
- mContext.getOpPackageName(),
- mContext.getAttributionTag(),
- false /* isShutdown */);
+ mService.cancel(mListener, /*isShutdown*/ false);
if (DBG) Log.d(TAG, "service cancel command succeded");
} catch (final RemoteException e) {
Log.e(TAG, "cancel() failed", e);
@@ -463,8 +457,7 @@ public class SpeechRecognizer {
public void destroy() {
if (mService != null) {
try {
- mService.cancel(mListener, mContext.getOpPackageName(),
- mContext.getAttributionTag(), true /* isShutdown */);
+ mService.cancel(mListener, /*isShutdown*/ true);
} catch (final RemoteException e) {
// Not important
}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index eecd0cfe66a4..01bb199a1a6a 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -20,6 +20,7 @@ import android.app.AppOpsManager;
import android.app.AsyncNotedAppOp;
import android.app.SyncNotedAppOp;
import android.app.RuntimeAppOpAccessMessage;
+import android.content.AttributionSource;
import android.content.pm.ParceledListSlice;
import android.os.Bundle;
import android.os.RemoteCallback;
@@ -52,17 +53,13 @@ interface IAppOpsService {
// End of methods also called by native code.
// Any new method exposed to native must be added after the last one, do not reorder
- int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
- String proxiedAttributionTag, int proxyUid, String proxyPackageName,
- String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message,
- boolean shouldCollectMessage);
- int startProxyOperation(IBinder clientId, int code, int proxiedUid, String proxiedPackageName,
- @nullable String proxiedAttributionTag, int proxyUid, String proxyPackageName,
- @nullable String proxyAttributionTag, boolean startIfModeDefault,
- boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
- void finishProxyOperation(IBinder clientId, int code, int proxiedUid, String proxiedPackageName,
- @nullable String proxiedAttributionTag, int proxyUid, String proxyPackageName,
- @nullable String proxyAttributionTag);
+ int noteProxyOperation(int code, in AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation);
+ int startProxyOperation(IBinder clientId, int code, in AttributionSource attributionSource,
+ boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
+ boolean shouldCollectMessage, boolean skipProxyOperation);
+ void finishProxyOperation(IBinder clientId, int code, in AttributionSource attributionSource);
// Remaining methods are only used in Java.
int checkPackage(int uid, String packageName);
@@ -83,6 +80,7 @@ interface IAppOpsService {
void setHistoryParameters(int mode, long baseSnapshotInterval, int compressionStep);
void addHistoricalOps(in AppOpsManager.HistoricalOps ops);
void resetHistoryParameters();
+ void resetPackageOpsNoHistory(String packageName);
void clearHistory();
void rebootHistory(long offlineDurationMillis);
List<AppOpsManager.PackageOps> getUidOps(int uid, in int[] ops);
@@ -100,6 +98,8 @@ interface IAppOpsService {
void startWatchingActive(in int[] ops, IAppOpsActiveCallback callback);
void stopWatchingActive(IAppOpsActiveCallback callback);
boolean isOperationActive(int code, int uid, String packageName);
+ boolean isProxying(int op, String proxyPackageName, String proxyAttributionTag, int proxiedUid,
+ String proxiedPackageName);
void startWatchingStarted(in int[] ops, IAppOpsStartedCallback callback);
void stopWatchingStarted(IAppOpsStartedCallback callback);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index be306e07a7a5..ada670459c13 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1313,7 +1313,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_recordBackgroundAudio"
android:description="@string/permdesc_recordBackgroundAudio"
- android:protectionLevel="internal" />
+ android:protectionLevel="internal|role" />
<!-- ====================================================================== -->
<!-- Permissions for activity recognition -->
@@ -1405,7 +1405,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_backgroundCamera"
android:description="@string/permdesc_backgroundCamera"
- android:protectionLevel="internal" />
+ android:protectionLevel="internal|role" />
<!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access
system only camera devices.
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index b517428f5b59..01e240a7ff8a 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -86,7 +86,7 @@ public class ContentResolverTest {
final AssetFileDescriptor afd = new AssetFileDescriptor(
new ParcelFileDescriptor(mImage.getFileDescriptor()), 0, mSize, null);
- when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any(), any())).thenReturn(
+ when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any())).thenReturn(
afd);
}
diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
index 4212ef21e0af..97e66c481dda 100644
--- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java
+++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
@@ -33,6 +33,7 @@ import android.test.mock.MockContentResolver;
import android.util.MemoryIntArray;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
@@ -73,7 +74,8 @@ public class NameValueCacheTest {
Settings.Config.clearProviderForTest();
MockitoAnnotations.initMocks(this);
when(mMockContentProvider.getIContentProvider()).thenReturn(mMockIContentProvider);
- mMockContentResolver = new MockContentResolver();
+ mMockContentResolver = new MockContentResolver(InstrumentationRegistry
+ .getInstrumentation().getContext());
mMockContentResolver.addProvider(DeviceConfig.CONTENT_URI.getAuthority(),
mMockContentProvider);
mCacheGenerationStore = new MemoryIntArray(1);
@@ -82,10 +84,10 @@ public class NameValueCacheTest {
// Stores keyValues for a given prefix and increments the generation. (Note that this
// increments the generation no matter what, it doesn't pay attention to if anything
// actually changed).
- when(mMockIContentProvider.call(any(), any(), eq(DeviceConfig.CONTENT_URI.getAuthority()),
+ when(mMockIContentProvider.call(any(), eq(DeviceConfig.CONTENT_URI.getAuthority()),
eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
any(), any(Bundle.class))).thenAnswer(invocationOnMock -> {
- Bundle incomingBundle = invocationOnMock.getArgument(5);
+ Bundle incomingBundle = invocationOnMock.getArgument(4);
HashMap<String, String> keyValues =
(HashMap<String, String>) incomingBundle.getSerializable(
Settings.CALL_METHOD_FLAGS_KEY);
@@ -101,10 +103,10 @@ public class NameValueCacheTest {
// Returns the keyValues corresponding to a namespace, or an empty map if the namespace
// doesn't have anything stored for it. Returns the generation key if the caller asked
// for one.
- when(mMockIContentProvider.call(any(), any(), eq(DeviceConfig.CONTENT_URI.getAuthority()),
+ when(mMockIContentProvider.call(any(), eq(DeviceConfig.CONTENT_URI.getAuthority()),
eq(Settings.CALL_METHOD_LIST_CONFIG),
any(), any(Bundle.class))).thenAnswer(invocationOnMock -> {
- Bundle incomingBundle = invocationOnMock.getArgument(5);
+ Bundle incomingBundle = invocationOnMock.getArgument(4);
String prefix = incomingBundle.getString(Settings.CALL_METHOD_PREFIX_KEY);
@@ -132,14 +134,14 @@ public class NameValueCacheTest {
HashMap<String, String> keyValues = new HashMap<>();
keyValues.put("a", "b");
Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues);
- verify(mMockIContentProvider).call(any(), any(), any(),
+ verify(mMockIContentProvider).call(any(), any(),
eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
any(), any(Bundle.class));
Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
NAMESPACE,
Collections.emptyList());
- verify(mMockIContentProvider).call(any(), any(), any(),
+ verify(mMockIContentProvider).call(any(), any(),
eq(Settings.CALL_METHOD_LIST_CONFIG),
any(), any(Bundle.class));
assertThat(returnedValues).containsExactlyEntriesIn(keyValues);
@@ -152,13 +154,13 @@ public class NameValueCacheTest {
// Modify the value to invalidate the cache.
keyValues.put("a", "c");
Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues);
- verify(mMockIContentProvider, times(2)).call(any(), any(), any(),
+ verify(mMockIContentProvider, times(2)).call(any(), any(),
eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
any(), any(Bundle.class));
Map<String, String> returnedValues2 = Settings.Config.getStrings(mMockContentResolver,
NAMESPACE, Collections.emptyList());
- verify(mMockIContentProvider, times(2)).call(any(), any(), any(),
+ verify(mMockIContentProvider, times(2)).call(any(), any(),
eq(Settings.CALL_METHOD_LIST_CONFIG),
any(), any(Bundle.class));
assertThat(returnedValues2).containsExactlyEntriesIn(keyValues);
@@ -174,7 +176,7 @@ public class NameValueCacheTest {
HashMap<String, String> keyValues = new HashMap<>();
keyValues.put("a", "b");
Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues);
- verify(mMockIContentProvider).call(any(), any(), any(),
+ verify(mMockIContentProvider).call(any(), any(),
eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
any(), any(Bundle.class));
@@ -182,14 +184,14 @@ public class NameValueCacheTest {
keyValues2.put("c", "d");
keyValues2.put("e", "f");
Settings.Config.setStrings(mMockContentResolver, NAMESPACE2, keyValues2);
- verify(mMockIContentProvider, times(2)).call(any(), any(), any(),
+ verify(mMockIContentProvider, times(2)).call(any(), any(),
eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
any(), any(Bundle.class));
Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
NAMESPACE,
Collections.emptyList());
- verify(mMockIContentProvider).call(any(), any(), any(),
+ verify(mMockIContentProvider).call(any(), any(),
eq(Settings.CALL_METHOD_LIST_CONFIG),
any(), any(Bundle.class));
assertThat(returnedValues).containsExactlyEntriesIn(keyValues);
@@ -197,7 +199,7 @@ public class NameValueCacheTest {
Map<String, String> returnedValues2 = Settings.Config.getStrings(mMockContentResolver,
NAMESPACE2,
Collections.emptyList());
- verify(mMockIContentProvider, times(2)).call(any(), any(), any(),
+ verify(mMockIContentProvider, times(2)).call(any(), any(),
eq(Settings.CALL_METHOD_LIST_CONFIG),
any(), any(Bundle.class));
assertThat(returnedValues2).containsExactlyEntriesIn(keyValues2);
@@ -218,7 +220,7 @@ public class NameValueCacheTest {
Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
NAMESPACE,
Collections.emptyList());
- verify(mMockIContentProvider).call(any(), any(), any(),
+ verify(mMockIContentProvider).call(any(), any(),
eq(Settings.CALL_METHOD_LIST_CONFIG),
any(), any(Bundle.class));
assertThat(returnedValues).isEmpty();
diff --git a/core/tests/coretests/src/android/provider/TestDocumentsProvider.java b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
index 5f640bee7ce9..449698301e75 100644
--- a/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
+++ b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
@@ -18,13 +18,13 @@ package android.provider;
import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.CancellationSignal;
-import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract.Path;
@@ -93,14 +93,12 @@ public class TestDocumentsProvider extends DocumentsProvider {
}
@Override
- protected int enforceReadPermissionInner(Uri uri, String callingPkg,
- @Nullable String callingFeatureId, IBinder callerToken) {
+ protected int enforceReadPermissionInner(Uri uri, AttributionSource attributionSource) {
return AppOpsManager.MODE_ALLOWED;
}
@Override
- protected int enforceWritePermissionInner(Uri uri, String callingPkg,
- @Nullable String callingFeatureId, IBinder callerToken) {
+ protected int enforceWritePermissionInner(Uri uri, AttributionSource attributionSource) {
return AppOpsManager.MODE_ALLOWED;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index de353bf58aec..a0d449239b2a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -24,8 +24,10 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.AttributionSource;
import android.content.ContentProviderClient;
import android.content.ContentValues;
+import android.content.Context;
import android.content.IContentProvider;
import android.media.MediaInserter;
import android.net.Uri;
@@ -46,7 +48,7 @@ public class MediaInserterTest extends InstrumentationTestCase {
private MediaInserter mMediaInserter;
private static final int TEST_BUFFER_SIZE = 10;
private @Mock IContentProvider mMockProvider;
- private String mPackageName;
+ private AttributionSource mAttributionSource;
private int mFilesCounter;
private int mAudioCounter;
@@ -86,11 +88,14 @@ public class MediaInserterTest extends InstrumentationTestCase {
super.setUp();
MockitoAnnotations.initMocks(this);
- final ContentProviderClient client = new ContentProviderClient(
- getInstrumentation().getContext().createFeatureContext(TEST_FEATURE_ID)
+ final Context attributionContext = getInstrumentation().getContext()
+ .createFeatureContext(TEST_FEATURE_ID);
+
+ final ContentProviderClient client = new ContentProviderClient(attributionContext
.getContentResolver(), mMockProvider, true);
+
mMediaInserter = new MediaInserter(client, TEST_BUFFER_SIZE);
- mPackageName = getInstrumentation().getContext().getPackageName();
+ mAttributionSource = attributionContext.getAttributionSource();
mFilesCounter = 0;
mAudioCounter = 0;
mVideoCounter = 0;
@@ -144,13 +149,13 @@ public class MediaInserterTest extends InstrumentationTestCase {
fillBuffer(sVideoUri, TEST_BUFFER_SIZE - 2);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
- verify(mMockProvider, never()).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ verify(mMockProvider, never()).bulkInsert(eq(mAttributionSource), any(),
any());
}
@SmallTest
public void testInsertContentsEqualToBufferSize() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), any(),
any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE);
@@ -158,13 +163,13 @@ public class MediaInserterTest extends InstrumentationTestCase {
fillBuffer(sVideoUri, TEST_BUFFER_SIZE);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE);
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ verify(mMockProvider, times(4)).bulkInsert(eq(mAttributionSource), any(),
any());
}
@SmallTest
public void testInsertContentsMoreThanBufferSize() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), any(),
any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE + 1);
@@ -172,7 +177,7 @@ public class MediaInserterTest extends InstrumentationTestCase {
fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4);
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ verify(mMockProvider, times(4)).bulkInsert(eq(mAttributionSource), any(),
any());
}
@@ -183,7 +188,7 @@ public class MediaInserterTest extends InstrumentationTestCase {
@SmallTest
public void testFlushAllWithSomeContents() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), any(),
any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
@@ -192,13 +197,13 @@ public class MediaInserterTest extends InstrumentationTestCase {
fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
mMediaInserter.flushAll();
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ verify(mMockProvider, times(4)).bulkInsert(eq(mAttributionSource), any(),
any());
}
@SmallTest
public void testInsertContentsAfterFlushAll() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), any(),
any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
@@ -212,19 +217,19 @@ public class MediaInserterTest extends InstrumentationTestCase {
fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4);
- verify(mMockProvider, times(8)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ verify(mMockProvider, times(8)).bulkInsert(eq(mAttributionSource), any(),
any());
}
@SmallTest
public void testInsertContentsWithDifferentSizePerContentType() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sFilesUri),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), eqUri(sFilesUri),
any())).thenReturn(1);
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sAudioUri),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), eqUri(sAudioUri),
any())).thenReturn(1);
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sVideoUri),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), eqUri(sVideoUri),
any())).thenReturn(1);
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sImagesUri),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), eqUri(sImagesUri),
any())).thenReturn(1);
for (int i = 0; i < TEST_BUFFER_SIZE; ++i) {
@@ -234,13 +239,13 @@ public class MediaInserterTest extends InstrumentationTestCase {
fillBuffer(sImagesUri, 4);
}
- verify(mMockProvider, times(1)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ verify(mMockProvider, times(1)).bulkInsert(eq(mAttributionSource),
eqUri(sFilesUri), any());
- verify(mMockProvider, times(2)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ verify(mMockProvider, times(2)).bulkInsert(eq(mAttributionSource),
eqUri(sAudioUri), any());
- verify(mMockProvider, times(3)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ verify(mMockProvider, times(3)).bulkInsert(eq(mAttributionSource),
eqUri(sVideoUri), any());
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ verify(mMockProvider, times(4)).bulkInsert(eq(mAttributionSource),
eqUri(sImagesUri), any());
}
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 087275e73ee8..801b490406cc 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -19,6 +19,7 @@ package com.android.externalstorage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.StorageStatsManager;
+import android.content.AttributionSource;
import android.content.ContentResolver;
import android.content.UriPermission;
import android.database.Cursor;
@@ -28,7 +29,6 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
-import android.os.IBinder;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.DiskInfo;
@@ -141,17 +141,17 @@ public class ExternalStorageProvider extends FileSystemProvider {
}
@Override
- protected int enforceReadPermissionInner(Uri uri, String callingPkg,
- @Nullable String featureId, IBinder callerToken) throws SecurityException {
+ protected int enforceReadPermissionInner(Uri uri,
+ @NonNull AttributionSource attributionSource) throws SecurityException {
enforceShellRestrictions();
- return super.enforceReadPermissionInner(uri, callingPkg, featureId, callerToken);
+ return super.enforceReadPermissionInner(uri, attributionSource);
}
@Override
- protected int enforceWritePermissionInner(Uri uri, String callingPkg,
- @Nullable String featureId, IBinder callerToken) throws SecurityException {
+ protected int enforceWritePermissionInner(Uri uri,
+ @NonNull AttributionSource attributionSource) throws SecurityException {
enforceShellRestrictions();
- return super.enforceWritePermissionInner(uri, callingPkg, featureId, callerToken);
+ return super.enforceWritePermissionInner(uri, attributionSource);
}
public void updateVolumes() {
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index 49f6bd8c3334..a2bec334f206 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -555,7 +555,7 @@ public class TileUtils {
bundle.putString(META_DATA_PREFERENCE_KEYHINT, key);
}
try {
- return provider.call(context.getPackageName(), context.getAttributionTag(),
+ return provider.call(context.getAttributionSource(),
uri.getAuthority(), method, uri.toString(), bundle);
} catch (RemoteException e) {
return null;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index fdbbc391082f..df6ff73cc046 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -18,6 +18,7 @@ package com.android.providers.settings;
import android.annotation.SystemApi;
import android.app.ActivityManager;
+import android.content.AttributionSource;
import android.content.IContentProvider;
import android.os.Binder;
import android.os.Bundle;
@@ -250,7 +251,8 @@ public final class DeviceConfigService extends Binder {
Bundle args = new Bundle();
args.putInt(Settings.CALL_METHOD_USER_KEY,
ActivityManager.getService().getCurrentUser().id);
- Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
+ Bundle b = provider.call(new AttributionSource(Process.myUid(),
+ resolveCallingPackage(), null), Settings.AUTHORITY,
Settings.CALL_METHOD_DELETE_CONFIG, compositeKey, args);
success = (b != null && b.getInt(SettingsProvider.RESULT_ROWS_DELETED) == 1);
} catch (RemoteException e) {
@@ -266,7 +268,8 @@ public final class DeviceConfigService extends Binder {
Bundle args = new Bundle();
args.putInt(Settings.CALL_METHOD_USER_KEY,
ActivityManager.getService().getCurrentUser().id);
- Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
+ Bundle b = provider.call(new AttributionSource(Process.myUid(),
+ resolveCallingPackage(), null), Settings.AUTHORITY,
Settings.CALL_METHOD_LIST_CONFIG, null, args);
if (b != null) {
Map<String, String> flagsToValues =
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index 3b3ca5b02417..17ebf6fc3235 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -17,6 +17,7 @@
package com.android.providers.settings;
import android.app.ActivityManager;
+import android.content.AttributionSource;
import android.content.IContentProvider;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -309,7 +310,9 @@ final public class SettingsService extends Binder {
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle result = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
+ final AttributionSource attributionSource = new AttributionSource(
+ Binder.getCallingUid(), resolveCallingPackage(), /*attributionTag*/ null);
+ Bundle result = provider.call(attributionSource, Settings.AUTHORITY,
callListCommand, null, arg);
lines.addAll(result.getStringArrayList(SettingsProvider.RESULT_SETTINGS_LIST));
Collections.sort(lines);
@@ -334,7 +337,9 @@ final public class SettingsService extends Binder {
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
+ final AttributionSource attributionSource = new AttributionSource(
+ Binder.getCallingUid(), resolveCallingPackage(), /*attributionTag*/ null);
+ Bundle b = provider.call(attributionSource, Settings.AUTHORITY,
callGetCommand, key, arg);
if (b != null) {
result = b.getPairValue();
@@ -372,7 +377,9 @@ final public class SettingsService extends Binder {
if (makeDefault) {
arg.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
- provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
+ final AttributionSource attributionSource = new AttributionSource(
+ Binder.getCallingUid(), resolveCallingPackage(), /*attributionTag*/ null);
+ provider.call(attributionSource, Settings.AUTHORITY,
callPutCommand, key, arg);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
@@ -396,7 +403,9 @@ final public class SettingsService extends Binder {
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle result = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
+ final AttributionSource attributionSource = new AttributionSource(
+ Binder.getCallingUid(), resolveCallingPackage(), /*attributionTag*/ null);
+ Bundle result = provider.call(attributionSource, Settings.AUTHORITY,
callDeleteCommand, key, arg);
return result.getInt(SettingsProvider.RESULT_ROWS_DELETED);
} catch (RemoteException e) {
@@ -423,7 +432,9 @@ final public class SettingsService extends Binder {
}
String packageName = mPackageName != null ? mPackageName : resolveCallingPackage();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- provider.call(packageName, null, Settings.AUTHORITY, callResetCommand, null, arg);
+ final AttributionSource attributionSource = new AttributionSource(
+ Binder.getCallingUid(), resolveCallingPackage(), /*attributionTag*/ null);
+ provider.call(attributionSource, Settings.AUTHORITY, callResetCommand, null, arg);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index cff8ad1ed964..4c56db4282f6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -35,6 +35,8 @@
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
+ <uses-permission android:name="android.permission.READ_CALL_LOG" />
+ <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
<uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
@@ -194,11 +196,14 @@
<uses-permission android:name="android.permission.MANAGE_CAMERA" />
<!-- Permissions needed to test system only camera devices -->
<uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.BACKGROUND_CAMERA" />
<uses-permission android:name="android.permission.SYSTEM_CAMERA" />
<!-- Permissions needed to test onCameraOpened/Closed callbacks -->
<uses-permission android:name="android.permission.CAMERA_OPEN_CLOSE_LISTENER" />
<!-- Permissions needed for CTS camera test: RecordingTest.java when assuming shell id -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.RECORD_BACKGROUND_AUDIO" />
+
<!-- Permission needed to enable/disable Bluetooth/Wifi -->
<uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
<uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 036b88e6fc1c..fa88d6603840 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -188,6 +188,7 @@ import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
+import android.content.AttributionSource;
import android.content.AutofillOptions;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
@@ -340,7 +341,11 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.QuintFunction;
+import com.android.internal.util.function.TriFunction;
import com.android.server.AlarmManagerInternal;
import com.android.server.DeviceIdleInternal;
import com.android.server.DisplayThread;
@@ -367,6 +372,7 @@ import com.android.server.graphics.fonts.FontManagerInternal;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.os.NativeTombstoneManager;
import com.android.server.pm.Installer;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.uri.GrantUri;
import com.android.server.uri.NeededUriGrants;
@@ -1123,24 +1129,6 @@ public class ActivityManagerService extends IActivityManager.Stub
CoreSettingsObserver mCoreSettingsObserver;
/**
- * Thread-local storage used to carry caller permissions over through
- * indirect content-provider access.
- */
- private class Identity {
- public final IBinder token;
- public final int pid;
- public final int uid;
-
- Identity(IBinder _token, int _pid, int _uid) {
- token = _token;
- pid = _pid;
- uid = _uid;
- }
- }
-
- private static final ThreadLocal<Identity> sCallerIdentity = new ThreadLocal<Identity>();
-
- /**
* All information we have collected about the runtime performance of
* any user id that can impact battery performance.
*/
@@ -5344,26 +5332,6 @@ public class ActivityManagerService extends IActivityManager.Stub
return checkComponentPermission(permission, pid, uid, -1, true);
}
- @Override
- public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken) {
- if (permission == null) {
- return PackageManager.PERMISSION_DENIED;
- }
-
- // We might be performing an operation on behalf of an indirect binder
- // invocation, e.g. via {@link #openContentUri}. Check and adjust the
- // client identity accordingly before proceeding.
- Identity tlsIdentity = sCallerIdentity.get();
- if (tlsIdentity != null && tlsIdentity.token == callerToken) {
- Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"
- + tlsIdentity.pid + "," + tlsIdentity.uid + "}");
- uid = tlsIdentity.uid;
- pid = tlsIdentity.pid;
- }
-
- return checkComponentPermission(permission, pid, uid, -1, true);
- }
-
/**
* Binder IPC calls go through the public entry point.
* This can be called with or without the global lock held.
@@ -5618,14 +5586,6 @@ public class ActivityManagerService extends IActivityManager.Stub
final int modeFlags, int userId, IBinder callerToken) {
enforceNotIsolatedCaller("checkUriPermission");
- // Another redirected-binder-call permissions check as in
- // {@link checkPermissionWithToken}.
- Identity tlsIdentity = sCallerIdentity.get();
- if (tlsIdentity != null && tlsIdentity.token == callerToken) {
- uid = tlsIdentity.uid;
- pid = tlsIdentity.pid;
- }
-
// Our own process gets to do everything.
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
@@ -6159,23 +6119,22 @@ public class ActivityManagerService extends IActivityManager.Stub
Binder.getCallingUid(), "*opencontent*", userId);
ParcelFileDescriptor pfd = null;
if (cph != null) {
- // We record the binder invoker's uid in thread-local storage before
- // going to the content provider to open the file. Later, in the code
- // that handles all permissions checks, we look for this uid and use
- // that rather than the Activity Manager's own uid. The effect is that
- // we do the check against the caller's permissions even though it looks
- // to the content provider like the Activity Manager itself is making
- // the request.
- Binder token = new Binder();
- sCallerIdentity.set(new Identity(
- token, Binder.getCallingPid(), Binder.getCallingUid()));
try {
- pfd = cph.provider.openFile(null, null, uri, "r", null, token);
+ // This method is exposed to the VNDK and to avoid changing its
+ // signature we just use the first package in the UID. For shared
+ // UIDs we may blame the wrong app but that is Okay as they are
+ // in the same security/privacy sandbox.
+ final AndroidPackage androidPackage = mPackageManagerInt
+ .getPackage(Binder.getCallingUid());
+ if (androidPackage == null) {
+ return null;
+ }
+ final AttributionSource attributionSource = new AttributionSource(
+ Binder.getCallingUid(), androidPackage.getPackageName(), null);
+ pfd = cph.provider.openFile(attributionSource, uri, "r", null);
} catch (FileNotFoundException e) {
// do nothing; pfd will be returned null
} finally {
- // Ensure that whatever happens, we clean up the identity state
- sCallerIdentity.remove();
// Ensure we're done with the provider.
mCpHelper.removeContentProviderExternalUnchecked(name, null, userId);
}
@@ -16639,6 +16598,74 @@ public class ActivityManagerService extends IActivityManager.Stub
message, shouldCollectMessage);
}
+ @Override
+ public int noteProxyOperation(int code, @NonNull AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage, boolean skiProxyOperation,
+ @NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean,
+ Boolean, Integer> superImpl) {
+ if (attributionSource.getUid() == mTargetUid && isTargetOp(code)) {
+ final int shellUid = UserHandle.getUid(UserHandle.getUserId(
+ attributionSource.getUid()), Process.SHELL_UID);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return superImpl.apply(code, new AttributionSource(shellUid,
+ "com.android.shell", attributionSource.getAttributionTag(),
+ attributionSource.getNext()),
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skiProxyOperation);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp,
+ message, shouldCollectMessage, skiProxyOperation);
+ }
+
+ @Override
+ public int startProxyOperation(IBinder token, int code,
+ @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProsyOperation, @NonNull OctFunction<IBinder, Integer,
+ AttributionSource, Boolean, Boolean, String, Boolean, Boolean,
+ Integer> superImpl) {
+ if (attributionSource.getUid() == mTargetUid && isTargetOp(code)) {
+ final int shellUid = UserHandle.getUid(UserHandle.getUserId(
+ attributionSource.getUid()), Process.SHELL_UID);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return superImpl.apply(token, code, new AttributionSource(shellUid,
+ "com.android.shell", attributionSource.getAttributionTag(),
+ attributionSource.getNext()), startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skipProsyOperation);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return superImpl.apply(token, code, attributionSource, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProsyOperation);
+ }
+
+ @Override
+ public void finishProxyOperation(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource,
+ @NonNull TriFunction<IBinder, Integer, AttributionSource, Void> superImpl) {
+ if (attributionSource.getUid() == mTargetUid && isTargetOp(code)) {
+ final int shellUid = UserHandle.getUid(UserHandle.getUserId(
+ attributionSource.getUid()), Process.SHELL_UID);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ superImpl.apply(clientId, code, new AttributionSource(shellUid,
+ "com.android.shell", attributionSource.getAttributionTag(),
+ attributionSource.getNext()));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ superImpl.apply(clientId, code, attributionSource);
+ }
+
private boolean isTargetOp(int code) {
// null permissions means all ops are targeted
if (mPermissions == null) {
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index ee4526b99d1c..b44699bab0bb 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -33,6 +33,7 @@ import android.app.ApplicationExitInfo;
import android.app.ContentProviderHolder;
import android.app.IApplicationThread;
import android.app.usage.UsageEvents.Event;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -42,6 +43,7 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.database.ContentObserver;
@@ -67,7 +69,9 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
import com.android.server.RescueParty;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -1037,7 +1041,19 @@ public class ContentProviderHelper {
holder = getContentProviderExternalUnchecked(name, null, callingUid,
"*checkContentProviderUriPermission*", userId);
if (holder != null) {
- return holder.provider.checkUriPermission(null, null, uri, callingUid, modeFlags);
+
+ final PackageManagerInternal packageManagerInt = LocalServices.getService(
+ PackageManagerInternal.class);
+ final AndroidPackage androidPackage = packageManagerInt
+ .getPackage(Binder.getCallingUid());
+ if (androidPackage == null) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ final AttributionSource attributionSource = new AttributionSource(
+ callingUid, androidPackage.getPackageName(), null);
+ return holder.provider.checkUriPermission(attributionSource, uri, callingUid,
+ modeFlags);
}
} catch (RemoteException e) {
Log.w(TAG, "Content provider dead retrieving " + uri, e);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7bc71051c627..07ee5a2f84ec 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -94,6 +94,7 @@ import android.app.AsyncNotedAppOp;
import android.app.RuntimeAppOpAccessMessage;
import android.app.SyncNotedAppOp;
import android.app.admin.DevicePolicyManagerInternal;
+import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -131,6 +132,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.KeyValueListParser;
+import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pair;
import android.util.Pools;
@@ -159,6 +161,9 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.QuintFunction;
+import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
@@ -1999,8 +2004,10 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
- mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
+ final int callingUid = Binder.getCallingUid();
+ final boolean hasAllPackageAccess = mContext.checkPermission(
+ Manifest.permission.GET_APP_OPS_STATS, Binder.getCallingPid(),
+ Binder.getCallingUid(), null) == PackageManager.PERMISSION_GRANTED;
ArrayList<AppOpsManager.PackageOps> res = null;
synchronized (this) {
final int uidStateCount = mUidStates.size();
@@ -2016,11 +2023,14 @@ public class AppOpsService extends IAppOpsService.Stub {
ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
if (resOps != null) {
if (res == null) {
- res = new ArrayList<AppOpsManager.PackageOps>();
+ res = new ArrayList<>();
}
AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
pkgOps.packageName, pkgOps.uidState.uid, resOps);
- res.add(resPackage);
+ // Caller can always see their packages and with a permission all.
+ if (hasAllPackageAccess || callingUid == pkgOps.uidState.uid) {
+ res.add(resPackage);
+ }
}
}
}
@@ -2031,8 +2041,7 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
int[] ops) {
- mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
+ enforceGetAppOpsStatsPermissionIfNeeded(uid,packageName);
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
return Collections.emptyList();
@@ -2054,6 +2063,22 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
+ private void enforceGetAppOpsStatsPermissionIfNeeded(int uid, String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ // We get to access everything
+ if (callingUid == Process.myPid()) {
+ return;
+ }
+ // Apps can access their own data
+ if (uid == callingUid && packageName != null
+ && checkPackage(uid, packageName) == MODE_ALLOWED) {
+ return;
+ }
+ // Otherwise, you need a permission...
+ mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+ Binder.getCallingPid(), callingUid, null);
+ }
+
/**
* Verify that historical appop request arguments are valid.
*/
@@ -3078,15 +3103,52 @@ public class AppOpsService extends IAppOpsService.Stub {
}
@Override
- public int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
- String proxiedAttributionTag, int proxyUid, String proxyPackageName,
- String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message,
- boolean shouldCollectMessage) {
- verifyIncomingUid(proxyUid);
+ public int noteProxyOperation(int code, AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation) {
+ final CheckOpsDelegate policy;
+ final CheckOpsDelegateDispatcher delegateDispatcher;
+ synchronized (AppOpsService.this) {
+ policy = mAppOpsPolicy;
+ delegateDispatcher = mCheckOpsDelegateDispatcher;
+ }
+ if (policy != null) {
+ if (delegateDispatcher != null) {
+ return policy.noteProxyOperation(code, attributionSource,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skipProxyOperation, delegateDispatcher::noteProxyOperationImpl);
+ } else {
+ return policy.noteProxyOperation(code, attributionSource,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skipProxyOperation, AppOpsService.this::noteProxyOperationImpl);
+ }
+ } else if (delegateDispatcher != null) {
+ delegateDispatcher.getCheckOpsDelegate().noteProxyOperation(code,
+ attributionSource, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skipProxyOperation,
+ AppOpsService.this::noteProxyOperationImpl);
+ }
+ return noteProxyOperationImpl(code, attributionSource, shouldCollectAsyncNotedOp,
+ message, shouldCollectMessage,skipProxyOperation);
+ }
+
+ private int noteProxyOperationImpl(int code, AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation) {
+ final int proxyUid = attributionSource.getUid();
+ final String proxyPackageName = attributionSource.getPackageName();
+ final String proxyAttributionTag = attributionSource.getAttributionTag();
+ final int proxiedUid = attributionSource.getNextUid();
+ final String proxiedPackageName = attributionSource.getNextPackageName();
+ final String proxiedAttributionTag = attributionSource.getNextAttributionTag();
+
+ verifyIncomingProxyUid(attributionSource);
verifyIncomingOp(code);
verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
+ skipProxyOperation = resolveSkipProxyOperation(skipProxyOperation, attributionSource);
+
String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
if (resolveProxyPackageName == null) {
return AppOpsManager.MODE_IGNORED;
@@ -3097,19 +3159,23 @@ public class AppOpsService extends IAppOpsService.Stub {
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
== PackageManager.PERMISSION_GRANTED || isSelfBlame;
- final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
- : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
- final int proxyMode = noteOperationUnchecked(code, proxyUid, resolveProxyPackageName,
- proxyAttributionTag, Process.INVALID_UID, null, null, proxyFlags,
- !isProxyTrusted, "proxy " + message, shouldCollectMessage);
- if (proxyMode != AppOpsManager.MODE_ALLOWED) {
- return proxyMode;
+ if (!skipProxyOperation) {
+ final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
+ : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
+
+ final int proxyMode = noteOperationUnchecked(code, proxyUid, resolveProxyPackageName,
+ proxyAttributionTag, Process.INVALID_UID, null, null, proxyFlags,
+ !isProxyTrusted, "proxy " + message, shouldCollectMessage);
+ if (proxyMode != AppOpsManager.MODE_ALLOWED) {
+ return proxyMode;
+ }
}
String resolveProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
if (resolveProxiedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
}
+
final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;
return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
@@ -3558,16 +3624,56 @@ public class AppOpsService extends IAppOpsService.Stub {
}
@Override
- public int startProxyOperation(IBinder clientId, int code, int proxiedUid,
- String proxiedPackageName, @Nullable String proxiedAttributionTag, int proxyUid,
- String proxyPackageName, @Nullable String proxyAttributionTag,
+ public int startProxyOperation(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation) {
+ final CheckOpsDelegate policy;
+ final CheckOpsDelegateDispatcher delegateDispatcher;
+ synchronized (AppOpsService.this) {
+ policy = mAppOpsPolicy;
+ delegateDispatcher = mCheckOpsDelegateDispatcher;
+ }
+ if (policy != null) {
+ if (delegateDispatcher != null) {
+ return policy.startProxyOperation(clientId, code, attributionSource,
+ startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skipProxyOperation,
+ delegateDispatcher::startProxyOperationImpl);
+ } else {
+ return policy.startProxyOperation(clientId, code, attributionSource,
+ startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skipProxyOperation,
+ AppOpsService.this::startProxyOperationImpl);
+ }
+ } else if (delegateDispatcher != null) {
+ delegateDispatcher.getCheckOpsDelegate().startProxyOperation(clientId, code,
+ attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skipProxyOperation,
+ AppOpsService.this::startProxyOperationImpl);
+ }
+ return startProxyOperationImpl(clientId, code, attributionSource, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation);
+ }
+
+ private int startProxyOperationImpl(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
- boolean shouldCollectMessage) {
- verifyIncomingUid(proxyUid);
+ boolean shouldCollectMessage, boolean skipProxyOperation) {
+ final int proxyUid = attributionSource.getUid();
+ final String proxyPackageName = attributionSource.getPackageName();
+ final String proxyAttributionTag = attributionSource.getAttributionTag();
+ final int proxiedUid = attributionSource.getNextUid();
+ final String proxiedPackageName = attributionSource.getNextPackageName();
+ final String proxiedAttributionTag = attributionSource.getNextAttributionTag();
+
+ verifyIncomingProxyUid(attributionSource);
verifyIncomingOp(code);
verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
+ skipProxyOperation = resolveSkipProxyOperation(skipProxyOperation, attributionSource);
+
String resolvedProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
if (resolvedProxyPackageName == null) {
return AppOpsManager.MODE_IGNORED;
@@ -3578,31 +3684,34 @@ public class AppOpsService extends IAppOpsService.Stub {
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
== PackageManager.PERMISSION_GRANTED || isSelfBlame;
- final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
- : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
-
String resolvedProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
if (resolvedProxiedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
}
+
final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;
- // Test if the proxied operation will succeed before starting the proxy operation
- final int testProxiedMode = startOperationUnchecked(clientId, code, proxiedUid,
- resolvedProxiedPackageName, proxiedAttributionTag, proxyUid,
- resolvedProxyPackageName, proxyAttributionTag, proxiedFlags, startIfModeDefault,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, true);
- if (!shouldStartForMode(testProxiedMode, startIfModeDefault)) {
- return testProxiedMode;
- }
+ if (!skipProxyOperation) {
+ // Test if the proxied operation will succeed before starting the proxy operation
+ final int testProxiedMode = startOperationUnchecked(clientId, code, proxiedUid,
+ resolvedProxiedPackageName, proxiedAttributionTag, proxyUid,
+ resolvedProxyPackageName, proxyAttributionTag, proxiedFlags, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, true);
+ if (!shouldStartForMode(testProxiedMode, startIfModeDefault)) {
+ return testProxiedMode;
+ }
- final int proxyMode = startOperationUnchecked(clientId, code, proxyUid,
- resolvedProxyPackageName, proxyAttributionTag, Process.INVALID_UID, null, null,
- proxyFlags, startIfModeDefault, !isProxyTrusted, "proxy " + message,
- shouldCollectMessage, false);
- if (!shouldStartForMode(proxyMode, startIfModeDefault)) {
- return proxyMode;
+ final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
+ : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
+
+ final int proxyMode = startOperationUnchecked(clientId, code, proxyUid,
+ resolvedProxyPackageName, proxyAttributionTag, Process.INVALID_UID, null, null,
+ proxyFlags, startIfModeDefault, !isProxyTrusted, "proxy " + message,
+ shouldCollectMessage, false);
+ if (!shouldStartForMode(proxyMode, startIfModeDefault)) {
+ return proxyMode;
+ }
}
return startOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName,
@@ -3723,9 +3832,38 @@ public class AppOpsService extends IAppOpsService.Stub {
}
@Override
- public void finishProxyOperation(IBinder clientId, int code, int proxiedUid,
- String proxiedPackageName, @Nullable String proxiedAttributionTag, int proxyUid,
- @Nullable String proxyPackageName, @Nullable String proxyAttributionTag) {
+ public void finishProxyOperation(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource) {
+ final CheckOpsDelegate policy;
+ final CheckOpsDelegateDispatcher delegateDispatcher;
+ synchronized (AppOpsService.this) {
+ policy = mAppOpsPolicy;
+ delegateDispatcher = mCheckOpsDelegateDispatcher;
+ }
+ if (policy != null) {
+ if (delegateDispatcher != null) {
+ policy.finishProxyOperation(clientId, code, attributionSource,
+ delegateDispatcher::finishProxyOperationImpl);
+ } else {
+ policy.finishProxyOperation(clientId, code, attributionSource,
+ AppOpsService.this::finishProxyOperationImpl);
+ }
+ } else if (delegateDispatcher != null) {
+ delegateDispatcher.getCheckOpsDelegate().finishProxyOperation(clientId, code,
+ attributionSource, AppOpsService.this::finishProxyOperationImpl);
+ }
+ finishProxyOperationImpl(clientId, code, attributionSource);
+ }
+
+ private Void finishProxyOperationImpl(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource) {
+ final int proxyUid = attributionSource.getUid();
+ final String proxyPackageName = attributionSource.getPackageName();
+ final String proxyAttributionTag = attributionSource.getAttributionTag();
+ final int proxiedUid = attributionSource.getNextUid();
+ final String proxiedPackageName = attributionSource.getNextPackageName();
+ final String proxiedAttributionTag = attributionSource.getNextAttributionTag();
+
verifyIncomingUid(proxyUid);
verifyIncomingOp(code);
verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
@@ -3733,7 +3871,7 @@ public class AppOpsService extends IAppOpsService.Stub {
String resolvedProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
if (resolvedProxyPackageName == null) {
- return;
+ return null;
}
finishOperationUnchecked(clientId, code, proxyUid, resolvedProxyPackageName,
@@ -3741,11 +3879,13 @@ public class AppOpsService extends IAppOpsService.Stub {
String resolvedProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
if (resolvedProxiedPackageName == null) {
- return;
+ return null;
}
finishOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName,
proxiedAttributionTag);
+
+ return null;
}
private void finishOperationUnchecked(IBinder clientId, int code, int uid, String packageName,
@@ -3953,6 +4093,20 @@ public class AppOpsService extends IAppOpsService.Stub {
|| (permInfo.getProtectionFlags() & PROTECTION_FLAG_APPOP) != 0;
}
+ private void verifyIncomingProxyUid(@NonNull AttributionSource attributionSource) {
+ if (attributionSource.getUid() == Binder.getCallingUid()) {
+ return;
+ }
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return;
+ }
+ if (attributionSource.isTrusted(mContext)) {
+ return;
+ }
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ }
+
private void verifyIncomingUid(int uid) {
if (uid == Binder.getCallingUid()) {
return;
@@ -3979,6 +4133,20 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
+ private boolean resolveSkipProxyOperation(boolean requestsSkipProxyOperation,
+ @NonNull AttributionSource attributionSource) {
+ if (!requestsSkipProxyOperation) {
+ return false;
+ }
+ if (attributionSource.getUid() != Binder.getCallingUid()
+ && attributionSource.isTrusted(mContext)) {
+ return true;
+ }
+ return mContext.checkPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
private @Nullable UidState getUidStateLocked(int uid, boolean edit) {
UidState uidState = mUidStates.get(uid);
if (uidState == null) {
@@ -4098,7 +4266,6 @@ public class AppOpsService extends IAppOpsService.Stub {
/**
* Create a restriction description matching the properties of the package.
*
- * @param context A context to use
* @param pkg The package to create the restriction description for
*
* @return The restriction matching the package
@@ -4141,15 +4308,25 @@ public class AppOpsService extends IAppOpsService.Stub {
int callingUid = Binder.getCallingUid();
int userId = UserHandle.getUserId(uid);
-
RestrictionBypass bypass = null;
+
+ // Allow any attribution tag for resolvable uids
+ int pkgUid = resolveUid(packageName);
+ if (pkgUid != Process.INVALID_UID) {
+ // Special case for the shell which is a package but should be able
+ // to bypass app attribution tag restrictions.
+ if (pkgUid != UserHandle.getAppId(uid)) {
+ throw new SecurityException("Specified package " + packageName + " under uid "
+ + UserHandle.getAppId(uid) + " but it is really " + pkgUid);
+ }
+ return RestrictionBypass.UNRESTRICTED;
+ }
+
final long ident = Binder.clearCallingIdentity();
try {
- int pkgUid;
- AndroidPackage pkg = LocalServices.getService(PackageManagerInternal.class).getPackage(
- packageName);
boolean isAttributionTagValid = false;
-
+ AndroidPackage pkg = LocalServices.getService(PackageManagerInternal.class)
+ .getPackage(packageName);
if (pkg != null) {
if (attributionTag == null) {
isAttributionTagValid = true;
@@ -4166,20 +4343,7 @@ public class AppOpsService extends IAppOpsService.Stub {
pkgUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
bypass = getBypassforPackage(pkg);
- } else {
- // Allow any attribution tag for resolvable uids
- isAttributionTagValid = true;
-
- pkgUid = resolveUid(packageName);
- if (pkgUid >= 0) {
- bypass = RestrictionBypass.UNRESTRICTED;
- }
}
- if (pkgUid != uid) {
- throw new SecurityException("Specified package " + packageName + " under uid " + uid
- + " but it is really " + pkgUid);
- }
-
if (!isAttributionTagValid) {
String msg = "attributionTag " + attributionTag + " not declared in"
+ " manifest of " + packageName;
@@ -4187,7 +4351,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (mPlatformCompat.isChangeEnabledByPackageName(
SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, packageName,
userId) && mPlatformCompat.isChangeEnabledByUid(
- SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, callingUid)) {
+ SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, callingUid)) {
throw new SecurityException(msg);
} else {
Slog.e(TAG, msg);
@@ -4199,6 +4363,11 @@ public class AppOpsService extends IAppOpsService.Stub {
Binder.restoreCallingIdentity(ident);
}
+ if (pkgUid != uid) {
+ throw new SecurityException("Specified package " + packageName + " under uid " + uid
+ + " but it is really " + pkgUid);
+ }
+
return bypass;
}
@@ -6103,6 +6272,57 @@ public class AppOpsService extends IAppOpsService.Stub {
}
@Override
+ public boolean isProxying(int op, @NonNull String proxyPackageName,
+ @NonNull String proxyAttributionTag, int proxiedUid,
+ @NonNull String proxiedPackageName) {
+ Objects.requireNonNull(proxyPackageName);
+ Objects.requireNonNull(proxiedPackageName);
+ Binder.withCleanCallingIdentity(() -> {
+ final List<AppOpsManager.PackageOps> packageOps = getOpsForPackage(proxiedUid,
+ proxiedPackageName, new int[] {op});
+ if (packageOps == null || packageOps.isEmpty()) {
+ return false;
+ }
+ final List<OpEntry> opEntries = packageOps.get(0).getOps();
+ if (opEntries.isEmpty()) {
+ return false;
+ }
+ final OpEntry opEntry = opEntries.get(0);
+ if (!opEntry.isRunning()) {
+ return false;
+ }
+ final OpEventProxyInfo proxyInfo = opEntry.getLastProxyInfo(
+ AppOpsManager.OP_FLAG_TRUSTED_PROXY
+ | AppOpsManager.OP_FLAG_UNTRUSTED_PROXY);
+ return proxyInfo != null && Binder.getCallingUid() == proxyInfo.getUid()
+ && proxyPackageName.equals(proxyInfo.getPackageName())
+ && Objects.equals(proxyAttributionTag, proxyInfo.getAttributionTag());
+ });
+ return false;
+ }
+
+ @Override
+ public void resetPackageOpsNoHistory(@NonNull String packageName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+ "resetPackageOpsNoHistory");
+ synchronized (AppOpsService.this) {
+ final int uid = mPackageManagerInternal.getPackageUid(packageName, 0,
+ UserHandle.getCallingUserId());
+ if (uid == Process.INVALID_UID) {
+ return;
+ }
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null || uidState.pkgOps == null) {
+ return;
+ }
+ Ops removedOps = uidState.pkgOps.remove(packageName);
+ if (removedOps != null) {
+ scheduleFastWriteLocked();
+ }
+ }
+ }
+
+ @Override
public void setHistoryParameters(@AppOpsManager.HistoricalMode int mode,
long baseSnapshotInterval, int compressionStep) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
@@ -6455,6 +6675,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return Process.ROOT_UID;
case "shell":
case "dumpstate":
+ case "com.android.shell":
return Process.SHELL_UID;
case "media":
return Process.MEDIA_UID;
@@ -6831,5 +7052,29 @@ public class AppOpsService extends IAppOpsService.Stub {
shouldCollectAsyncNotedOp, message, shouldCollectMessage,
AppOpsService.this::noteOperationImpl);
}
+
+ public int noteProxyOperationImpl(int code, @NonNull AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage, boolean skipProxyOperation) {
+ return mCheckOpsDelegate.noteProxyOperation(code, attributionSource,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation,
+ AppOpsService.this::noteProxyOperationImpl);
+ }
+
+ public int startProxyOperationImpl(IBinder token, int code,
+ @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation) {
+ return mCheckOpsDelegate.startProxyOperation(token, code, attributionSource,
+ startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skipProxyOperation, AppOpsService.this::startProxyOperationImpl);
+ }
+
+ public Void finishProxyOperationImpl(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource) {
+ mCheckOpsDelegate.finishProxyOperation(clientId, code, attributionSource,
+ AppOpsService.this::finishProxyOperationImpl);
+ return null;
+ }
}
}
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 920d044989a0..142b36e8eaf3 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -85,11 +85,13 @@ import android.content.pm.parsing.component.ParsedPermissionGroup;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.metrics.LogMaker;
import android.os.AsyncTask;
+import android.content.AttributionSource;
import android.os.Binder;
import android.os.Build;
import android.os.Debug;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
@@ -159,6 +161,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.WeakHashMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -254,6 +257,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@NonNull
private final PermissionRegistry mRegistry = new PermissionRegistry();
+ @NonNull
+ private final AttributionSourceRegistry mAttributionSourceRegistry =
+ new AttributionSourceRegistry();
+
@GuardedBy("mLock")
@Nullable
private ArraySet<String> mPrivappPermissionsViolations;
@@ -3231,6 +3238,16 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
+ public @NonNull AttributionSource registerAttributionSource(@NonNull AttributionSource source) {
+ return mAttributionSourceRegistry.registerAttributionSource(source);
+ }
+
+ @Override
+ public boolean isRegisteredAttributionSource(@NonNull AttributionSource source) {
+ return mAttributionSourceRegistry.isRegisteredAttributionSource(source);
+ }
+
+ @Override
public List<String> getAutoRevokeExemptionRequestedPackages(int userId) {
return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISCOURAGED, userId);
}
@@ -5265,4 +5282,73 @@ public class PermissionManagerService extends IPermissionManager.Stub {
|| mDelegatedPermissionNames.contains(permissionName);
}
}
+
+ private static final class AttributionSourceRegistry {
+ private final Object mLock = new Object();
+
+ private final WeakHashMap<IBinder, AttributionSource> mAttributions = new WeakHashMap<>();
+
+ public @NonNull AttributionSource registerAttributionSource(
+ @NonNull AttributionSource source) {
+ // Here we keep track of attribution sources that were created by an app
+ // from an attribution chain that called into the app and the apps's
+ // own attribution source. An app can register an attribution chain up
+ // to itself inclusive if and only if it is adding a node for itself which
+ // optionally points to an attribution chain that was created by each
+ // preceding app recursively up to the beginning of the chain.
+ // The only special case is when the first app in the attribution chain
+ // creates a source that points to another app (not a chain of apps). We
+ // allow this even if the source the app points to is not registered since
+ // in app ops we allow every app to blame every other app (untrusted if not
+ // holding a special permission).
+ // This technique ensures that a bad actor in the middle of the attribution
+ // chain can neither prepend nor append an invalid attribution sequence, i.e.
+ // a sequence that is not constructed by trusted sources up to the that bad
+ // actor's app.
+ // Note that passing your attribution source to another app means you allow
+ // it to blame private data access on your app. This can be mediated by the OS
+ // in, which case security is already enforced; by other app's code running in
+ // your process calling into the other app, in which case it can already access
+ // the private data in your process; or by you explicitly calling to another
+ // app passing the source, in which case you must trust the other side;
+
+ final int callingUid = Binder.getCallingUid();
+ if (source.getUid() != callingUid) {
+ throw new SecurityException("Cannot register attribution source for uid:"
+ + source.getUid() + " from uid:" + callingUid);
+ }
+
+ final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ if (packageManagerInternal.getPackageUid(source.getPackageName(), 0,
+ UserHandle.getUserId(callingUid)) != source.getUid()) {
+ throw new SecurityException("Cannot register attribution source for package:"
+ + source.getPackageName() + " from uid:" + callingUid);
+ }
+
+ final AttributionSource next = source.getNext();
+ if (next != null && next.getNext() != null
+ && !isRegisteredAttributionSource(next)) {
+ throw new SecurityException("Cannot register forged attribution source:"
+ + source);
+ }
+
+ synchronized (mLock) {
+ final IBinder token = new Binder();
+ final AttributionSource result = source.withToken(token);
+ mAttributions.put(token, result);
+ return result;
+ }
+ }
+
+ public boolean isRegisteredAttributionSource(@NonNull AttributionSource source) {
+ synchronized (mLock) {
+ final AttributionSource cachedSource = mAttributions.get(source.getToken());
+ if (cachedSource != null) {
+ return cachedSource.equals(source);
+ }
+ return false;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index c9653909adb6..5db63c6db2b4 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -20,13 +20,18 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
+import android.content.AttributionSource;
import android.location.LocationManagerInternal;
+import android.os.IBinder;
import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.TriFunction;
import com.android.server.LocalServices;
import java.util.Set;
@@ -52,7 +57,7 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
@GuardedBy("mLock - writes only - see above")
@NonNull
private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags =
- new ConcurrentHashMap();
+ new ConcurrentHashMap<>();
public AppOpsPolicy() {
final LocationManagerInternal locationManagerInternal = LocalServices.getService(
@@ -112,22 +117,59 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
@Override
public int noteOperation(int code, int uid, @Nullable String packageName,
- @Nullable String featureId, boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer, String, String,
- Boolean, String, Boolean, Integer> superImpl) {
- if (isHandledOp(code)) {
+ @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable
+ String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer,
+ String, String, Boolean, String, Boolean, Integer> superImpl) {
+ return superImpl.apply(resolveOpCode(code, uid, packageName, attributionTag), uid,
+ packageName, attributionTag, shouldCollectAsyncNotedOp,
+ message, shouldCollectMessage);
+ }
+
+ @Override
+ public int noteProxyOperation(int code, @NonNull AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage, boolean skipProxyOperation, @NonNull HexFunction<Integer,
+ AttributionSource, Boolean, String, Boolean, Boolean, Integer> superImpl) {
+ return superImpl.apply(resolveOpCode(code, attributionSource.getUid(),
+ attributionSource.getPackageName(), attributionSource.getAttributionTag()),
+ attributionSource, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skipProxyOperation);
+ }
+
+ @Override
+ public int startProxyOperation(IBinder token, int code,
+ @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation, @NonNull OctFunction<IBinder, Integer, AttributionSource,
+ Boolean, Boolean, String, Boolean, Boolean, Integer> superImpl) {
+ return superImpl.apply(token, resolveOpCode(code, attributionSource.getUid(),
+ attributionSource.getPackageName(), attributionSource.getAttributionTag()),
+ attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skipProxyOperation);
+ }
+
+ @Override
+ public void finishProxyOperation(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource,
+ @NonNull TriFunction<IBinder, Integer, AttributionSource, Void> superImpl) {
+ superImpl.apply(clientId, resolveOpCode(code, attributionSource.getUid(),
+ attributionSource.getPackageName(), attributionSource.getAttributionTag()),
+ attributionSource);
+ }
+
+ private int resolveOpCode(int code, int uid, @NonNull String packageName,
+ @Nullable String attributionTag) {
+ if (isHandledOp(code) && attributionTag != null) {
// Only a single lookup from the underlying concurrent data structure
final ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid);
if (uidTags != null) {
final ArraySet<String> packageTags = uidTags.get(packageName);
- if (packageTags != null && packageTags.contains(featureId)) {
- return superImpl.apply(resolveLocationOp(code), uid, packageName, featureId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ if (packageTags != null && packageTags.contains(attributionTag)) {
+ return resolveHandledOp(code);
}
}
}
- return superImpl.apply(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage);
+ return code;
}
private static boolean isHandledOp(int code) {
@@ -139,7 +181,7 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
return false;
}
- private static int resolveLocationOp(int code) {
+ private static int resolveHandledOp(int code) {
switch (code) {
case AppOpsManager.OP_FINE_LOCATION:
return AppOpsManager.OP_FINE_LOCATION_SOURCE;
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 907743314c7c..89d54158b006 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -578,7 +578,7 @@ public final class PermissionPolicyService extends SystemService {
private final @NonNull AppOpsManager mAppOpsManager;
private final @NonNull AppOpsManagerInternal mAppOpsManagerInternal;
- private final @NonNull ArrayMap<String, PermissionInfo> mRuntimePermissionInfos;
+ private final @NonNull ArrayMap<String, PermissionInfo> mRuntimeAndTheirBgPermissionInfos;
/**
* All ops that need to be flipped to allow.
@@ -618,7 +618,7 @@ public final class PermissionPolicyService extends SystemService {
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mAppOpsManagerInternal = LocalServices.getService(AppOpsManagerInternal.class);
- mRuntimePermissionInfos = new ArrayMap<>();
+ mRuntimeAndTheirBgPermissionInfos = new ArrayMap<>();
PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
PermissionManagerServiceInternal.class);
List<PermissionInfo> permissionInfos =
@@ -627,7 +627,30 @@ public final class PermissionPolicyService extends SystemService {
int permissionInfosSize = permissionInfos.size();
for (int i = 0; i < permissionInfosSize; i++) {
PermissionInfo permissionInfo = permissionInfos.get(i);
- mRuntimePermissionInfos.put(permissionInfo.name, permissionInfo);
+ mRuntimeAndTheirBgPermissionInfos.put(permissionInfo.name, permissionInfo);
+ // Make sure we scoop up all background permissions as they may not be runtime
+ if (permissionInfo.backgroundPermission != null) {
+ String backgroundNonRuntimePermission = permissionInfo.backgroundPermission;
+ for (int j = 0; j < permissionInfosSize; j++) {
+ PermissionInfo bgPermissionCandidate = permissionInfos.get(j);
+ if (permissionInfo.backgroundPermission.equals(
+ bgPermissionCandidate.name)) {
+ backgroundNonRuntimePermission = null;
+ break;
+ }
+ }
+ if (backgroundNonRuntimePermission != null) {
+ try {
+ PermissionInfo backgroundPermissionInfo = mPackageManager
+ .getPermissionInfo(backgroundNonRuntimePermission, 0);
+ mRuntimeAndTheirBgPermissionInfos.put(backgroundPermissionInfo.name,
+ backgroundPermissionInfo);
+ } catch (NameNotFoundException e) {
+ Slog.w(LOG_TAG, "Unknown background permission: "
+ + backgroundNonRuntimePermission);
+ }
+ }
+ }
}
}
@@ -691,7 +714,7 @@ public final class PermissionPolicyService extends SystemService {
*/
private void addAppOps(@NonNull PackageInfo packageInfo, @NonNull AndroidPackage pkg,
@NonNull String permissionName) {
- PermissionInfo permissionInfo = mRuntimePermissionInfos.get(permissionName);
+ PermissionInfo permissionInfo = mRuntimeAndTheirBgPermissionInfos.get(permissionName);
if (permissionInfo == null) {
return;
}
@@ -726,7 +749,7 @@ public final class PermissionPolicyService extends SystemService {
boolean shouldGrantAppOp = shouldGrantAppOp(packageInfo, pkg, permissionInfo);
if (shouldGrantAppOp) {
if (permissionInfo.backgroundPermission != null) {
- PermissionInfo backgroundPermissionInfo = mRuntimePermissionInfos.get(
+ PermissionInfo backgroundPermissionInfo = mRuntimeAndTheirBgPermissionInfos.get(
permissionInfo.backgroundPermission);
boolean shouldGrantBackgroundAppOp = backgroundPermissionInfo != null
&& shouldGrantAppOp(packageInfo, pkg, backgroundPermissionInfo);
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
index 3f203026a219..9c8ff685d14d 100644
--- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -18,12 +18,12 @@ package com.android.server.speech;
import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
+import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.speech.IRecognitionListener;
@@ -40,10 +40,6 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
private static final String TAG = RemoteSpeechRecognitionService.class.getSimpleName();
private static final boolean DEBUG = false;
- private static final String APP_OP_MESSAGE = "Recording audio for speech recognition";
- private static final String RECORD_AUDIO_APP_OP =
- AppOpsManager.permissionToOp(android.Manifest.permission.RECORD_AUDIO);
-
private final Object mLock = new Object();
private boolean mConnected = false;
@@ -53,14 +49,6 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
@Nullable
@GuardedBy("mLock")
- private String mPackageName;
-
- @Nullable
- @GuardedBy("mLock")
- private String mFeatureId;
-
- @Nullable
- @GuardedBy("mLock")
private DelegatingListener mDelegatingListener;
// Makes sure we can block startListening() if session is still in progress.
@@ -72,7 +60,6 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
private boolean mRecordingInProgress = false;
private final int mCallingUid;
- private final AppOpsManager mAppOpsManager;
private final ComponentName mComponentName;
RemoteSpeechRecognitionService(
@@ -87,7 +74,6 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
IRecognitionService.Stub::asInterface);
mCallingUid = callingUid;
- mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mComponentName = serviceName;
if (DEBUG) {
@@ -99,11 +85,12 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
return mComponentName;
}
- void startListening(Intent recognizerIntent, IRecognitionListener listener, String packageName,
- String featureId) {
+ void startListening(Intent recognizerIntent, IRecognitionListener listener,
+ @NonNull AttributionSource attributionSource) {
if (DEBUG) {
Slog.i(TAG, String.format("#startListening for package: %s, feature=%s, callingUid=%d",
- packageName, featureId, mCallingUid));
+ attributionSource.getPackageName(), attributionSource.getAttributionTag(),
+ mCallingUid));
}
if (listener == null) {
@@ -123,10 +110,6 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
return;
}
- if (startProxyOp(packageName, featureId) != AppOpsManager.MODE_ALLOWED) {
- tryRespondWithError(listener, SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
- return;
- }
mSessionInProgress = true;
mRecordingInProgress = true;
@@ -141,23 +124,18 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
resetStateLocked();
}
});
- mPackageName = packageName;
- mFeatureId = featureId;
run(service ->
service.startListening(
recognizerIntent,
mDelegatingListener,
- packageName,
- featureId,
- mCallingUid));
+ attributionSource));
}
}
- void stopListening(
- IRecognitionListener listener, String packageName, String featureId) {
+ void stopListening(IRecognitionListener listener) {
if (DEBUG) {
- Slog.i(TAG, "#stopListening for package: " + packageName + ", feature=" + featureId);
+ Slog.i(TAG, "#stopListening");
}
if (!mConnected) {
@@ -184,19 +162,13 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
}
mRecordingInProgress = false;
- finishProxyOp(packageName, featureId);
-
- run(service -> service.stopListening(mDelegatingListener, packageName, featureId));
+ run(service -> service.stopListening(mDelegatingListener));
}
}
- void cancel(
- IRecognitionListener listener,
- String packageName,
- String featureId,
- boolean isShutdown) {
+ void cancel(IRecognitionListener listener, boolean isShutdown) {
if (DEBUG) {
- Slog.i(TAG, "#cancel for package: " + packageName + ", feature=" + featureId);
+ Slog.i(TAG, "#cancel");
}
if (!mConnected) {
@@ -220,11 +192,8 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
// Temporary reference to allow for resetting the hard link mDelegatingListener to null.
IRecognitionListener delegatingListener = mDelegatingListener;
- run(service -> service.cancel(delegatingListener, packageName, featureId, isShutdown));
+ run(service -> service.cancel(delegatingListener, isShutdown));
- if (mRecordingInProgress) {
- finishProxyOp(packageName, featureId);
- }
mRecordingInProgress = false;
mSessionInProgress = false;
@@ -249,7 +218,7 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
}
}
- cancel(mListener, mPackageName, mFeatureId, true /* isShutdown */);
+ cancel(mListener, true /* isShutdown */);
}
@Override // from ServiceConnector.Impl
@@ -286,40 +255,12 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
}
private void resetStateLocked() {
- if (mRecordingInProgress && mPackageName != null) {
- finishProxyOp(mPackageName, mFeatureId);
- }
-
mListener = null;
mDelegatingListener = null;
mSessionInProgress = false;
mRecordingInProgress = false;
}
- private int startProxyOp(String packageName, String featureId) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return mAppOpsManager.startProxyOp(
- RECORD_AUDIO_APP_OP,
- mCallingUid,
- packageName,
- featureId,
- APP_OP_MESSAGE);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private void finishProxyOp(String packageName, String featureId) {
- final long identity = Binder.clearCallingIdentity();
- try {
- mAppOpsManager.finishProxyOp(
- RECORD_AUDIO_APP_OP, mCallingUid, packageName, featureId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
private static void tryRespondWithError(IRecognitionListener listener, int errorCode) {
if (DEBUG) {
Slog.i(TAG, "Responding with error " + errorCode);
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
index 52c1467bd5d0..22eeb346a292 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
@@ -75,7 +75,7 @@ public final class SpeechRecognitionManagerService extends
@Override
protected SpeechRecognitionManagerServiceImpl newServiceLocked(
@UserIdInt int resolvedUserId, boolean disabled) {
- return new SpeechRecognitionManagerServiceImpl(this, mLock, resolvedUserId, disabled);
+ return new SpeechRecognitionManagerServiceImpl(this, mLock, resolvedUserId);
}
final class SpeechRecognitionManagerServiceStub extends IRecognitionServiceManager.Stub {
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
index 769e049c8d0e..eee08c221bfa 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -36,8 +37,6 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.infra.AbstractPerUserSystemService;
-import com.google.android.collect.Sets;
-
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -60,7 +59,7 @@ final class SpeechRecognitionManagerServiceImpl extends
SpeechRecognitionManagerServiceImpl(
@NonNull SpeechRecognitionManagerService master,
- @NonNull Object lock, @UserIdInt int userId, boolean disabled) {
+ @NonNull Object lock, @UserIdInt int userId) {
super(master, lock, userId);
}
@@ -108,10 +107,6 @@ final class SpeechRecognitionManagerServiceImpl extends
}
final int creatorCallingUid = Binder.getCallingUid();
- Set<String> creatorPackageNames =
- Sets.newArraySet(
- getContext().getPackageManager().getPackagesForUid(creatorCallingUid));
-
RemoteSpeechRecognitionService service = createService(creatorCallingUid, serviceComponent);
if (service == null) {
@@ -127,6 +122,7 @@ final class SpeechRecognitionManagerServiceImpl extends
} catch (RemoteException e) {
// RemoteException == binder already died, schedule disconnect anyway.
handleClientDeath(creatorCallingUid, service, true /* invoke #cancel */);
+ return;
}
service.connect().thenAccept(binderService -> {
@@ -137,41 +133,24 @@ final class SpeechRecognitionManagerServiceImpl extends
public void startListening(
Intent recognizerIntent,
IRecognitionListener listener,
- String packageName,
- String featureId,
- int callingUid) throws RemoteException {
- verifyCallerIdentity(
- creatorCallingUid, packageName, creatorPackageNames, listener);
- if (callingUid != creatorCallingUid) {
- listener.onError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
- return;
- }
-
- service.startListening(
- recognizerIntent, listener, packageName, featureId);
+ @NonNull AttributionSource attributionSource)
+ throws RemoteException {
+ attributionSource.enforceCallingUid();
+ service.startListening(recognizerIntent, listener, attributionSource);
}
@Override
public void stopListening(
- IRecognitionListener listener,
- String packageName,
- String featureId) throws RemoteException {
- verifyCallerIdentity(
- creatorCallingUid, packageName, creatorPackageNames, listener);
-
- service.stopListening(listener, packageName, featureId);
+ IRecognitionListener listener) throws RemoteException {
+ service.stopListening(listener);
}
@Override
public void cancel(
IRecognitionListener listener,
- String packageName,
- String featureId,
boolean isShutdown) throws RemoteException {
- verifyCallerIdentity(
- creatorCallingUid, packageName, creatorPackageNames, listener);
- service.cancel(listener, packageName, featureId, isShutdown);
+ service.cancel(listener, isShutdown);
if (isShutdown) {
handleClientDeath(
@@ -192,17 +171,6 @@ final class SpeechRecognitionManagerServiceImpl extends
});
}
- private void verifyCallerIdentity(
- int creatorCallingUid,
- String packageName,
- Set<String> creatorPackageNames,
- IRecognitionListener listener) throws RemoteException {
- if (creatorCallingUid != Binder.getCallingUid()
- || !creatorPackageNames.contains(packageName)) {
- listener.onError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
- }
- }
-
private void handleClientDeath(
int callingUid,
RemoteSpeechRecognitionService service, boolean invokeCancel) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index acda4d5661e4..04c144a4ce7e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -71,6 +71,7 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.content.AttributionSource;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -86,6 +87,7 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
+import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -207,37 +209,35 @@ public class PreferencesHelperTest extends UiServiceTestCase {
throw new UnsupportedOperationException("unimplemented mock method");
});
doAnswer(invocation -> {
- String callingPkg = invocation.getArgument(0);
- String featureId = invocation.getArgument(1);
- Uri uri = invocation.getArgument(2);
- RemoteCallback cb = invocation.getArgument(3);
+ AttributionSource attributionSource = invocation.getArgument(0);
+ Uri uri = invocation.getArgument(1);
+ RemoteCallback cb = invocation.getArgument(2);
IContentProvider mock = (IContentProvider) (invocation.getMock());
AsyncTask.SERIAL_EXECUTOR.execute(() -> {
final Bundle bundle = new Bundle();
try {
bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- mock.canonicalize(callingPkg, featureId, uri));
+ mock.canonicalize(attributionSource, uri));
} catch (RemoteException e) { /* consume */ }
cb.sendResult(bundle);
});
return null;
- }).when(mTestIContentProvider).canonicalizeAsync(any(), any(), any(), any());
+ }).when(mTestIContentProvider).canonicalizeAsync(any(), any(), any());
doAnswer(invocation -> {
- String callingPkg = invocation.getArgument(0);
- String featureId = invocation.getArgument(1);
- Uri uri = invocation.getArgument(2);
- RemoteCallback cb = invocation.getArgument(3);
+ AttributionSource attributionSource = invocation.getArgument(0);
+ Uri uri = invocation.getArgument(1);
+ RemoteCallback cb = invocation.getArgument(2);
IContentProvider mock = (IContentProvider) (invocation.getMock());
AsyncTask.SERIAL_EXECUTOR.execute(() -> {
final Bundle bundle = new Bundle();
try {
bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- mock.uncanonicalize(callingPkg, featureId, uri));
+ mock.uncanonicalize(attributionSource, uri));
} catch (RemoteException e) { /* consume */ }
cb.sendResult(bundle);
});
return null;
- }).when(mTestIContentProvider).uncanonicalizeAsync(any(), any(), any(), any());
+ }).when(mTestIContentProvider).uncanonicalizeAsync(any(), any(), any());
doAnswer(invocation -> {
Uri uri = invocation.getArgument(0);
RemoteCallback cb = invocation.getArgument(1);
@@ -256,11 +256,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
doReturn(CANONICAL_SOUND_URI)
- .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI));
+ .when(mTestIContentProvider).canonicalize(any(), eq(SOUND_URI));
doReturn(CANONICAL_SOUND_URI)
- .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ .when(mTestIContentProvider).canonicalize(any(), eq(CANONICAL_SOUND_URI));
doReturn(SOUND_URI)
- .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ .when(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
@@ -594,7 +594,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// Testing that in restore we are given the canonical version
loadStreamXml(baos, true, UserHandle.USER_SYSTEM);
- verify(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
}
@Test
@@ -605,12 +605,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
.appendQueryParameter("canonical", "1")
.build();
doReturn(canonicalBasedOnLocal)
- .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ .when(mTestIContentProvider).canonicalize(any(), eq(CANONICAL_SOUND_URI));
doReturn(localUri)
- .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ .when(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
doReturn(localUri)
- .when(mTestIContentProvider).uncanonicalize(any(), any(),
- eq(canonicalBasedOnLocal));
+ .when(mTestIContentProvider).uncanonicalize(any(), eq(canonicalBasedOnLocal));
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -630,9 +629,9 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
Thread.sleep(3000);
doReturn(null)
- .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ .when(mTestIContentProvider).canonicalize(any(), eq(CANONICAL_SOUND_URI));
doReturn(null)
- .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ .when(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -657,7 +656,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
// Not a local uncanonicalized uri, simulating that it fails to exist locally
doReturn(null)
- .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI));
+ .when(mTestIContentProvider).canonicalize(any(), eq(SOUND_URI));
String id = "id";
String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 5018166d9ef1..b83f9f266ad7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -132,11 +132,11 @@ public class RankingHelperTest extends UiServiceTestCase {
when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
- when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI)))
.thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(SOUND_URI);
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index aceed86220c6..baae25cad32e 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -110,7 +110,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
TestableLooper.get(this).processAllMessages();
- verify(mIContentProvider).call(anyString(), nullable(String.class), anyString(),
+ verify(mIContentProvider).call(any(), anyString(),
eq(SliceProvider.METHOD_PIN), eq(null), argThat(b -> {
assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
@@ -168,8 +168,8 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
// Throw exception when trying to pin
doAnswer(invocation -> {
throw new Exception("Pin failed");
- }).when(mIContentProvider).call(anyString(), nullable(String.class), anyString(),
- anyString(), eq(null), any());
+ }).when(mIContentProvider).call(any(), anyString(), anyString(),
+ nullable(String.class), any());
TestableLooper.get(this).processAllMessages();
@@ -177,7 +177,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
TestableLooper.get(this).processAllMessages();
- verify(mIContentProvider).call(anyString(), nullable(String.class), anyString(),
+ verify(mIContentProvider).call(any(), anyString(),
eq(SliceProvider.METHOD_PIN), eq(null), argThat(b -> {
assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index 5b9f67efd95d..7be42f4f36f5 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -18,6 +18,7 @@ package android.test.mock;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.AttributionSource;
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
@@ -60,21 +61,20 @@ public class MockContentProvider extends ContentProvider {
*/
private class InversionIContentProvider implements IContentProvider {
@Override
- public ContentProviderResult[] applyBatch(String callingPackage,
- @Nullable String featureId, String authority,
- ArrayList<ContentProviderOperation> operations)
+ public ContentProviderResult[] applyBatch(@NonNull AttributionSource attributionSource,
+ String authority, ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
return MockContentProvider.this.applyBatch(authority, operations);
}
@Override
- public int bulkInsert(String callingPackage, @Nullable String featureId, Uri url,
+ public int bulkInsert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues[] initialValues) throws RemoteException {
return MockContentProvider.this.bulkInsert(url, initialValues);
}
@Override
- public int delete(String callingPackage, @Nullable String featureId, Uri url,
+ public int delete(@NonNull AttributionSource attributionSource, Uri url,
Bundle extras) throws RemoteException {
return MockContentProvider.this.delete(url, extras);
}
@@ -90,40 +90,40 @@ public class MockContentProvider extends ContentProvider {
}
@Override
- public Uri insert(String callingPackage, @Nullable String featureId, Uri url,
+ public Uri insert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues initialValues, Bundle extras) throws RemoteException {
return MockContentProvider.this.insert(url, initialValues, extras);
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPackage,
- @Nullable String featureId, Uri url, String mode, ICancellationSignal signal)
+ public AssetFileDescriptor openAssetFile(@NonNull AttributionSource attributionSource,
+ Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openAssetFile(url, mode);
}
@Override
- public ParcelFileDescriptor openFile(String callingPackage, @Nullable String featureId,
- Uri url, String mode, ICancellationSignal signal, IBinder callerToken)
+ public ParcelFileDescriptor openFile(@NonNull AttributionSource attributionSource,
+ Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openFile(url, mode);
}
@Override
- public Cursor query(String callingPackage, @Nullable String featureId, Uri url,
+ public Cursor query(@NonNull AttributionSource attributionSource, Uri url,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable ICancellationSignal cancellationSignal) throws RemoteException {
return MockContentProvider.this.query(url, projection, queryArgs, null);
}
@Override
- public int update(String callingPackage, @Nullable String featureId, Uri url,
+ public int update(@NonNull AttributionSource attributionSource, Uri url,
ContentValues values, Bundle extras) throws RemoteException {
return MockContentProvider.this.update(url, values, extras);
}
@Override
- public Bundle call(String callingPackage, @Nullable String featureId, String authority,
+ public Bundle call(@NonNull AttributionSource attributionSource, String authority,
String method, String request, Bundle args) throws RemoteException {
return MockContentProvider.this.call(authority, method, request, args);
}
@@ -139,9 +139,10 @@ public class MockContentProvider extends ContentProvider {
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPackage,
- @Nullable String featureId, Uri url, String mimeType, Bundle opts,
- ICancellationSignal signal) throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(
+ @NonNull AttributionSource attributionSource, Uri url, String mimeType,
+ Bundle opts, ICancellationSignal signal)
+ throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openTypedAssetFile(url, mimeType, opts);
}
@@ -151,37 +152,37 @@ public class MockContentProvider extends ContentProvider {
}
@Override
- public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ public Uri canonicalize(@NonNull AttributionSource attributionSource, Uri uri)
throws RemoteException {
return MockContentProvider.this.canonicalize(uri);
}
@Override
- public void canonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ public void canonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback callback) {
MockContentProvider.this.canonicalizeAsync(uri, callback);
}
@Override
- public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ public Uri uncanonicalize(@NonNull AttributionSource attributionSource, Uri uri)
throws RemoteException {
return MockContentProvider.this.uncanonicalize(uri);
}
@Override
- public void uncanonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ public void uncanonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback callback) {
MockContentProvider.this.uncanonicalizeAsync(uri, callback);
}
@Override
- public boolean refresh(String callingPkg, @Nullable String featureId, Uri url,
+ public boolean refresh(@NonNull AttributionSource attributionSource, Uri url,
Bundle args, ICancellationSignal cancellationSignal) throws RemoteException {
return MockContentProvider.this.refresh(url, args);
}
@Override
- public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri,
+ public int checkUriPermission(@NonNull AttributionSource attributionSource, Uri uri,
int uid, int modeFlags) {
return MockContentProvider.this.checkUriPermission(uri, uid, modeFlags);
}
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index 82a1cf7d1796..b81c70704d79 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -16,7 +16,9 @@
package android.test.mock;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.AttributionSource;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
@@ -46,14 +48,14 @@ import java.util.ArrayList;
*/
public class MockIContentProvider implements IContentProvider {
@Override
- public int bulkInsert(String callingPackage, @Nullable String featureId, Uri url,
+ public int bulkInsert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues[] initialValues) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
@SuppressWarnings("unused")
- public int delete(String callingPackage, @Nullable String featureId, Uri url,
+ public int delete(@NonNull AttributionSource attributionSource, Uri url,
Bundle extras) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -75,31 +77,31 @@ public class MockIContentProvider implements IContentProvider {
@Override
@SuppressWarnings("unused")
- public Uri insert(String callingPackage, @Nullable String featureId, Uri url,
+ public Uri insert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues initialValues, Bundle extras) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public ParcelFileDescriptor openFile(String callingPackage, @Nullable String featureId,
- Uri url, String mode, ICancellationSignal signal, IBinder callerToken) {
+ public ParcelFileDescriptor openFile(@NonNull AttributionSource attributionSource,
+ Uri url, String mode, ICancellationSignal signal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPackage, @Nullable String featureId,
+ public AssetFileDescriptor openAssetFile(@NonNull AttributionSource attributionSource,
Uri uri, String mode, ICancellationSignal signal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public ContentProviderResult[] applyBatch(String callingPackage, @Nullable String featureId,
+ public ContentProviderResult[] applyBatch(@NonNull AttributionSource attributionSource,
String authority, ArrayList<ContentProviderOperation> operations) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public Cursor query(String callingPackage, @Nullable String featureId, Uri url,
+ public Cursor query(@NonNull AttributionSource attributionSource, Uri url,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable ICancellationSignal cancellationSignal) {
throw new UnsupportedOperationException("unimplemented mock method");
@@ -111,13 +113,13 @@ public class MockIContentProvider implements IContentProvider {
}
@Override
- public int update(String callingPackage, @Nullable String featureId, Uri url,
+ public int update(@NonNull AttributionSource attributionSource, Uri url,
ContentValues values, Bundle extras) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public Bundle call(String callingPackage, @Nullable String featureId, String authority,
+ public Bundle call(@NonNull AttributionSource attributionSource, String authority,
String method, String request, Bundle args) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -133,9 +135,9 @@ public class MockIContentProvider implements IContentProvider {
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPackage,
- @Nullable String featureId, Uri url, String mimeType, Bundle opts,
- ICancellationSignal signal) throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(@NonNull AttributionSource attributionSource,
+ Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
+ throws RemoteException, FileNotFoundException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -145,48 +147,48 @@ public class MockIContentProvider implements IContentProvider {
}
@Override
- public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) {
+ public Uri canonicalize(@NonNull AttributionSource attributionSource, Uri uri) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
@SuppressWarnings("deprecation")
- public void canonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ public void canonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback remoteCallback) {
AsyncTask.SERIAL_EXECUTOR.execute(() -> {
final Bundle bundle = new Bundle();
bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- canonicalize(callingPkg, featureId, uri));
+ canonicalize(attributionSource, uri));
remoteCallback.sendResult(bundle);
});
}
@Override
- public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) {
+ public Uri uncanonicalize(@NonNull AttributionSource attributionSource, Uri uri) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
@SuppressWarnings("deprecation")
- public void uncanonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ public void uncanonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback remoteCallback) {
AsyncTask.SERIAL_EXECUTOR.execute(() -> {
final Bundle bundle = new Bundle();
bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- uncanonicalize(callingPkg, featureId, uri));
+ uncanonicalize(attributionSource, uri));
remoteCallback.sendResult(bundle);
});
}
@Override
- public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle args,
+ public boolean refresh(@NonNull AttributionSource attributionSource, Uri url, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
/** {@hide} */
@Override
- public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, int uid,
+ public int checkUriPermission(@NonNull AttributionSource attributionSource, Uri uri, int uid,
int modeFlags) {
throw new UnsupportedOperationException("unimplemented mock method call");
}