亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

PowerManagerService之喚醒鎖的使用獲取創(chuàng)建示例解析

 更新時間:2022年10月28日 09:03:37   作者:大胃粥  
這篇文章主要為大家介紹了PowerManagerService之喚醒鎖的使用獲取創(chuàng)建示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

在開發(fā)中,或多或少會使用喚醒鎖(wake lock),有的是為了保持屏幕長亮,有的是為了保持 CPU 運行。

喚醒鎖的本質,其實是對屏幕狀態(tài)的控制,以及對 CPU 掛起的控制。

屏幕狀態(tài)的控制,指的是保持屏幕處于點亮的狀態(tài),或者直接喚醒屏幕,或者延長亮屏時間。

CPU 掛起的控制,指的是否阻止 CPU 掛起,如果阻止了 CPU 掛起,其實就是保持 CPU 運行。

本文重點分析喚醒鎖是如何實現(xiàn)對屏幕狀態(tài)的控制,以及對 CPU 掛起的控制。

本文仍以前面的三篇文章為基礎,重復的過程不會分析,只會簡要概述,因此請讀者務必仔細閱讀如下三篇文章

PowerManagerService之亮屏流程分析

PowerManagerService之手動滅屏

PowerManagerService之自動滅屏

使用喚醒鎖

首先介紹下如何使用喚醒鎖,如下

PowerManager pm = mContext.getSystemService(PowerManager.class);
// 1. 創(chuàng)建喚醒鎖
// 保持屏幕處于點亮狀態(tài),但是允許變暗
PowerManager.WakeLock wl = pm.newWakeLock(
                PowerManager.SCREEN_DIM_WAKE_LOCK
                | PowerManager.ON_AFTER_RELEASE, 
                TAG);
// 2. 獲取喚醒鎖
wl.acquire();
// ... 執(zhí)行任務 ...
// 3. 釋放喚醒鎖
wl.release();

使用喚醒鎖的步驟為

  • 創(chuàng)建喚醒鎖
  • 獲取喚醒鎖
  • 在不需要喚醒鎖的時候,釋放它。

注意,使用喚醒時,還需要在 AndroidManifest.xml 中聲明權限 android.Manifest.permission.WAKE_LOCK

創(chuàng)建喚醒鎖

首先介紹下創(chuàng)建喚醒鎖的API

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

參數(shù) levelAndFlags 是由 level 和 flag 以按位或的方式組成,其中必須指定一個 level,但是 flag 是可選的。

第三方 app 能使用的 level 有如下幾個

level描述
PARTIAL_WAKE_LOCK保證 CPU 運行,但是屏幕和鍵盤背光可以關閉
FULL_WAKE_LOCK保證屏幕和鍵盤背光處于最大亮度
SCREEN_DIM_WAKE_LOCK確保屏幕處于點亮狀態(tài),但是可以變暗,鍵盤背光允許關閉
SCREEN_BRIGHT_WAKE_LOCK確認屏幕處于最大亮度,但是鍵盤背光允許關閉
PROXIMITY_SCREEN_OFF_WAKE_LOCK當距離傳感器檢測到物體靠近時,滅屏,檢測到物體遠離時,點亮屏幕

注意,F(xiàn)ULL_WAKE_LOCK 、SCREEN_DIM_WAKE_LOCK、SCREEN_BRIGHT_WAKE_LOCK 不僅會使屏幕處于點亮狀態(tài),同時也會保持 CPU 處于運行狀態(tài),我們將在后面的分析得到驗證。

第三方 app 能使用的 flag 有如下幾個

flag描述
ACQUIRE_CAUSES_WAKEUP當喚醒鎖被獲取時,點亮屏幕
ON_AFTER_RELEASE當喚醒鎖被釋放時,如果屏幕處于點亮的狀態(tài),那么延長亮屏的時間

注意,ACQUIRE_CAUSES_WAKEUP 和 ON_AFTER_RELEASE 要配合屏幕喚醒鎖 FULL_WAKE_LOCK, SCREEN_BRIGHT_WAKE_LOCK, SCREEN_DIM_WAKE_LOCK一起使用。我們將在后面的分析得到驗證。

