1. 程式人生 > >[Android Framework] 8.1 PowerManagerService分析(三)——WakeLock機制

[Android Framework] 8.1 PowerManagerService分析(三)——WakeLock機制

WakeLock機制概述

WakeLock是android系統中一種鎖的機制,只要有程序持有這個鎖,系統就無法進入休眠狀態。應用程式要申請WakeLock時,需要在清單檔案中配置android.Manifest.permission.WAKE_LOCK許可權。

根據作用時間,WakeLock可以分為永久鎖和超時鎖,永久鎖表示只要獲取了WakeLock鎖,必須顯式的進行釋放,否則系統會一直持有該鎖;後者表示在到達給定時間後,自動釋放WakeLock鎖,其實現原理為方法內部維護了一個Handler。

根據釋放原則,WakeLock可以分為計數鎖和非計數鎖,預設為計數鎖,如果一個WakeLock物件為計數鎖,則一次申請必須對應一次釋放;如果為非計數鎖,則不管申請多少次,一次就可以釋放該WakeLock。以下程式碼為WakeLock申請釋放示例,要申請WakeLock,必須有PowerManager例項,如下:

 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
 wl.acquire();
 Wl.acquire(int timeout);//超時鎖

釋放時:

 wl.release();

Wakelock申請和釋放流程如下:
這裡寫圖片描述
在整個WakeLock機制中,對應不同的範圍,有三種表現形式:

  • PowerManger.WakeLock:PowerManagerService和其他應用、服務互動的介面;
  • PowerManagerService.WakeLock:PowerManager.WakeLock在PMS中的表現形式;
  • SuspendBlocker:PowerManagerService.WakeLock在向底層節點操作時的表現形式。

下面開始對wakelock的詳細分析。

PowerManager中的WakeLock

要獲取、申請Wakelock時,直接通過PowerManager的WakeLock進行。它作為系統服務的介面來供應用呼叫。
1.1.獲取WakeLock物件
獲取WakeLock例項在PowerManager中進行。
在應用中獲取WakeLock物件,方式如下:

 PowerManager.
WakeLock mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);

應用中獲取wakelock物件,獲取的是位於PowerManager中的內部類——WakeLock的例項,在PowerManager中看看相關方法:

public WakeLock newWakeLock(int levelAndFlags, String tag) {
    validateWakeLockParameters(levelAndFlags, tag);
    return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName());
}

在PowerManager的newWakeLock()方法中,首先進行了引數的校驗,然後呼叫WakeLock構造方法獲取例項,構造方法如下:

WakeLock(int flags, String tag, String packageName) {
//表示wakelock型別或等級
    mFlags = flags;
//一個tag,一般為當前類名
    mTag = tag;
//獲取wakelock的包名
    mPackageName = packageName;
//一個Binder標記
    mToken = new Binder();
    mTraceName = "WakeLock (" + mTag + ")";
}

除了構造方法中必須要傳入的引數之外,還有如下幾個屬性:

//表示內部計數
private int mInternalCount;
//表示內部計數
private int mExternalCount;
//表示是否是計數鎖
private boolean mRefCounted = true;
//表示是否已經持有該鎖
private boolean mHeld;
//表示和該wakelock相關聯的工作源,這在當一個服務獲取wakelock執行工作時很有用,以便計算工作成本
private WorkSource mWorkSource;
//表示一個歷史標籤
private String mHistoryTag;

1.2.WakeLock等級(類別)

Wakelock共有以下幾種等級:

//如果持有該型別的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

申請WakeLock

當獲取到WakeLock例項後,就可以申請WakeLock了。前面說過了,根據作用時間,WakeLock鎖可以分為永久鎖和超時鎖,根據釋放原則,WakeLock可以分為計數鎖和非計數鎖。申請方式如下:

 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
 wl.acquire();//申請一個永久鎖
 Wl.acquire(int timeout);//申請一個超時鎖

acquire()方法原始碼如下:

