Android: keyguard分析之二:息屏与亮屏流程篇

息屏与亮屏时, Keyguard绘制的基本流程
 

手机的Power键在灭屏是会加载keyguard, 保证用户在亮屏时,第一时间看到锁屏界面,以保证用户的良好体验.

在亮屏过程中涉及到了两条主要的线程: PowerManager Thread 和DisplayManager Thread


> PowerManager: 主要是Power的按键事件(PhoneWindowManager.java)传递到PowerManagerService.java, 然后进行一系列的操作.

> DisplayManager: 会负责进行屏幕状态的设置以及屏幕的熄灭与点亮(与屏幕有关),此外跟屏幕相关的一些可见性操作. 显示操作.

根据上面所述, 找到power按键的事件, 手机的实体按键基本都是在PhoneWindowManaer.java 被处理的.找到入口:

# PhoneWindowManger

    private void powerPress(long eventTime, boolean interactive, int count) {
        ...
        if (interactive && !mBeganFromNonInteractive) {
            switch (mShortPressOnPowerBehavior) {
                ...
                case SHORT_PRESS_POWER_GO_TO_SLEEP:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                    break;
                case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                    break;
                case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                    launchHomeFromHotKey();
                    break;
                case SHORT_PRESS_POWER_GO_HOME://进入home界面,应该是亮屏操作
                    launchHomeFromHotKey(true /* awakenFromDreams */, false /*respectKeyguard*/);
                    break;
            }
        }
    }

息屏操作的入口在:  mPowerManager.goToSleep(), 这是强制手机进入休眠状态.

# PowerManager

    public void goToSleep(long time, int reason, int flags) {
        try {
            mService.goToSleep(time, reason, flags);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

mService是一个Ipowermanager对象, 其使用的aidl机制进行数据共享, 其AIDL Service为 PowerMangerService.java

# PowerManagerService$BinderService

    public void goToSleep(long eventTime, int reason, int flags) {
        ...      
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.DEVICE_POWER, null);

        final int uid = Binder.getCallingUid();
        final long ident = Binder.clearCallingIdentity();
        try {
            goToSleepInternal(eventTime, reason, flags, uid);
        } finally {
            Binder.restoreCallingIdentity(ident);//恢复远程调用端的uid和pid信息
        }
    }
    
    private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
        synchronized (mLock) {
            ...//判断与sensor有关的息屏
            if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
                updatePowerStateLocked();
            }
        }
    }
    
    //全局更新power状态. 主要告知手机即将息屏.
    private void updatePowerStateLocked() {
        ...
        try {
            ...
            for (;;) {
                ...                         
                if (!updateWakefulnessLocked(dirtyPhase1)) {
                    break;
                }
            }
        ...
        }
    }

    private boolean updateWakefulnessLocked(int dirty) {
        ...//多重判断    
                if (shouldNapAtBedTimeLocked()) {
                    changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
                } else {
                    changed = goToSleepNoUpdateLocked(time,
                            PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
                }
            
        //多重判断结束
        return changed;
    }

通过分析: goToSleepNoUpdateLocked() 和 napNoUpdateLocked() 最终会调用:

# PowerManagerService.java

    private void setWakefulnessLocked(int wakefulness, int reason) {
        if (mWakefulness != wakefulness) {
            mWakefulness = wakefulness;
            mWakefulnessChanging = true;
            mDirty |= DIRTY_WAKEFULNESS;
            mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
        }
    }

只是这两个方法的区别是: 是否是自动去息屏操作. 

Notifier 主要是用来发送 Power 状态的广播. 在这里边经过一些列的判断操作, 直接调用了 handleEarlyInteractiveChange() -> mPolicy.startedGoingToSleep(why), 此时PhoneWindowManager开始事件分发:

# PhoneWindowManager.java
    
    public void startedGoingToSleep(int why) {
        ...
        mGoingToSleep = true;
        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.onStartedGoingToSleep(why);
        }
    }

这里就不看KeyguardServiceDelegate.java 与 KeyguardService.java, 只要了解他们之间的aidl的跨进程服务. Keyguard.java 是
服务端. KeyguardServiceWrapper.java 是客户端.

息屏操作, 此时进入到了SystemUI, 通知SystemUI的手机进入息屏状态.

