Rk3288w Android 8.1 记录系统运行总时间

前言

功能实现

1.如何定位较为合理的地方去启动定时服务

2.如何根据系统运行情况打开/关闭服务

3.如何在系统休眠时保持服务运行状态


前言

需求:Rk3288 Android 8.1 需实现 记录系统开机时间功能.

实现:在PowerManagerService中跑开机流程时,启动此计时服务,且不随着系统休眠而停止.

遇到的困难:

1.如何定位较为合理的地方去启动定时服务

2.如何手动停止即使服务

3.如何在系统休眠时保持服务运行状态


作者Android入门,对framework层见解尚浅,欢迎大佬指点一二,不胜感激!

功能实现

在适当的位置加入计时服务,并设置好计时周期,换算好显示单位.

            ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
            service.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    // command
                 }
            }, initialDelay, period, unit);

同时需要将此Service设为即使PowerOff,扔可以在后台运行. 即可在此Service创建时,设置为:

private PowerManager.WakeLock wakeLock;
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock =    powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,MyService.class.getName());
wakeLock.acquire();

销毁时:按如下方法,便可以快速实现该功能.

        if (wakeLock != null)
            wakeLock.release();
        wakeLock = null;

1.如何定位较为合理的地方去启动定时服务

在 PowerManagerService.java 中的 BinderService 内部类中寻找.

各个内部类中增加Log判断启动顺序,Log如下:

Slog.e(TAG, "----MethodName-----");

对比后,选择在 getLastShutdownReason() 此方法中加入时机最好,且仅有在AC 上电的情况下.

若需要另外计算屏幕使用时间 , 需要在以下地方加入 :

关闭屏幕 : Android 关背光的逻辑在 goToSleep() 调用.

        // 步骤一
        @Override // Binder call
        public void goToSleep(long eventTime, int reason, int flags) {
            if (eventTime > SystemClock.uptimeMillis()) {
                throw new IllegalArgumentException("event time must not be in the future");
            }

            mContext.enforceCallingOrSelfPermission(
                    android.Manifest.permission.DEVICE_POWER, null);

            final int uid = Binder.getCallingUid();
            final long ident = Binder.clearCallingIdentity();
            try {
                // 重要代码,在此执行goToSleep操作
                goToSleepInternal(eventTime, reason, flags, uid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

    // 步骤二
    private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
        synchronized (mLock) {
            // 在此判断,是否需要更新电源状态
            if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
                // 在此加入判断设备休眠状态,已关闭屏幕运行时间的计时器
                Slog.e(TAG, "----goToSleep-----");
                startBacklightTimer(false);
                updatePowerStateLocked();
            }
        }
    }

    // 步骤三
    @SuppressWarnings("deprecation")
    private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {
        if (DEBUG_SPEW) {
            Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime
                    + ", reason=" + reason + ", flags=" + flags + ", uid=" + uid);
        }

        if (eventTime < mLastWakeTime
                || mWakefulness == WAKEFULNESS_ASLEEP
                || mWakefulness == WAKEFULNESS_DOZING
                || !mBootCompleted || !mSystemReady) {
            return false;
        }

        // 判断由于不同的条件进入的休眠,可以分情况去区分
        Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep");
        try {
            switch (reason) {
                // 由于设备管理策略而进入睡眠状态
                case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
                    Slog.i(TAG, "Going to sleep due to device administration policy "
                            + "(uid " + uid +")...");
                    break;
                // 由于屏幕超时而进入睡眠状态 -- 无操作休眠
                case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
                    Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")...");
                    break;
                // 由于盖子开关而进入睡眠状态
                case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH:
                    Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")...");
                    break;
                // 由于电源按钮而进入睡眠状态
                case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
                    Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")...");
                    break;
                // 由于睡眠按钮而进入睡眠状态
                case PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON:
                    Slog.i(TAG, "Going to sleep due to sleep button (uid " + uid +")...");
                    break;
                // 因HDMI待机而进入睡眠状态
                case PowerManager.GO_TO_SLEEP_REASON_HDMI:
                    Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")...");
                    break;
                // 按应用程序请求进入睡眠状态
                default:
                    Slog.i(TAG, "Going to sleep by application request (uid " + uid +")...");
                    reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION;
                    break;
            }

            mLastSleepTime = eventTime;
            mSandmanSummoned = true;
            setWakefulnessLocked(WAKEFULNESS_DOZING, reason);

            // Report the number of wake locks that will be cleared by going to sleep.
            int numWakeLocksCleared = 0;
            final int numWakeLocks = mWakeLocks.size();
            for (int i = 0; i < numWakeLocks; i++) {
                final WakeLock wakeLock = mWakeLocks.get(i);
                switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
                    case PowerManager.FULL_WAKE_LOCK:
                    case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
                    case PowerManager.SCREEN_DIM_WAKE_LOCK:
                        numWakeLocksCleared += 1;
                        break;
                }
            }
            EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared);

            // Skip dozing if requested.
            if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
                reallyGoToSleepNoUpdateLocked(eventTime, uid);
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_POWER);
        }
        return true;
    }

