源码链接:AppOpsManager.java

AppOpsManager 是记录源头,应用入口,调用硬件使用权限时会调用 startOp 方法记录行为,同时也提供了行为等监听。

/**
     * @see #startOp(String, int, String, String, String)
     *
     * @hide
     */
    public int startOp(int op, int uid, @Nullable String packageName, boolean startIfModeDefault,
            @Nullable String attributionTag, @Nullable String message) {
        final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, attributionTag,
                message);
        if (mode == MODE_ERRORED) {
            throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
        }
        return mode;
    }
AppOpsManager.startOp()
private int startOpNoThrow(@NonNull IBinder token, int op, int uid, @NonNull String packageName,
            boolean startIfModeDefault, @Nullable String attributionTag, int virtualDeviceId,
            @Nullable String message, @AttributionFlags int attributionFlags,
            int attributionChainId) {
        try {
            collectNoteOpCallsForValidation(op);
            int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
            boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID;
            if (collectionMode == COLLECT_ASYNC) {
                if (message == null) {
                    // Set stack trace as default message
                    message = getFormattedStackTrace();
                    shouldCollectMessage = true;
                }
            }

            SyncNotedAppOp syncOp;
            if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
                syncOp = mService.startOperation(token, op, uid, packageName,
                    attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message,
                    shouldCollectMessage, attributionFlags, attributionChainId);
            } else {
                syncOp = mService.startOperationForDevice(token, op, uid, packageName,
                    attributionTag, virtualDeviceId, startIfModeDefault,
                    collectionMode == COLLECT_ASYNC, message, shouldCollectMessage,
                    attributionFlags, attributionChainId);
            }
            if (syncOp.getOpMode() == MODE_ALLOWED) {
                if (collectionMode == COLLECT_SELF) {
                    collectNotedOpForSelf(syncOp);
                } else if (collectionMode == COLLECT_SYNC) {
                    collectNotedOpSync(syncOp);
                }
            }

            return syncOp.getOpMode();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
AppOpsManager.startOpNoThrow()

ops系统服务负责处理相关逻辑,权限检查、状态更新、回调分发、存储记录等。

源码链接:AppOpsService.java

通过start方法,最后会执行到 AttributedOp 的 started 方法。

@Override
    public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
            @Nullable String packageName, @Nullable String attributionTag,
            boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
            String message, boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
            int attributionChainId) {
        return mCheckOpsDelegateDispatcher.startOperation(token, code, uid, packageName,
                attributionTag, Context.DEVICE_ID_DEFAULT, startIfModeDefault,
                shouldCollectAsyncNotedOp, message, shouldCollectMessage, attributionFlags,
                attributionChainId
        );
    }

    @Override
    public SyncNotedAppOp startOperationForDevice(IBinder token, int code, int uid,
            @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId,
            boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
            boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
            int attributionChainId) {
        return mCheckOpsDelegateDispatcher.startOperation(token, code, uid, packageName,
                attributionTag, virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp,
                message, shouldCollectMessage, attributionFlags, attributionChainId
        );
    }
AppOpsService.startOperation()

 mPolicy.startOperation 跟 startDelegateOperationImpl 是系统用于自定义拦截委托,允许系统组件(如 DevicePolicyManager)在 AppOps 原生逻辑执行之前插入自定义检查(setAppOpsPolicy)。

public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
                @Nullable String packageName, @NonNull String attributionTag, int virtualDeviceId,
                boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
                @Nullable String message, boolean shouldCollectMessage,
                @AttributionFlags int attributionFlags, int attributionChainId) {
            if (mPolicy != null) {
                if (mCheckOpsDelegate != null) {
                    return mPolicy.startOperation(token, code, uid, packageName, attributionTag,
                            virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
                            shouldCollectMessage, attributionFlags, attributionChainId,
                            this::startDelegateOperationImpl
                    );
                } else {
                    return mPolicy.startOperation(token, code, uid, packageName, attributionTag,
                            virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
                            shouldCollectMessage, attributionFlags, attributionChainId,
                            AppOpsService.this::startOperationImpl
                    );
                }
            } else if (mCheckOpsDelegate != null) {
                return startDelegateOperationImpl(token, code, uid, packageName, attributionTag,
                        virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
                        shouldCollectMessage, attributionFlags, attributionChainId
                );
            }
            return startOperationImpl(token, code, uid, packageName, attributionTag,
                    virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
                    shouldCollectMessage, attributionFlags, attributionChainId
            );
        }