public void acquire() {
    synchronized (mToken) {
        acquireLocked();
    }
}
public void acquire(long timeout) {
    synchronized (mToken) {
        acquireLocked();
        //申請鎖之後,內部會維護一個Handler去完成自動釋放鎖
        mHandler.postDelayed(mReleaser, timeout);
    }
}

可以看到這兩種方式申請方式完全一樣,只不過如果是申請一個超時鎖,則會通過Handler延時傳送一個訊息,到達時間後去自動釋放鎖。
到這一步,對於申請wakelock的應用或系統服務來說就完成了,具體的申請在PowerManager中進行,繼續看看
acquireLocked()方法:

private void acquireLocked() {
    //應用每次申請wakelock,內部計數和外部計數加1
    mInternalCount++;
    mExternalCount++;
    //如果是非計數鎖或者內部計數值為1,即第一次申請該鎖,才會真正去申請
    if (!mRefCounted || mInternalCount == 1) {
        mHandler.removeCallbacks(mReleaser);
        Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
        try {
            //向PowerManagerService申請鎖
            mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
                    mHistoryTag);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        //表示此時持有該鎖
        mHeld = true;
    }
}

是否是計數鎖可以通過setReferenceCount()來設定,預設為計數鎖:

public void setReferenceCounted(boolean value) {
    synchronized (mToken) {
        mRefCounted = value;
    }
}

從acquire()方法可以看出,對於計數鎖來說,只會在第一次申請時向PowerManagerService去申請鎖,當該wakelock例項第二次、第三次去申請時,如果沒有進行過釋放,則只會對計數引用加1,不會向PowerManagerService去申請。如果是非計數鎖,則每次申請,都會調到PowerManagerService中去。

釋放WakeLock鎖

如果是通過acquire(long timeout)方法申請的超時鎖,則會在到達時間後自動去釋放,如果是通過acquire()方法申請的永久鎖,則必須進行顯式的釋放,否則由於系統一直持有wakelock鎖,將導致無法進入休眠狀態,從而導致耗電過快等功耗問題。

在前面分析申請鎖時已經說了,如果是超時鎖,通過Handler.post(Runnable)的方式進行釋放,該Runnable定義如下:

private final Runnable mReleaser = new Runnable() {
    public void run() {
        release(RELEASE_FLAG_TIMEOUT);
    }
};

RELEASE_FLAG_TIMEOUT是一個用於release()方法的flag,表示釋放的為超時鎖。
如果是永久鎖,則必須通過呼叫release()方法進行釋放了,該方法如下:

public void release() {
    release(0);
}

因此,不管是哪種鎖的釋放,其實都是在release(int)中進行的,只不過引數不同,該方法如下:

public void release(int flags) {
    synchronized (mToken) {
        //內部計數-1
        mInternalCount--;
        //如果釋放超時鎖,外部計數-1
        if ((flags & RELEASE_FLAG_TIMEOUT) == 0) {
            mExternalCount--;
        }
        //如果釋放非計數鎖或內部計數為0,並且該鎖還在持有,則通過PowerManagerService去釋放
        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;
            }
        }
        //如果時計數鎖,並且外部計數小於0,則丟擲異常
        if (mRefCounted && mExternalCount < 0) {
            throw new RuntimeException("WakeLock under-locked " + mTag);
        }
    }
}

對於計數鎖的釋放,每次都會對內部計數值減一,只有當你內部計數值減為0時,才會去呼叫PowerManagerService去真正的釋放鎖;如果釋放非計數鎖,則每次都會呼叫PowerManagerService進行釋放。

WakeLock型別及其特點見下表:

