概述
從 Android 6.0(API 級(jí)別 23)開(kāi)始危虱,Android 引入了兩項(xiàng)省電功能,通過(guò)管理應(yīng)用在設(shè)備未連接至電源時(shí)的行為方式,幫助用戶(hù)延長(zhǎng)電池壽命。
應(yīng)用待機(jī)模式會(huì)延遲用戶(hù)近期未與之交互的應(yīng)用的后臺(tái)網(wǎng)絡(luò)活動(dòng)。
應(yīng)用待機(jī)模式允許系統(tǒng)判定應(yīng)用在用戶(hù)未主動(dòng)使用它時(shí)是否處于閑置狀態(tài)徘键。當(dāng)用戶(hù)有一段時(shí)間未觸摸應(yīng)用時(shí),系統(tǒng)便會(huì)作出此判定,以下條件均不適用:
1.用戶(hù)明確啟動(dòng)應(yīng)用酱固。
2.應(yīng)用當(dāng)前有一個(gè)進(jìn)程在前臺(tái)運(yùn)行(作為活動(dòng)或前臺(tái)服務(wù),或者正在由其他活動(dòng)或前臺(tái)服務(wù)使用)头朱。
注意:您只能將前臺(tái)服務(wù)用于用戶(hù)希望系統(tǒng)立即執(zhí)行或不中斷的任務(wù)运悲。 此類(lèi)情況包括將照片上傳到社交媒體,或者即使在音樂(lè)播放器應(yīng)用不在前臺(tái)運(yùn)行時(shí)也能播放音樂(lè)项钮。您不應(yīng)該只是為了阻止系統(tǒng)判定您的應(yīng)用處于閑置狀態(tài)而啟動(dòng)前臺(tái)服務(wù)班眯。
3.應(yīng)用生成用戶(hù)可在鎖定屏幕或通知欄中看到的通知。
4.應(yīng)用是正在使用中的設(shè)備管理應(yīng)用(例如設(shè)備政策控制器)烁巫。雖然設(shè)備管理應(yīng)用通常在后臺(tái)運(yùn)行署隘,但永遠(yuǎn)不會(huì)進(jìn)入應(yīng)用待機(jī)模式,因?yàn)樗鼈儽仨毐3挚捎眯匝窍叮员汶S時(shí)從服務(wù)器接收策略磁餐。
當(dāng)用戶(hù)將設(shè)備插入電源時(shí),系統(tǒng)會(huì)從待機(jī)狀態(tài)釋放應(yīng)用阿弃,允許它們自由訪問(wèn)網(wǎng)絡(luò)并執(zhí)行任何待處理的作業(yè)和同步诊霹。如果設(shè)備長(zhǎng)時(shí)間處于閑置狀態(tài)羞延,系統(tǒng)將允許閑置應(yīng)用訪問(wèn)網(wǎng)絡(luò),頻率大約每天一次脾还。
adb查詢(xún)
adb shell dumpsys usagestats
app接口
UsageStatsManager stats = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE)
代碼簡(jiǎn)要分析
frameworks/base/services/core/java/com/android/server/SystemServer.java
frameworks/base/services/core/java/com/android/server/usage/UsageStatsService.java
frameworks/base/services/core/java/com/android/server/usage/AppIdleHistory.java
注:
1)7.1
1.啟動(dòng)
SystemServer.java::ServerThread:run --> startCoreServices
private void startCoreServices() {
···
// Tracks application usage stats.
mSystemServiceManager.startService(UsageStatsService.class);
mActivityManagerService.setUsageStatsManager(
LocalServices.getService(UsageStatsManagerInternal.class));
···
}
2.UsageStatsService分析
1)UsageStatsService extends SystemService
2)onStart()
public void onStart() {
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
mPackageManager = getContext().getPackageManager();
mHandler = new H(BackgroundThread.get().getLooper());
File systemDataDir = new File(Environment.getDataDirectory(), "system");
mUsageStatsDir = new File(systemDataDir, "usagestats");
mUsageStatsDir.mkdirs();
if (!mUsageStatsDir.exists()) {
throw new IllegalStateException("Usage stats directory does not exist: "
+ mUsageStatsDir.getAbsolutePath());
}
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_STARTED);
getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
null, mHandler);
IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
packageFilter.addDataScheme("package");
getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
null, mHandler);
//config_enableAutoPowerModes跟Doze模式共用一個(gè)配置文件肴楷,釋放開(kāi)啟app standby功能
mAppIdleEnabled = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enableAutoPowerModes);
if (mAppIdleEnabled) {
IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
getContext().registerReceiver(new DeviceStateReceiver(), deviceStates);
}
synchronized (mLock) {
cleanUpRemovedUsersLocked();
mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
}
//開(kāi)機(jī)時(shí)長(zhǎng)記錄
mRealTimeSnapshot = SystemClock.elapsedRealtime();
//系統(tǒng)時(shí)長(zhǎng)記錄,可通過(guò)網(wǎng)絡(luò)荠呐、手動(dòng)切換的時(shí)間格式
mSystemTimeSnapshot = System.currentTimeMillis();
//注冊(cè)共享服務(wù)
publishLocalService(UsageStatsManagerInternal.class, new LocalService());
publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
}
onStart做了一些初始化及監(jiān)聽(tīng)業(yè)務(wù)
3)onBootPhase
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
// Observe changes to the threshold
// 初始化appidle臨界值的變化赛蔫。adb查詢(xún)方式:adb shell settings get global app_idle_constants
// 格式:idle_duration2=long,wallclock_threshold=long,parole_interval=long,parole_duration=long
// long為數(shù)據(jù)類(lèi)型。臨界值可自定義
SettingsObserver settingsObserver = new SettingsObserver(mHandler);
settingsObserver.registerObserver();
settingsObserver.updateSettings();
mAppWidgetManager = getContext().getSystemService(AppWidgetManager.class);
mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
mBatteryStats = IBatteryStats.Stub.asInterface(
ServiceManager.getService(BatteryStats.SERVICE_NAME));
mDisplayManager = (DisplayManager) getContext().getSystemService(
Context.DISPLAY_SERVICE);
mPowerManager = getContext().getSystemService(PowerManager.class);
//監(jiān)聽(tīng)屏幕狀態(tài)
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
synchronized (mLock) {
//更新appidle數(shù)據(jù)
mAppIdleHistory.updateDisplayLocked(isDisplayOn(), SystemClock.elapsedRealtime());
}
if (mPendingOneTimeCheckIdleStates) {
//檢查各個(gè)idle狀態(tài)
postOneTimeCheckIdleStates();
}
mSystemServicesReady = true;
} else if (phase == PHASE_BOOT_COMPLETED) {
setChargingState(getContext().getSystemService(BatteryManager.class).isCharging());
}
}
如果說(shuō)onStart()初始化業(yè)務(wù)泥张,那onBootPhase是更新業(yè)務(wù)呵恢,核心點(diǎn)是mAppIdleHistory中數(shù)據(jù)的更新
3.臨界數(shù)據(jù)分析
//應(yīng)用是否進(jìn)入idle狀態(tài)的臨界值,這里比較的是亮屏狀態(tài)的時(shí)間媚创。默認(rèn)12小時(shí)
long mAppIdleScreenThresholdMillis;
//定時(shí)檢查app idle狀態(tài)渗钉。默認(rèn)3小時(shí)
long mCheckIdleIntervalMillis;
//應(yīng)用是否進(jìn)入idle狀態(tài)的臨界值,這里比較的累計(jì)開(kāi)機(jī)狀態(tài)的時(shí)間钞钙。默認(rèn)兩天
long mAppIdleWallclockThresholdMillis;
//相鄰 2 次進(jìn)入假釋狀態(tài)的時(shí)間間隔鳄橘。默認(rèn)1天
long mAppIdleParoleIntervalMillis;
//假釋狀態(tài)的持續(xù)時(shí)間 10分鐘
long mAppIdleParoleDurationMillis;
以下是數(shù)據(jù)的來(lái)源:
private class SettingsObserver extends ContentObserver {
/**
* This flag has been used to disable app idle on older builds with bug b/26355386.
*/
@Deprecated
private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
private static final String KEY_IDLE_DURATION = "idle_duration2";
private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
private static final String KEY_PAROLE_INTERVAL = "parole_interval";
private static final String KEY_PAROLE_DURATION = "parole_duration";
private final KeyValueListParser mParser = new KeyValueListParser(',');
SettingsObserver(Handler handler) {
super(handler);
}
void registerObserver() {
getContext().getContentResolver().registerContentObserver(Settings.Global.getUriFor(
Settings.Global.APP_IDLE_CONSTANTS), false, this);
}
@Override
public void onChange(boolean selfChange) {
updateSettings();
postOneTimeCheckIdleStates();
}
//數(shù)據(jù)來(lái)源
void updateSettings() {
synchronized (mLock) {
// Look at global settings for this.
// TODO: Maybe apply different thresholds for different users.
try {
mParser.setString(Settings.Global.getString(getContext().getContentResolver(),
Settings.Global.APP_IDLE_CONSTANTS));
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
// fallthrough, mParser is empty and all defaults will be returned.
}
// Default: 12 hours of screen-on time sans dream-time
mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
// Default: 24 hours between paroles
mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
mAppIdleScreenThresholdMillis);
}
}
}
4.案例分析
app的Activity調(diào)用onResume的數(shù)據(jù)采集(onPause也會(huì)進(jìn)行數(shù)據(jù)采集,這樣對(duì)等起來(lái))
1)ActivityStackSupervisor.reportResumedActivityLocked
boolean reportResumedActivityLocked(ActivityRecord r) {
final ActivityStack stack = r.task.stack;
if (isFocusedStack(stack)) {
mService.updateUsageStats(r, true);//調(diào)用AMS更新app使用情況
}
···
return false;
}
ActivityManagerService
void updateUsageStats(ActivityRecord component, boolean resumed) {
···
if (resumed) {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
UsageEvents.Event.MOVE_TO_FOREGROUND);//通知UsageStatsService
}
···
} else {
···
}
}
通過(guò)AMS通知UsageStatsService更新app數(shù)據(jù)
2)UsageStatsService接收通知進(jìn)行更新
private final class LocalService extends UsageStatsManagerInternal {
@Override
public void reportEvent(ComponentName component, int userId, int eventType) {
if (component == null) {
Slog.w(TAG, "Event reported without a component name");
return;
}
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = component.getPackageName();
event.mClass = component.getClassName();
// This will later be converted to system time.
event.mTimeStamp = SystemClock.elapsedRealtime();
event.mEventType = eventType;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();//有handler處理
}
···
}
class H extends Handler
case MSG_REPORT_EVENT:
reportEvent((UsageEvents.Event) msg.obj, msg.arg1);
break;
void reportEvent(UsageEvents.Event event, int userId) {
synchronized (mLock) {
//獲取timeNow的函數(shù)芒炼,做了兩件事情:1.直接給timeNow值 2.判斷系統(tǒng)的時(shí)間是否改變
final long timeNow = checkAndGetTimeLocked();
final long elapsedRealtime = SystemClock.elapsedRealtime();
convertToSystemTimeLocked(event);
//通過(guò)userId獲取對(duì)應(yīng)的UserUsageStatsService
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
// TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
// about apps that are on some kind of whitelist anyway.
//判斷app是否為idle狀態(tài)
final boolean previouslyIdle = mAppIdleHistory.isIdleLocked(
event.mPackage, userId, elapsedRealtime);
service.reportEvent(event);
// Inform listeners if necessary
if ((event.mEventType == Event.MOVE_TO_FOREGROUND
|| event.mEventType == Event.MOVE_TO_BACKGROUND
|| event.mEventType == Event.SYSTEM_INTERACTION
|| event.mEventType == Event.USER_INTERACTION)) {
mAppIdleHistory.reportUsageLocked(event.mPackage, userId, elapsedRealtime);
//如果以前是idle狀態(tài)瘫怜,則通知監(jiān)聽(tīng)者進(jìn)行策略更新
if (previouslyIdle) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
/* idle = */ 0, event.mPackage));
notifyBatteryStats(event.mPackage, userId, false);
}
}
}
}
這一階段是業(yè)務(wù)的核心之處,有4處可說(shuō)本刽,3處值得好好分析
第一處:timeNow
//方法的目的是監(jiān)測(cè)系統(tǒng)時(shí)間是否有更新
//如果有更新鲸湃,則通知各個(gè)UserUsageStatsService更新數(shù)據(jù)
private long checkAndGetTimeLocked() {
final long actualSystemTime = System.currentTimeMillis();//當(dāng)前系統(tǒng)時(shí)間
final long actualRealtime = SystemClock.elapsedRealtime();//當(dāng)前開(kāi)機(jī)時(shí)長(zhǎng)
//期望系統(tǒng)時(shí)間=當(dāng)前開(kāi)機(jī)時(shí)長(zhǎng) - 上一次記錄的開(kāi)機(jī)時(shí)長(zhǎng) + 上一次的系統(tǒng)時(shí)間
//換一句話(huà)說(shuō),期望系統(tǒng)時(shí)間=上一次的系統(tǒng)時(shí)間+距離當(dāng)前時(shí)間的時(shí)間差
final long expectedSystemTime = (actualRealtime - mRealTimeSnapshot) + mSystemTimeSnapshot;
//當(dāng)前系統(tǒng)時(shí)間-期望系統(tǒng)時(shí)間 > 時(shí)間臨界值
//說(shuō)明系統(tǒng)時(shí)間已經(jīng)有更新子寓,此時(shí)應(yīng)該通知各個(gè)UserUsageStatsService更新數(shù)據(jù)
final long diffSystemTime = actualSystemTime - expectedSystemTime;
if (Math.abs(diffSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS) {//
// The time has changed.
Slog.i(TAG, "Time changed in UsageStats by " + (diffSystemTime / 1000) + " seconds");
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
final UserUsageStatsService service = mUserState.valueAt(i);
service.onTimeChanged(expectedSystemTime, actualSystemTime);
}
mRealTimeSnapshot = actualRealtime;
mSystemTimeSnapshot = actualSystemTime;
}
return actualSystemTime;
}
第二處:判斷app是否為idle狀態(tài)
AppIdleHistory.isIdleLocked
public boolean isIdleLocked(String packageName, int userId, long elapsedRealtime) {
//從mIdleHistory獲取對(duì)應(yīng)userid的map
ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
//從userHistory中獲取對(duì)應(yīng)pkg的數(shù)據(jù)結(jié)構(gòu):PackageHistory
PackageHistory packageHistory =
getPackageHistoryLocked(userHistory, packageName, elapsedRealtime);
if (packageHistory == null) {
return false; // Default to not idle
} else {
//根據(jù)packageHistory判斷是否是idle狀態(tài)
return hasPassedThresholdsLocked(packageHistory, elapsedRealtime);
}
}
private boolean hasPassedThresholdsLocked(PackageHistory packageHistory, long elapsedRealtime) {
//臨界值終于發(fā)揮作用了:
//mScreenOnTimeThreshold對(duì)應(yīng)UsageStatsService.mAppIdleScreenThresholdMillis
//mScreenOnTimeThreshold對(duì)應(yīng)UsageStatsService.mAppIdleWallclockThresholdMillis
return (packageHistory.lastUsedScreenTime
<= getScreenOnTimeLocked(elapsedRealtime) - mScreenOnTimeThreshold)
&& (packageHistory.lastUsedElapsedTime
<= getElapsedTimeLocked(elapsedRealtime) - mElapsedTimeThreshold);
}
第三處:AppIdleHistory的數(shù)據(jù)記錄
/data/system/screen_on_time
0 --- 開(kāi)機(jī)之后的亮屏幕的時(shí)間
55017962 --- 從第一次開(kāi)機(jī)累計(jì)的開(kāi)機(jī)時(shí)長(zhǎng)
app idle一列表
/data/system/users/0/app_idle_stats.xml
UsageStatsManager.queryUsageStats 可查詢(xún)?nèi)缦滦畔?/data/system/usagestats/0/
例如:
jj(S0):/data/system/usagestats/0 # ls
daily monthly version weekly yearly
第四處:UsageStatsService通知監(jiān)聽(tīng)者
class H extends Handler
case MSG_INFORM_LISTENERS:
informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
break;
void informListeners(String packageName, int userId, boolean isIdle) {
for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
listener.onAppIdleStateChanged(packageName, userId, isIdle);
}
}
此時(shí)NetworkPolicyManagerService接收到命令:
NetworkPolicyManagerService在systemReady就注冊(cè)了監(jiān)聽(tīng):
mUsageStats.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
private class AppIdleStateChangeListener
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
try {
final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
if (LOGV) Log.v(TAG, "onAppIdleStateChanged(): uid=" + uid + ", idle=" + idle);
synchronized (mUidRulesFirstLock) {
updateRuleForAppIdleUL(uid);//控制網(wǎng)絡(luò)
updateRulesForPowerRestrictionsUL(uid);
}
} catch (NameNotFoundException nnfe) {
}
}
···
}
void updateRuleForAppIdleUL(int uid) {
if (!isUidValidForBlacklistRules(uid)) return;
int appId = UserHandle.getAppId(uid);
//不在白名單 + app在idle + 不是前臺(tái)業(yè)務(wù)
//ture 進(jìn)行網(wǎng)絡(luò)限制
//false 保持默認(rèn)狀態(tài)
if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
&& !isUidForegroundOnRestrictPowerUL(uid)) {
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
} else {
setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
}
}
private void setUidFirewallRule(int chain, int uid, int rule) {
···
try {
//通過(guò)NetworkManagementService控制網(wǎng)絡(luò)
mNetworkManager.setFirewallUidRule(chain, uid, rule);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting firewall uid rules", e);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
}
5.總結(jié)
1.UsageStatsService本身不帶白名單機(jī)制暗挑,它主要負(fù)責(zé)把信息分發(fā)出去,讓執(zhí)行者判斷斜友。例如NetworkPolicyManagerService根據(jù)自身的白名單做業(yè)務(wù)處理
2.UsageStatsService有控制開(kāi)關(guān)炸裆。framework-res.apk的config_enableAutoPowerModes
3.UsageStatsService是被動(dòng)調(diào)用者,例如AMS鲜屏、NMS調(diào)用它烹看,它根據(jù)實(shí)際事情分發(fā)給對(duì)應(yīng)的監(jiān)聽(tīng)者NetworkPolicy、JobSchedulerService等等
3.對(duì)于檢查app是否為idle墙歪,也有一些名單強(qiáng)制控制听系。具體如下:
private boolean isAppIdleFiltered(String packageName, int appId, int userId,
long elapsedRealtime) {
if (packageName == null) return false;
// If not enabled at all, of course nobody is ever idle.
if (!mAppIdleEnabled) { //app Standby開(kāi)關(guān)
return false;
}
if (appId < Process.FIRST_APPLICATION_UID) {//系統(tǒng)應(yīng)用沒(méi)有idle業(yè)務(wù)
// System uids never go idle.
return false;
}
if (packageName.equals("android")) { //包名為android的app沒(méi)有idle
// Nor does the framework (which should be redundant with the above, but for MR1 we will
// retain this for safety).
return false;
}
if (mSystemServicesReady) {
try {
// We allow all whitelisted apps, including those that don't want to be whitelisted
// for idle mode, because app idle (aka app standby) is really not as big an issue
// for controlling who participates vs. doze mode.
if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
return false;
}
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
if (isActiveDeviceAdmin(packageName, userId)) {
return false;
}
if (isActiveNetworkScorer(packageName)) {
return false;
}
if (mAppWidgetManager != null
&& mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {//widget
return false;
}
if (isDeviceProvisioningPackage(packageName)) {//開(kāi)機(jī)向?qū)? return false;
}
}
if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
return false;
}
// Check this last, as it is the most expensive check
// TODO: Optimize this by fetching the carrier privileged apps ahead of time
if (isCarrierApp(packageName)) {//通信app
return false;
}
return true;
}
原生設(shè)置案例
9.x
1.原生settings中,有一個(gè)界面:
UsageStatsActivity.java
統(tǒng)計(jì)apk使用情況
mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST,
cal.getTimeInMillis(), System.currentTimeMillis());
2.調(diào)用的是UsageStatsManager.java
UsageStatsManager usm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE)
ParceledListSlice<UsageStats> slice = mService.queryUsageStats(intervalType, beginTime,
endTime, mContext.getOpPackageName());
3.跨進(jìn)程調(diào)用的服務(wù)為UsageStatsService.java
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
List<UsageStats> list = service.queryUsageStats(bucketType, beginTime, endTime);
if (list == null) {
return null;
}
4.而最終實(shí)現(xiàn)的地方在UserUsageStatsService中
List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner);
}
5.數(shù)據(jù)真正處理的地方在UsageStatsDatabase中虹菲,也就是
/data/system/usagestats/用戶(hù)/daily或weekly或monthly或yearly
例如靠胜,/data/system/usagestats/0/daily或weekly或monthly或yearly
6.UsageStatsService中關(guān)聯(lián)了AppStandbyController,生成了一個(gè)對(duì)象。
AppStandbyController對(duì)象被處理,主要由UsageStatsService操作
7.AppStandbyController核心的是AppIdleHistory對(duì)象mAppIdleHistory浪漠,此對(duì)象
涉及到一個(gè)文件:app_idle_stats.xml陕习,具體目錄為:/data/system/users/用戶(hù)/app_idle_stats.xml
注:可以通過(guò)adb指令生成最新?tīng)顟B(tài)的xml文件
adb shell dumpsys usagestats flush
參考學(xué)習(xí)
https://developer.android.com/training/monitoring-device-state/doze-standby
https://blog.csdn.net/liu362732346/article/details/113107337
https://lishuaiqi.top/2017/01/03/UsageStats1-usageStatsServiceStartProcess/#1-new-