這里介紹的 level 和 flag 只適用于第三方 app 使用,其實系統(tǒng)還定義了一些,用于完成特殊的功能。

參數(shù) tag,名字其實可以隨意,但是官方說,最好以 app:mytag 的方式命名,例如 gmail:mytag。

參數(shù)介紹完了,我現(xiàn)在想提另外一個話題,與多屏相關。 不知從何時起,Android 把多屏進行了分組,內置的屏幕是在默認的分組中。PowerManager#newWakeLock(int levelAndFlags, String tag) 這個 API 會作用于所有的屏幕分組,但是如果我們想指定某組顯示屏呢,那么需要使用下面的 API,但是它是系統(tǒng) API

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

參數(shù) displayId 其實應該叫做 display group id,它表示喚醒鎖作用于指定分組顯示屏。

現(xiàn)在看下 WakeLock 的構造函數(shù)

// 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;
}

構造函數(shù)就是簡單保存幾個參數(shù),但是有一點需要注意,mToken 是一個 Binder,它會傳給服務端 PowerManagerService,服務端會注冊它的死亡事件。那么這個 Binder 對象其實就是為了監(jiān)控服務端進程的生死。這個技術大家要學會,我曾經(jīng)用這個技術優(yōu)化過自己寫的服務端代碼。

獲取喚醒鎖

// PowerManager.java
public void acquire() {
    synchronized (mToken) {
        acquireLocked();
    }
}
public void acquire(long timeout) {
    synchronized (mToken) {
        acquireLocked();
        // 發(fā)送一個延時消息,自動釋放喚醒鎖
        mHandler.postDelayed(mReleaser, timeout);
    }
}
private void acquireLocked() {
    mInternalCount++;
    mExternalCount++;
    // mRefCounted 默認為 true,它表示對喚醒鎖引用計數(shù)
    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;
    }
}

獲取喚醒鎖時,可以指定一個超時時間,如果時間到了,喚醒鎖還沒有釋放,那么會自動釋放喚醒鎖。

默認的情況下,喚醒鎖是計數(shù)的。如果多次獲取喚醒鎖,需要進行相應次數(shù)的釋放。

而如果通過 wakeLock.setReferenceCounted(false) 設置喚醒鎖為不計數(shù)

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

那么多次獲取喚醒鎖后,只需要釋放一次。

現(xiàn)在讓我們看下服務端 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) { // 喚醒鎖已經(jīng)存在
            // ... 
        } else { // 喚醒鎖不存在
            // mUidState 由 ActivityManagerService 同步給 PowerManagerService
            // UidState 代表一個 app 進程的狀態(tài)
            UidState state = mUidState.get(uid);
            if (state == null) {
                state = new UidState(uid);
                state.mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
                mUidState.put(uid, state);
            }
            // 保存喚醒鎖的數(shù)量
            state.mNumWakeLocks++;
            // 1. 創(chuàng)建喚醒鎖
            wakeLock = new WakeLock(lock, displayId, flags, tag, packageName, ws, historyTag,
                    uid, pid, state);
            try {
                // 2. 監(jiān)聽客戶端進程的死亡
                // 當客戶端進程死亡時,釋放它所申請的喚醒鎖
                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 狀態(tài)
            setWakeLockDisabledStateLocked(wakeLock);
            notifyAcquire = true;
        }
        // 5. 處理 PowerManager.ACQUIRE_CAUSES_WAKEUP 喚醒鎖亮屏的情況
        applyWakeLockFlagsOnAcquireLocked(wakeLock, uid);
        // 6. 標記喚醒鎖已經(jīng)改變
        mDirty |= DIRTY_WAKE_LOCKS;
        // 7. 更新電源狀態(tài)
        updatePowerStateLocked();
        if (notifyAcquire) {
            // 記錄喚醒鎖
            notifyWakeLockAcquiredLocked(wakeLock);
        }
    }
}

