summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSvet Ganov <svetoslavganov@google.com>2021-02-24 04:09:05 +0000
committerSvet Ganov <svetoslavganov@google.com>2021-03-29 16:49:33 +0000
commit8d2ed506045d5d6cf25ef7ef77a30da910733657 (patch)
tree6bbe71d982f29433262bb5ecb1bcf628a3c1a279
parente3585e106fac76bada696186ac2ba147bc40c2b8 (diff)
Runtime permission attribution improvements
When an app is proxying access to runtime permission protected data it needs to check whether the calling app has a permission to the data it is about to proxy which leaves a trace in app ops that the requesting app perofmed a data access. However, then the app doing the work needs to get the protected data itself from the OS which access gets attributed only to itself. As a result there are two data accesses in app ops where only the first one is a proxy one that app A got access to Foo through app B - that is the one we want to show in the permission tracking UIs - and one for the data access - that is the one we would want to blame on the calling app, and in fact, these two accesses should be one - that app A accessed Foo though B. This limitation requires fragile one off workarounds where both accesses use the same attribution tag and sys UI has hardcoded rules to dedupe. Since this is not documented we cannot expect that the ecosystem would reliably do this workaround in apps that that the workaround in the OS would be respected by every OEM. This change adds a mechaism to resolve this issue. It allows for an app to create an attribution context for another app and then any private data access thorugh this context would result in a single app op blame that A accessed Foo though B, i.e. we no longer have double accounting. Also this can be nested through apps, e.g. app A asks app B which asks app C for contacts. In this case app B creates an attribution context for app A and calls into app C which creates an attribution context for app B. When app C gets contacts the entire attribution chain would get a porper, single blame: that C accessed the data, that B got the data from C, and that A got the data form B. Furthermore, this mechanism ensures that apps cannot forget to check permissions for the caller before proxying private data. In our example B and C don't need to check the permisisons for A and B, respectively, since the permisisons for the entire attribution chain are checked before data delivery. Attribution chains are not forgeable preventing a bad actor to create an arbitrary one - each attribution is created by the app it refers to and points to a chain of attributions created by their corresponding apps. This change also fixes a bug where all content provider accesses were double counted in app ops due to double noting. While at this it also fixes that apps can now access their own last ops. There was a bug where one could not pass null getting the attributed ops from a historical package ops while this is a valid use case since if there is no attribution everything is mapped to the null tag. There were some app op APIs not being piped thorough the app ops delegate and by extension through the app ops policy. Also now that we have nice way to express the permission chain in a call we no longer need the special casing in activity manager to handle content provider accesses through the OS. Fixed a bug where we don't properly handle the android.os.shell calls with an invlaid tag which was failing while the shell can do any tag. Finally, to ensure the mechanims is validated and works end-to-end we are adding support for a voice recognizer to blame the client app for the mic access. The recognition service can create a blaming context when opening the mic and if the mic is open, which would do all permission checks, we would not do so again. Since changes to PermissionChercker for handling attribution sources were made the CL also hooks up renounced permissoins in the request permission flow and in the permission checks. bug:158792096 bug:180647319 Test:atest CtsPermissionsTestCases atest CtsPermissions2TestCases atest CtsPermissions3TestCases atest CtsPermissions4TestCases atest CtsPermissions5TestCases atest CtsAppOpsTestCases atest CtsAppOps2TestCases Change-Id: Ib04585515d3dc3956966005ae9d94955b2f3ee08
-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");
}