Level CPU Screen Keyboard Power Button 備註
PARTIAL_WAKE_LOCK On On/Off On/Off No Influence Cpu不受power鍵影響,一直執行直到所有鎖被釋放
FULL_WAKE_LOCK On Bright Bright release API17中已啟用,改用WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,當用戶在不同的應用程式間切換時可以正確的管理,同時不需要許可權
SCREEN_DIM_WAKE_LOCK On Dim/Bright Off release 同上
SCREEN_BRIGHT_WAKE_LOCK On Bright Off release 同上
PROXIMITY_SCREEN_OFF_WAKE_LOCK On/Off Bright/off Off relesase 不能和ACQUIRE_CAUSES_WAKEUP一起使用
DOZE_WAKE_LOCK On/Off Off Off release @hide,允許在doze狀態下使cpu進入suspend狀態,僅在doze狀態下有效,需要android.Manifest.permission.DEVICE_POWER許可權
DRAW_WAKE_LOCK On/Off Off Off No @hide,允許在doze狀態下進行螢幕繪製,僅在doze狀態下有效,需要DEVICE_POWER許可權
ACQUIRE_CAUSES_WAKEUP Wakelock 標記,一般情況下,獲取wakelock並不能喚醒裝置,加上這個標誌後,申請wakelock後也會喚醒螢幕。如通知、鬧鐘… 不能和PARTIAL_WAKE_LOCK一起使用
ON_AFTER_RELEASE Wakelock 標記,當釋放該標記的鎖時,會亮一小會再滅屏 同上

對於開發者來說,只能申請非@hide的鎖,即PARTIAL_WAKE_LOCK、SCREEN_DIM_WAKE_LOCK、SCREEN_BRIGHT_WAKE_LOCK、FULL_WAKE_LOCK四類。

比如,我要獲取一個wakelock型別為PARTIAL_WAKE_LOCK的WakeLock鎖,則在申請這個鎖後,雖然螢幕、鍵盤燈可以關閉,但CPU將一直處於活動狀態,不受power鍵的控制。獲得WakeLock物件後,可以根據自己的需求來申請不同形式的鎖。接下來我們繼續分析在申請、釋放鎖時PowerManagerService中的流程。

PowerManagerService中的WakeLock

WakeLock申請

在申請WakeLock時,當應用層呼叫完acquire()方法後,由PowerManager去處理了。對於兩種申請方式,最終都呼叫了acquireLocked()進行申請,acquireLocked()又向下呼叫,讓mService去處理,我們通過上面的分析知道,這個mService就是PMS.BinderService,該方法如下:

