diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index eb14c11e7e3ce..96d59b80b4794 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -3105,24 +3105,11 @@ public class AppOpsManager { */ public boolean isRecordAudioRestrictionExcept; - /** - * Is attribution tag not null and not contained in the package attributions - */ - public boolean isAttributionTagNotFound = false; - public RestrictionBypass(boolean isPrivileged, boolean isRecordAudioRestrictionExcept) { this.isPrivileged = isPrivileged; this.isRecordAudioRestrictionExcept = isRecordAudioRestrictionExcept; } - public void setIsAttributionTagNotFound(boolean isAttributionTagNotFound) { - this.isAttributionTagNotFound = isAttributionTagNotFound; - } - - public boolean getIsAttributionTagNotFound() { - return this.isAttributionTagNotFound; - } - public static RestrictionBypass UNRESTRICTED = new RestrictionBypass(true, true); } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index c2259eb98e589..ce8cbafeba33b 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -667,12 +667,30 @@ public class AppOpsService extends IAppOpsService.Stub { /** Lazily populated cache of attributionTags of this package */ final @NonNull ArraySet knownAttributionTags = new ArraySet<>(); + /** + * Lazily populated cache of valid attributionTags of this package, a set smaller + * than or equal to {@link #knownAttributionTags}. + */ + final @NonNull ArraySet validAttributionTags = new ArraySet<>(); + Ops(String _packageName, UidState _uidState) { packageName = _packageName; uidState = _uidState; } } + /** Returned from {@link #verifyAndGetBypass(int, String, String, String, boolean)}. */ + private static final class PackageVerificationResult { + + final RestrictionBypass bypass; + final boolean isAttributionTagValid; + + PackageVerificationResult(RestrictionBypass bypass, boolean isAttributionTagValid) { + this.bypass = bypass; + this.isAttributionTagValid = isAttributionTagValid; + } + } + /** A in progress startOp->finishOp event */ private static final class InProgressStartOpEvent implements IBinder.DeathRecipient { /** Wall clock time of startOp event (not monotonic) */ @@ -2225,7 +2243,8 @@ public class AppOpsService extends IAppOpsService.Stub { return Collections.emptyList(); } synchronized (this) { - Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false /* edit */); + Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, false, null, + /* edit */ false); if (pkgOps == null) { return null; } @@ -2387,7 +2406,7 @@ public class AppOpsService extends IAppOpsService.Stub { op.removeAttributionsWithNoTime(); if (op.mAttributions.isEmpty()) { - Ops ops = getOpsLocked(uid, packageName, null, null, false /* edit */); + Ops ops = getOpsLocked(uid, packageName, null, false, null, /* edit */ false); if (ops != null) { ops.remove(op.op); if (ops.size() <= 0) { @@ -2697,9 +2716,9 @@ public class AppOpsService extends IAppOpsService.Stub { ArraySet repCbs = null; code = AppOpsManager.opToSwitch(code); - RestrictionBypass bypass; + PackageVerificationResult pvr; try { - bypass = verifyAndGetBypass(uid, packageName, null); + pvr = verifyAndGetBypass(uid, packageName, null); } catch (SecurityException e) { Slog.e(TAG, "Cannot setMode", e); return; @@ -2708,7 +2727,7 @@ public class AppOpsService extends IAppOpsService.Stub { int previousMode = MODE_DEFAULT; synchronized (this) { UidState uidState = getUidStateLocked(uid, false); - Op op = getOpLocked(code, uid, packageName, null, bypass, true); + Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ true); if (op != null) { if (op.mode != mode) { previousMode = op.mode; @@ -3148,9 +3167,9 @@ public class AppOpsService extends IAppOpsService.Stub { */ private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean raw) { - RestrictionBypass bypass; + PackageVerificationResult pvr; try { - bypass = verifyAndGetBypass(uid, packageName, null); + pvr = verifyAndGetBypass(uid, packageName, null); } catch (SecurityException e) { Slog.e(TAG, "checkOperation", e); return AppOpsManager.opToDefaultMode(code); @@ -3160,7 +3179,7 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_IGNORED; } synchronized (this) { - if (isOpRestrictedLocked(uid, code, packageName, attributionTag, bypass)) { + if (isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass)) { return AppOpsManager.MODE_IGNORED; } code = AppOpsManager.opToSwitch(code); @@ -3170,7 +3189,7 @@ public class AppOpsService extends IAppOpsService.Stub { final int rawMode = uidState.opModes.get(code); return raw ? rawMode : uidState.evalMode(code, rawMode); } - Op op = getOpLocked(code, uid, packageName, null, bypass, false); + Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false); if (op == null) { return AppOpsManager.opToDefaultMode(code); } @@ -3341,16 +3360,16 @@ public class AppOpsService extends IAppOpsService.Stub { @Nullable String proxyAttributionTag, @OpFlags int flags, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage) { - RestrictionBypass bypass; + PackageVerificationResult pvr; try { boolean isLocOrActivity = code == AppOpsManager.OP_FINE_LOCATION || code == AppOpsManager.OP_FINE_LOCATION_SOURCE || code == AppOpsManager.OP_ACTIVITY_RECOGNITION || code == AppOpsManager.OP_ACTIVITY_RECOGNITION_SOURCE; - bypass = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName, + pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName, isLocOrActivity); boolean wasNull = attributionTag == null; - if (bypass != null && bypass.getIsAttributionTagNotFound()) { + if (!pvr.isAttributionTagValid) { attributionTag = null; } if (attributionTag == null && isLocOrActivity @@ -3365,8 +3384,8 @@ public class AppOpsService extends IAppOpsService.Stub { } synchronized (this) { - final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, - true /* edit */); + final Ops ops = getOpsLocked(uid, packageName, attributionTag, + pvr.isAttributionTagValid, pvr.bypass, /* edit */ true); if (ops == null) { scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_IGNORED); @@ -3386,7 +3405,7 @@ public class AppOpsService extends IAppOpsService.Stub { final int switchCode = AppOpsManager.opToSwitch(code); final UidState uidState = ops.uidState; - if (isOpRestrictedLocked(uid, code, packageName, attributionTag, bypass)) { + if (isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass)) { attributedOp.rejected(uidState.state, flags); scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_IGNORED); @@ -3862,15 +3881,15 @@ public class AppOpsService extends IAppOpsService.Stub { boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage, @AttributionFlags int attributionFlags, int attributionChainId, boolean dryRun) { - RestrictionBypass bypass; + PackageVerificationResult pvr; try { boolean isLocOrActivity = code == AppOpsManager.OP_FINE_LOCATION || code == AppOpsManager.OP_FINE_LOCATION_SOURCE || code == AppOpsManager.OP_ACTIVITY_RECOGNITION || code == AppOpsManager.OP_ACTIVITY_RECOGNITION_SOURCE; - bypass = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName, + pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName, isLocOrActivity); - if (bypass != null && bypass.getIsAttributionTagNotFound()) { + if (!pvr.isAttributionTagValid) { attributionTag = null; } if (attributionTag == null && isLocOrActivity @@ -3886,7 +3905,8 @@ public class AppOpsService extends IAppOpsService.Stub { boolean isRestricted = false; synchronized (this) { - final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, true /* edit */); + final Ops ops = getOpsLocked(uid, packageName, attributionTag, + pvr.isAttributionTagValid, pvr.bypass, /* edit */ true); if (ops == null) { if (!dryRun) { scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, @@ -3901,7 +3921,7 @@ public class AppOpsService extends IAppOpsService.Stub { final Op op = getOpLocked(ops, code, uid, true); final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag); final UidState uidState = ops.uidState; - isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, bypass); + isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass); final int switchCode = AppOpsManager.opToSwitch(code); // If there is a non-default per UID policy (we set UID op mode only if // non-default) it takes over, otherwise use the per package policy. @@ -4033,10 +4053,10 @@ public class AppOpsService extends IAppOpsService.Stub { private void finishOperationUnchecked(IBinder clientId, int code, int uid, String packageName, String attributionTag) { - RestrictionBypass bypass; + PackageVerificationResult pvr; try { - bypass = verifyAndGetBypass(uid, packageName, attributionTag); - if (bypass != null && bypass.getIsAttributionTagNotFound()) { + pvr = verifyAndGetBypass(uid, packageName, attributionTag); + if (!pvr.isAttributionTagValid) { attributionTag = null; } } catch (SecurityException e) { @@ -4045,7 +4065,8 @@ public class AppOpsService extends IAppOpsService.Stub { } synchronized (this) { - Op op = getOpLocked(code, uid, packageName, attributionTag, bypass, true); + Op op = getOpLocked(code, uid, packageName, attributionTag, pvr.isAttributionTagValid, + pvr.bypass, /* edit */ true); if (op == null) { Slog.e(TAG, "Operation not found: uid=" + uid + " pkg=" + packageName + "(" + attributionTag + ") op=" + AppOpsManager.opToName(code)); @@ -4427,32 +4448,35 @@ public class AppOpsService extends IAppOpsService.Stub { } /** - * @see verifyAndGetBypass(int, String, String, String) + * @see #verifyAndGetBypass(int, String, String, String, boolean) */ - private @Nullable RestrictionBypass verifyAndGetBypass(int uid, String packageName, + private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName, @Nullable String attributionTag) { return verifyAndGetBypass(uid, packageName, attributionTag, null, false); } /** * Verify that package belongs to uid and return the {@link RestrictionBypass bypass - * description} for the package. + * description} for the package, along with a boolean indicating whether the attribution tag is + * valid. * * @param uid The uid the package belongs to * @param packageName The package the might belong to the uid * @param attributionTag attribution tag or {@code null} if no need to verify * @param proxyPackageName The proxy package, from which the attribution tag is to be pulled * - * @return {@code true} iff the package is privileged + * @return PackageVerificationResult containing {@link RestrictionBypass} and whether the + * attribution tag is valid */ - private @Nullable RestrictionBypass verifyAndGetBypass(int uid, String packageName, + private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName, @Nullable String attributionTag, @Nullable String proxyPackageName, boolean extraLog) { if (uid == Process.ROOT_UID) { // For backwards compatibility, don't check package name for root UID. - return null; + return new PackageVerificationResult(null, + /* isAttributionTagValid */ true); } - // Do not check if uid/packageName/attributionTag is already known + // Do not check if uid/packageName/attributionTag is already known. synchronized (this) { UidState uidState = mUidStates.get(uid); if (uidState != null && uidState.pkgOps != null) { @@ -4460,14 +4484,13 @@ public class AppOpsService extends IAppOpsService.Stub { if (ops != null && (attributionTag == null || ops.knownAttributionTags.contains( attributionTag)) && ops.bypass != null) { - return ops.bypass; + return new PackageVerificationResult(ops.bypass, + ops.validAttributionTags.contains(attributionTag)); } } } int callingUid = Binder.getCallingUid(); - int userId = UserHandle.getUserId(uid); - RestrictionBypass bypass = null; // Allow any attribution tag for resolvable uids int pkgUid = resolveUid(packageName); @@ -4479,12 +4502,16 @@ public class AppOpsService extends IAppOpsService.Stub { throw new SecurityException("Specified package " + packageName + " under uid " + UserHandle.getAppId(uid) + otherUidMessage); } - return RestrictionBypass.UNRESTRICTED; + return new PackageVerificationResult(RestrictionBypass.UNRESTRICTED, + /* isAttributionTagValid */ true); } + int userId = UserHandle.getUserId(uid); + RestrictionBypass bypass = null; + boolean isAttributionTagValid = false; + final long ident = Binder.clearCallingIdentity(); try { - boolean isAttributionTagValid = false; PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class); AndroidPackage pkg = pmInt.getPackage(packageName); if (pkg != null) { @@ -4509,15 +4536,15 @@ public class AppOpsService extends IAppOpsService.Stub { if (!isAttributionTagValid) { AndroidPackage proxyPkg = proxyPackageName != null ? pmInt.getPackage(proxyPackageName) : null; - boolean foundInProxy = isAttributionInPackage(proxyPkg, attributionTag); + // Re-check in proxy. + isAttributionTagValid = isAttributionInPackage(proxyPkg, attributionTag); String msg; - if (pkg != null && foundInProxy) { + if (pkg != null && isAttributionTagValid) { msg = "attributionTag " + attributionTag + " declared in manifest of the proxy" + " package " + proxyPackageName + ", this is not advised"; } else if (pkg != null) { msg = "attributionTag " + attributionTag + " not declared in manifest of " + packageName; - bypass.setIsAttributionTagNotFound(true); } else { msg = "package " + packageName + " not found, can't check for " + "attributionTag " + attributionTag; @@ -4528,7 +4555,7 @@ public class AppOpsService extends IAppOpsService.Stub { SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, packageName, userId) && mPlatformCompat.isChangeEnabledByUid( SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, - callingUid) && !foundInProxy) { + callingUid) && !isAttributionTagValid) { Slog.e(TAG, msg); } else { Slog.e(TAG, msg); @@ -4546,7 +4573,7 @@ public class AppOpsService extends IAppOpsService.Stub { + otherUidMessage); } - return bypass; + return new PackageVerificationResult(bypass, isAttributionTagValid); } private boolean isAttributionInPackage(@Nullable AndroidPackage pkg, @@ -4574,13 +4601,14 @@ public class AppOpsService extends IAppOpsService.Stub { * @param uid The uid the package belongs to * @param packageName The name of the package * @param attributionTag attribution tag + * @param isAttributionTagValid whether the given attribution tag is valid * @param bypass When to bypass certain op restrictions (can be null if edit == false) * @param edit If an ops does not exist, create the ops? * @return The ops */ private Ops getOpsLocked(int uid, String packageName, @Nullable String attributionTag, - @Nullable RestrictionBypass bypass, boolean edit) { + boolean isAttributionTagValid, @Nullable RestrictionBypass bypass, boolean edit) { UidState uidState = getUidStateLocked(uid, edit); if (uidState == null) { return null; @@ -4609,6 +4637,11 @@ public class AppOpsService extends IAppOpsService.Stub { if (attributionTag != null) { ops.knownAttributionTags.add(attributionTag); + if (isAttributionTagValid) { + ops.validAttributionTags.add(attributionTag); + } else { + ops.validAttributionTags.remove(attributionTag); + } } } @@ -4638,14 +4671,17 @@ public class AppOpsService extends IAppOpsService.Stub { * @param uid The uid the of the package * @param packageName The package name for which to get the state for * @param attributionTag The attribution tag + * @param isAttributionTagValid Whether the given attribution tag is valid * @param bypass When to bypass certain op restrictions (can be null if edit == false) * @param edit Iff {@code true} create the {@link Op} object if not yet created * * @return The {@link Op state} of the op */ private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, - @Nullable String attributionTag, @Nullable RestrictionBypass bypass, boolean edit) { - Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, edit); + @Nullable String attributionTag, boolean isAttributionTagValid, + @Nullable RestrictionBypass bypass, boolean edit) { + Ops ops = getOpsLocked(uid, packageName, attributionTag, isAttributionTagValid, bypass, + edit); if (ops == null) { return null; } @@ -6510,7 +6546,7 @@ public class AppOpsService extends IAppOpsService.Stub { } // TODO moltmann: Allow to check for attribution op activeness synchronized (AppOpsService.this) { - Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false); + Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, false, null, false); if (pkgOps == null) { return false; }