# KeyguardViewMediator.java

    public void onStartedGoingToSleep(int why) {
        ...
        synchronized (this) {
            ...
            if (mShowing) {
                mPendingReset = true;
            } else if ((why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT && timeout > 0)
                    || (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately/*keyguard的状态,非wipe状态*/)
                    && !mIsIPOShutDown/*非关机*/) {
                doKeyguardLaterLocked(timeout);
                mLockLater = true;
            } 
            ...
        }
        KeyguardUpdateMonitor.getInstance(mContext).dispatchStartedGoingToSleep(why);//稍后分析
        notifyStartedGoingToSleep(); //通知息屏结束,锁屏了.
    }
    
    
    private void doKeyguardLaterLocked(long timeout) {
        long when = SystemClock.elapsedRealtime() + timeout;
        Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
        intent.putExtra("seq", mDelayedShowingSequence);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        //发送一个广播,调用锁屏函数.
        PendingIntent sender = PendingIntent.getBroadcast(mContext,
                0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
        doKeyguardLaterForChildProfilesLocked();
    }
    
    //DELAYED_KEYGUARD_ACTION 作为广播信息
    private final BroadcastReceiver mBroadcastReceiver = new roadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (DELAYED_KEYGUARD_ACTION.equals(action)) {
                final int sequence = intent.getIntExtra("seq", 0);
                ...
                synchronized (KeyguardViewMediator.this) {
                    if (mDelayedShowingSequence == sequence) {
                        mSuppressNextLockSound = true;
                        doKeyguardLocked(null); //锁屏路径
                    }
                }
            }
            ...
        }
    };

总这里看出: 其实在息屏操作中, KeyguardViewMediator最终通过广播调用了  doKeyguardLocked(null), 这个函数应该有印象, 在开机的时候就被调用过. 不过调用它的函数为 onSystemReady(). 这样,我们就不用再通过这条路径往下走了, 其结果应该差不多. 也就证明了keyguard 是在息屏时, 被创建的.

> 在开机状态中, Keyguar是在息屏时被创建.

> 息屏下创建Keyguard, 与是否是主动息屏这个行为无关.   


<br/>

屏幕的亮与灭

在点击 Power 按键进行亮屏时:

# PhoneWindowManager

void launchHomeFromHotKey(final boolean awakenFromDreams, final boolean respectKeyguard) {
        if (respectKeyguard) {
            if (isKeyguardShowingAndNotOccluded()) {
                // don't launch home if keyguard showing
                return;
            }
            ...    
        }
        ...
    }

这里仅仅是判断是有 keyguard 这个界面. 如果有, 将停留在这个 keyguard 的界面,; 如果没有, 将进行后续其他的处理.

屏幕的亮起与熄灭,主要在 DisplayPowerController 中驱动. 之前已经说了 Power 的
的控制主要在 Powermanager. 

# PowerMnagerService

    private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
        synchronized (mLock) {
            if (mProximityPositive && reason == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON) {
                ...
                updatePowerStateLocked();
                return;
            }

            if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
                updatePowerStateLocked();
            }
        }
    }

    private void updatePowerStateLocked() {
        ...
        try {
            ...
            // Phase 2: Update display power state.
            boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
            ...
        }
    }

    private boolean updateDisplayPowerStateLocked(int dirty) {
        final boolean oldDisplayReady = mDisplayReady;
        if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
                | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
                | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_BOOT_IPO)) != 0) {
            ...
            mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
                    mRequestWaitForNegativeProximity);
            ...
        }
        return mDisplayReady && !oldDisplayReady;
    }

上述可以看出, 在息屏的时候, PowerManager服务通过一定的操作, 调用了 DisplayPowerController, 来进行显示操作. DisplayManagerInternal 和DisplayManagerService$LocalService作为中间类来进行处理.

首先从requestPowerState()方法开始分析. 