先大致了解下,首次向 PowerManagerService 申請喚醒鎖的過程

  • 創(chuàng)建服務端的 WakeLock。
  • 監(jiān)聽客戶端傳遞過來的 Binder 的死亡事件,其實就是監(jiān)聽客戶端進程的死亡。當客戶端進程死亡時,釋放它所申請的喚醒鎖。
  • PowerManagerService 使用 ArrayList< WakeLock > mWakeLocks 保存創(chuàng)建的喚醒鎖。

更新喚醒鎖 PowerManager.PARTIAL_WAKE_LOCK 的 disable 狀態(tài),因此有些情況下,是不允許獲取這種喚醒鎖的,這些特殊情況如下

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 狀態(tài)
        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。很顯然,這些屏幕鎖,都是保持屏幕處于點亮的狀態(tài)。

根據(jù)前面的文章,wakeDisplayGroupNoUpdateLocked() 其實就是更新 wakefulness 為 WAKEFULNESS_AWAKE。當后面更新電源狀態(tài)時,會向 DisplayManagerService 發(fā)起屏幕請示,從而進行亮屏。這個過程,請讀者參考前面的文章,自行分析。

  • 標記喚醒鎖已經(jīng)改變。
  • 更新電源狀態(tài),根據(jù) mDirty 處理喚醒鎖的改變 。

現(xiàn)在來看下最后一步,更新電源狀態(tài)

// 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. 更新顯示屏的電源狀態(tài)
        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);
    }
}

與喚醒鎖相關的主要流程如下

  • 歸納喚醒鎖。這個過程會把所有的喚醒鎖整合到一起,它會影響請求策略,也就是會影響屏幕最終狀態(tài)。并用它也會決定是否阻止CPU掛起,也就是是否保持CPU運行。詳見【歸納喚醒鎖
  • 更新電源狀態(tài)。根據(jù)前面的文章可知,屏幕的最終狀態(tài)是由請求的策略所決定的,而喚醒鎖可以響應策略。詳見【更新請求策略
  • 如果有喚醒鎖需要保證 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。當然這其中有幾個重要的函數(shù)需要搞清楚

通過 getWakeLockSummaryFlags() 把 PowerManager 定義的喚醒鎖轉化為 PowerManagerService 定義的喚醒鎖

private int getWakeLockSummaryFlags(WakeLock wakeLock) {
    switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
        // 這個喚醒鎖用于保持 CPU 運行
        case PowerManager.PARTIAL_WAKE_LOCK:
            // disabled 狀態(tài)的喚醒鎖,是不能保證 CPU 運行的
            if (!wakeLock.mDisabled) {
                return WAKE_LOCK_CPU;
            }
            break;
        // 以下三個喚醒鎖用于保持屏幕處于點亮狀態(tài)
        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 獲取
        // 它使設備真正進入打盹狀態(tài)
        case PowerManager.DOZE_WAKE_LOCK:
            return WAKE_LOCK_DOZE;
        // 由 window manager 獲取,允許應用在系統(tǒng)doze狀態(tài)下能夠繪制
        case PowerManager.DRAW_WAKE_LOCK:
            return WAKE_LOCK_DRAW;
    }
    return 0;
}

通過 adjustWakeLockSummaryLocked() 調整歸納的喚醒鎖

