当前位置 : 主页 > 手机开发 > android >

PowerManagerService之唤醒锁的使用获取创建示例解析

来源:互联网 收集:自由互联 发布时间:2023-02-01
目录 前言 使用唤醒锁 创建唤醒锁 获取唤醒锁 归纳唤醒锁 更新请求策略 唤醒锁保持 CPU 运行 释放锁 结束 前言 在开发中,或多或少会使用唤醒锁(wake lock),有的是为了保持屏幕长亮,
目录
  • 前言
  • 使用唤醒锁
  • 创建唤醒锁
  • 获取唤醒锁
    • 归纳唤醒锁
    • 更新请求策略
    • 唤醒锁保持 CPU 运行
  • 释放锁
    • 结束

      前言

      在开发中,或多或少会使用唤醒锁(wake lock),有的是为了保持屏幕长亮,有的是为了保持 CPU 运行。

      唤醒锁的本质,其实是对屏幕状态的控制,以及对 CPU 挂起的控制。

      屏幕状态的控制,指的是保持屏幕处于点亮的状态,或者直接唤醒屏幕,或者延长亮屏时间。

      CPU 挂起的控制,指的是否阻止 CPU 挂起,如果阻止了 CPU 挂起,其实就是保持 CPU 运行。

      本文重点分析唤醒锁是如何实现对屏幕状态的控制,以及对 CPU 挂起的控制。

      本文仍以前面的三篇文章为基础,重复的过程不会分析,只会简要概述,因此请读者务必仔细阅读如下三篇文章

      PowerManagerService之亮屏流程分析

      PowerManagerService之手动灭屏

      PowerManagerService之自动灭屏

      使用唤醒锁

      首先介绍下如何使用唤醒锁,如下

      PowerManager pm = mContext.getSystemService(PowerManager.class);
      // 1. 创建唤醒锁
      // 保持屏幕处于点亮状态,但是允许变暗
      PowerManager.WakeLock wl = pm.newWakeLock(
                      PowerManager.SCREEN_DIM_WAKE_LOCK
                      | PowerManager.ON_AFTER_RELEASE, 
                      TAG);
      // 2. 获取唤醒锁
      wl.acquire();
      // ... 执行任务 ...
      // 3. 释放唤醒锁
      wl.release();
      

      使用唤醒锁的步骤为

      • 创建唤醒锁
      • 获取唤醒锁
      • 在不需要唤醒锁的时候,释放它。

      注意,使用唤醒时,还需要在 AndroidManifest.xml 中声明权限 android.Manifest.permission.WAKE_LOCK

      创建唤醒锁

      首先介绍下创建唤醒锁的API

      // PowerManager.java
      public WakeLock newWakeLock(int levelAndFlags, String tag) {
          validateWakeLockParameters(levelAndFlags, tag);
          return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName(),
                  Display.INVALID_DISPLAY);
      } 
      

      参数 levelAndFlags 是由 level 和 flag 以按位或的方式组成,其中必须指定一个 level,但是 flag 是可选的。

      第三方 app 能使用的 level 有如下几个

      level描述PARTIAL_WAKE_LOCK保证 CPU 运行,但是屏幕和键盘背光可以关闭FULL_WAKE_LOCK保证屏幕和键盘背光处于最大亮度SCREEN_DIM_WAKE_LOCK确保屏幕处于点亮状态,但是可以变暗,键盘背光允许关闭SCREEN_BRIGHT_WAKE_LOCK确认屏幕处于最大亮度,但是键盘背光允许关闭PROXIMITY_SCREEN_OFF_WAKE_LOCK当距离传感器检测到物体靠近时,灭屏,检测到物体远离时,点亮屏幕

      注意,FULL_WAKE_LOCK 、SCREEN_DIM_WAKE_LOCK、SCREEN_BRIGHT_WAKE_LOCK 不仅会使屏幕处于点亮状态,同时也会保持 CPU 处于运行状态,我们将在后面的分析得到验证。

      第三方 app 能使用的 flag 有如下几个

      flag描述ACQUIRE_CAUSES_WAKEUP当唤醒锁被获取时,点亮屏幕ON_AFTER_RELEASE当唤醒锁被释放时,如果屏幕处于点亮的状态,那么延长亮屏的时间

      注意,ACQUIRE_CAUSES_WAKEUP 和 ON_AFTER_RELEASE 要配合屏幕唤醒锁 FULL_WAKE_LOCK, SCREEN_BRIGHT_WAKE_LOCK, SCREEN_DIM_WAKE_LOCK一起使用。我们将在后面的分析得到验证。

      这里介绍的 level 和 flag 只适用于第三方 app 使用,其实系统还定义了一些,用于完成特殊的功能。

      参数 tag,名字其实可以随意,但是官方说,最好以 app:mytag 的方式命名,例如 gmail:mytag

      参数介绍完了,我现在想提另外一个话题,与多屏相关。 不知从何时起,Android 把多屏进行了分组,内置的屏幕是在默认的分组中。PowerManager#newWakeLock(int levelAndFlags, String tag) 这个 API 会作用于所有的屏幕分组,但是如果我们想指定某组显示屏呢,那么需要使用下面的 API,但是它是系统 API

      // PowerManager.java
      /**
       * @hide
       */
      public WakeLock newWakeLock(int levelAndFlags, String tag, int displayId) {
          validateWakeLockParameters(levelAndFlags, tag);
          return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName(), displayId);
      }
      

      参数 displayId 其实应该叫做 display group id,它表示唤醒锁作用于指定分组显示屏。

      现在看下 WakeLock 的构造函数

      // PowerManager.java
      WakeLock(int flags, String tag, String packageName, int displayId) {
          mFlags = flags;
          mTag = tag;
          mPackageName = packageName;
          mToken = new Binder();
          mTraceName = "WakeLock (" + mTag + ")";
          mDisplayId = displayId;
      }
      

      构造函数就是简单保存几个参数,但是有一点需要注意,mToken 是一个 Binder,它会传给服务端 PowerManagerService,服务端会注册它的死亡事件。那么这个 Binder 对象其实就是为了监控服务端进程的生死。这个技术大家要学会,我曾经用这个技术优化过自己写的服务端代码。

      获取唤醒锁

      // PowerManager.java
      public void acquire() {
          synchronized (mToken) {
              acquireLocked();
          }
      }
      public void acquire(long timeout) {
          synchronized (mToken) {
              acquireLocked();
              // 发送一个延时消息,自动释放唤醒锁
              mHandler.postDelayed(mReleaser, timeout);
          }
      }
      private void acquireLocked() {
          mInternalCount++;
          mExternalCount++;
          // mRefCounted 默认为 true,它表示对唤醒锁引用计数
          if (!mRefCounted || mInternalCount == 1) {
              mHandler.removeCallbacks(mReleaser);
              Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
              try {
                  mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
                          mHistoryTag, mDisplayId);
              } catch (RemoteException e) {
                  throw e.rethrowFromSystemServer();
              }
              mHeld = true;
          }
      }
      

      获取唤醒锁时,可以指定一个超时时间,如果时间到了,唤醒锁还没有释放,那么会自动释放唤醒锁。

      默认的情况下,唤醒锁是计数的。如果多次获取唤醒锁,需要进行相应次数的释放。

      而如果通过 wakeLock.setReferenceCounted(false) 设置唤醒锁为不计数

      // PowerManager.java
      public void setReferenceCounted(boolean value) {
          synchronized (mToken) {
              mRefCounted = value;
          }
      }
      

      那么多次获取唤醒锁后,只需要释放一次。

      现在让我们看下服务端 PowerManagerService 是如何获取唤醒锁的

      // PowerManagerService.java
      public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName,
              WorkSource ws, String historyTag, int displayId) {
          // ... 省略权限检测
          try {
              acquireWakeLockInternal(lock, displayId, flags, tag, packageName, ws, historyTag,
                      uid, pid);
          } finally {
              Binder.restoreCallingIdentity(ident);
          }
      }
      private void acquireWakeLockInternal(IBinder lock, int displayId, int flags, String tag,
              String packageName, WorkSource ws, String historyTag, int uid, int pid) {
          synchronized (mLock) {
              // ... 省略显示屏分组的检测
              WakeLock wakeLock;
              int index = findWakeLockIndexLocked(lock);
              boolean notifyAcquire;
              if (index >= 0) { // 唤醒锁已经存在
                  // ... 
              } else { // 唤醒锁不存在
                  // mUidState 由 ActivityManagerService 同步给 PowerManagerService
                  // UidState 代表一个 app 进程的状态
                  UidState state = mUidState.get(uid);
                  if (state == null) {
                      state = new UidState(uid);
                      state.mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
                      mUidState.put(uid, state);
                  }
                  // 保存唤醒锁的数量
                  state.mNumWakeLocks++;
                  // 1. 创建唤醒锁
                  wakeLock = new WakeLock(lock, displayId, flags, tag, packageName, ws, historyTag,
                          uid, pid, state);
                  try {
                      // 2. 监听客户端进程的死亡
                      // 当客户端进程死亡时,释放它所申请的唤醒锁
                      lock.linkToDeath(wakeLock, 0);
                  } catch (RemoteException ex) {
                      throw new IllegalArgumentException("Wake lock is already dead.");
                  }
                  // 3. 保存唤醒锁
                  mWakeLocks.add(wakeLock);
                  // 4. 更新唤醒锁 PowerManager.PARTIAL_WAKE_LOCK 的 disable 状态
                  setWakeLockDisabledStateLocked(wakeLock);
                  notifyAcquire = true;
              }
              // 5. 处理 PowerManager.ACQUIRE_CAUSES_WAKEUP 唤醒锁亮屏的情况
              applyWakeLockFlagsOnAcquireLocked(wakeLock, uid);
              // 6. 标记唤醒锁已经改变
              mDirty |= DIRTY_WAKE_LOCKS;
              // 7. 更新电源状态
              updatePowerStateLocked();
              if (notifyAcquire) {
                  // 记录唤醒锁
                  notifyWakeLockAcquiredLocked(wakeLock);
              }
          }
      }
      

      先大致了解下,首次向 PowerManagerService 申请唤醒锁的过程

      • 创建服务端的 WakeLock。
      • 监听客户端传递过来的 Binder 的死亡事件,其实就是监听客户端进程的死亡。当客户端进程死亡时,释放它所申请的唤醒锁。
      • PowerManagerService 使用 ArrayList< WakeLock > mWakeLocks 保存创建的唤醒锁。

      更新唤醒锁 PowerManager.PARTIAL_WAKE_LOCK 的 disable 状态,因此有些情况下,是不允许获取这种唤醒锁的,这些特殊情况如下

      private boolean setWakeLockDisabledStateLocked(WakeLock wakeLock) {
          if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
                  == PowerManager.PARTIAL_WAKE_LOCK) {
              boolean disabled = false;
              final int appid = UserHandle.getAppId(wakeLock.mOwnerUid);
              if (appid >= Process.FIRST_APPLICATION_UID) {
                  // Cached inactive processes are never allowed to hold wake locks.
                  // 1. 缓存的不活跃的进程的唤醒锁需要disable
                  if (mConstants.NO_CACHED_WAKE_LOCKS) {
                      disabled = mForceSuspendActive
                              || (!wakeLock.mUidState.mActive && wakeLock.mUidState.mProcState
                                      != ActivityManager.PROCESS_STATE_NONEXISTENT &&
                              wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER);
                  }
                  if (mDeviceIdleMode) {
                      // 2. idle 模式下,不处理白名单的进程的唤醒锁,也需要 disable
                      final UidState state = wakeLock.mUidState;
                      if (Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 &&
                              Arrays.binarySearch(mDeviceIdleTempWhitelist, appid) < 0 &&
                              state.mProcState != ActivityManager.PROCESS_STATE_NONEXISTENT &&
                              state.mProcState >
                                      ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
                          disabled = true;
                      }
                  }
              }
              // 3. 更新唤醒锁的 disable 状态
              if (wakeLock.mDisabled != disabled) {
                  wakeLock.mDisabled = disabled;
                  return true;
              }
          }
          return false;
      }
      

      处理 PowerManager.ACQUIRE_CAUSES_WAKEUP 唤醒锁亮屏的情况。

      private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock, int uid) {
          // 注意,PowerManager.ACQUIRE_CAUSES_WAKEUP 要与如下几个屏幕锁一起使用才有效
          // PowerManager.FULL_WAKE_LOCK
          // PowerManager.SCREEN_BRIGHT_WAKE_LOCK 
          // PowerManager.SCREEN_DIM_WAKE_LOCK
          if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0
                  && isScreenLock(wakeLock)) {
              String opPackageName;
              int opUid;
              if (wakeLock.mWorkSource != null && !wakeLock.mWorkSource.isEmpty()) {
                  // ...
              } else {
                  opPackageName = wakeLock.mPackageName;
                  opUid = wakeLock.mOwnerUid;
              }
              for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
                  // 更新 wakefulness 为 WAKEFULNESS_AWAKE
                  wakeDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(),
                          PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag,
                          opUid, opPackageName, opUid);
              }
          }
      }
      

      注意,PowerManager.ACQUIRE_CAUSES_WAKEUP 是要下屏幕锁一直使用,屏幕锁为 PowerManager.FULL_WAKE_LOCK、PowerManager.SCREEN_BRIGHT_WAKE_LOCK、PowerManager.SCREEN_DIM_WAKE_LOCK。很显然,这些屏幕锁,都是保持屏幕处于点亮的状态。

      根据前面的文章,wakeDisplayGroupNoUpdateLocked() 其实就是更新 wakefulness 为 WAKEFULNESS_AWAKE。当后面更新电源状态时,会向 DisplayManagerService 发起屏幕请示,从而进行亮屏。这个过程,请读者参考前面的文章,自行分析。

      • 标记唤醒锁已经改变。
      • 更新电源状态,根据 mDirty 处理唤醒锁的改变 。

      现在来看下最后一步,更新电源状态

      // PowerManagerService.java
      private void updatePowerStateLocked() {
          if (!mSystemReady || mDirty == 0) {
              return;
          }
          // 注意这里的技术,线程可以判断是否获取了某个锁
          if (!Thread.holdsLock(mLock)) {
              Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked");
          }
          Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState");
          try {
              // Phase 0: Basic state updates.
              updateIsPoweredLocked(mDirty);
              updateStayOnLocked(mDirty);
              updateScreenBrightnessBoostLocked(mDirty);
              // Phase 1: Update wakefulness.
              // Loop because the wake lock and user activity computations are influenced
              // by changes in wakefulness.
              final long now = mClock.uptimeMillis();
              int dirtyPhase2 = 0;
              for (;;) {
                  int dirtyPhase1 = mDirty;
                  dirtyPhase2 |= dirtyPhase1;
                  mDirty = 0;
                  // 1. 归纳唤醒锁
                  updateWakeLockSummaryLocked(dirtyPhase1);
                  // 更新用户行为
                  updateUserActivitySummaryLocked(now, dirtyPhase1);
                  updateAttentiveStateLocked(now, dirtyPhase1);
                  if (!updateWakefulnessLocked(dirtyPhase1)) {
                      break;
                  }
              }
              // Phase 2: Lock profiles that became inactive/not kept awake.
              updateProfilesLocked(now);
              // Phase 3: Update display power state.
              // 2. 更新显示屏的电源状态
              final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
              // Phase 4: Update dream state (depends on display ready signal).
              updateDreamLocked(dirtyPhase2, displayBecameReady);
              // Phase 5: Send notifications, if needed.
              finishWakefulnessChangeIfNeededLocked();
              // Phase 6: Update suspend blocker.
              // Because we might release the last suspend blocker here, we need to make sure
              // we finished everything else first!
              // 3. 唤醒锁保持 CPU 运行
              updateSuspendBlockerLocked();
          } finally {
              Trace.traceEnd(Trace.TRACE_TAG_POWER);
          }
      }
      

      与唤醒锁相关的主要流程如下

      • 归纳唤醒锁。这个过程会把所有的唤醒锁整合到一起,它会影响请求策略,也就是会影响屏幕最终状态。并用它也会决定是否阻止CPU挂起,也就是是否保持CPU运行。详见【归纳唤醒锁
      • 更新电源状态。根据前面的文章可知,屏幕的最终状态是由请求的策略所决定的,而唤醒锁可以响应策略。详见【更新请求策略
      • 如果有唤醒锁需要保证 CPU 运行,那么 PMS 会向底层获取锁,保证 CPU 运行。详见【唤醒锁保持 CPU 运行

      归纳唤醒锁

      private void updateWakeLockSummaryLocked(int dirty) {
          if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS | DIRTY_DISPLAY_GROUP_WAKEFULNESS))
                  != 0) {
              //1. wake lock summary 清 0
              mWakeLockSummary = 0;
              final int numProfiles = mProfilePowerState.size();
              for (int i = 0; i < numProfiles; i++) {
                  mProfilePowerState.valueAt(i).mWakeLockSummary = 0;
              }
              for (int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
                  mDisplayGroupPowerStateMapper.setWakeLockSummaryLocked(groupId, 0);
              }
              // 2. 获取 wake lock summary
              int invalidGroupWakeLockSummary = 0;
              final int numWakeLocks = mWakeLocks.size();
              // 遍历所有的 WakeLock
              for (int i = 0; i < numWakeLocks; i++) {
                  final WakeLock wakeLock = mWakeLocks.get(i);
                  final Integer groupId = wakeLock.getDisplayGroupId();
                  if (groupId == null) {
                      continue;
                  }
                  // 把 PowerManager 定义的 WakeLock flag 转化为 PowerManagerService 定义的 WakeLock flag
                  final int wakeLockFlags = getWakeLockSummaryFlags(wakeLock);
                  // 更新 PMS 的 mWakeLockSummary
                  mWakeLockSummary |= wakeLockFlags;
                  if (groupId != Display.INVALID_DISPLAY_GROUP) {
                      int wakeLockSummary = mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(
                              groupId);
                      wakeLockSummary |= wakeLockFlags;
                      mDisplayGroupPowerStateMapper.setWakeLockSummaryLocked(groupId,
                              wakeLockSummary);
                  } else {
                      // 没有指定 group id 的唤醒锁,保存到 invalidGroupWakeLockSummary
                      invalidGroupWakeLockSummary |= wakeLockFlags;
                  }
                  for (int j = 0; j < numProfiles; j++) {
                      // ...
                  }
              } // 遍历所有 WakeLock 结束
              // 3. 调整的 wake lock summary
              for (int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
                  // 从这里可以看出,invalidGroupWakeLockSummary 应用到了所有的 display group 中
                  // 因此,在获取 WakeLock 没有指定 group id 时,这个 WakeLock 是应用到所有的 display group 上
                  final int wakeLockSummary = adjustWakeLockSummaryLocked(
                          mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId),
                          invalidGroupWakeLockSummary
                                  | mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId));
                  mDisplayGroupPowerStateMapper.setWakeLockSummaryLocked(groupId, wakeLockSummary);
              }
              mWakeLockSummary = adjustWakeLockSummaryLocked(getWakefulnessLocked(),
                      mWakeLockSummary);
              for (int i = 0; i < numProfiles; i++) {
                  // ...
              }
          }
      }
      

      这里的逻辑很清晰,其实就是遍历所有的唤醒锁,然后归纳保存到 mWakeLockSummary。当然这其中有几个重要的函数需要搞清楚

      通过 getWakeLockSummaryFlags() 把 PowerManager 定义的唤醒锁转化为 PowerManagerService 定义的唤醒锁

      private int getWakeLockSummaryFlags(WakeLock wakeLock) {
          switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
              // 这个唤醒锁用于保持 CPU 运行
              case PowerManager.PARTIAL_WAKE_LOCK:
                  // disabled 状态的唤醒锁,是不能保证 CPU 运行的
                  if (!wakeLock.mDisabled) {
                      return WAKE_LOCK_CPU;
                  }
                  break;
              // 以下三个唤醒锁用于保持屏幕处于点亮状态
              case PowerManager.FULL_WAKE_LOCK:
                  return WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
              case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
                  return WAKE_LOCK_SCREEN_BRIGHT;
              case PowerManager.SCREEN_DIM_WAKE_LOCK:
                  return WAKE_LOCK_SCREEN_DIM;
              // 用距离传感器进行灭屏、亮屏
              case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
                  return WAKE_LOCK_PROXIMITY_SCREEN_OFF;
              // PowerManager.DOZE_WAKE_LOCK 由 DreamManagerService 获取
              // 它使设备真正进入打盹状态
              case PowerManager.DOZE_WAKE_LOCK:
                  return WAKE_LOCK_DOZE;
              // 由 window manager 获取,允许应用在系统doze状态下能够绘制
              case PowerManager.DRAW_WAKE_LOCK:
                  return WAKE_LOCK_DRAW;
          }
          return 0;
      }
      

      通过 adjustWakeLockSummaryLocked() 调整归纳的唤醒锁

      private static int adjustWakeLockSummaryLocked(int wakefulness, int wakeLockSummary) {
          // 系统处于 非doze 状态,PowerManager.DOZE_WAKE_LOCK 和 PowerManager.DRAW_WAKE_LOCK 无效
          // 看来,这两个锁只有当系统处于 doze 状态,才有效果
          if (wakefulness != WAKEFULNESS_DOZING) {
              wakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW);
          }
          // 系统处于休眠或者doze状态下,如下三个保持屏幕点亮状态的锁是无效的
          // PowerManager.FULL_WAKE_LOCK
          // PowerManager.SCREEN_BRIGHT_WAKE_LOCK
          // PowerManager.SCREEN_DIM_WAKE_LOCK
          if (wakefulness == WAKEFULNESS_ASLEEP
                  || (wakeLockSummary & WAKE_LOCK_DOZE) != 0) {
              wakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
                      | WAKE_LOCK_BUTTON_BRIGHT);
              // 甚至,当系统处于休眠状态,PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK 无法唤醒屏幕
              if (wakefulness == WAKEFULNESS_ASLEEP) {
                  wakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF;
              }
          }
          // 如下三个保持屏幕点亮状态的锁
          // PowerManager.FULL_WAKE_LOCK
          // PowerManager.SCREEN_BRIGHT_WAKE_LOCK
          // PowerManager.SCREEN_DIM_WAKE_LOCK
          // 当系统处于唤醒状态或者屏保状态,这两个其实都是亮屏状态
          // 需要保证 CPU 运行,也就是下面添加的 WAKE_LOCK_CPU
          // 并且系统处于唤醒状态时,还要屏幕长亮,这正好符合上面三个唤醒锁的定义
          if ((wakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0) {
              // 并且如果系统处理唤醒状态,还要保持长亮
              if (wakefulness == WAKEFULNESS_AWAKE) {
                  wakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_STAY_AWAKE;
              } else if (wakefulness == WAKEFULNESS_DREAMING) { // 系统处于屏保状态
                  wakeLockSummary |= WAKE_LOCK_CPU;
              }
          }
          // 系统处于 doze 状态,PowerManager.DRAW_WAKE_LOCK 需要保持 CPU 运行
          if ((wakeLockSummary & WAKE_LOCK_DRAW) != 0) {
              wakeLockSummary |= WAKE_LOCK_CPU;
          }
          return wakeLockSummary;
      }
      

      调整归纳的唤醒锁,其实就是针对系统处于不同的状态,去掉一些不兼容的唤醒锁或者添加一些合适的锁。

      例如,前面说过,PowerManager.FULL_WAKE_LOCKPowerManager.SCREEN_BRIGHT_WAKE_LOCKPowerManager.SCREEN_DIM_WAKE_LOCK 不仅仅要保持屏幕的亮度,而且还要保持 CPU 运行,这里就可以看出端倪。

      更新请求策略

      通过前面的文章可知,屏幕最终的状态是通过请求策略控制的,函数如下

      int getDesiredScreenPolicyLocked(int groupId) {
          final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
          final int wakeLockSummary = mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId);
          if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
              // 1. 系统处于休眠状态,任何唤醒锁都不起作用,屏幕会进入关闭状态
              return DisplayPowerRequest.POLICY_OFF;
          } else if (wakefulness == WAKEFULNESS_DOZING) {
              // 2. 系统处于 doze 状态,PowerManager.DRAW_WAKE_LOCK 会让屏幕进入 doze 状态
              // 当 dream manager 成功启动 doze dream,才会获取 PowerManager.DRAW_WAKE_LOCK,此时系统才真正进入 doze 状态
              if ((wakeLockSummary & WAKE_LOCK_DOZE) != 0) {
                  return DisplayPowerRequest.POLICY_DOZE;
              }
              if (mDozeAfterScreenOff) {
                  return DisplayPowerRequest.POLICY_OFF;
              }
          }
          if (mIsVrModeEnabled) {
              return DisplayPowerRequest.POLICY_VR;
          }
          // 下面处理的是系统处于唤醒和屏保状态,都是亮屏的状态
          // 3. PowerManager.FULL_WAKE_LOCK 和 PowerManager.SCREEN_BRIGHT_WAKE_LOCK 会
          // 让屏幕处于亮屏状态
          if ((wakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
                  || !mBootCompleted
                  || (mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId)
                  & USER_ACTIVITY_SCREEN_BRIGHT) != 0
                  || mScreenBrightnessBoostInProgress) {
              return DisplayPowerRequest.POLICY_BRIGHT;
          }
          // 4. PowerManager.SCREEN_DIM_WAKE_LOCK 允许屏幕变暗
          // 当屏幕快要超时时,会进入变暗的状态,此时持有 PowerManager.SCREEN_DIM_WAKE_LOCK 会保持屏幕
          // 一直处于 dim 状态
          return DisplayPowerRequest.POLICY_DIM;
      }
      

      从获取请求策略的过程,我们可以看到,当系统处于不同的状态,不同的唤醒锁,是如何影响屏幕状态的。

      例如,PowerManager.FULL_WAKE_LOCK 和 PowerManager.SCREEN_BRIGHT_WAKE_LOCK 会保证屏幕一直处于亮屏状态,而 PowerManager.SCREEN_DIM_WAKE_LOCK 会保证屏幕也处于亮屏状态,但是允许变暗。

      唤醒锁保持 CPU 运行

      private void updateSuspendBlockerLocked() {
          // 1. 检测是否有唤醒锁需要保持 CPU 运行
          final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
          // 2. 检测屏幕的某些状态是否需要保持 CPU 运行
          final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
          final boolean autoSuspend = !needDisplaySuspendBlocker;
          final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked();
          boolean interactive = false;
          for (int id : groupIds) {
              interactive |= mDisplayGroupPowerStateMapper.getPowerRequestLocked(id).isBrightOrDim();
          }
          // mDecoupleHalAutoSuspendModeFromDisplayConfig 默认为 false    
          if (!autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
              setHalAutoSuspendModeLocked(false);
          }
          // 3. 向底层获取锁,保证 CPU 运行
          // First acquire suspend blockers if needed.
          if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
              mWakeLockSuspendBlocker.acquire();
              mHoldingWakeLockSuspendBlocker = true;
          }
          if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) {
              mDisplaySuspendBlocker.acquire();
              mHoldingDisplaySuspendBlocker = true;
          }
          // mDecoupleHalInteractiveModeFromDisplayConfig 默认为 false
          if (mDecoupleHalInteractiveModeFromDisplayConfig) {
              // ...
          }
          // 下面表示没有对应的唤醒锁,就需要向底层释放锁
          if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
              mWakeLockSuspendBlocker.release();
              mHoldingWakeLockSuspendBlocker = false;
          }
          if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
              mDisplaySuspendBlocker.release();
              mHoldingDisplaySuspendBlocker = false;
          }
          // mDecoupleHalAutoSuspendModeFromDisplayConfig 默认为 flase
          if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
              setHalAutoSuspendModeLocked(true);
          }
      }
      

      如下几个锁会保持 CPU 运行

      • PowerManager.PARTIAL_WAKE_LOCK(缓存的后台进程 或 idle模式下,不处于白名单的进程,唤醒锁无效)
      • PowerManager.FULL_WAKE_LOCK(系统处于唤醒或屏保状态)
      • PowerManager.SCREEN_BRIGHT_WAKE_LOCK(系统处于唤醒或屏保状态)
      • PowerManager.SCREEN_DIM_WAKE_LOCK(系统处于唤醒或屏保状态)
      • PowerManager.DOZE_WAKE_LOCK(系统处于doze状态)
      • PowerManager.DRAW_WAKE_LOCK(系统处于doze状态)

      屏幕的几种状态也需要保持 CPU 运行,请看下面代码所展示的所有情况

      private boolean needDisplaySuspendBlockerLocked() {
          // 1. DisplayManagerService 正在处理请求, 需要保持 CPU 运行
          if (!mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
              return true;
          }
          // 2. 屏幕亮度正在增强中,需要保持 CPU 运行
          if (mScreenBrightnessBoostInProgress) {
              return true;
          }
          // When we transition to DOZING, we have to keep the display suspend blocker
          // up until the Doze service has a change to acquire the DOZE wakelock.
          // Here we wait for mWakefulnessChanging to become false since the wakefulness
          // transition to DOZING isn't considered "changed" until the doze wake lock is
          // acquired.
          // 3. doze状态的转换中,需要保持 CPU 运行
          if (getWakefulnessLocked() == WAKEFULNESS_DOZING && mDozeStartInProgress) {
              return true;
          }
          final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked();
          for (int id : groupIds) {
              final DisplayPowerRequest displayPowerRequest =
                      mDisplayGroupPowerStateMapper.getPowerRequestLocked(id);
              // 3. 亮屏状态下,需要保持 CPU 运行
              // 屏幕处于点亮或者变暗的状态,都是亮屏的状态
              if (displayPowerRequest.isBrightOrDim()) {
                  // If we asked for the screen to be on but it is off due to the proximity
                  // sensor then we may suspend but only if the configuration allows it.
                  // On some hardware it may not be safe to suspend because the proximity
                  // sensor may not be correctly configured as a wake-up source.
                  if (!displayPowerRequest.useProximitySensor || !mProximityPositive
                          || !mSuspendWhenScreenOffDueToProximityConfig) {
                      return true;
                  }
              }
              // 4. 系统真正处于 doze 状态,也需要保持 CPU 运行
              // 因此需要在屏幕绘制一些东西,例如时间
              if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE
                      && displayPowerRequest.dozeScreenState == Display.STATE_ON) {
                  // Although we are in DOZE and would normally allow the device to suspend,
                  // the doze service has explicitly requested the display to remain in the ON
                  // state which means we should hold the display suspend blocker.
                  return true;
              }
          }
          // Let the system suspend if the screen is off or dozing.
          return false;
      }
      

      为何屏幕的状态有时候也需要 CPU 保持运行?举个最简单的例子,如果处于亮屏状态,CPU 允许挂起的话,app 进程就无法运行了。

      释放锁

      // PowerManager.java
      public void release() {
          release(0);
      }
      /**
       * Releases the wake lock with flags to modify the release behavior.
       * <p>
       * This method releases your claim to the CPU or screen being on.
       * The screen may turn off shortly after you release the wake lock, or it may
       * not if there are other wake locks still held.
       * </p>
       *
       * @param flags Combination of flag values to modify the release behavior.
       * Currently only {@link #RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY} is supported.
       * Passing 0 is equivalent to calling {@link #release()}.
       */
      public void release(int flags) {
          synchronized (mToken) {
              if (mInternalCount > 0) {
                  // internal count must only be decreased if it is > 0 or state of
                  // the WakeLock object is broken.
                  mInternalCount--;
              }
              if ((flags & RELEASE_FLAG_TIMEOUT) == 0) {
                  mExternalCount--;
              }
              if (!mRefCounted || mInternalCount == 0) {
                  mHandler.removeCallbacks(mReleaser);
                  if (mHeld) {
                      Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
                      try {
                          mService.releaseWakeLock(mToken, flags);
                      } catch (RemoteException e) {
                          throw e.rethrowFromSystemServer();
                      }
                      mHeld = false;
                  }
              }
              if (mRefCounted && mExternalCount < 0) {
                  throw new RuntimeException("WakeLock under-locked " + mTag);
              }
          }
      }
      

      从这里我们可以验证前面说的一个结论,mRefCounted 默认为 true,表示唤醒锁是引用计数的,如果多少获取唤醒锁,需要释放相应次数的唤醒锁。如果不计数,那么只需要释放一次。

      现在看下服务端 PowerManagerService 是如何释放锁的

      public void releaseWakeLock(IBinder lock, int flags) {
          if (lock == null) {
              throw new IllegalArgumentException("lock must not be null");
          }
          // 需要 android.Manifest.permission.WAKE_LOCK 权限
          mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
          final long ident = Binder.clearCallingIdentity();
          try {
              releaseWakeLockInternal(lock, flags);
          } finally {
              Binder.restoreCallingIdentity(ident);
          }
      }
      private void releaseWakeLockInternal(IBinder lock, int flags) {
          synchronized (mLock) {
              // 1. 找到服务端保存的唤醒锁
              int index = findWakeLockIndexLocked(lock);
              if (index < 0) {
                  return;
              }
              WakeLock wakeLock = mWakeLocks.get(index);
              // 延迟释放 PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK 
              // 直到距离传感器检测到物体远离
              if ((flags & PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY) != 0) {
                  mRequestWaitForNegativeProximity = true;
              }
              // 不再监听客户端进程的死亡
              wakeLock.mLock.unlinkToDeath(wakeLock, 0);
              // 移除唤醒锁
              removeWakeLockLocked(wakeLock, index);
          }
      }
      private void removeWakeLockLocked(WakeLock wakeLock, int index) {
          // 2. 从数据结构中移除唤醒锁
          mWakeLocks.remove(index);
          // 进程状态中减少唤醒锁的数量
          UidState state = wakeLock.mUidState;
          state.mNumWakeLocks--;
          if (state.mNumWakeLocks <= 0 &&
                  state.mProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
              mUidState.remove(state.mUid);
          }
          // 记录唤醒锁被释放的数据
          notifyWakeLockReleasedLocked(wakeLock);
          // 3. 处理 PowerManager.ON_AFTER_RELEASE
          // 带有这个 flag 的锁,在释放的时候,会更新用户行为时间,从而可以延长亮屏的时间
          applyWakeLockFlagsOnReleaseLocked(wakeLock);
          // 4.标记唤醒已经改变,并更新电源状态
          mDirty |= DIRTY_WAKE_LOCKS;
          updatePowerStateLocked();
      }
      

      PowerManagerService 移除唤醒锁的过程一般如下

      • 从数据结构中移除。
      • 处于带有 PowerManager.ON_AFTER_RELEASE 这个 flag 的唤醒锁。在释放带有这个 flag 的唤醒锁的时候,会更新用户行为时间,从而可以延长亮屏的时间。
      • 标记唤醒锁已经改变,更新电源状态。

      距离传感器锁的原理,在看完本文后,大家可以自行分析。

      现在来看下,释放带有 PowerManager.ON_AFTER_RELEASE 的唤醒锁,是如何延长亮屏的时间的

      private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
          // PowerManager.ON_AFTER_RELEASE 必须与如下的屏幕锁一起使用
          // PowerManager.FULL_WAKE_LOCK
          // PowerManager.SCREEN_BRIGHT_WAKE_LOCK
          // PowerManager.SCREEN_DIM_WAKE_LOCK
          if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0
                  && isScreenLock(wakeLock)) {
              userActivityNoUpdateLocked(mClock.uptimeMillis(),
                      PowerManager.USER_ACTIVITY_EVENT_OTHER,
                      PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
                      wakeLock.mOwnerUid);
          }
      }
      private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
          boolean updatePowerState = false;
          // 注意,PowerManager.ON_AFTER_RELEASE 影响了所有的 display group 的 用户行为时间
          // 那么也说明,它会导致所有的屏幕延长亮屏的时间
          for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
              if (userActivityNoUpdateLocked(id, eventTime, event, flags, uid)) {
                  updatePowerState = true;
              }
          }
          return updatePowerState;
      }
      // PowerManagerService.java
      private boolean userActivityNoUpdateLocked(int groupId, long eventTime, int event, int flags,
              int uid) {
          // ...
          try {
              // ...
              if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
                  // ...
              } else {
                  if (eventTime > mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
                          groupId)) {
                      // 记录用户行为的时间
                      mDisplayGroupPowerStateMapper.setLastUserActivityTimeLocked(groupId, eventTime);
                      // 标记用户活动有改变
                      mDirty |= DIRTY_USER_ACTIVITY;
                      if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
                          mDirty |= DIRTY_QUIESCENT;
                      }
                      return true;
                  }
              }
          } finally {
              Trace.traceEnd(Trace.TRACE_TAG_POWER);
          }
          return false;
      }
      

      处理 PowerManager.ON_AFTER_RELEASE 的过程,我们要注意以下几点事情

      • PowerManager.ON_AFTER_RELEASE 必须要与屏幕唤醒锁 PowerManager.FULL_WAKE_LOCK, PowerManager.SCREEN_BRIGHT_WAKE_LOCK, PowerManager.SCREEN_DIM_WAKE_LOCK 一起使用.
      • PowerManager.ON_AFTER_RELEASE 更新了所有屏幕分组的用户行为时间,也就是说最终会导致所有屏幕都延长亮屏的时间。
      • 更新用户行为时间,根据 PowerManagerService之自动灭屏 可知,用户行为时间的更新,最终会导致延长亮屏的时间

      结束

      通过本文的分析,我们可以看到唤醒锁是如何控制屏幕状态,以及如何保持CPU运行。但是本文写的比较简洁,是因为很多东西已经在前文分析过了,如果读者看本文的时候,有点压力,不妨再回头看看前面的文章。

      PowerManagerService 系列的文章,就此结束。虽然还有一些功能我并未分析,但是我写的这些文章都是基础,只要掌握基础,其它功能的分析,岂不是信手拈来。

      以上就是PowerManagerService之唤醒锁的使用获取创建示例解析的详细内容,更多关于PowerManagerService 唤醒锁的资料请关注自由互联其它相关文章!

      上一篇:JetpackCompose自定义动画与Animatable详解
      下一篇:没有了
      网友评论