AppOpsService.CheckOpsDelegateDispatcher.startOperation()

 startOperationImpl 主要验证解析,如果无效,返回 MODE_IGNORED 或者 MODE_ERRORED,最终调用 startOperationUnchecked。

private SyncNotedAppOp startOperationImpl(@NonNull IBinder clientId, int code, int uid,
            @Nullable String packageName, @Nullable String attributionTag, int virtualDeviceId,
            boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @NonNull String message,
            boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
            int attributionChainId) {
        String resolvedPackageName;
        if (!shouldUseNewCheckOp()) {
            verifyIncomingUid(uid);
            verifyIncomingOp(code);
            if (!isValidVirtualDeviceId(virtualDeviceId)) {
                Slog.w(TAG, "startOperationImpl returned MODE_IGNORED as virtualDeviceId "
                        + virtualDeviceId + " is invalid");
                return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
                        packageName);
            }
            if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
                return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                        packageName);
            }

            resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
            if (resolvedPackageName == null) {
                return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
                        packageName);
            }
        } else {
            // Note, this flag changes the behavior in this case:
            // invalid package is now IGNORE instead of ERROR for consistency
            resolvedPackageName = validateOpRequest(code, uid, packageName,
                    virtualDeviceId, true, "startOperation");
            if (resolvedPackageName == null) {
                return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
                        packageName);
            }
        }

        // As a special case for OP_RECORD_AUDIO_HOTWORD, OP_RECEIVE_AMBIENT_TRIGGER_AUDIO and
        // OP_RECORD_AUDIO_SANDBOXED which we use only for attribution purposes and not as a check,
        // also make sure that the caller is allowed to access the data gated by OP_RECORD_AUDIO.
        //
        // TODO: Revert this change before Android 12.
        int result = MODE_DEFAULT;
        if (code == OP_RECORD_AUDIO_HOTWORD || code == OP_RECEIVE_AMBIENT_TRIGGER_AUDIO
                || code == OP_RECORD_AUDIO_SANDBOXED) {
            result = checkOperation(OP_RECORD_AUDIO, uid, packageName);
            // Check result
            if (result != AppOpsManager.MODE_ALLOWED) {
                return new SyncNotedAppOp(result, code, attributionTag, packageName);
            }
        }
        // As a special case for OP_CAMERA_SANDBOXED.
        if (code == OP_CAMERA_SANDBOXED) {
            result = checkOperation(OP_CAMERA, uid, packageName);
            // Check result
            if (result != AppOpsManager.MODE_ALLOWED) {
                return new SyncNotedAppOp(result, code, attributionTag, packageName);
            }
        }

        return startOperationUnchecked(clientId, code, uid, packageName, attributionTag,
                virtualDeviceId, Process.INVALID_UID, null, null, Context.DEVICE_ID_DEFAULT,
                OP_FLAG_SELF, startIfModeDefault, shouldCollectAsyncNotedOp, message,
                shouldCollectMessage, attributionFlags, attributionChainId);
    }
AppOpsService.startOperationImpl()

 