# DisplayPowerController.java

 public boolean requestPowerState(DisplayPowerRequest request,
            boolean waitForNegativeProximity) {
        synchronized (mLock) {
            boolean changed = false;
            ...
            if (changed && !mPendingRequestChangedLocked) {
                mPendingRequestChangedLocked = true;
                sendUpdatePowerStateLocked(); //屏幕发生变化
            }
            ...
            return mDisplayReadyLocked;
        }
    }
    
    
    private void sendUpdatePowerStateLocked() {
        if (!mPendingUpdatePowerStateLocked) {
            ...
            Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);//发送到Handler子线程去执行.
            msg.setAsynchronous(true);
            mHandler.sendMessage(msg);
        }
    }


    private void updatePowerState() {//一系列的更新操作.
        ...
        //驱动屏幕状态变化.
        animateScreenStateChange(state, performScreenOffTransition);
        state = mPowerState.getScreenState();
        }
    }
    
    
    private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
        ...
        //息屏操作, 指向了setScreenState();
        if (mPendingScreenOff && target != Display.STATE_OFF) {
            setScreenState(Display.STATE_OFF);
            mPendingScreenOff = false;
            ...
        }
        
        //亮屏操作, 指向了setScreenState()
        if (target == Display.STATE_ON) {
            if (!setScreenState(Display.STATE_ON)) {
                return; // screen on blocked
            }
        }
        ...
    }
    //从上述可以看出,亮屏操作与息屏操作都是需要走setScreenState()方法的.
    
    private boolean setScreenState(int state) {
        ...
        //告诉窗体管理WindowManagerPolicy的屏幕状态.
        final boolean isOff = (state == Display.STATE_OFF);
        if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF
                && !mScreenOffBecauseOfProximity) {
            unblockScreenOn();
            mWindowManagerPolicy.screenTurnedOff();
        } else if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF) {
            if (mPowerState.getColorFadeLevel() == 0.0f) {//控制显示状态
                blockScreenOn(); //阻止屏幕亮
            } else {
                unblockScreenOn();//解除阻止屏幕状态.
            }
            mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker);
        }

        // Return true if the screen isn't blocked.
        return mPendingScreenOnUnblocker == null;
    }
    

可以看出, 息屏与亮屏过程都会触发WindowManagerPolicy进行一系列事件的操作. 在 PhoneWindowManagerPolicy.java  中, 亮屏与息屏这个消息都会触发 keyguard 代理进行处理.

# PhoneWindowManagerPolicy
    //息屏时
    public void screenTurnedOff() {
        ...
        synchronized (mLock) {
            ...
            if (mKeyguardDelegate != null) {
                mKeyguardDelegate.onScreenTurnedOff();
            }
        }
    }

    //亮屏时
    public void screenTurningOn(final ScreenOnListener screenOnListener) {
        ...
        synchronized (mLock) {
            ...
            if (mKeyguardDelegate != null) {
                mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
                mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000);
                mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
            } else {
               ...
                finishKeyguardDrawn();
            }
        }
    }

接着对 KeyguardServiceDelegate 的对象进行判断.如果为空, 即表示未进行keyguard的设置;如果为非空,即表明:keyguard设置了某一 model 形式.

# KeyguardServiceDelegate.java
    //息屏
    public void onScreenTurnedOff() {
        if (mKeyguardService != null) {
            mKeyguardService.onScreenTurnedOff();
        }
        mKeyguardState.screenState = SCREEN_STATE_OFF;
    }

    //亮屏
    public void onScreenTurningOn(final DrawnListener drawnListener) {
        if (mKeyguardService != null) {
            //当手机在息状态时,必然是开启了keyguard的服务.如果设置了keyguard.
            mKeyguardService.onScreenTurningOn(new KeyguardShowDelegate(drawnListener));
        } else {
            mDrawnListenerWhenConnect = drawnListener;
            showScrim();
        }
        mKeyguardState.screenState = SCREEN_STATE_TURNING_ON;
    }
    
    public void screenTurnedOn() {
        synchronized (mLock) {
            if (mKeyguardDelegate != null) {
                mKeyguardDelegate.onScreenTurnedOn();
            }
        }
    }

接着进入到SystemUI,开启Keyguard服务,KeyguardService.java.我们可以发现KeyguardServiceWrapper这个类对象是启动KeyguardService的一个中间类,代理分派任务到Systemui.

# KeyguardService.java

    private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
        //息屏
        public void onScreenTurnedOff() {
            checkPermission();
            mKeyguardViewMediator.onScreenTurnedOff();
        }
        
        //亮屏
        ...
        @Override // Binder interface
        public void onScreenTurningOn(IKeyguardDrawnCallback callback) {
            checkPermission();
            mKeyguardViewMediator.onScreenTurningOn(callback);
        }
        ...
    }

Keyguard服务开启后,也是先检查权限,然后再将事件分派出去.

#KeyguardViewMediator.java
    
    //息屏
    public void onScreenTurnedOff() {
        notifyScreenTurnedOff();
        mUpdateMonitor.dispatchScreenTurnedOff();
    }
    
    //> 息屏做了两件事:
    //1. notifyScreenTurnedOff()将事件交由StatusBarKeyguardViewManager处理
    //2. dispatchScreenTurnedOff()将事件交于KeyguardUpdateMonitor处理.
    
    
    //亮屏
    public void onScreenTurningOn(IKeyguardDrawnCallback callback) {
        notifyScreenOn(callback);
    }