private static int adjustWakeLockSummaryLocked(int wakefulness, int wakeLockSummary) {
    // 系統(tǒng)處于 非doze 狀態(tài),PowerManager.DOZE_WAKE_LOCK 和 PowerManager.DRAW_WAKE_LOCK 無效
    // 看來,這兩個鎖只有當系統(tǒng)處于 doze 狀態(tài),才有效果
    if (wakefulness != WAKEFULNESS_DOZING) {
        wakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW);
    }
    // 系統(tǒng)處于休眠或者doze狀態(tài)下,如下三個保持屏幕點亮狀態(tài)的鎖是無效的
    // 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);
        // 甚至,當系統(tǒng)處于休眠狀態(tài),PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK 無法喚醒屏幕
        if (wakefulness == WAKEFULNESS_ASLEEP) {
            wakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF;
        }
    }
    // 如下三個保持屏幕點亮狀態(tài)的鎖
    // PowerManager.FULL_WAKE_LOCK
    // PowerManager.SCREEN_BRIGHT_WAKE_LOCK
    // PowerManager.SCREEN_DIM_WAKE_LOCK
    // 當系統(tǒng)處于喚醒狀態(tài)或者屏保狀態(tài),這兩個其實都是亮屏狀態(tài)
    // 需要保證 CPU 運行,也就是下面添加的 WAKE_LOCK_CPU
    // 并且系統(tǒng)處于喚醒狀態(tài)時,還要屏幕長亮,這正好符合上面三個喚醒鎖的定義
    if ((wakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0) {
        // 并且如果系統(tǒng)處理喚醒狀態(tài),還要保持長亮
        if (wakefulness == WAKEFULNESS_AWAKE) {
            wakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_STAY_AWAKE;
        } else if (wakefulness == WAKEFULNESS_DREAMING) { // 系統(tǒng)處于屏保狀態(tài)
            wakeLockSummary |= WAKE_LOCK_CPU;
        }
    }
    // 系統(tǒng)處于 doze 狀態(tài),PowerManager.DRAW_WAKE_LOCK 需要保持 CPU 運行
    if ((wakeLockSummary & WAKE_LOCK_DRAW) != 0) {
        wakeLockSummary |= WAKE_LOCK_CPU;
    }
    return wakeLockSummary;
}

調整歸納的喚醒鎖,其實就是針對系統(tǒng)處于不同的狀態(tài),去掉一些不兼容的喚醒鎖或者添加一些合適的鎖。

例如,前面說過,PowerManager.FULL_WAKE_LOCK、PowerManager.SCREEN_BRIGHT_WAKE_LOCK、PowerManager.SCREEN_DIM_WAKE_LOCK 不僅僅要保持屏幕的亮度,而且還要保持 CPU 運行,這里就可以看出端倪。

更新請求策略

通過前面的文章可知,屏幕最終的狀態(tài)是通過請求策略控制的,函數(shù)如下

int getDesiredScreenPolicyLocked(int groupId) {
    final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
    final int wakeLockSummary = mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId);
    if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
        // 1. 系統(tǒng)處于休眠狀態(tài),任何喚醒鎖都不起作用,屏幕會進入關閉狀態(tài)
        return DisplayPowerRequest.POLICY_OFF;
    } else if (wakefulness == WAKEFULNESS_DOZING) {
        // 2. 系統(tǒng)處于 doze 狀態(tài),PowerManager.DRAW_WAKE_LOCK 會讓屏幕進入 doze 狀態(tài)
        // 當 dream manager 成功啟動 doze dream,才會獲取 PowerManager.DRAW_WAKE_LOCK,此時系統(tǒng)才真正進入 doze 狀態(tài)
        if ((wakeLockSummary & WAKE_LOCK_DOZE) != 0) {
            return DisplayPowerRequest.POLICY_DOZE;
        }
        if (mDozeAfterScreenOff) {
            return DisplayPowerRequest.POLICY_OFF;
        }
    }
    if (mIsVrModeEnabled) {
        return DisplayPowerRequest.POLICY_VR;
    }
    // 下面處理的是系統(tǒng)處于喚醒和屏保狀態(tài),都是亮屏的狀態(tài)
    // 3. PowerManager.FULL_WAKE_LOCK 和 PowerManager.SCREEN_BRIGHT_WAKE_LOCK 會
    // 讓屏幕處于亮屏狀態(tài)
    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 允許屏幕變暗
    // 當屏幕快要超時時,會進入變暗的狀態(tài),此時持有 PowerManager.SCREEN_DIM_WAKE_LOCK 會保持屏幕
    // 一直處于 dim 狀態(tài)
    return DisplayPowerRequest.POLICY_DIM;
}

從獲取請求策略的過程,我們可以看到,當系統(tǒng)處于不同的狀態(tài),不同的喚醒鎖,是如何影響屏幕狀態(tài)的。