private SyncNotedAppOp startProxyOperationImpl(@NonNull IBinder clientId, int code,
            @NonNull AttributionSource attributionSource,
            boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
            boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags
            int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags,
            int attributionChainId) {
        final int proxyUid = attributionSource.getUid();
        final String proxyPackageName = attributionSource.getPackageName();
        final String proxyAttributionTag = attributionSource.getAttributionTag();
        final int proxyVirtualDeviceId = attributionSource.getDeviceId();

        final int proxiedUid = attributionSource.getNextUid();
        final String proxiedPackageName = attributionSource.getNextPackageName();
        final String proxiedAttributionTag = attributionSource.getNextAttributionTag();
        final int proxiedVirtualDeviceId = attributionSource.getNextDeviceId();

        verifyIncomingProxyUid(attributionSource);
        verifyIncomingOp(code);
        if (!isValidVirtualDeviceId(proxyVirtualDeviceId)) {
            Slog.w(
                    TAG,
                    "startProxyOperationImpl returned MODE_IGNORED as proxyVirtualDeviceId "
                            + proxyVirtualDeviceId
                            + " is invalid");
            return new SyncNotedAppOp(
                    AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag, proxiedPackageName);
        }
        if (!isValidVirtualDeviceId(proxiedVirtualDeviceId)) {
            Slog.w(
                    TAG,
                    "startProxyOperationImpl returned MODE_IGNORED as proxiedVirtualDeviceId "
                            + proxiedVirtualDeviceId
                            + " is invalid");
            return new SyncNotedAppOp(
                    AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag, proxiedPackageName);
        }
        if (!isIncomingPackageValid(proxyPackageName, UserHandle.getUserId(proxyUid))
                || !isIncomingPackageValid(proxiedPackageName, UserHandle.getUserId(proxiedUid))) {
            return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, proxiedAttributionTag,
                    proxiedPackageName);
        }

        boolean isCallerTrusted = isCallerAndAttributionTrusted(attributionSource);
        skipProxyOperation = isCallerTrusted && skipProxyOperation;

        String resolvedProxyPackageName = AppOpsManager.resolvePackageName(proxyUid,
                proxyPackageName);
        if (resolvedProxyPackageName == null) {
            return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag,
                    proxiedPackageName);
        }

        final boolean isChainTrusted = isCallerTrusted
                && attributionChainId != ATTRIBUTION_CHAIN_ID_NONE
                && ((proxyAttributionFlags & ATTRIBUTION_FLAG_TRUSTED) != 0
                || (proxiedAttributionFlags & ATTRIBUTION_FLAG_TRUSTED) != 0);
        final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
        final boolean isProxyTrusted = mContext.checkPermission(
                Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
                == PackageManager.PERMISSION_GRANTED || isSelfBlame
                || isChainTrusted;

        String resolvedProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
                proxiedPackageName);
        if (resolvedProxiedPackageName == null) {
            return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag,
                    proxiedPackageName);
        }

        final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
                : AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;

        if (!skipProxyOperation) {
            // Test if the proxied operation will succeed before starting the proxy operation
            final SyncNotedAppOp testProxiedOp = startOperationDryRun(code,
                    proxiedUid, resolvedProxiedPackageName, proxiedAttributionTag,
                    proxiedVirtualDeviceId, proxyUid, resolvedProxyPackageName, proxiedFlags,
                    startIfModeDefault);

            if (!shouldStartForMode(testProxiedOp.getOpMode(), startIfModeDefault)) {
                return testProxiedOp;
            }

            final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
                    : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;

            final SyncNotedAppOp proxyAppOp = startOperationUnchecked(clientId, code, proxyUid,
                    resolvedProxyPackageName, proxyAttributionTag, proxyVirtualDeviceId,
                    Process.INVALID_UID, null, null, Context.DEVICE_ID_DEFAULT, proxyFlags,
                    startIfModeDefault, !isProxyTrusted, "proxy " + message,
                    shouldCollectMessage, proxyAttributionFlags, attributionChainId);
            if (!shouldStartForMode(proxyAppOp.getOpMode(), startIfModeDefault)) {
                return proxyAppOp;
            }
        }

        return startOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName,
                proxiedAttributionTag, proxiedVirtualDeviceId, proxyUid, resolvedProxyPackageName,
                proxyAttributionTag, proxyVirtualDeviceId, proxiedFlags, startIfModeDefault,
                shouldCollectAsyncNotedOp, message, shouldCollectMessage, proxiedAttributionFlags,
                attributionChainId);
    }
