diff --git a/api/current.txt b/api/current.txt index 94e90a75f644d..27b15c5d10086 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 ef4f099f441d9..7de6a383ef4c9 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 983f20aad2e3a..a401bcdde2e5c 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 7dbb39e3cb399..d8cca6c58d0d4 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 proxyPool + ) throws RemoteException { mStartTime = startTime; mStartElapsedTime = startElapsedTime; mClientId = clientId; mOnDeath = onDeath; mUidState = uidState; + mFlags = flags; + + if (mProxy != null) { + proxyPool.release(mProxy); + } + mProxy = proxy; clientId.linkToDeath(this, 0); } @@ -744,9 +780,19 @@ public class AppOpsService extends IAppOpsService.Stub { } /** @return uidstate used when calling startOp */ - public int getUidState() { + public @AppOpsManager.UidState int getUidState() { return mUidState; } + + /** @return proxy info for the access */ + public @Nullable OpEventProxyInfo getProxy() { + return mProxy; + } + + /** @return flags used for the access */ + public @OpFlags int getFlags() { + return mFlags; + } } private final class AttributedOp { @@ -876,14 +922,22 @@ public class AppOpsService extends IAppOpsService.Stub { * Update state when start was called * * @param clientId Id of the startOp caller + * @param proxyUid The UID of the proxy app + * @param proxyPackageName The package name of the proxy app + * @param proxyAttributionTag The attribution tag of the proxy app * @param uidState UID state of the app startOp is called for + * @param flags The proxy flags */ - public void started(@NonNull IBinder clientId, @AppOpsManager.UidState int uidState) - throws RemoteException { - started(clientId, uidState, true); + public void started(@NonNull IBinder clientId, int proxyUid, + @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, + @AppOpsManager.UidState int uidState, @OpFlags int flags) throws RemoteException { + started(clientId, proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags, + true); } - private void started(@NonNull IBinder clientId, @AppOpsManager.UidState int uidState, + private void started(@NonNull IBinder clientId, int proxyUid, + @Nullable String proxyPackageName, @Nullable String proxyAttributionTag, + @AppOpsManager.UidState int uidState, @OpFlags int flags, boolean triggerCallbackIfNeeded) throws RemoteException { if (triggerCallbackIfNeeded && !parent.isRunning()) { scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, @@ -899,7 +953,7 @@ public class AppOpsService extends IAppOpsService.Stub { event = mInProgressStartOpEventPool.acquire(System.currentTimeMillis(), SystemClock.elapsedRealtime(), clientId, PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId), - uidState); + proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags); mInProgressEvents.put(clientId, event); } else { if (uidState != event.mUidState) { @@ -909,9 +963,8 @@ public class AppOpsService extends IAppOpsService.Stub { event.numUnfinishedStarts++; - // startOp events don't support proxy, hence use flags==SELF mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName, - tag, uidState, OP_FLAG_SELF); + tag, uidState, flags); } /** @@ -945,14 +998,17 @@ public class AppOpsService extends IAppOpsService.Stub { mAccessEvents = new LongSparseArray<>(1); } - // startOp events don't support proxy, hence use flags==SELF + OpEventProxyInfo proxyCopy = event.getProxy() != null + ? new OpEventProxyInfo(event.getProxy()) : null; + NoteOpEvent finishedEvent = new NoteOpEvent(event.getStartTime(), - SystemClock.elapsedRealtime() - event.getStartElapsedTime(), null); - mAccessEvents.put(makeKey(event.getUidState(), OP_FLAG_SELF), finishedEvent); + SystemClock.elapsedRealtime() - event.getStartElapsedTime(), proxyCopy); + mAccessEvents.put(makeKey(event.getUidState(), event.getFlags()), + finishedEvent); mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid, parent.packageName, tag, event.getUidState(), - AppOpsManager.OP_FLAG_SELF, finishedEvent.getDuration()); + event.getFlags(), finishedEvent.getDuration()); mInProgressStartOpEventPool.release(event); @@ -1010,9 +1066,17 @@ public class AppOpsService extends IAppOpsService.Stub { event.numUnfinishedStarts = 1; finished(event.getClientId(), false); + OpEventProxyInfo proxy = event.getProxy(); + // Call started() to add a new start event object and then add the // previously removed unfinished start counts back - started(event.getClientId(), newState, false); + if (proxy != null) { + started(event.getClientId(), proxy.getUid(), proxy.getPackageName(), + proxy.getAttributionTag(), newState, event.getFlags(), false); + } else { + started(event.getClientId(), Process.INVALID_UID, null, null, newState, + OP_FLAG_SELF, false); + } event.numUnfinishedStarts += numPreviousUnfinishedStarts - 1; } catch (RemoteException e) { if (DEBUG) Slog.e(TAG, "Cannot switch to new uidState " + newState); @@ -1116,10 +1180,9 @@ public class AppOpsService extends IAppOpsService.Stub { for (int i = 0; i < numInProgressEvents; i++) { InProgressStartOpEvent event = mInProgressEvents.valueAt(i); - // startOp events don't support proxy - accessEvents.append(makeKey(event.getUidState(), OP_FLAG_SELF), + accessEvents.append(makeKey(event.getUidState(), event.getFlags()), new NoteOpEvent(event.getStartTime(), now - event.getStartElapsedTime(), - null)); + event.getProxy())); } } @@ -2182,14 +2245,14 @@ public class AppOpsService extends IAppOpsService.Stub { if (mode == defaultMode) { return; } - previousMode = AppOpsManager.MODE_DEFAULT; + previousMode = MODE_DEFAULT; uidState = new UidState(uid); uidState.opModes = new SparseIntArray(); uidState.opModes.put(code, mode); mUidStates.put(uid, uidState); scheduleWriteLocked(); } else if (uidState.opModes == null) { - previousMode = AppOpsManager.MODE_DEFAULT; + previousMode = MODE_DEFAULT; if (mode != defaultMode) { uidState.opModes = new SparseIntArray(); uidState.opModes.put(code, mode); @@ -2430,7 +2493,7 @@ public class AppOpsService extends IAppOpsService.Stub { return; } - int previousMode = AppOpsManager.MODE_DEFAULT; + int previousMode = MODE_DEFAULT; synchronized (this) { UidState uidState = getUidStateLocked(uid, false); Op op = getOpLocked(code, uid, packageName, null, bypass, true); @@ -3409,7 +3472,75 @@ public class AppOpsService extends IAppOpsService.Stub { return result; } } + return startOperationUnchecked(clientId, code, uid, packageName, attributionTag, + Process.INVALID_UID, null, null, OP_FLAG_SELF, startIfModeDefault, + shouldCollectAsyncNotedOp, message, shouldCollectMessage, false); + } + + @Override + public 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) { + verifyIncomingUid(proxyUid); + verifyIncomingOp(code); + verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid)); + verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid)); + + String resolvedProxyPackageName = resolvePackageName(proxyUid, proxyPackageName); + if (resolvedProxyPackageName == null) { + return AppOpsManager.MODE_IGNORED; + } + + final boolean isProxyTrusted = mContext.checkPermission( + Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid) + == PackageManager.PERMISSION_GRANTED; + + 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; + } + + 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) + || Binder.getCallingUid() == proxiedUid) { + return proxyMode; + } + + return startOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName, + proxiedAttributionTag, proxyUid, resolvedProxyPackageName, proxyAttributionTag, + proxiedFlags, startIfModeDefault, shouldCollectAsyncNotedOp, message, + shouldCollectMessage, false); + } + + private boolean shouldStartForMode(int mode, boolean startIfModeDefault) { + return (mode == MODE_ALLOWED || (mode == MODE_DEFAULT && startIfModeDefault)); + } + + private int startOperationUnchecked(IBinder clientId, int code, int uid, + @NonNull String packageName, @Nullable String attributionTag, int proxyUid, + String proxyPackageName, @Nullable String proxyAttributionTag, @OpFlags int flags, + boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @Nullable String message, + boolean shouldCollectMessage, boolean dryRun) { RestrictionBypass bypass; try { bypass = verifyAndGetBypass(uid, packageName, attributionTag); @@ -3419,19 +3550,25 @@ public class AppOpsService extends IAppOpsService.Stub { } synchronized (this) { - final Ops ops = getOpsLocked(uid, resolvedPackageName, attributionTag, bypass, - true /* edit */); + final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, true /* edit */); if (ops == null) { - scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); + if (!dryRun) { + scheduleOpStartedIfNeededLocked(code, uid, packageName, + AppOpsManager.MODE_IGNORED); + } if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid - + " package " + resolvedPackageName); + + " package " + packageName); return AppOpsManager.MODE_ERRORED; } final Op op = getOpLocked(ops, code, uid, true); - if (isOpRestrictedLocked(uid, code, resolvedPackageName, bypass)) { - scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); + if (isOpRestrictedLocked(uid, code, packageName, bypass)) { + if (!dryRun) { + scheduleOpStartedIfNeededLocked(code, uid, packageName, + AppOpsManager.MODE_IGNORED); + } return AppOpsManager.MODE_IGNORED; } + final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag); final int switchCode = AppOpsManager.opToSwitch(code); final UidState uidState = ops.uidState; @@ -3439,13 +3576,16 @@ public class AppOpsService extends IAppOpsService.Stub { // non-default) it takes over, otherwise use the per package policy. if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) { final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode)); - if (uidMode != AppOpsManager.MODE_ALLOWED - && (!startIfModeDefault || uidMode != AppOpsManager.MODE_DEFAULT)) { - if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code " - + switchCode + " (" + code + ") uid " + uid + " package " - + resolvedPackageName); - attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF); - scheduleOpStartedIfNeededLocked(code, uid, packageName, uidMode); + if (!shouldStartForMode(uidMode, startIfModeDefault)) { + if (DEBUG) { + Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code " + + switchCode + " (" + code + ") uid " + uid + " package " + + packageName); + } + if (!dryRun) { + attributedOp.rejected(uidState.state, flags); + scheduleOpStartedIfNeededLocked(code, uid, packageName, uidMode); + } return uidMode; } } else { @@ -3453,26 +3593,31 @@ public class AppOpsService extends IAppOpsService.Stub { : op; final int mode = switchOp.evalMode(); if (mode != AppOpsManager.MODE_ALLOWED - && (!startIfModeDefault || mode != AppOpsManager.MODE_DEFAULT)) { + && (!startIfModeDefault || mode != MODE_DEFAULT)) { if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " - + resolvedPackageName); - attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF); - scheduleOpStartedIfNeededLocked(code, uid, packageName, mode); + + packageName); + if (!dryRun) { + attributedOp.rejected(uidState.state, flags); + scheduleOpStartedIfNeededLocked(code, uid, packageName, mode); + } return mode; } } if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid - + " package " + resolvedPackageName); - scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_ALLOWED); - try { - attributedOp.started(clientId, uidState.state); - } catch (RemoteException e) { - throw new RuntimeException(e); + + " package " + packageName); + if (!dryRun) { + scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_ALLOWED); + try { + attributedOp.started(clientId, proxyUid, proxyPackageName, proxyAttributionTag, + uidState.state, flags); + } catch (RemoteException e) { + throw new RuntimeException(e); + } } } - if (shouldCollectAsyncNotedOp) { + if (shouldCollectAsyncNotedOp && !dryRun) { collectAsyncNotedOp(uid, packageName, code, attributionTag, AppOpsManager.OP_FLAG_SELF, message, shouldCollectMessage); } @@ -3492,6 +3637,37 @@ public class AppOpsService extends IAppOpsService.Stub { return; } + finishOperationUnchecked(clientId, code, uid, resolvedPackageName, attributionTag); + } + + @Override + public void finishProxyOperation(IBinder clientId, int code, int proxiedUid, + String proxiedPackageName, @Nullable String proxiedAttributionTag, int proxyUid, + @Nullable String proxyPackageName, @Nullable String proxyAttributionTag) { + verifyIncomingUid(proxyUid); + verifyIncomingOp(code); + verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid)); + verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid)); + + String resolvedProxyPackageName = resolvePackageName(proxyUid, proxyPackageName); + if (resolvedProxyPackageName == null) { + return; + } + + finishOperationUnchecked(clientId, code, proxyUid, resolvedProxyPackageName, + proxyAttributionTag); + + String resolvedProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName); + if (resolvedProxiedPackageName == null) { + return; + } + + finishOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName, + proxiedAttributionTag); + } + + private void finishOperationUnchecked(IBinder clientId, int code, int uid, String packageName, + String attributionTag) { RestrictionBypass bypass; try { bypass = verifyAndGetBypass(uid, packageName, attributionTag); @@ -3501,7 +3677,7 @@ public class AppOpsService extends IAppOpsService.Stub { } synchronized (this) { - Op op = getOpLocked(code, uid, resolvedPackageName, attributionTag, bypass, true); + Op op = getOpLocked(code, uid, packageName, attributionTag, bypass, true); if (op == null) { Slog.e(TAG, "Operation not found: uid=" + uid + " pkg=" + packageName + "(" + attributionTag + ") op=" + AppOpsManager.opToName(code));