diff --git a/api/current.txt b/api/current.txt index 7b48514bc5bb6..83df1d97dbfff 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4376,6 +4376,7 @@ package android.app { method @Deprecated public void checkPackage(int, @NonNull String); method @Deprecated public void finishOp(@NonNull String, int, @NonNull String); method public void finishOp(@NonNull String, int, @NonNull String, @Nullable String); + method public void finishProxyOp(@NonNull String, int, @NonNull String, @Nullable String); method public boolean isOpActive(@NonNull String, int, @NonNull String); method @Deprecated public int noteOp(@NonNull String, int, @NonNull String); method public int noteOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String); @@ -4392,6 +4393,8 @@ package android.app { method public int startOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String); method @Deprecated public int startOpNoThrow(@NonNull String, int, @NonNull String); method public int startOpNoThrow(@NonNull String, int, @NonNull String, @NonNull String, @Nullable String); + method public int startProxyOp(@NonNull String, int, @NonNull String, @Nullable String, @Nullable String); + method public int startProxyOpNoThrow(@NonNull String, int, @NonNull String, @Nullable String, @Nullable String); method public void startWatchingActive(@NonNull String[], @NonNull java.util.concurrent.Executor, @NonNull android.app.AppOpsManager.OnOpActiveChangedListener); method public void startWatchingMode(@NonNull String, @Nullable String, @NonNull android.app.AppOpsManager.OnOpChangedListener); method public void startWatchingMode(@NonNull String, @Nullable String, int, @NonNull android.app.AppOpsManager.OnOpChangedListener); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index a8cd9aac39b16..d1fbd76c6db12 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -7978,6 +7978,91 @@ 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 + * proxied package name match, and if not, return {@link #MODE_IGNORED}. + * + * @param op The op to note + * @param proxiedUid The uid to note the op for {@code null} + * @param proxiedUid The package name the uid belongs to + * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext + * attribution tag} or {@code null} for default attribution + * @param message A message describing the reason the op was noted + * + * @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 proxy or proxied app has been configured to crash on this + * op. + */ + 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); + 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)]); + } + return mode; + } + + /** + *Like {@link #startProxyOp(String, int, String, String, String)} but instead + * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}. + * + * @param op The op to note + * @param proxiedUid The uid to note the op for {@code null} + * @param proxiedUid The package name the uid belongs to + * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext + * attribution tag} or {@code null} for default attribution + * @param message A message describing the reason the op was noted* + *
This API requires package with the {@code proxiedPackageName} to belong to
+ * {@code proxiedUid}.
+ *
+ * @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).
+ */
+ public int startProxyOpNoThrow(@NonNull String op, int proxiedUid,
+ @NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag,
+ @Nullable String message) {
+ try {
+ int opInt = strOpToOp(op);
+
+ collectNoteOpCallsForValidation(opInt);
+ int collectionMode = getNotedOpCollectionMode(proxiedUid, proxiedPackageName, opInt);
+ boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID;
+ if (collectionMode == COLLECT_ASYNC) {
+ if (message == null) {
+ // Set stack trace as default message
+ message = getFormattedStackTrace();
+ shouldCollectMessage = true;
+ }
+ }
+
+ int mode = mService.startProxyOperation(getClientId(), opInt, proxiedUid,
+ proxiedPackageName, proxiedAttributionTag, Process.myUid(),
+ mContext.getOpPackageName(), mContext.getAttributionTag(), false,
+ collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
+
+ if (mode == MODE_ALLOWED) {
+ if (collectionMode == COLLECT_SELF) {
+ collectNotedOpForSelf(opInt, proxiedAttributionTag);
+ } 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) {
+ collectNotedOpSync(opInt, proxiedAttributionTag);
+ }
+ }
+
+ return mode;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
/**
* @deprecated Use {@link #finishOp(String, int, String, String)} instead
@@ -8033,6 +8118,28 @@ public class AppOpsManager {
}
}
+ /**
+ * 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
+ */
+ public void finishProxyOp(@NonNull String op, int proxiedUid,
+ @NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag) {
+ try {
+ mService.finishProxyOperation(getClientId(), strOpToOp(op), proxiedUid,
+ proxiedPackageName, proxiedAttributionTag, Process.myUid(),
+ mContext.getOpPackageName(), mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Checks whether the given op for a package is active, i.e. did someone call {@link #startOp}
* without {@link #finishOp} yet.
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 51e56b7fca43c..04146bcad083a 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -56,6 +56,13 @@ interface IAppOpsService {
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);
// Remaining methods are only used in Java.
int checkPackage(int uid, String packageName);
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 3c591d50ddda6..a0aa0e09084bc 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -4376,6 +4376,7 @@ package android.app {
method @Deprecated public void checkPackage(int, @NonNull String);
method @Deprecated public void finishOp(@NonNull String, int, @NonNull String);
method public void finishOp(@NonNull String, int, @NonNull String, @Nullable String);
+ method public void finishProxyOp(@NonNull String, int, @NonNull String, @Nullable String);
method public boolean isOpActive(@NonNull String, int, @NonNull String);
method @Deprecated public int noteOp(@NonNull String, int, @NonNull String);
method public int noteOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String);
@@ -4392,6 +4393,8 @@ package android.app {
method public int startOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String);
method @Deprecated public int startOpNoThrow(@NonNull String, int, @NonNull String);
method public int startOpNoThrow(@NonNull String, int, @NonNull String, @NonNull String, @Nullable String);
+ method public int startProxyOp(@NonNull String, int, @NonNull String, @Nullable String, @Nullable String);
+ method public int startProxyOpNoThrow(@NonNull String, int, @NonNull String, @Nullable String, @Nullable String);
method public void startWatchingActive(@NonNull String[], @NonNull java.util.concurrent.Executor, @NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
method public void startWatchingMode(@NonNull String, @Nullable String, @NonNull android.app.AppOpsManager.OnOpChangedListener);
method public void startWatchingMode(@NonNull String, @Nullable String, int, @NonNull android.app.AppOpsManager.OnOpChangedListener);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 53a27941573ef..5379f3218a6e3 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -29,6 +29,7 @@ import static android.app.AppOpsManager.KEY_BG_STATE_SETTLE_TIME;
import static android.app.AppOpsManager.KEY_FG_SERVICE_STATE_SETTLE_TIME;
import static android.app.AppOpsManager.KEY_TOP_STATE_SETTLE_TIME;
import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.NoteOpEvent;
@@ -132,6 +133,7 @@ import android.util.AtomicFile;
import android.util.KeyValueListParser;
import android.util.LongSparseArray;
import android.util.Pair;
+import android.util.Pools;
import android.util.Pools.SimplePool;
import android.util.Slog;
import android.util.SparseArray;
@@ -408,14 +410,26 @@ public class AppOpsService extends IAppOpsService.Stub {
}
InProgressStartOpEvent acquire(long startTime, long elapsedTime, @NonNull IBinder clientId,
- @NonNull Runnable onDeath, int uidState) throws RemoteException {
+ @NonNull Runnable onDeath, int proxyUid, @Nullable String proxyPackageName,
+ @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState,
+ @OpFlags int flags) throws RemoteException {
+
InProgressStartOpEvent recycled = acquire();
+
+ OpEventProxyInfo proxyInfo = null;
+ if (proxyUid != Process.INVALID_UID) {
+ proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
+ proxyAttributionTag);
+ }
+
if (recycled != null) {
- recycled.reinit(startTime, elapsedTime, clientId, onDeath, uidState);
+ recycled.reinit(startTime, elapsedTime, clientId, onDeath, uidState, flags,
+ proxyInfo, mOpEventProxyInfoPool);
return recycled;
}
- return new InProgressStartOpEvent(startTime, elapsedTime, clientId, onDeath, uidState);
+ return new InProgressStartOpEvent(startTime, elapsedTime, clientId, onDeath, uidState,
+ proxyInfo, flags);
}
}
@@ -670,6 +684,12 @@ public class AppOpsService extends IAppOpsService.Stub {
/** uidstate used when calling startOp */
private @AppOpsManager.UidState int mUidState;
+ /** Proxy information of the startOp event */
+ private @Nullable OpEventProxyInfo mProxy;
+
+ /** Proxy flag information */
+ private @OpFlags int mFlags;
+
/** How many times the op was started but not finished yet */
int numUnfinishedStarts;
@@ -681,17 +701,22 @@ public class AppOpsService extends IAppOpsService.Stub {
* @param clientId The client id of the caller of {@link #startOperation}
* @param onDeath The code to execute on client death
* @param uidState The uidstate of the app {@link #startOperation} was called for
+ * @param proxy The proxy information, if {@link #startProxyOperation} was called
+ * @param flags The trusted/nontrusted/self flags.
*
* @throws RemoteException If the client is dying
*/
private InProgressStartOpEvent(long startTime, long startElapsedTime,
- @NonNull IBinder clientId, @NonNull Runnable onDeath, int uidState)
- throws RemoteException {
+ @NonNull IBinder clientId, @NonNull Runnable onDeath,
+ @AppOpsManager.UidState int uidState, @Nullable OpEventProxyInfo proxy,
+ @OpFlags int flags) throws RemoteException {
mStartTime = startTime;
mStartElapsedTime = startElapsedTime;
mClientId = clientId;
mOnDeath = onDeath;
mUidState = uidState;
+ mProxy = proxy;
+ mFlags = flags;
clientId.linkToDeath(this, 0);
}
@@ -714,16 +739,27 @@ public class AppOpsService extends IAppOpsService.Stub {
* @param clientId The client id of the caller of {@link #startOperation}
* @param onDeath The code to execute on client death
* @param uidState The uidstate of the app {@link #startOperation} was called for
+ * @param flags The flags relating to the proxy
+ * @param proxy The proxy information, if {@link #startProxyOperation} was called
+ * @param proxyPool The pool to release previous {@link OpEventProxyInfo} to
*
* @throws RemoteException If the client is dying
*/
public void reinit(long startTime, long startElapsedTime, @NonNull IBinder clientId,
- @NonNull Runnable onDeath, int uidState) throws RemoteException {
+ @NonNull Runnable onDeath, @AppOpsManager.UidState int uidState, @OpFlags int flags,
+ @Nullable OpEventProxyInfo proxy, @NonNull Pools.Pool