源码链接: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; }
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(); } }
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 ); }
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 ); }
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); }
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); }
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); }
在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); } }
在构造函数中可以看到 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); } }
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); } } }
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; }
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); }
@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(); }
最后一行看到 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); } } }
具体写入内存
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); } } }
具体权限通过枚举定义
/** @hide Causing GPS to run. */@UnsupportedAppUsagepublic static final int OP_GPS = AppOpEnums.APP_OP_GPS;
app_op_enums.proto:AppOpEnums
ops通过 startWatchingActive 来监听权限使用行为,通过 startWatchingMode 来监听权限开关,所以当权限在使用时,或者授权跟关闭授权时,都会触发回调。

浙公网安备 33010602011771号