private void acquireLocked() {
    if (!mRefCounted || mCount++ == 0) {
        mHandler.removeCallbacks(mReleaser);
        try {
        	//向下呼叫PMS去處理
            mService.acquireWakeLock(mToken, mFlags, mTag, 
                         mPackageName, mWorkSource,mHistoryTag);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        mHeld = true;//持有鎖標記置為true
    }

其中mHeld是一個判斷是否持有鎖的標記,在應用中可以通過呼叫WakeLock的isHeld()來判斷是否持有WakeLock。

PMS中的acquireWakeLock()方法如下:

@Override // Binder call
public void acquireWakeLock(IBinder lock, int flags, String tag, 
                     String packageName,WorkSource ws, String historyTag) {
	......
    //檢查wakelock級別
    PowerManager.validateWakeLockParameters(flags, tag);
	//檢查WAKE_LOCK許可權
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LO   
                        CK, null);
    //如果是DOZE_WAKE_LOCK級別wakelock,還要檢查DEVICE_POWER許可權
    if ((flags & PowerManager.DOZE_WAKE_LOCK) != 0) {
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.DEVICE_POWER, null);
    }
    //ws = null
	......
    //重置當前執行緒上傳入的IPC標誌
    final long ident = Binder.clearCallingIdentity();
    try {
        acquireWakeLockInternal(lock, flags, tag, packageName, ws, historyTag,
                  uid, pid);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

在這個方法中,首先進行了WakeLock型別檢查,避免無效的WakeLock型別,然後進行許可權的檢查,WakeLock需要android.Manifest.permission.WAKE_LOCK許可權,如果申請的WakeLock型別是DOZE_WAKE_LOCK,則還需要android.Manifest.permission.DEVICE_POWER許可權(見上表),檢查完畢後重置Binder的IPC標誌,然後呼叫下一個方法acquireWakeLockInternal()

private void acquireWakeLockInternal(IBinder lock, int flags, String tag, String packageName,
        WorkSource ws, String historyTag, int uid, int pid) {
    synchronized (mLock) {
        //PMS中的WakeLock類
        WakeLock wakeLock;
        //查詢是否已存在該PM.WakeLock例項
        int index = findWakeLockIndexLocked(lock);
        boolean notifyAcquire;
        //是否存在wakelock
        if (index >= 0) {
            wakeLock = mWakeLocks.get(index);
            if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
            	//更新wakelock
                notifyWakeLockChangingLocked(wakeLock, flags, tag, packageName,
                        uid, pid, ws, historyTag);
                wakeLock.updateProperties(flags, tag, packageName, 
                                ws, historyTag, uid, pid);
            }
            notifyAcquire = false;
        } else {
              //從SpareArray<UidState>中查詢是否存在該uid
              UidState state = mUidState.get(uid);
              if (state == null) {
                  state = new UidState(uid);
                  //設定該Uid的程序狀態
                  state.mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
                  mUidState.put(uid, state);
              }
            //將該uid申請的WakeLock計數加1
            //建立新的PMS.WakeLock例項
            wakeLock = new WakeLock(lock, flags, tag, packageName, ws, 
                              historyTag, uid, pid);
            try {
                lock.linkToDeath(wakeLock, 0);
            } catch (RemoteException ex) {
                throw new IllegalArgumentException("Wake lock is already dead.");
            }
            //新增到wakelock集合中
            mWakeLocks.add(wakeLock);
            //用於設定PowerManger.PARTIAL_WAKE_LOCK能否可用
            //1.快取的不活動程序不能持有wakelock鎖               
            //2.如果處於idle模式,則會忽略掉所有未處於白名單中的應用申請的鎖
            setWakeLockDisabledStateLocked(wakeLock);
            //表示有新的wakelock申請了
            notifyAcquire = true;
        }
        //判斷是否直接點亮螢幕,如果帶有點亮螢幕標誌值,並且wakelock型別為
        //FULL_WAKE_LOCK,SCREEN_BRIGHT_WAKE_LOCK,SCREEN_DIM_WAKE_LOCK,則進行下 
        //步處理
        applyWakeLockFlagsOnAcquireLocked(wakeLock, uid);
        //更新標誌位
        mDirty |= DIRTY_WAKE_LOCKS;
        updatePowerStateLocked();
        if (notifyAcquire) {
           //當申請了鎖後,在該方法中進行長時鎖的判斷,通知BatteryStatsService      
           // 進行統計持鎖時間等
            notifyWakeLockAcquiredLocked(wakeLock);
        }
    }
}

首先通過傳入的第一個引數IBinder進行查詢WakeLock是否已經存在,若存在,則不再進行例項化,在原有的WakeLock上更新其屬性值;若不存在,則建立一個WakeLock物件,同時將該WakeLock儲存到List中。此時已經獲取到了WakeLock物件,這裡需要注意的是,此處的WakeLock物件和PowerManager中獲取的不是同一個WakeLock哦!

獲取到WakeLock例項後,還通過setWakeLockDisabledStateLocked(wakeLock)進行了判斷該WakeLock是否可用,主要有兩種情況:

  • 1.快取的不活動程序不能持有WakeLock鎖;
  • 2.如果處於idle模式,則會忽略掉所有未處於白名單中的應用申請的鎖。

根據情況會設定WakeLock例項的disable屬性值表示該WakeLock是否不可用。

下一步進行判斷是否直接點亮螢幕,如果獲得的WakeLock帶有ACQUIRE_CAUSES_WAKEUP標誌,並且WakeLock型別為FULL_WAKE_LOCK,SCREEN_BRIGHT_WAKE_LOCK,SCREEN_DIM_WAKE_LOCK這三種其中之一(isScreenLock判斷),則會直接喚醒螢幕,如下程式碼中的applyWakeLockFlagsOnAcquireLocked(wakeLock, uid)方法:

private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock, int uid) {
    if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0
            && isScreenLock(wakeLock)) {
		......
        wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), wakeLock.mTag, opUid,
                opPackageName, opUid);
    }
}

