Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 947cb752 authored by Erik Wolsheimer's avatar Erik Wolsheimer
Browse files

Store whether attribution tag is valid separate from bypass

Bug: 189484870
Bug: 190073375
Test: Manual
Change-Id: Iccc0e887d28c837a8ef4099af611794590af25c5
parent 38101db3
Loading
Loading
Loading
Loading
+0 −13
Original line number Diff line number Diff line
@@ -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);
    }

+81 −45
Original line number Diff line number Diff line
@@ -667,12 +667,30 @@ public class AppOpsService extends IAppOpsService.Stub {
        /** Lazily populated cache of attributionTags of this package */
        final @NonNull ArraySet<String> knownAttributionTags = new ArraySet<>();

        /**
         * Lazily populated cache of <b>valid</b> attributionTags of this package, a set smaller
         * than or equal to {@link #knownAttributionTags}.
         */
        final @NonNull ArraySet<String> 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<ModeCallback> 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;
            }