• Android7.0 PowerManagerService 之亮灭屏(一)


    本篇从按下power按键后,按键事件从InputManagerService 传到PhoneWindowManager.java开始分析power 按键做屏幕亮灭过程的分析,关于power 按键的其他行为参考另一篇博文(Android 7.0 Power 按键处理流程

        (注:博客园显示的图片很模糊,上传的为大图,可以图片另存为查看)

        言归正传,本篇涉及的几个模块(文件)如下,先做个简单的介绍有个直观大概的了解,方便后面流程细节的理解。

    Ø  PowerManagerService.Java(/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java)

       PMS,是Android系统中的电源处理服务,主要负责电源相关的计算和决策,如是否应该灭屏 或者让屏幕变暗,是否应该让系统休眠等等。

    Ø  DisplayPowerController.java(/frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java

      DPC,管理显示设备(这里指的显示设备是屏幕)状态,主要处理距离传感器(如打电话时候靠近则灭屏,离开时候屏幕亮起)以及亮灭屏动画(包括根据光感传感器计算屏幕目标亮度值)。在DisplayManagerService.java(DMS)中实例化一个对象,以DMS为桥梁与PMS进行交互通过异步回调机制来通知PMS那些发生了改变。同时也与WMS进行交互。

    Ø  DisplayPowerState.java/frameworks/base/services/core/java/com/android/server/display/DisplayPowerState.java)

      DPS,管理显示设备的状态仅在DPC中实例化一个对象,是DPC的一部分。

    Ø  Notifier.java:( /frameworks/base/services/core/java/com/android/server/power/Notifier.java

      将电源状态的重要变化,通过广播通知出去。

    Ø  ColorFade.java:(/frameworks/base/services/core/java/com/android/server/display/ColorFade.java

      是负责屏幕由关到开,由开到关的一些GL动画,由DPC进行控制。

    Ø  AutomaticBrightnessController.java:(/frameworks/base/services/core/java/com/android/server/display/AutomaticBrightnessController.java)

      主要处理光传感器,将底层上传的参数进行处理计算,将计算的新的亮度值传给DPC来设定屏幕的亮度值(即根据环境的光线强度来计算屏幕的亮暗程度)。

    Ø  RampAnimator.java:(/frameworks/base/services/core/java/com/android/server/display/RampAnimator.java)

      仅仅是屏幕亮度渐变动画。

    一、Power按键的上报与处理

    详细见【Android 7.0 Power 按键处理流程】此处仅略微的复习一下,方便了后面的理解。

    1Power按键的上报

    InputManagerService收到power按键事件经过一系列的处理和转换最终将会传递到PhoneWindowManager(PWM)的interceptKeyBeforeQueueing()函数来做具体的业务逻辑.下图为上报的流程图。

    2power 按键关于量灭屏处理

    关于量灭屏的处理主要在interceptKeyBeforeQueueing()函数中。判断是否是isWakeKey,如果是则在此函数的最后通过调用wakeUp()函数来具体处理,这就拉开了本文的序幕。

    注:此函数很长本文删除无关的部分,仅仅保留部分和量灭屏相关的说明具体处理流程即可。详细参考【Android 7.0 Power 按键处理流程

    A: 按键处理判断是否要量灭屏

    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    
            boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
                    || event.isWakeKey();
            if (interactive || (isInjected && !isWakeKey)) {
                // When the device is interactive or the key is injected pass the
                // key to the application.
                result = ACTION_PASS_TO_USER;
                isWakeKey = false;
    
                if (interactive) {
                    // If the screen is awake, but the button pressed was the one that woke the device
                    // then don't pass it to the application
                    if (keyCode == mPendingWakeKey && !down) {
                        result = 0;
                    }
                    // Reset the pending key
                    mPendingWakeKey = PENDING_KEY_NULL;
                }
            } else if (!interactive && shouldDispatchInputWhenNonInteractive(event)) {
                // If we're currently dozing with the screen on and the keyguard showing, pass the key
                // to the application but preserve its wake key status to make sure we still move
                // from dozing to fully interactive if we would normally go from off to fully
                // interactive.
                result = ACTION_PASS_TO_USER;
                // Since we're dispatching the input, reset the pending key
                mPendingWakeKey = PENDING_KEY_NULL;
            } else {
                // When the screen is off and the key is not injected, determine whether
                // to wake the device but don't pass the key to the application.
                result = 0;
                if (isWakeKey && (!down || !isWakeKeyWhenScreenOff(keyCode))) {
                    isWakeKey = false;
                }
                // Cache the wake key on down event so we can also avoid sending the up event to the app
                if (isWakeKey && down) {
                    mPendingWakeKey = keyCode;
                }
            }
    
            // If the key would be handled globally, just return the result, don't worry about special
            // key processing.
            if (isValidGlobalKey(keyCode)
                    && mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) {
                if (isWakeKey) {
                    wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");
                }
                return result;
            }
    
         ....................
    
                case KeyEvent.KEYCODE_ENDCALL: {
                    result &= ~ACTION_PASS_TO_USER;
                    if (down) {
                        TelecomManager telecomManager = getTelecommService();
                        boolean hungUp = false;
                        if (telecomManager != null) {
                            hungUp = telecomManager.endCall();
                        }
                        if (interactive && !hungUp) {
                            mEndCallKeyHandled = false;
                            mHandler.postDelayed(mEndCallLongPress,
                                    ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
                        } else {
                            mEndCallKeyHandled = true;
                        }
                    } else {
                        if (!mEndCallKeyHandled) {
                            mHandler.removeCallbacks(mEndCallLongPress);
                            if (!canceled) {
                                if ((mEndcallBehavior
                                        & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {
                                    if (goHome()) {
                                        break;
                                    }
                                }
                                if ((mEndcallBehavior
                                        & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
                                    mPowerManager.goToSleep(event.getEventTime(),
                                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                                    isWakeKey = false;
                                }
                            }
                        }
                    }
                    break;
                }
    
      
    
    ....................
    
            if (useHapticFeedback) {
                performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
            }
    
            if (isWakeKey) {
                wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");
            }
    
            return result;
        }

    BwakeUp处理量灭屏

    private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) {  
        final boolean theaterModeEnabled = isTheaterModeEnabled();  
        if (!wakeInTheaterMode && theaterModeEnabled) {  
            return false;  
        }  
      
        if (theaterModeEnabled) {  
            Settings.Global.putInt(mContext.getContentResolver(),  
                        Settings.Global.THEATER_MODE_ON, 0);  
            }  
          
            mPowerManager.wakeUp(wakeTime, reason);      //调用PowerManagerService亮屏操作
            return true;  
        } 
    }

    二、PowerManagerService处理量灭屏过程

    从上面的分析可知,在PWM中处理按键事件如果需要唤醒屏幕则会调用PWM的wakeUp()函数,此函数会调用PMS 的wakeUp()函数来具体处理。

    从上面的分析可知,在PWM中处理按键事件如果需要唤醒屏幕则会调用PWM的wakeUp()函数,此函数会调用PMS 的wakeUp()函数来具体处理。

      注:限于篇幅本文仅列出重要的函数和调用过程

    首先来个概览,了解一下主要完成了如下三件事

    1)  由Notifier根据系统的具体状态来发出广播

    2)  PMS 通过updatePowerStateLocked()计算和更新电源的全局状态

    3)  PMS将最新的电源状态等传入DPC中,根据距离传感器和光感传感器,计算具体的屏幕亮度和量灭屏动画等

    4)  DPC通知PWM绘制keyguard与windows(此时会block等待绘制完成,灭屏时候无需绘制),绘制完成后通知DPC继续

    5)  DPC具体调用显示设备开启或关闭屏幕(执行量灭屏的动画效果)

    1)PowerManagerService--wakeUpNoUpdateLocked()

    PMS首先讲wakefullness状态. 之后发送亮灭屏广播通知其他应用手机处于亮屏还是灭屏状态。

    private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid,  
            String opPackageName, int opUid) {  
        if (DEBUG_SPEW) {  
            Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid);  
        }  
      
        if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE  
                || !mBootCompleted || !mSystemReady) {  
            return false;        //判断是否要去亮屏  
         }  
       
         Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");  
         try {  
             switch (mWakefulness) {  
                 case WAKEFULNESS_ASLEEP:  
                     Slog.i(TAG, "Waking up from sleep due to"+opPackageName+" "+reason+" (uid " + reasonUid +")...");  
                     break;  
                 case WAKEFULNESS_DREAMING:  
                     Slog.i(TAG, "Waking up from dream due to"+opPackageName+" "+reason+" (uid " + reasonUid +")...");  
                     break;  
                 case WAKEFULNESS_DOZING:  
                     Slog.i(TAG, "Waking up from dozing due to"+opPackageName+" "+reason+" (uid " + reasonUid +")...");  
                     break;  
             }  
       
             mLastWakeTime = eventTime;   //设置最后一次唤醒的时间  
             setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);   //Notifier调用onWakefulnessChangeStarted发送亮屏广播  
       
             mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid);  //调用Notifier通知battery处理  
             userActivityNoUpdateLocked(     //更新最后一次用户事件时间  
                     eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);  
         } finally {  
             Trace.traceEnd(Trace.TRACE_TAG_POWER);  
         }  
         return true;  
     }  

    1)Notifier 发送量灭屏广播

    Notifier发送广播前会与与AMS,WMS,IMS进行交互,通知各模块电源状态的改变,各模块会自行处理电源状态改变通知。

    A:onWakefulnessChangeStarted()

    public void onWakefulnessChangeStarted(final int wakefulness, int reason) {  
       final boolean interactive = PowerManagerInternal.isInteractive(wakefulness); //亮屏true, 灭屏false  
       if (DEBUG) {  
           Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness  
                   + ", reason=" + reason + ", interactive=" + interactive);  
       }  
     
       // Tell the activity manager about changes in wakefulness, not just interactivity.  
       // It needs more granularity than other components.  
        mHandler.post(new Runnable() {  
            @Override  
            public void run() {  
                mActivityManagerInternal.onWakefulnessChanged(wakefulness);   //与AMS交互处理  
            }  
        });  
      
        // Handle any early interactive state changes.  
        // Finish pending incomplete ones from a previous cycle.  
        if (mInteractive != interactive) {  
            // Finish up late behaviors if needed.  
            if (mInteractiveChanging) {  
                handleLateInteractiveChange();  
            }  
      
            // Start input as soon as we start waking up or going to sleep.  
            mInputManagerInternal.setInteractive(interactive);    //在IMS中记录现在的屏幕状态  
            mInputMethodManagerInternal.setInteractive(interactive);  
      
            // Notify battery stats.  
            try {  
                mBatteryStats.noteInteractive(interactive);   //唤醒battery状态  
            } catch (RemoteException ex) { }  
      
            // Handle early behaviors.  
            mInteractive = interactive;  
            mInteractiveChangeReason = reason;  
            mInteractiveChanging true;  
            handleEarlyInteractiveChange();   //初期处理交互模式改变  
        }  
    }  

    B: handleEarlyInteractiveChange()

    当屏幕在wakingup时需要通知window进行更新手势监听,更新方向监听,更新锁屏超时时间 

    private void handleEarlyInteractiveChange() {  
        synchronized (mLock) {  
            if (mInteractive) {  
                // Waking up...    //亮屏  
                mHandler.post(new Runnable() {  
                    @Override  
                    public void run() {  
                        EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);  
                        mPolicy.startedWakingUp();   
                     }  
                 });  
       
                 // Send interactive broadcast.  
                 mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;  
                 mPendingWakeUpBroadcast = true;  
                 updatePendingBroadcastLocked();   //更新亮屏广播  
             } else {  
                 // Going to sleep...   //灭屏  
                 // Tell the policy that we started going to sleep.  
                 final int why = translateOffReason(mInteractiveChangeReason);  
                 mHandler.post(new Runnable() {  
                     @Override  
                     public void run() {  
                         mPolicy.startedGoingToSleep(why);  
                     }  
                 });  
             }  
         } 

    (1):updatePendingBroadcastLocked()

        private void updatePendingBroadcastLocked() {
            if (!mBroadcastInProgress
                    && mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN
                    && (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
                            || mPendingInteractiveState != mBroadcastedInteractiveState)) {
                mBroadcastInProgress = true;
                mSuspendBlocker.acquire();
                Message msg = mHandler.obtainMessage(MSG_BROADCAST);
                msg.setAsynchronous(true);
                mHandler.sendMessage(msg);
            }
        }

    (2)sendNextBroadcast()

     private void sendNextBroadcast() {
            final int powerState;
            synchronized (mLock) {
                if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) {
                    // Broadcasted power state is unknown.  Send wake up.
                    mPendingWakeUpBroadcast = false;
                    mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
                } else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) {
                    // Broadcasted power state is awake.  Send asleep if needed.
                    if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
                            || mPendingInteractiveState == INTERACTIVE_STATE_ASLEEP) {
                        mPendingGoToSleepBroadcast = false;
                        mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP;
                    } else {
                        finishPendingBroadcastLocked();
                        return;
                    }
                } else {
                    // Broadcasted power state is asleep.  Send awake if needed.
                    if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
                            || mPendingInteractiveState == INTERACTIVE_STATE_AWAKE) {
                        mPendingWakeUpBroadcast = false;
                        mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
                    } else {
                        finishPendingBroadcastLocked();
                        return;
                    }
                }
    
                mBroadcastStartTime = SystemClock.uptimeMillis();
                powerState = mBroadcastedInteractiveState;
            }
    
            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);
    
            if (powerState == INTERACTIVE_STATE_AWAKE) {
                sendWakeUpBroadcast();//这里发送亮屏广播
            } else {
                sendGoToSleepBroadcast();
            }
        }

     (3)sendWakeUpBroadcast()

     注意:Notifier 自身也会接收亮屏广播,其受到后会调用finishPendingBroadcastLocked()函数来释放wakeLock

        private void sendWakeUpBroadcast() {
            if (DEBUG) {
                Slog.d(TAG, "Sending wake up broadcast.");
            }
    
            if (ActivityManagerNative.isSystemReady()) {
                mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
                        mWakeUpBroadcastDone, mHandler, 0, null, null);
            } else {
                EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
                sendNextBroadcast();
            }
        }

  • 相关阅读:
    吊打面试官系列:Redis 性能优化的 13 条军规大全
    Laravel 7.6 发布
    Laravel 8 新功能:使用 schema:dump 来加速 Migration 和测试
    php中常用的4种运行方式
    基于 Redis 的订阅与发布
    [ida]使用pycharm编写IDApython
    visual studio 配置使生成的pdb文件中包含所有符号
    D/B位、一致与非一致代码段、向下拓展的实验与总结
    [debug] CE指针扫描扫出来为空
    error LNK2019: 无法解析的外部符号 _main,该符号在函数___tmainCRTStartup 中被引用
  • 原文地址:https://www.cnblogs.com/dyufei/p/8017604.html
Copyright © 2020-2023  润新知