wakeUpNoUpdateLocked()中:

private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid,
        String opPackageName, int opUid) {
//如果eventTime<上次休眠時間、裝置當前處於喚醒狀態、沒有啟動完成、沒有準備
//完成,則不需要更新,返回false
    if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
            || !mBootCompleted || !mSystemReady) {
        return false;
    }
        //更新最後一次喚醒時間值
        mLastWakeTime = eventTime;
   //設定wakefulness
        setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
        //通知BatteryStatsService/AppService螢幕狀態發生改變
        mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid);
    //更新使用者活動事件時間值
        userActivityNoUpdateLocked(
                eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_POWER);
    }
    return true;
}

wakeUpNoUpdateLocked()方法是喚醒裝置的主要方法。在這個方法中,首先更新了mLastWakeTime這個值,表示上次喚醒裝置的時間,在系統超時休眠時用到這個值進行判斷。現在,只需要知道每次亮屏,都走的是這個方法,關於具體是如何喚醒螢幕的,在第5節中進行分析。

現在我們繼續回到acquireWakeLockInternal()方法的結尾處,當檢查完WakeLock的ACQUIRE_CAUSES_WAKEUP標誌後,更新mDirty,然後呼叫updatePowerStateLocked()方法,這個方法在第二篇文章中說過了,是整個PMS的核心方法,在這個方法中呼叫了幾個關鍵方法,這些方法已經進行了分析,只剩一個WakeLock相關的updateSuspendBlockerLocked()沒有分析,現在開始分析這個方法,該方法如下:

private void updateSuspendBlockerLocked() {
	//是否需要保持CPU活動狀態的SuspendBlocker鎖,具體表現為持有Partical WakeLock
final boolean needWakeLockSuspendBlocker = 
           ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
    //是否需要保持CPU活動狀態的SuspendBlocker鎖,具體表現保持螢幕亮
    final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
    //是否自動掛起,如果不需要螢幕保持喚醒,則說明可以自動掛起CPU
    final boolean autoSuspend = !needDisplaySuspendBlocker;
    //是否處於互動模式,螢幕處於Bright或者Dim狀態時為true
    final boolean interactive = mDisplayPowerRequest.isBrightOrDim();
    //mDecoupleHalAutoSuspendModeFromDisplayConfig:自動掛起模式和顯示狀態解偶
    if (!autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
    	//禁止CPU自動掛起模式
        setHalAutoSuspendModeLocked(false);
    }
    //如果存在PARTIAL_WAKE_LOCK型別的WakeLock,申請mWakeLockSuspendBlocker鎖
    if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
        mWakeLockSuspendBlocker.acquire();
        mHoldingWakeLockSuspendBlocker = true;
    }
    //如果當前螢幕需要保持亮屏,申請mDisplaySuspendBlocker鎖
    if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) {
        mDisplaySuspendBlocker.acquire();
        mHoldingDisplaySuspendBlocker = true;
    }
    //???
    if (mDecoupleHalInteractiveModeFromDisplayConfig) {
    	//設定Hal層互動模式?
        if (interactive || mDisplayReady) {
        	//???
            setHalInteractiveModeLocked(interactive);
        }
    }
    //如果不再持有PARTIAL_WAKELOCK型別的WakeLock鎖,釋放mWakeLockSuspendBlocker鎖
    if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
        mWakeLockSuspendBlocker.release();
        mHoldingWakeLockSuspendBlocker = false;
    }
    //如果不再需要螢幕保持亮屏,釋放mDispla