先看看息屏状态时, 流程走向

notifyScreenTurnedOff(): 通知StatusBarKeyguardViewManager去进行处理

# StatusBarKeyguardViewManager.java
    
    public void onScreenTurnedOff() {
        mScreenTurnedOn = false;
        mPhoneStatusBar.onScreenTurnedOff();
    }
    
# PhoneStatusBar.java

    public void onScreenTurnedOff() {
        mFalsingManager.onScreenOff();
    }

# FalsingManager.jva
 
   public void onScreenOff() {
        mDataCollector.onScreenOff(); 
        mScreenOn = false;
        sessionExitpoint(false /* force */); //传感器接触注册, 停止传感器的使用
    }
    
# DataCollector.java

    public void onScreenOff() {
        addEvent(PhoneEvent.ON_SCREEN_OFF); //传感器log日志手机
        sessionExitpoint(Session.FAILURE);  //touch事件收集器
    }

 
dispatchScreenTurnedOff() 通知 KeyguardUpdateMonitor进行处理

# KeyguardUpdateMonitor.java

    private void handleScreenTurnedOff() {
        final int count = mCallbacks.size();
        for (int i = 0; i < count; i++) {
            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onScreenTurnedOff();
            }
        }
    }

看看cb的实现体
# KeyguardHostView.java

    public void onScreenTurnedOff() {
        mSecurityContainer.onScreenTurnedOff(); //空实现
    }

对于亮屏的这个过程, 其余息屏的方式是一样的.都是需要经过

# KeyguardViewMediator.java

    private void handleNotifyScreenTurningOn(IKeyguardDrawnCallback callback) {
        synchronized (KeyguardViewMediator.this) {
            //更新notification的时间和传感器注册, 以及事件的收集.
            mStatusBarKeyguardViewManager.onScreenTurningOn();
            if (callback != null) {
                if (mWakeAndUnlocking) {//判断锁的形式.
                    mDrawnCallback = callback;
                } else {
                    //回调一个接口对象进行回调绘制.
                    notifyDrawn(callback);
                }
            }
        }
    }

可以看出, "息屏"与"正在亮屏"最后都会调用接口对象实现其息屏与亮屏的操作.

,那么也存在"完成开机"这个实现, 再次回到原点.直接看 StatusBarKeyguardViewManager.java

# StatusBarKeyguardViewManager.java

    public void onScreenTurnedOn() {
        mScreenTurnedOn = true;
        if (mDeferScrimFadeOut) {
            mDeferScrimFadeOut = false;
            animateScrimControllerKeyguardFadingOut(0, WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
                    true /* skipFirstFrame */);
            updateStates();
        }
        if (mPhoneStatusBar != null) {
            mPhoneStatusBar.onScreenTurnedOn();//后续操作, 也是一层一层的通知
        }
    }


---

> 在亮屏与灭屏阶段, keyguard的绘制其实在灭屏阶段.

> 屏幕的显示亮屏与灭屏, 与keyguard的并无直接关系.

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是Android Studio简单实现监听系统亮屏的广播的步骤: 1.在AndroidManifest.xml文件中添加权限和广播接收器的声明: ```xml <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <receiver android:name=".ScreenReceiver"> <intent-filter> <action android:name="android.intent.action.SCREEN_ON" /> <action android:name="android.intent.action.SCREEN_OFF" /> </intent-filter> </receiver> ``` 2.创建一个ScreenReceiver类,继承BroadcastReceiver类,并实现onReceive()方法: ```java public class ScreenReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { // 幕亮起时的操作 } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { // 幕熄灭时的操作 } } } ``` 3.在onReceive()方法中添加相应的操作,例如: ```java public class ScreenReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { Toast.makeText(context, "幕已亮起", Toast.LENGTH_SHORT).show(); } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { Toast.makeText(context, "幕已熄灭", Toast.LENGTH_SHORT).show(); } } } ``` 4.在需要监听幕亮灭的Activity中注册广播接收器: ```java public class MainActivity extends AppCompatActivity { private ScreenReceiver mScreenReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mScreenReceiver = new ScreenReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); registerReceiver(mScreenReceiver, filter); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(mScreenReceiver); } } ``` 5.在Activity销毁时注销广播接收器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值