例如,PowerManager.FULL_WAKE_LOCK 和 PowerManager.SCREEN_BRIGHT_WAKE_LOCK 會保證屏幕一直處于亮屏狀態(tài),而 PowerManager.SCREEN_DIM_WAKE_LOCK 會保證屏幕也處于亮屏狀態(tài),但是允許變暗。

喚醒鎖保持 CPU 運行

private void updateSuspendBlockerLocked() {
    // 1. 檢測是否有喚醒鎖需要保持 CPU 運行
    final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
    // 2. 檢測屏幕的某些狀態(tài)是否需要保持 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(系統(tǒng)處于喚醒或屏保狀態(tài))
  • PowerManager.SCREEN_BRIGHT_WAKE_LOCK(系統(tǒng)處于喚醒或屏保狀態(tài))
  • PowerManager.SCREEN_DIM_WAKE_LOCK(系統(tǒng)處于喚醒或屏保狀態(tài))
  • PowerManager.DOZE_WAKE_LOCK(系統(tǒng)處于doze狀態(tài))
  • PowerManager.DRAW_WAKE_LOCK(系統(tǒng)處于doze狀態(tài))

屏幕的幾種狀態(tài)也需要保持 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狀態(tài)的轉換中,需要保持 CPU 運行
    if (getWakefulnessLocked() == WAKEFULNESS_DOZING && mDozeStartInProgress) {
        return true;
    }
    final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked();
    for (int id : groupIds) {
        final DisplayPowerRequest displayPowerRequest =
                mDisplayGroupPowerStateMapper.getPowerRequestLocked(id);
        // 3. 亮屏狀態(tài)下,需要保持 CPU 運行
        // 屏幕處于點亮或者變暗的狀態(tài),都是亮屏的狀態(tài)
        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. 系統(tǒng)真正處于 doze 狀態(tài),也需要保持 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;
}

為何屏幕的狀態(tài)有時候也需要 CPU 保持運行?舉個最簡單的例子,如果處于亮屏狀態(tài),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,表示喚醒鎖是引用計數(shù)的,如果多少獲取喚醒鎖,需要釋放相應次數(shù)的喚醒鎖。如果不計數(shù),那么只需要釋放一次。

現(xiàn)在看下服務端 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;
        }
        // 不再監(jiān)聽客戶端進程的死亡
        wakeLock.mLock.unlinkToDeath(wakeLock, 0);
        // 移除喚醒鎖
        removeWakeLockLocked(wakeLock, index);
    }
}
private void removeWakeLockLocked(WakeLock wakeLock, int index) {
    // 2. 從數(shù)據(jù)結構中移除喚醒鎖
    mWakeLocks.remove(index);
    // 進程狀態(tài)中減少喚醒鎖的數(shù)量
    UidState state = wakeLock.mUidState;
    state.mNumWakeLocks--;
    if (state.mNumWakeLocks <= 0 &&
            state.mProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
        mUidState.remove(state.mUid);
    }
    // 記錄喚醒鎖被釋放的數(shù)據(jù)
    notifyWakeLockReleasedLocked(wakeLock);
    // 3. 處理 PowerManager.ON_AFTER_RELEASE
    // 帶有這個 flag 的鎖,在釋放的時候,會更新用戶行為時間,從而可以延長亮屏的時間
    applyWakeLockFlagsOnReleaseLocked(wakeLock);
    // 4.標記喚醒已經(jīng)改變,并更新電源狀態(tài)
    mDirty |= DIRTY_WAKE_LOCKS;
    updatePowerStateLocked();
}

PowerManagerService 移除喚醒鎖的過程一般如下

  • 從數(shù)據(jù)結構中移除。
  • 處于帶有 PowerManager.ON_AFTER_RELEASE 這個 flag 的喚醒鎖。在釋放帶有這個 flag 的喚醒鎖的時候,會更新用戶行為時間,從而可以延長亮屏的時間。
  • 標記喚醒鎖已經(jīng)改變,更新電源狀態(tài)。