AppOpsService.startProxyOperationImpl()

 

private SyncNotedAppOp startOperationUnchecked(IBinder clientId, int code, int uid,
            @NonNull String packageName, @Nullable String attributionTag, int virtualDeviceId,
            int proxyUid, String proxyPackageName, @Nullable String proxyAttributionTag,
            int proxyVirtualDeviceId, @OpFlags int flags, boolean startIfModeDefault,
            boolean shouldCollectAsyncNotedOp, @Nullable String message,
            boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
            int attributionChainId) {
        PackageVerificationResult pvr;
        try {
            pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyUid, proxyPackageName);
            if (!pvr.isAttributionTagValid) {
                attributionTag = null;
            }
        } catch (SecurityException e) {
            logVerifyAndGetBypassFailure(uid, e, "startOperation");
            return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                    packageName);
        }
        if (proxyAttributionTag != null
                && !isAttributionTagDefined(packageName, proxyPackageName, proxyAttributionTag)) {
            proxyAttributionTag = null;
        }

        boolean isRestricted = false;
        int startType = START_TYPE_FAILED;
        synchronized (this) {
            final Ops ops = getOpsLocked(uid, packageName, attributionTag,
                    pvr.isAttributionTagValid, pvr.bypass, /* edit */ true);
            if (ops == null) {
                scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
                        virtualDeviceId, flags, AppOpsManager.MODE_IGNORED, startType,
                        attributionFlags, attributionChainId);
                if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
                        + " package " + packageName + " flags: "
                        + AppOpsManager.flagsToString(flags));
                return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                        packageName);
            }
            final Op op = getOpLocked(ops, code, uid, true);
            final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag,
                    getPersistentDeviceIdForOp(virtualDeviceId, code));
            final UidState uidState = ops.uidState;
            isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag,
                    virtualDeviceId, pvr.bypass, false);
            final int switchCode = AppOpsManager.opToSwitch(code);

            int rawUidMode;
            if (isOpAllowedForUid(uid)) {
                // Op is always allowed for the UID, do nothing.

                // 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.
            } else if ((rawUidMode =
                    mAppOpsCheckingService.getUidMode(
                            uidState.uid, getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
                            switchCode))
                    != AppOpsManager.opToDefaultMode(switchCode)) {
                final int uidMode = uidState.evalMode(code, rawUidMode);
                if (!shouldStartForMode(uidMode, startIfModeDefault)) {
                    if (DEBUG) {
                        Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
                                + switchCode + " (" + code + ") uid " + uid + " package "
                                + packageName + " flags: "
                                + AppOpsManager.flagsToString(flags));
                    }
                    attributedOp.rejected(uidState.getState(), flags);
                    scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
                            virtualDeviceId, flags, uidMode, startType, attributionFlags,
                            attributionChainId);
                    return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
                }
            } else {
                final Op switchOp =
                        switchCode != code ? getOpLocked(ops, switchCode, uid, true) : op;
                final int mode =
                        switchOp.uidState.evalMode(
                                switchOp.op,
                                mAppOpsCheckingService.getPackageMode(
                                        switchOp.packageName,
                                        switchOp.op,
                                        UserHandle.getUserId(switchOp.uid)));
                if (mode != AppOpsManager.MODE_ALLOWED
                        && (!startIfModeDefault || mode != MODE_DEFAULT)) {
                    if (DEBUG) {
                        Slog.d(TAG, "startOperation: reject #" + mode + " for code "
                                + switchCode + " (" + code + ") uid " + uid + " package "
                                + packageName + " flags: "
                                + AppOpsManager.flagsToString(flags));
                    }
                    attributedOp.rejected(uidState.getState(), flags);
                    scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
                            virtualDeviceId, flags, mode, startType, attributionFlags,
                            attributionChainId);
                    return new SyncNotedAppOp(mode, code, attributionTag, packageName);
                }
            }

            if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
                    + " package " + packageName + " restricted: " + isRestricted
                    + " flags: " + AppOpsManager.flagsToString(flags));
            try {
                if (isRestricted) {
                    attributedOp.createPaused(clientId, virtualDeviceId, proxyUid, proxyPackageName,
                            proxyAttributionTag,
                            getPersistentDeviceIdForOp(proxyVirtualDeviceId, code),
                            uidState.getState(), flags, attributionFlags, attributionChainId);
                } else {
                    attributedOp.started(clientId, virtualDeviceId, proxyUid, proxyPackageName,
                            proxyAttributionTag,
                            getPersistentDeviceIdForOp(proxyVirtualDeviceId, code),
                            uidState.getState(), flags, attributionFlags, attributionChainId);
                    startType = START_TYPE_STARTED;
                }
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
            scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, virtualDeviceId,
                    flags, isRestricted ? MODE_IGNORED : MODE_ALLOWED, startType, attributionFlags,
                    attributionChainId);
        }

        if (shouldCollectAsyncNotedOp && !isRestricted) {
            collectAsyncNotedOp(uid, packageName, code, attributionTag, AppOpsManager.OP_FLAG_SELF,
                    message, shouldCollectMessage, 1);
        }

        return new SyncNotedAppOp(isRestricted ? MODE_IGNORED : MODE_ALLOWED, code, attributionTag,
                packageName);
    }
