1. 程式人生 > >13 展訊Sprd設定-電池-應用事件變化資料收集(8.0 Android O)

13 展訊Sprd設定-電池-應用事件變化資料收集(8.0 Android O)

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的邏輯鏈路如下

收集應用的事件模組與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

PowerControllerHelper-reportEvent.jpg

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的邏輯鏈路.jpg

至此完成 應用事件統計-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

狀態變化詳情

  1. STATE_NOCHANGE 沒有改變
  2. STATE_BG2FG 後臺變更前臺
  3. 前臺變更後臺
  • 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);
    }