开启屏幕 : Android开屏幕的逻辑在 wakeUp() 调用.

        // 步骤一
        @Override // Binder call
        public void wakeUp(long eventTime, String reason, String opPackageName) {
            if (eventTime > SystemClock.uptimeMillis()) {
                throw new IllegalArgumentException("event time must not be in the future");
            }

            mContext.enforceCallingOrSelfPermission(
                    android.Manifest.permission.DEVICE_POWER, null);

            final int uid = Binder.getCallingUid();
            final long ident = Binder.clearCallingIdentity();
            try {
                // 在此进入唤醒逻辑
                wakeUpInternal(eventTime, reason, uid, opPackageName, uid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        // 步骤二
     private void wakeUpInternal(long eventTime, String reason, int uid, String     opPackageName,
            int opUid) {
        synchronized (mLock) {
            // 判断是否有唤醒锁
            if (wakeUpNoUpdateLocked(eventTime, reason, uid, opPackageName, opUid)) {
                // 若有,则更新电源状态
                // 在此判断屏幕是否唤醒,继而打开屏幕运行时间的计时器
                Slog.e(TAG, "----wakeUp-----");
                startBacklightTimer(true);
                updatePowerStateLocked();
            }
        }
    }

        // 步骤三
private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid,
            String opPackageName, int opUid) {
        if (DEBUG_SPEW) {
            Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid);
        }

        if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
                || !mBootCompleted || !mSystemReady) {
            return false;
        }

        Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);

        Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");
        try {
            switch (mWakefulness) {
                case WAKEFULNESS_ASLEEP:
                    Slog.i(TAG, "Waking up from sleep (uid=" + reasonUid + " reason=" + reason
                            + ")...");
                    break;
                case WAKEFULNESS_DREAMING:
                    Slog.i(TAG, "Waking up from dream (uid=" + reasonUid + " reason=" + reason
                            + ")...");
                    break;
                case WAKEFULNESS_DOZING:
                    Slog.i(TAG, "Waking up from dozing (uid=" + reasonUid + " reason=" + reason
                            + ")...");
                    break;
            }

            mLastWakeTime = eventTime;
            setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);

            mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid);
            userActivityNoUpdateLocked(
                    eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_POWER);
        }
        return true;
    }

2.如何根据系统运行情况打开/关闭服务

因为需要用到计时服务(打开/关闭),故采用 ScheduledExecutorService 的 scheduleAtFixedRate() 计时.

/**
     *
     * @param command the task to execute
     * @param initialDelay the time to delay first execution
     * @param period the period between successive executions
     * @param unit the time unit of the initialDelay and period parameters
     * @return a ScheduledFuture representing pending completion of
     *         the series of repeated tasks.  The future's {@link
     *         Future#get() get()} method will never return normally,
     *         and will throw an exception upon task cancellation or
     *         abnormal termination of a task execution.
     * @throws RejectedExecutionException if the task cannot be
     *         scheduled for execution
     * @throws NullPointerException if command is null
     * @throws IllegalArgumentException if period less than or equal to zero
     */
    public ScheduledFuture scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);

 scheduleAtFixedRate 需要传四个参数,分别对应以下的关系.

  1. command - 将要执行的方法

  2. initialDelay - 第一次执行的延时时间

  3. period - 后续执行的间隔时间

  4. unit - 间隔时间的单位,常用的有:
    TimeUnit.SECONDS(秒),TimeUnit.MINUTES(分),TimeUnit.HOURS(时),TimeUnit.DAYS(天)

 关于计时器的代码也放出来了 , 此处为6分钟记一次时间.

