惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Check Point Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 聂微东
月光博客
月光博客
博客园 - 司徒正美
爱范儿
爱范儿
aimingoo的专栏
aimingoo的专栏
量子位
Recent Announcements
Recent Announcements
V
V2EX
P
Proofpoint News Feed
小众软件
小众软件
云风的 BLOG
云风的 BLOG
腾讯CDC
宝玉的分享
宝玉的分享
Microsoft Azure Blog
Microsoft Azure Blog
大猫的无限游戏
大猫的无限游戏
Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
A
About on SuperTechFans
B
Blog
博客园_首页
GbyAI
GbyAI
博客园 - Franky

博客园 - 翻滚的咸鱼

TaskView方案框架原理 视频解码器问题 常用命令 扫光动效 引导记录 性能优化 View共享动效 常用脚本文件 Launcher 卡片框架多模块集成 Launcher 桌面源码笔记二 Launcher 桌面源码笔记一 氛围灯动态屏保取色方案二 氛围灯动态屏保取色方案一 自定义最近任务管理器 anr问题 TV RecyclerView 焦点处理笔记 多类型适配器 多屏下字体自动取色 阻尼拉伸方案 图片区域点击处理 RecyclerView设置默认焦点跟多页面焦点抢占 TV RecyclerView 滑动后操作保持落焦在左侧第一个View Fragment翻盖动效 当个View下,使用Drawable入场退场动画
Ops权限行为记录源码解析
翻滚的咸鱼 · 2026-09-08 · via 博客园 - 翻滚的咸鱼

 源码链接: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()

具体权限通过枚举定义

@UnsupportedAppUsage

public static final int OP_GPS = AppOpEnums.APP_OP_GPS;

app_op_enums.proto:AppOpEnums

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

ops_20260908_b16cca