AppOpsService.startOperationUnchecked()

在started方法对具体行为进行了存储 。

源码链接:AttributedOp.java

/**
     * Update state when start was called
     *
     * @param clientId            Id of the startOp caller
     * @param virtualDeviceId     The virtual device 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 proxyDeviceId       The device id of the proxy app
     * @param uidState            UID state of the app startOp is called for
     * @param flags               The proxy flags
     * @param attributionFlags    The attribution flags associated with this operation.
     * @param attributionChainId  The if of the attribution chain this operations is a part of
     */
    public void started(@NonNull IBinder clientId, int virtualDeviceId, int proxyUid,
            @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
            @Nullable String proxyDeviceId, @AppOpsManager.UidState int uidState,
            @AppOpsManager.OpFlags int flags, @AppOpsManager.AttributionFlags int attributionFlags,
            int attributionChainId) throws RemoteException {
        startedOrPaused(clientId, virtualDeviceId, proxyUid, proxyPackageName, proxyAttributionTag,
                proxyDeviceId, uidState, flags, attributionFlags, attributionChainId, false,
                true);
    }

    @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService
    private void startedOrPaused(@NonNull IBinder clientId, int virtualDeviceId, int proxyUid,
            @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
            @Nullable String proxyDeviceId, @AppOpsManager.UidState int uidState,
            @AppOpsManager.OpFlags int flags, @AppOpsManager.AttributionFlags int attributionFlags,
            int attributionChainId, boolean triggeredByUidStateChange, boolean isStarted)
            throws RemoteException {
        if (!triggeredByUidStateChange && !parent.isRunning() && isStarted) {
            mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
                    parent.packageName, tag, virtualDeviceId, true, attributionFlags,
                    attributionChainId);
        }

        if (isStarted && mInProgressEvents == null) {
            mInProgressEvents = new ArrayMap<>(1);
        } else if (!isStarted && mPausedInProgressEvents == null) {
            mPausedInProgressEvents = new ArrayMap<>(1);
        }
        ArrayMap<IBinder, InProgressStartOpEvent> events = isStarted
                ? mInProgressEvents : mPausedInProgressEvents;

        long startTime = System.currentTimeMillis();
        InProgressStartOpEvent event = events.get(clientId);
        if (event == null) {
            event = mAppOpsService.mInProgressStartOpEventPool.acquire(startTime,
                    SystemClock.elapsedRealtime(), clientId, tag, virtualDeviceId,
                    PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId),
                    proxyUid, proxyPackageName, proxyAttributionTag, proxyDeviceId, uidState, flags,
                    attributionFlags, attributionChainId);
            events.put(clientId, event);
        } else {
            if (uidState != event.getUidState()) {
                onUidStateChanged(uidState);
            }
        }

        event.mNumUnfinishedStarts++;

        if (isStarted) {
            mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                    parent.packageName, persistentDeviceId, tag, uidState, flags, startTime,
                    attributionFlags, attributionChainId, 1);
        }
    }