private static void startTimer(boolean flag) {
        if (service == null)
            service = Executors.newScheduledThreadPool(10);
        /* flag
          true -> start timer
          false -> stop timer
         */
        if (flag) {
            /* isRunning 默认为false,第一次打开需要声明计时器
            if true , Do not restart the timer
            if false , Start timer
            */
            if (!isRunning) {
                service.scheduleAtFixedRate(new Runnable() {
                    @Override
                    public void run() {
                        // 获取设置前的系统运行时间 time1
                        int time1 = Integer.parseInt(SystemProperties.get("com.android.ScreenUseTime"));
                        Log.d(TAG, "before Set " + time1);
                        SystemProperties.set("com.android.ScreenUseTime",String.valueOf(time1+1));
                        // 获取设置前的系统运行时间 time2
                        int time2 = Integer.parseInt(SystemProperties.get("com.android.ScreenUseTime"));
                        Log.d(TAG, "after Set " + time2);
                    }
                }, 6, 6, TimeUnit.MINUTES);
                // 当前已经打开了计时器,无需重复开启,故置为 true. 
                isRunning = true;
            }
        } else {
            if (service != null) 
                service.shutdownNow();
            service = null;
            Log.d(TAG, "shutdown: is runnings ");
            // 此时已经关闭了计时器,下次使用需重新打开,故置为 false. 
            isRunning = false;
        }
    }

 3.如何在系统休眠时保持服务运行状态

前两个困难已经解决掉了,在PowerManagerSerivce 中调用 StartTimer() 的方法.系统运行时间眼看就要成功了.

但是这时候需要考虑当设备在休眠(也就是ScreenOff)时,如何才能继续计时,不随着系统的 goToSleep()而停止呢 ? 

解决办法 : 为当前计时服务提高等级

在服务开启时, 需要实例化wakeLock锁,并赋予电源级别; 并且在onDestory时,将锁释放掉.具体代码如下:

这样设置初始化后, 即使在goToSleep()后,服务仍能保持存活并运行.

    private PowerManager.WakeLock wakeLock;    

    @Override
    public void onCreate() {
        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TimerService.class.getName());
        wakeLock.acquire();
        service = Executors.newScheduledThreadPool(10);
        mVendorStorage = new VendorStorage();
        startTimer(true);
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        if (wakeLock != null)
            wakeLock.release();
        wakeLock = null;
        super.onDestroy();
    }

以下是 PowerManager.java的一些常用系统的电源等级和标记.

//如果持有该类型的wakelock锁,则按Power键灭屏后,即使允许屏幕、按键灯灭,也不会释放该锁,CPU不会进入休眠状态
public static final int PARTIAL_WAKE_LOCK;
//Deprecated,如果持有该类型的wakelock锁,则使屏幕保持亮/Dim的状态,键盘灯允许灭,按Power键灭屏后,会立即释放
public static final int SCREEN_DIM_WAKE_LOCK;
//Deprecated,如果持有该类型的wakelock锁,则使屏幕保持亮的状态,键盘灯允许灭,按Power键灭屏后,会立即释放
public static final int SCREEN_BRIGHT_WAKE_LOCK
//Deprecated,如果持有该类型的wakelock锁,则使屏幕、键盘灯都保持亮,按Power键灭屏后,会立即释放
public static final int FULL_WAKE_LOCK
//如果持有该锁,则当PSensor检测到有物体靠近时关闭屏幕,远离时又亮屏,该类型锁不会阻止系统进入睡眠状态,比如
//当到达休眠时间后会进入睡眠状态,但是如果当前屏幕由该wakelock关闭,则不会进入睡眠状态。
public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK
//如果持有该锁,则会使屏幕处于DOZE状态,同时允许CPU挂起,该锁用于DreamManager实现Doze模式,如SystemUI的DozeService
public static final int DOZE_WAKE_LOCK
//如果持有该锁,则会时设备保持唤醒状态,以进行绘制屏幕,该锁常用于WindowManager中,允许应用在系统处于Doze状态下时进行绘制
public static final int DRAW_WAKE_LOCK


//该值为0x0000FFFF,用于根据flag判断Wakelock的级别,如:
//if((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) == PowerManager.PARTIAL_WAKE_LOCK){}
public static final int WAKE_LOCK_LEVEL_MASK
//用于在申请锁时唤醒设备,一般情况下,申请wakelock锁时不会唤醒设备,它只会导致屏幕保持打开状态,如果带有这个flag,则会在申
//请wakelock时就点亮屏幕,如常见通知来时屏幕亮,该flag不能和PowerManager.PARTIAL_WAKE_LOCE一起使用。
public static final int ACQUIRE_CAUSES_WAKEUP
//在释放锁时,如果wakelock带有该标志,则会小亮一会再灭屏,该flag不能和PowerManager.PARTIAL_WAKE_LOCE一起使用。
public static final int ON_AFTER_RELEASE
//和其他标记不同,该标记是作为release()方法的参数,且仅仅用于释放PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK类型的
//锁,如果带有该参数,则会延迟释放锁,直到传感器不再感到对象接近
public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY

你可能感兴趣的:(Android,O,Framework,android,java,android-studio)