13 展訊Sprd設定-電池-應用事件變化資料收集(8.0 Android O)
阿新 • • 發佈:2018-11-21
1. UsageStatsService 的功能介紹
- frameworks/base/services/usage/java/com/android/server/usage/UsageStatsService.java
2. SystemServer 啟動 UsageStatsService
- frameworks/base/services/java/com/android/server/SystemServer.java
package com.android.server; public final class SystemServer { /** * Starts some essential services that are not tangled up in the bootstrap process. */ private void startCoreServices() { // 啟動 USS 服務 // Tracks application usage stats. traceBeginAndSlog("StartUsageService"); mSystemServiceManager.startService(UsageStatsService.class); mActivityManagerService.setUsageStatsManager( LocalServices.getService(UsageStatsManagerInternal.class)); traceEnd();
3. 啟動 UsageStatsService
- frameworks/base/services/usage/java/com/android/server/usage/UsageStatsService.java
package com.android.server.usage; /** * A service that collects, aggregates, and persists application usage data. * This data can be queried by apps that have been granted permission by AppOps. */ public class UsageStatsService extends SystemService implements UserUsageStatsService.StatsUpdatedListener {
3.1 UsageStatsService.onStart()
初始化引數新資訊
@Override public void onStart() { // 許可權管理服務 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); // 訪客模式服務或者多使用者服務 mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); // 包管理服務 mPackageManager = getContext().getPackageManager(); // 包管理服務的內部使用 mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mHandler = new H(BackgroundThread.get().getLooper()); // 檔案存放的路徑 data/system/usagestats 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); // 是否自啟動開啟省電模式 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(); } synchronized (mAppIdleLock) { // 應用待機下歷史資料 mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime()); } mRealTimeSnapshot = SystemClock.elapsedRealtime(); mSystemTimeSnapshot = System.currentTimeMillis(); // 初始化省電策略介面 mPowerControllerHelper = new PowerControllerHelper(); // 相當於 LocalServices.addService(LocalService()) publishLocalService(UsageStatsManagerInternal.class, new LocalService()); // 相當於 ServiceManager.addService(BinderService()) publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService()); }
3.2 UsageStatsService.publishLocalService
//引數中的LocalService繼承自PowerManagerInternal類
protected final <t> void publishLocalService(Class<t> type, T service) {
LocalServices.addService(type, service);
}
/**
* Adds a service instance of the specified interface to the global registry of local services.
*/
public static <t> void addService(Class<t> type, T service) {
synchronized (sLocalServiceObjects) {
if (sLocalServiceObjects.containsKey(type)) {
throw new IllegalStateException("Overriding service registration");
}
sLocalServiceObjects.put(type, service);
}
}
3.3 UsageStatsService.publishBinderService
//PMS中的BinderService繼承自IPowerManager.Stub,實現了IBinder介面
//name為PMS的名稱,power
protected final void publishBinderService(String name, IBinder service) {
publishBinderService(name, service, false);
}
protected final void publishBinderService(String name, IBinder service,
boolean allowIsolated) {
//呼叫ServiceManger的介面,實際上利用Binder通訊向Service Manger程序註冊服務
ServiceManager.addService(name, service, allowIsolated);
}
3.4 BinderService
private final class BinderService extends IUsageStatsManager.Stub {
private boolean hasPermission(String callingPackage) {
final int callingUid = Binder.getCallingUid();
// 系統 UIS 預設有許可權
if (callingUid == Process.SYSTEM_UID) {
return true;
}
// 是否註冊了使用使用者資料OP_GET_USAGE_STATS的許可權
final int mode = mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS,
callingUid, callingPackage);
if (mode == AppOpsManager.MODE_DEFAULT) {
// The default behavior here is to check if PackageManager has given the app
// permission.
return getContext().checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)
== PackageManager.PERMISSION_GRANTED;
}
return mode == AppOpsManager.MODE_ALLOWED;
}
// 上層呼叫的應用使用資料狀態介面,傳入型別:間隔型別、開始時間、結束時間、呼叫包名
@Override
public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime,
long endTime, String callingPackage) {
// 許可權檢查
if (!hasPermission(callingPackage)) {
return null;
}
final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
Binder.getCallingUid(), UserHandle.getCallingUserId());
final int userId = UserHandle.getCallingUserId();
final long token = Binder.clearCallingIdentity();
try {
final List<UsageStats> results = UsageStatsService.this.queryUsageStats(
userId, bucketType, beginTime, endTime, obfuscateInstantApps);
if (results != null) {
return new ParceledListSlice<>(results);
}
} finally {
Binder.restoreCallingIdentity(token);
}
return null;
}
@Override
public ParceledListSlice<ConfigurationStats> queryConfigurationStats(int bucketType,
long beginTime, long endTime, String callingPackage) throws RemoteException {
if (!hasPermission(callingPackage)) {
return null;
}
final int userId = UserHandle.getCallingUserId();
final long token = Binder.clearCallingIdentity();
try {
final List<ConfigurationStats> results =
UsageStatsService.this.queryConfigurationStats(userId, bucketType,
beginTime, endTime);
if (results != null) {
return new ParceledListSlice<>(results);
}
} finally {
Binder.restoreCallingIdentity(token);
}
return null;
}
// 上層呼叫的應用使用資料狀態介面,傳入型別:間隔型別、開始時間、結束時間、呼叫包名
@Override
public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) {
if (!hasPermission(callingPackage)) {
return null;
}
final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
Binder.getCallingUid(), UserHandle.getCallingUserId());
final int userId = UserHandle.getCallingUserId();
final long token = Binder.clearCallingIdentity();
try {
return UsageStatsService.this.queryEvents(userId, beginTime, endTime,
obfuscateInstantApps);
} finally {
Binder.restoreCallingIdentity(token);
}
}
// 是否應用為活動狀態
@Override
public boolean isAppInactive(String packageName, int userId) {
try {
userId = ActivityManager.getService().handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, true, "isAppInactive", null);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
Binder.getCallingUid(), userId);
final long token = Binder.clearCallingIdentity();
try {
return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId,
SystemClock.elapsedRealtime(), obfuscateInstantApps);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void setAppInactive(String packageName, boolean idle, int userId) {
final int callingUid = Binder.getCallingUid();
try {
userId = ActivityManager.getService().handleIncomingUser(
Binder.getCallingPid(), callingUid, userId, false, true,
"setAppInactive", null);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
getContext().enforceCallingPermission(Manifest.permission.CHANGE_APP_IDLE_STATE,
"No permission to change app idle state");
final long token = Binder.clearCallingIdentity();
try {
final int appId = getAppId(packageName);
if (appId < 0) return;
UsageStatsService.this.setAppIdleAsync(packageName, idle, userId);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void whitelistAppTemporarily(String packageName, long duration, int userId)
throws RemoteException {
StringBuilder reason = new StringBuilder(32);
reason.append("from:");
UserHandle.formatUid(reason, Binder.getCallingUid());
mDeviceIdleController.addPowerSaveTempWhitelistApp(packageName, duration, userId,
reason.toString());
}
@Override
public void onCarrierPrivilegedAppsChanged() {
if (DEBUG) {
Slog.i(TAG, "Carrier privileged apps changed");
}
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_CARRIER_SERVICES,
"onCarrierPrivilegedAppsChanged can only be called by privileged apps.");
UsageStatsService.this.clearCarrierPrivilegedApps();
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
UsageStatsService.this.dump(args, pw);
}
@Override
public void reportChooserSelection(String packageName, int userId, String contentType,
String[] annotations, String action) {
if (packageName == null) {
Slog.w(TAG, "Event report user selecting a null package");
return;
}
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = packageName;
// This will later be converted to system time.
event.mTimeStamp = SystemClock.elapsedRealtime();
event.mEventType = Event.CHOOSER_ACTION;
event.mAction = action;
event.mContentType = contentType;
event.mContentAnnotations = annotations;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
}
3.5 LocalService
/**
* This local service implementation is primarily used by ActivityManagerService.
* ActivityManagerService will call these methods holding the 'am' lock, which means we
* shouldn't be doing any IO work or other long running tasks in these methods.
*/
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();
}
@Override
public void reportEvent(String packageName, int userId, int eventType) {
if (packageName == null) {
Slog.w(TAG, "Event reported without a package name");
return;
}
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = packageName;
// This will later be converted to system time.
event.mTimeStamp = SystemClock.elapsedRealtime();
event.mEventType = eventType;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@Override
public void reportConfigurationChange(Configuration config, int userId) {
if (config == null) {
Slog.w(TAG, "Configuration event reported with a null config");
return;
}
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = "android";
// This will later be converted to system time.
event.mTimeStamp = SystemClock.elapsedRealtime();
event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
event.mConfiguration = new Configuration(config);
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@Override
public void reportShortcutUsage(String packageName, String shortcutId, int userId) {
if (packageName == null || shortcutId == null) {
Slog.w(TAG, "Event reported without a package name or a shortcut ID");
return;
}
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = packageName.intern();
event.mShortcutId = shortcutId.intern();
// This will later be converted to system time.
event.mTimeStamp = SystemClock.elapsedRealtime();
event.mEventType = Event.SHORTCUT_INVOCATION;
mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
}
@Override
public void reportContentProviderUsage(String name, String packageName, int userId) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = name;
args.arg2 = packageName;
args.arg3 = userId;
mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
.sendToTarget();
}
// 使用者是否休眠
@Override
public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId,
SystemClock.elapsedRealtime());
}
@Override
public int[] getIdleUidsForUser(int userId) {
return UsageStatsService.this.getIdleUidsForUser(userId);
}
@Override
public boolean isAppIdleParoleOn() {
return isParoledOrCharging();
}
@Override
public void prepareShutdown() {
// This method *WILL* do IO work, but we must block until it is finished or else
// we might not shutdown cleanly. This is ok to do with the 'am' lock held, because
// we are shutting down.
shutdown();
}
@Override
public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
UsageStatsService.this.addListener(listener);
listener.onParoleStateChanged(isAppIdleParoleOn());
}
@Override
public void removeAppIdleStateChangeListener(
AppIdleStateChangeListener listener) {
UsageStatsService.this.removeListener(listener);
}
@Override
public byte[] getBackupPayload(int user, String key) {
// Check to ensure that only user 0's data is b/r for now
synchronized (UsageStatsService.this.mLock) {
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats =
getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
return userStats.getBackupPayload(key);
} else {
return null;
}
}
}
@Override
public void applyRestoredPayload(int user, String key, byte[] payload) {
synchronized (UsageStatsService.this.mLock) {
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats =
getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
userStats.applyRestoredPayload(key, payload);
}
}
}
@Override
public List<UsageStats> queryUsageStatsForUser(
int userId, int intervalType, long beginTime, long endTime,
boolean obfuscateInstantApps) {
return UsageStatsService.this.queryUsageStats(
userId, intervalType, beginTime, endTime, obfuscateInstantApps);
}
// NOTE: Bug #627645 low power Feature BEG-->
@Override
public void addAppStateEventChangeListener(AppStateEventChangeListener listener) {
if (mPowerControllerHelper != null)
mPowerControllerHelper.addAppStateEventChangeListener(listener);
}
@Override
public void removeAppStateEventChangeListener(AppStateEventChangeListener listener) {
if (mPowerControllerHelper != null)
mPowerControllerHelper.removeAppStateEventChangeListener(listener);
}
@Override
public void setAppInactive(String packageName, boolean idle, int userId) {
if (mPowerControllerHelper != null)
mPowerControllerHelper.setAppInactive(packageName, idle, userId);
}
@Override
public void setAppIdleEnabled(boolean enable) {
if (mPowerControllerHelper != null)
mPowerControllerHelper.setAppIdleEnabled(enable);
}
// <-- NOTE: Bug #627645 low power Feature END
}
3.6 PowerControllerHelper
這裡重點採集 reportEvent
//
// ----------------- PowerController helper -- low power Feature BEG----------------------
//
private PowerControllerHelper mPowerControllerHelper;
// impletement functions that need for PowerController
private final class PowerControllerHelper {
static final int MSG_INFORM_APP_STATE = MSG_ONE_TIME_CHECK_IDLE_STATES + 1;
static final int MSG_SET_FORCEIDLE_FLAG = MSG_ONE_TIME_CHECK_IDLE_STATES + 2;
private ArrayList<UsageStatsManagerInternal.AppStateEventChangeListener>
mAppStateEventListeners = new ArrayList<>();
public boolean reportEvent(UsageEvents.Event event, int userId, boolean previouslyIdle, long elapsedRealtime) {
final boolean forceIdle = mAppIdleHistory.isForceIdleFlagSet(
event.mPackage, userId, elapsedRealtime);
if (!forceIdle
|| event.mEventType == Event.MOVE_TO_FOREGROUND
|| event.mEventType == Event.USER_INTERACTION) {
mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
if (previouslyIdle) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
/* idle = */ 0, event.mPackage));
notifyBatteryStats(event.mPackage, userId, false);
}
}
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_APP_STATE, userId, event.mEventType, event.mPackage));
return true;
}
public void informAppStateEventChangeListeners(String packageName, int userId, int state) {
for (UsageStatsManagerInternal.AppStateEventChangeListener listener : mAppStateEventListeners) {
listener.onAppStateEventChanged(packageName, userId, state);
}
}
public boolean handleMessage(Message msg) {
boolean ret = false;
switch (msg.what) {
case MSG_INFORM_APP_STATE:
informAppStateEventChangeListeners((String)msg.obj, msg.arg1, msg.arg2);
ret = true;
break;
case MSG_SET_FORCEIDLE_FLAG:
setAppForceIdleFlagInternal((String) msg.obj, msg.arg1, msg.arg2 == 1);
ret = true;
break;
}
return ret;
}
public void addAppStateEventChangeListener(UsageStatsManagerInternal.AppStateEventChangeListener listener) {
synchronized (mLock) {
if (!mAppStateEventListeners.contains(listener)) {
mAppStateEventListeners.add(listener);
}
}
}
public void removeAppStateEventChangeListener(
UsageStatsManagerInternal.AppStateEventChangeListener listener) {
synchronized (mLock) {
mAppStateEventListeners.remove(listener);
}
}
public void setAppInactive(String packageName, boolean idle, int userId) {
try {
PackageInfo pi = AppGlobals.getPackageManager()
.getPackageInfo(packageName, 0, userId);
if (pi == null) return;
UsageStatsService.this.setAppIdleAsync(packageName, idle, userId);
setAppForceIdleFlag(packageName, idle, userId);
} catch (RemoteException re) {
} finally {
}
}
public void setAppIdleEnabled(boolean enable) {
if (mAppIdleEnabled != enable) {
mAppIdleEnabled = enable;
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);
}
}
}
private void setAppForceIdleFlag(String packageName, boolean idle, int userId) {
if (packageName == null) return;
mHandler.obtainMessage(MSG_SET_FORCEIDLE_FLAG, userId, idle ? 1 : 0, packageName)
.sendToTarget();
}
/**
* set the force idle flag.
* then it will not exit idle, until call forceIdleState
*/
private void setAppForceIdleFlagInternal(String packageName, int userId, boolean idle) {
final int appId = getAppId(packageName);
if (appId < 0) return;
synchronized (mAppIdleLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
mAppIdleHistory.setForceIdleFlag(packageName, userId, idle, elapsedRealtime);
}
}
}
3.7 UsageStatsService.reportEvent
/**
* Called by the Binder stub.
*/
void reportEvent(UsageEvents.Event event, int userId) {
....
if (mPowerControllerHelper != null
&& mPowerControllerHelper.reportEvent(event, userId, previouslyIdle, elapsedRealtime)) {
return;
}
...
這裡重點看下 AMS 呼叫該介面情況
4. ActivityManagerService 與 UsageStatsService
- frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
package com.android.server.am;
public class ActivityManagerService extends ActivityManagerServiceExAbs
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
/**
* Information about component usage
*/
UsageStatsManagerInternal mUsageStatsService;
public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
mUsageStatsService = usageStatsManager;
}
/**
* Information about component usage
*/
UsageStatsManagerInternal mUsageStatsService;
4.1 ActivityManagerService.updateUsageStats
void updateUsageStats(ActivityRecord component, boolean resumed) {
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
"updateUsageStats: comp=" + component + "res=" + resumed);
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
if (resumed) {
if (mUsageStatsService != null) {
// 程序切換到前臺
mUsageStatsService.reportEvent(component.realActivity, component.userId,
UsageEvents.Event.MOVE_TO_FOREGROUND);
}
synchronized (stats) {
//SPRD: bug 871622, check the process of the activity before using.
if (component.app != null) {
stats.noteActivityResumedLocked(component.app.uid);
}
}
} else {
if (mUsageStatsService != null) {
// 程序切換到後臺
mUsageStatsService.reportEvent(component.realActivity, component.userId,
UsageEvents.Event.MOVE_TO_BACKGROUND);
}
synchronized (stats) {
if (component.app != null) {
stats.noteActivityPausedLocked(component.app.uid);
}
}
}
}
4.2 ActivityManagerService.maybeUpdateUsageStatsLocked
private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) {
...
if (packages != null) {
for (int i = 0; i < packages.length; i++) {
mUsageStatsService.reportEvent(packages[i], app.userId,
//
UsageEvents.Event.SYSTEM_INTERACTION);
4.3 UsageEvents 的狀態解釋
- frameworks/base/core/java/android/app/usage/UsageEvents.java
package android.app.usage;
/**
* A result returned from {@link android.app.usage.UsageStatsManager#queryEvents(long, long)}
* from which to read {@link android.app.usage.UsageEvents.Event} objects.
*/
public final class UsageEvents implements Parcelable {
/**
* An event representing a state change for a component.
*/
public static final class Event {
/**
* An event type denoting that a component moved to the foreground.
*/
// 該事件類似表示該程序進入前臺
public static final int MOVE_TO_FOREGROUND = 1;
/**
* An event type denoting that a component moved to the background.
*/
// 該事件類似表示該程序進入後臺
public static final int MOVE_TO_BACKGROUND = 2;
/**
* An event type denoting that a package was interacted with in some way by the system.
* @hide
*/
// 該事件型別表示該程序正與系統進行互動
public static final int SYSTEM_INTERACTION = 6;
5. 收集應用的事件型別 UsageStatsService.reportEvent
收集應用的事件模組與AMS和USS的邏輯鏈路如下
package com.android.server.usage;
/**
* A service that collects, aggregates, and persists application usage data.
* This data can be queried by apps that have been granted permission by AppOps.
*/
public class UsageStatsService extends SystemService implements
UserUsageStatsService.StatsUpdatedListener {
/**
* Called by the Binder stub.
*/
// 這裡接受程序的Event狀態資訊(MOVE_TO_FOREGROUND, MOVE_TO_BACKGROUND, SYSTEM_INTERACTION)
void reportEvent(UsageEvents.Event event, int userId) {
...
synchronized (mAppIdleLock) {
...
if (mPowerControllerHelper != null
&& mPowerControllerHelper.reportEvent(event, userId, previouslyIdle, elapsedRealtime)) {
return;
}
}
6. PowerControllerHelper.reportEvent
package com.android.server.usage;
/**
* A service that collects, aggregates, and persists application usage data.
* This data can be queried by apps that have been granted permission by AppOps.
*/
public class UsageStatsService extends SystemService implements
UserUsageStatsService.StatsUpdatedListener {
// impletement functions that need for PowerController
private final class PowerControllerHelper {
public boolean reportEvent(UsageEvents.Event event, int userId, boolean previouslyIdle, long elapsedRealtime) {
// 獲取當前是否為強制進入idle模式狀態
final boolean forceIdle = mAppIdleHistory.isForceIdleFlagSet(
event.mPackage, userId, elapsedRealtime);
// 是否滿足以下任一條件
// 1. 非強制進入idle模式
// 2. 事件狀態為移動到前臺
// 3. 事件狀態為使用者互動狀態
if (!forceIdle
|| event.mEventType == Event.MOVE_TO_FOREGROUND
|| event.mEventType == Event.USER_INTERACTION) {
// 記錄應用idle模式歷史
mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
// 是否之前為待機休眠模式
if (previouslyIdle) {
//
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
/* idle = */ 0, event.mPackage));
notifyBatteryStats(event.mPackage, userId, false);
}
}
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_APP_STATE, userId, event.mEventType, event.mPackage));
return true;
}
6.1 MSG_INFORM_LISTENERS
通知應用待機狀態發生改變
class H extends Handler {
public H(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
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);
}
}
6.2 notifyBatteryStats
重新整理電池狀態活動或非活動
private void notifyBatteryStats(String packageName, int userId, boolean idle) {
try {
final int uid = mPackageManager.getPackageUidAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
if (idle) {
mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
packageName, uid);
} else {
mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
packageName, uid);
}
} catch (NameNotFoundException | RemoteException e) {
}
}
7. USS.PowerControllerHelper.informAppStateEventChangeListeners
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_APP_STATE, userId, event.mEventType, event.mPackage));
通知所有註冊AppStateEventChangeListener事件更新應用狀態事件
package com.android.server.usage;
/**
* A service that collects, aggregates, and persists application usage data.
* This data can be queried by apps that have been granted permission by AppOps.
*/
public class UsageStatsService extends SystemService implements
UserUsageStatsService.StatsUpdatedListener {
private PowerControllerHelper mPowerControllerHelper;
// impletement functions that need for PowerController
private final class PowerControllerHelper {
static final int MSG_INFORM_APP_STATE = MSG_ONE_TIME_CHECK_IDLE_STATES + 1;
public boolean handleMessage(Message msg) {
boolean ret = false;
switch (msg.what) {
case MSG_INFORM_APP_STATE:
informAppStateEventChangeListeners((String)msg.obj, msg.arg1, msg.arg2);
ret = true;
break;
....
public void informAppStateEventChangeListeners(String packageName, int userId, int state) {
for (UsageStatsManagerInternal.AppStateEventChangeListener listener : mAppStateEventListeners) {
listener.onAppStateEventChanged(packageName, userId, state);
}
}
8. PowerController 處理onAppStateEventChanged回撥事件
- frameworks/base/services/core/java/com/android/server/power/PowerController.java
PowerController 處理onAppStateEventChanged事件
至此完成 應用事件統計-USS到PowerController的邏輯鏈路
package com.android.server.power;
public class PowerController //extends IPowerController.Stub
// 這裡繼承了 UsageStatsManagerInternal.AppStateEventChangeListener 應用事件變化狀態
extends UsageStatsManagerInternal.AppStateEventChangeListener {
//Message define
static final int MSG_APP_STATE_CHANGED = 0;
public void onAppStateEventChanged(String packageName, int userId, int state) {
msgHandler.sendMessage(msgHandler.obtainMessage(MSG_APP_STATE_CHANGED, userId, state, packageName));
}
@Override public void handleMessage(Message msg) {
if (DEBUG) Slog.d(TAG, "handleMessage(" + Msg2Str(msg.what) + ")");
switch (msg.what) {
case MSG_APP_STATE_CHANGED:
handleAppStateChanged((String)msg.obj, msg.arg1, msg.arg2);
break;
9. PowerController.onAppStateEventChanged 模組
private void handleAppStateChanged(String packageName, int userId, int state) {
int oldState = state;
// 獲取APP狀態
AppState appState = mAppStateInfoCollector.getAppState(packageName, userId);
if (DEBUG) Slog.d(TAG, "- handleAppStateChanged() E -");
// 如果狀態相同則不進行更新
if (appState != null) {
oldState = appState.mState;
if (oldState == state)
return;
}
// 是否記錄應用事件狀態流
if (mAppStateInfoCollector.reportAppStateEventInfo(packageName, userId, state)) {
// Note: Bug 698133 appIdle cts fail -->BEG
// Ugly: we have to check if doing cts/gts test
// is cts/gts test, then
// 若存在該程序,則檢查是否是CTS應用執行
checkCtsGtsTesting(packageName);
// Note: Bug 698133 appIdle cts fail <--END
}
if (DEBUG) Slog.d(TAG, "packageName:" + packageName + " state:" + Util.AppState2Str(state)+ " user:" + userId);
// get new app state
// 重新獲取應用狀態資訊
appState = mAppStateInfoCollector.getAppState(packageName, userId);
if (appState == null) {
Slog.w(TAG, "null appState for packageName:" + packageName + " state:" + Util.AppState2Str(state)+ " user:" + userId);
return;
}
//SPRD:Bug 814570 About apps auto run BEG
// 應用狀態為前臺程序且應用啟動次數為1
if (Event.MOVE_TO_FOREGROUND == state
&& appState.mTotalLaunchCount == 1) {
if (DEBUG) Slog.d(TAG, "- handleAppStateChanged() NEW -");
if (mBackgroundCleanHelper != null) {
// 通知應用為第一次啟動狀態
mBackgroundCleanHelper.noteAppFirstStarted(packageName, userId);
}
}
//SPRD:Bug 814570 About apps auto run END
// 演算法更新應用狀態事件
mRecogAlgorithm.reportEvent(packageName, appState.mUid, RecogAlgorithm.EVENT_TYPE_FG_STATE, state);
// 如果是充電或者亮屏,則結束流程
if (mCharging || mScreenOn)
return;
// 檢查是否要記錄應用網路使用情況
// recode the traffic stats if needed
// should be called before doing Evaluated time stamp update
appState.updateAppTrafficStats(false);
// notify helpers to update time stamp
// 更新時間戳,有些help類需要
updateAppEvaluatedTimeStamp(appState);
// 狀態變化詳情
// 1. STATE_NOCHANGE 沒有改變
// 2. STATE_BG2FG 後臺變更前臺
// 3. 前臺變更後臺
int stateChange = getStateChange(oldState, state);
// if app is already set in standby state, then its app state changed
// UsageStatsService will make the app to exit standby state
if (stateChange != STATE_NOCHANGE && appState.mInAppStandby) {
if (DEBUG) Slog.d(TAG, "packageName:" + packageName + " may exit standby by UsageStatsService");
}
if (stateChange == STATE_BG2FG) { //if bg2fg, clear list, remove from powerguru & appstandby
if (DEBUG) Slog.d(TAG, "packageName:" + packageName + " change from BG2FG");
// notify helpers to handle the app state changed
// 如果是後臺轉前臺,則移除powerguru和appstandby相關狀態
updateAppStateChanged(appState, stateChange);
} else { // if not bg2fg, just resend MSG_CHECK
}
cancelAlarmLocked();
msgHandler.removeMessages(MSG_CHECK);
msgHandler.sendMessage(msgHandler.obtainMessage(MSG_CHECK));
}
9.1 AppStateInfoCollector.reportAppStateEventInfo
主要更新應用狀態資訊和統計一些啟動次數和時間
- frameworks/base/services/core/java/com/android/server/power/AppStateInfoCollector.java
// return true: for new app state
// false: for others
public boolean reportAppStateEventInfo(String packageName, int userId, int stateEvent) {
ArrayMap<String, AppState> mAppStateInfoList = getAppStateInfoList(userId);
//update mAppStateInfoList
// 獲取包名對應的下標
int index = mAppStateInfoList.indexOfKey(packageName);
AppState appState = null;
boolean ret = true;
if (DEBUG) Slog.d(TAG, "- reportAppStateEventInfo() E -");
// 如果下標非0.表示AppState資料已存在,則更新應用狀態
if (index >= 0) {
appState = mAppStateInfoList.valueAt(index);
appState.updateAppState(stateEvent);
ret = false;
// 如果下標為0,表示AppState資料未被建立,則建立一組資料
} else {
appState = buildAppState(packageName, userId, stateEvent);
mAppStateInfoList.put(packageName, appState);
}
return ret;
}
9.1.1 建立AppState資料
- buildAppState(packageName, userId, stateEvent)
package com.android.server.power;
public class AppStateInfoCollector {
private AppState buildAppState(String packageName, int userId, int stateEvent) {
ApplicationInfo app = null;
int uid = 0;
int procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
int flags = 0;
try {
// 從PMS中獲取應用程式資訊
// 1. uid
// 2. flags
app = AppGlobals.getPackageManager().
getApplicationInfo(packageName, 0, userId);
} catch (RemoteException e) {
// can't happen; package manager is process-local
}
if (app != null) {
uid = app.uid;
flags = app.flags;
synchronized (mUidStateLock) {
// 獲取當前程序的優先順序adj
procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
}
}
// 引數型別:包名,userId,uid,應用事件狀態,程序優先順序,flags
AppState retVal = new AppState(packageName, userId, uid, stateEvent, procState, flags);
// check if is input method
// 是否為輸入法
retVal.mIsEnabledInputMethod = isEnabledIMEApp(packageName);
//if (DEBUG) Slog.d(TAG, "- buildAppState() :" + packageName);
return retVal;
}
9.1.2 更新AppState資料
- frameworks/base/services/core/java/com/android/server/power/AppState.java
- appState.updateAppState(引數型別為程序的事件變化[前臺或者後臺或者與系統互動]stateEvent)
package com.android.server.power;
public class AppState {
static final String TAG = "PowerController.AppState";
// return the old state
public int updateAppState(int stateEvent) {
int oldState = mState;
// 更新應用事件stateEvent
mLastState = mState;
mState = state;
// 如果當前是前臺程序
if (Event.MOVE_TO_FOREGROUND == mState) {
// 更新啟動次數,總啟動次數,上一次啟動時間
mLaunchCount++;
mTotalLaunchCount++;
mLastLaunchTime = SystemClock.elapsedRealtime();
}
// 如果當前為與系統互動事件
if (Event.SYSTEM_INTERACTION != mState) {
mLastTimeUsed = SystemClock.elapsedRealtime();
if (mTrackingLaunchCountWhenStandby && Event.MOVE_TO_FOREGROUND == mState) {
// 統計待機模式下啟動次數
mLaunchCountWhenStandby++;
if (DEBUG) Slog.d(TAG, "uid:" + mUid + " packageName:" + mPackageName
+ " mLaunchCountWhenStandby:" + mLaunchCountWhenStandby);
}
}
// 當前程序優先順序為快取程序或者空程序
// if target app exit
if (mProcState == ActivityManager.PROCESS_STATE_CACHED_EMPTY
&& Event.NONE == mState) {
if (mStartRunningTime > 0)
// 統計應用執行時間
mRunningDuration += (SystemClock.elapsedRealtime() - mStartRunningTime);
// clear mStartRunningTime
mStartRunningTime = 0;
} else if (mStartRunningTime == 0) {
mStartRunningTime = SystemClock.elapsedRealtime();
}
return oldState;
}
9.2 appState.updateAppTrafficStats
檢查是否要記錄應用網路使用情況
/**
* update the RxBytes && socket stats
* only for app with procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
*/
public boolean updateAppTrafficStats(boolean clear) {
if (clear) {
mRxBytesWhenStartEvaluated = 0;
mTimeStampForRxBytes = 0;
mSocketStreams.clear();
mUsedTimeSliceCount = 0;
mDoingDownload = false;
} else if (mRxBytesWhenStartEvaluated == 0
&& mProcState <= ActivityManager.PROCESS_STATE_SERVICE
&& mUid > Process.FIRST_APPLICATION_UID) {
mRxBytesWhenStartEvaluated = TrafficStats.getUidRxBytes(mUid);
mTimeStampForRxBytes = SystemClock.elapsedRealtime();
if (DEBUG) Slog.d(TAG, "uid:" + mUid + " packageName:" + mPackageName
+ " RxBytes:" + mRxBytesWhenStartEvaluated);
// get socket stream info
// getAppSocketStreams(state); // --> in fact this is not needed
}
return true;
}
9.3 getStateChange
狀態變化詳情
- STATE_NOCHANGE 沒有改變
- STATE_BG2FG 後臺變更前臺
- 前臺變更後臺
- int stateChange = getStateChange(oldState, state);
static final int STATE_NOCHANGE = 0;
static final int STATE_BG2FG = -2;
static final int STATE_FG2BG = 2;
private int getStateChange(int oldState, int newState) {
int oldFlag = isBGState(oldState)?-1:1;
int newFlag = isBGState(newState)?-1:1;
return(oldFlag - newFlag);
}
private boolean isBGState(int state) {
return (state == Event.MOVE_TO_BACKGROUND) ;
}
9.4 updateAppStateChanged
// notify helpers to handle the app state changed
private void updateAppStateChanged(AppState appState, int stateChange) {
for (int i = 0; i < mHelpers.size(); i++) {
PowerSaveHelper helper = mHelpers.get(i);
helper.updateAppStateChanged(appState, stateChange);
}
}
/*
* BackgroundCleanHelper
* handle the case:
* app move from BG2FG, and it can not be constrained again.
*/
void updateAppStateChanged(AppState appState, int stateChange) {
}
9.5 cancelAlarmLocked()
取消對齊喚醒相關的喚醒檢查
private static final String ACTION_CHECK_APPSTATE =
"com.android.server.powercontroller.CHECK_APPSTATE";
Intent intent = new Intent(ACTION_CHECK_APPSTATE)
.setPackage("android")
.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mAlarmIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
void cancelAlarmLocked() {
mAlarmManager.cancel(mAlarmIntent);
}
void scheduleAlarmLocked(long delay) {
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,
(SystemClock.elapsedRealtime()) + delay, mAlarmIntent);
}
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_CHECK_APPSTATE.equals(action)) {
msgHandler.removeMessages(MSG_CHECK);
msgHandler.sendMessage(msgHandler.obtainMessage(MSG_CHECK));
// for PowerGuru Interval upadate
// 對齊喚醒相關
if (mPowerGuruAligningStart) {
msgHandler.removeMessages(MSG_UPDATE_POWERGURU_INTERVAL);
msgHandler.sendMessage(msgHandler.obtainMessage(MSG_UPDATE_POWERGURU_INTERVAL));
}
9.6 MSG_CHECK
檢查所有應用狀態 checkAllAppStateInfo
static final int MSG_CHECK = 1;
@Override public void handleMessage(Message msg) {
if (DEBUG) Slog.d(TAG, "handleMessage(" + Msg2Str(msg.what) + ")");
switch (msg.what) {
case MSG_CHECK:
checkAllAppStateInfo();
break;
//Check period 5 分鐘間隔
private long CHECK_INTERVAL = (TEST ? 5 * 60 * 1000L : 5 * 60 * 1000L);
private void checkAllAppStateInfo() {
if (DEBUG) Slog.d(TAG, "- checkAllAppStateInfo() E -");
if (DEBUG) Slog.d(TAG, "mCharging:" + mCharging + " mScreenOn:" + mScreenOn + " mMobileConnected:" + mMobileConnected);
//set next check
//msgHandler.removeMessages(MSG_CHECK);
//msgHandler.sendMessageDelayed(msgHandler.obtainMessage(MSG_CHECK), CHECK_INTERVAL);
// 設定5分鐘一次間隔檢查alram
scheduleAlarmLocked(CHECK_INTERVAL);
if (mCharging || mScreenOn) {
if (mNeedCheckPowerModeForCall) {
// 更新省電狀態
updatePowerSaveMode();
}
return;
}
// 更新系統從開機到現在的毫秒數,且每隔30秒重新整理一次
checkSystemUpTime();
boolean bChanged = false;
// Note:use the same now elapsed time for all the AppStateInfo
// otherwise, when checking app Parole, some app may have not
// opportunity to exit app standby.
long now = SystemClock.elapsedRealtime();
// 更新省電模式
updatePowerSaveMode();
try {
final List<UserInfo> users = mUserManager.getUsers();
for (int ui = users.size() - 1; ui >= 0; ui--) {
UserInfo user = users.get(ui);
if (DEBUG) Slog.d(TAG, "- checkAllAppStateInfo() for user: " + user.id);
final ArrayMap<String, AppState> appStateInfoList = mAppStateInfoCollector.getAppStateInfoList(user.id);
for (int i=0;i<appStateInfoList.size();i++) {
AppState appState = appStateInfoList.valueAt(i);
//let app to be parole
// AppIdleHelper應用假釋
mAppIdleHelper.checkAppParole(appState, now);
// check app state info
if (checkAppStateInfo(appState, now)) {
bChanged = true;
}
}
}
} catch (Exception e) {
}
// note AppidleHelper all check done
mAppIdleHelper.noteCheckAllAppStateInfoDone();
// note mWakelockConstraintHelper all check done
mWakelockConstraintHelper.noteCheckAllAppStateInfoDone();
//send notification to powerguru & appstandby
if (bChanged) msgHandler.sendMessage(msgHandler.obtainMessage(MSG_NOTIFY_CHANGED));
if (needCheckNetworkConnection(now)) {
if (DEBUG) Slog.d(TAG, "- checkNetworkConnection() in checkAllAppStateInfo -");
checkNetworkConnection(true);
}
// check doze state
checkDozeState();
}
9.6.1 checkSystemUpTime
更新系統從開機到現在的毫秒數(手機睡眠的時間不包括在內),其中判斷是否系統休眠
private static long SYSTEM_UP_CHECK_INTERVAL = (30 * 1000L); // 30s
// the time stamp of last system is up using elapsed realtime
// 上一次開機到現在的毫秒的時間戳
private long mLastSystemUpTimeStamp = 0;
// next time stamp to check system up using elapsed realtime
// 下一次檢查開機到現在的毫秒的時間戳
private long mNextCheckSystemUpTimeStamp = 0;
// the time stamp of last check using up time
// 上一次檢查開機到現在的毫秒的時間戳
private long mLastCheckTimeStampUptime = 0;
private void checkSystemUpTime() {
// 如果是充電或者亮屏,初始化使用系統相關的時間戳
if (mCharging || mScreenOn) {
mLastSystemUpTimeStamp = 0;
mNextCheckSystemUpTimeStamp = 0;
mLastCheckTimeStampUptime = 0;
return;
}
long now = SystemClock.elapsedRealtime();
// 下一次檢查系統的時間戳大於當前,則結束流程
if (mNextCheckSystemUpTimeStamp > now)
return;
// 從開機到現在的毫秒數(手機睡眠的時間不包括在內)
long nowUptime = SystemClock.uptimeMillis();
if (DEBUG) Slog.d(TAG, "checkSystemUpTime : mLastSystemUpTimeStamp:" + mLastSystemUpTimeStamp
+ " now:" + now + " nowUptime:" + nowUptime);
// 如果開機到現在的時間戳為0
if (mLastSystemUpTimeStamp == 0) {
// 賦值當前開機到現在的時間戳
mLastSystemUpTimeStamp = now;
// 如果滿足如下:
// 1. 上一次檢查開機到現在的毫秒的時間戳大於0
// 2. 下一次檢查開機到現在的毫秒的時間戳大於0
} else if (mNextCheckSystemUpTimeStamp > 0
&& mLastCheckTimeStampUptime > 0) {
// upDuration = 當前開機到現在的時間戳 - 上一次檢查開機到現在的毫秒的時間戳
long upDuration = nowUptime - mLastCheckTimeStampUptime;
// totalDuration = 當前開機到現在的時間戳 - 下一次檢查開機到現在的毫秒的時間戳 + 30s
long totalDuration = now-mNextCheckSystemUpTimeStamp + SYSTEM_UP_CHECK_INTERVAL;
if ((totalDuration-upDuration) > 2*SYSTEM_UP_CHECK_INTERVAL) {
if (DEBUG) Slog.d(TAG, "system has sleep for : " + (totalDuration-upDuration));
// 系統休眠了,更新上一次檢查開機到現在的毫秒的時間戳為當前
mLastSystemUpTimeStamp = now;
}
}
mLastCheckTimeStampUptime = nowUptime;
mNextCheckSystemUpTimeStamp = now + SYSTEM_UP_CHECK_INTERVAL;
// 每隔30秒更新一下,當前開機到現在的時間戳,最終還是呼叫checkSystemUpTime
msgHandler.removeMessages(MSG_CHECK_SYSTEM_UP);
msgHandler.sendMessageDelayed(msgHandler.obtainMessage(MSG_CHECK_SYSTEM_UP), SYSTEM_UP_CHECK_INTERVAL);
}