距離傳感器鎖的原理,在看完本文后,大家可以自行分析。

現(xiàn)在來看下,釋放帶有 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 更新了所有屏幕分組的用戶行為時間,也就是說最終會導致所有屏幕都延長亮屏的時間。
  • 更新用戶行為時間,根據(jù) PowerManagerService之自動滅屏 可知,用戶行為時間的更新,最終會導致延長亮屏的時間

結束

通過本文的分析,我們可以看到喚醒鎖是如何控制屏幕狀態(tài),以及如何保持CPU運行。但是本文寫的比較簡潔,是因為很多東西已經(jīng)在前文分析過了,如果讀者看本文的時候,有點壓力,不妨再回頭看看前面的文章。

PowerManagerService 系列的文章,就此結束。雖然還有一些功能我并未分析,但是我寫的這些文章都是基礎,只要掌握基礎,其它功能的分析,豈不是信手拈來。

以上就是PowerManagerService之喚醒鎖的使用獲取創(chuàng)建示例解析的詳細內容,更多關于PowerManagerService 喚醒鎖的資料請關注腳本之家其它相關文章!

相關文章

  • Android UI:ListView - SimpleAdapter實例詳解

    Android UI:ListView - SimpleAdapter實例詳解

    這篇文章主要介紹了Android UI:ListView - SimpleAdapter實例詳解,SimpleAdapter是擴展性最好的適配器,可以定義各種你想要的布局,而且使用很方便,需要的朋友可以參考下
    2016-11-11
  • android的UDP編程實例

    android的UDP編程實例

    android的UDP編程實例和注意事項。
    2013-11-11
  • 查看apk簽名信息方法

    查看apk簽名信息方法

    用shell寫了一個查看apk簽名的腳本,下面與大家分享下如何查看apk簽名信息,感興趣的朋友可以了解下哈
    2013-06-06
  • 淺析Android文件管理器(項目一)

    淺析Android文件管理器(項目一)

    這篇文章主要介紹了淺析Android文件管理器(一)的相關資料,需要的朋友可以參考下
    2015-11-11
  • Android 實現(xiàn)右滑返回功能

    Android 實現(xiàn)右滑返回功能

    右滑返回功能在ios上非常實用,因為它的返回鍵在左上角,下面腳本之家小編給大家?guī)砹薃ndroid 實現(xiàn)右滑返回功能,感興趣的朋友一起看看吧
    2018-04-04
  • Android?Fragment實現(xiàn)頂部、底部導航欄

    Android?Fragment實現(xiàn)頂部、底部導航欄

    這篇文章主要為大家詳細介紹了Android?Fragment實現(xiàn)頂部、底部導航欄,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • Android 添加TextView刪除線(代碼簡單)

    Android 添加TextView刪除線(代碼簡單)

    最近接了個項目,其中有項目需求是這樣的,有這么個需求,就是一個產(chǎn)品下有兩個價格,一個是市場價,一個是銷售價,這時要把市場價添加個刪除線;怎么實現(xiàn)呢?下面小編給大家分享一段簡單的代碼實現(xiàn)Android 添加TextView刪除線
    2016-02-02
  • Android中AnimationDrawable使用的簡單實例

    Android中AnimationDrawable使用的簡單實例

    這篇文章介紹了Android中AnimationDrawable使用的簡單實例,有需要的朋友可以參考一下
    2013-10-10
  • Android調用手機拍照功能的方法

    Android調用手機拍照功能的方法

    這篇文章主要介紹了Android調用手機拍照功能的方法,實例分析了Android調用手機拍照功能及顯示圖片的相關技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-09-09
  • Android的Launcher啟動器中添加快捷方式及小部件實例

    Android的Launcher啟動器中添加快捷方式及小部件實例

    這篇文章主要介紹了在Android的Launcher啟動器中添加快捷方式及窗口小部件的方法,包括在自己的應用程序中添加窗口小部件AppWidget的例子,需要的朋友可以參考下
    2016-02-02

最新評論