AttributedOp.started()

在构造函数中可以看到 mHistoricalRegistry 的初始化。

@VisibleForTesting
    public AppOpsService(File recentAccessesFile, File storageFile, Handler handler,
            Context context) {
        if (Flags.enableAllSqliteAppopsAccesses()) {
            mHistoricalRegistry = new HistoricalRegistrySql(context);
        } else {
            mHistoricalRegistry = new LegacyHistoricalRegistry(this, context);
        }
    }
mHistoricalRegistry

HistoricalRegistrySql:使用sql来存储行为,目前是空实现,未来计划替代 XML 实现以提升查询性能。

LegacyHistoricalRegistry:使用xml来存储行为。

DiscreteOpsXmlRegistry.java:使用xml来存储行为

@Override
    public void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
            @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
            @OpFlags int flags, long accessTime,
            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
            int accessCount) {
        synchronized (mInMemoryLock) {
            if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                if (!isPersistenceInitializedMLocked()) {
                    Slog.v(LOG_TAG, "Interaction before persistence initialized");
                    return;
                }
                getUpdatedPendingHistoricalOpsMLocked(
                        System.currentTimeMillis()).increaseAccessCount(op, uid, packageName,
                        attributionTag, uidState, flags, accessCount);

                mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
                        attributionTag, flags, uidState, accessTime, -1, attributionFlags,
                        attributionChainId);
            }
        }
    }
LegacyHistoricalRegistry.incrementOpAccessedCount()

 

private @NonNull HistoricalOps getUpdatedPendingHistoricalOpsMLocked(long now) {
        if (mCurrentHistoricalOps != null) {
            final long remainingTimeMillis = mNextPersistDueTimeMillis - now;
            if (remainingTimeMillis > mBaseSnapshotInterval) {
                // If time went backwards we need to push history to the future with the
                // overflow over our snapshot interval. If time went forward do nothing
                // as we would naturally push history into the past on the next write.
                mPendingHistoryOffsetMillis = remainingTimeMillis - mBaseSnapshotInterval;
            }
            final long elapsedTimeMillis = mBaseSnapshotInterval - remainingTimeMillis;
            mCurrentHistoricalOps.setEndTime(elapsedTimeMillis);
            if (remainingTimeMillis > 0) {
                if (DEBUG) {
                    Slog.i(LOG_TAG, "Returning current in-memory state");
                }
                return mCurrentHistoricalOps;
            }
            if (mCurrentHistoricalOps.isEmpty()) {
                mCurrentHistoricalOps.setBeginAndEndTime(0, 0);
                mNextPersistDueTimeMillis = now + mBaseSnapshotInterval;
                return mCurrentHistoricalOps;
            }
            // The current batch is full, so persist taking into account overdue persist time.
            mCurrentHistoricalOps.offsetBeginAndEndTime(mBaseSnapshotInterval);
            mCurrentHistoricalOps.setBeginTime(mCurrentHistoricalOps.getEndTimeMillis()
                    - mBaseSnapshotInterval);
            final long overdueTimeMillis = Math.abs(remainingTimeMillis);
            mCurrentHistoricalOps.offsetBeginAndEndTime(overdueTimeMillis);
            schedulePersistHistoricalOpsMLocked(mCurrentHistoricalOps);
        }
        // The current batch is in the future, i.e. not complete yet.
        mCurrentHistoricalOps = new HistoricalOps(0, 0);
        mNextPersistDueTimeMillis = now + mBaseSnapshotInterval;
        if (DEBUG) {
            Slog.i(LOG_TAG, "Returning new in-memory state");
        }
        return mCurrentHistoricalOps;
    }
LegacyHistoricalRegistry.getUpdatedPendingHistoricalOpsMLocked()

schedulePersistHistoricalOpsMLocked 最后调用 LegacyHistoricalRegistry 的 persistPendingHistory

private void schedulePersistHistoricalOpsMLocked(@NonNull HistoricalOps ops) {
        final Message message = PooledLambda.obtainMessage(
                LegacyHistoricalRegistry::persistPendingHistory, LegacyHistoricalRegistry.this);
        message.what = MSG_WRITE_PENDING_HISTORY;
        IoThread.getHandler().sendMessage(message);
        mPendingWrites.offerFirst(ops);
    }
LegacyHistoricalRegistry.schedulePersistHistoricalOpsMLocked()

 

@Override
    public void persistPendingHistory() {
        final List<HistoricalOps> pendingWrites;
        synchronized (mOnDiskLock) {
            synchronized (mInMemoryLock) {
                pendingWrites = new ArrayList<>(mPendingWrites);
                mPendingWrites.clear();
                if (mPendingHistoryOffsetMillis != 0) {
                    resampleHistoryOnDiskInMemoryDMLocked(mPendingHistoryOffsetMillis);
                    mPendingHistoryOffsetMillis = 0;
                }
            }
            persistPendingHistory(pendingWrites);
        }
        mDiscreteRegistry.writeAndClearOldAccessHistory();
    }
LegacyHistoricalRegistry.persistPendingHistory()

最后一行看到 mDiscreteRegistry.writeAndClearOldAccessHistory()

void writeAndClearOldAccessHistory() {
        synchronized (mOnDiskLock) {
            if (mDiscreteAccessDir == null) {
                Slog.d(TAG, "State not saved - persistence not initialized.");
                return;
            }
            DiscreteOps discreteOps;
            synchronized (mInMemoryLock) {
                discreteOps = mDiscreteOps;
                mDiscreteOps = new DiscreteOps(discreteOps.mChainIdOffset);
                mCachedOps = null;
            }
            deleteOldDiscreteHistoryFilesLocked();
            if (!discreteOps.isEmpty()) {
                persistDiscreteOpsLocked(discreteOps);
            }
        }
    }
DiscreteOpsXmlRegistry.writeAndClearOldAccessHistory()

具体写入内存

private void persistDiscreteOpsLocked(DiscreteOps discreteOps) {
        long currentTimeStamp = Instant.now().toEpochMilli();
        final AtomicFile file = new AtomicFile(new File(mDiscreteAccessDir,
                currentTimeStamp + DISCRETE_HISTORY_FILE_SUFFIX));
        FileOutputStream stream = null;
        try {
            stream = file.startWrite();
            discreteOps.writeToStream(stream);
            file.finishWrite(stream);
        } catch (Throwable t) {
            Slog.e(TAG,
                    "Error writing timeline state: " + t.getMessage() + " "
                            + Arrays.toString(t.getStackTrace()));
            if (stream != null) {
                file.failWrite(stream);
            }
        }
    }
DiscreteOpsXmlRegistry.persistDiscreteOpsLocked()

具体权限通过枚举定义

/** @hide Causing GPS to run. */
@UnsupportedAppUsage
public static final int OP_GPS = AppOpEnums.APP_OP_GPS;

app_op_enums.proto:AppOpEnums

ops通过 startWatchingActive 来监听权限使用行为,通过 startWatchingMode 来监听权限开关,所以当权限在使用时,或者授权跟关闭授权时,都会触发回调。

 

ops_20260908_b16cca

 

posted on 2026-09-08 15:10  翻滚的咸鱼  阅读(17)  评论(0)    收藏  举报