PSensor工作流程

本文代码以MTK平台Android 4.4为分析对象,与Google原生AOSP有些许差异,请读者知悉。

前置文章:《Android 4.4 Kitkat Phone工作流程浅析(一)__概要和学习计划》

《Android 4.4 Kitkat Phone工作流程浅析(二)__UI结构分析》

《Android 4.4 Kitkat Phone工作流程浅析(三)__MO(去电)流程分析》

《Android 4.4 Kitkat Phone工作流程浅析(四)__RILJ工作流程简析》

《Android 4.4 Kitkat Phone工作流程浅析(五)__MT(来电)流程分析》

《Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程》

《Android 4.4 Kitkat Phone工作流程浅析(七)__来电(MT)响铃流程》

《Android 4.4 Kitkat Phone工作流程浅析(八)__Phone状态分析》

《Android 4.4 Kitkat Phone工作流程浅析(九)__状态通知流程分析》

《Android 4.4 Kitkat Phone工作流程浅析(十)__"通话显示"查询流程》

概要

在Android手机通话过程中,用户将手机靠近/远离头部,会导致手机屏幕灭/亮,这实际上是Proximity Sensor在起作用(参考1)。通俗的来讲Proximity Sensor就是近距离传感器,后文简写为PSensor,近距离传感器可用于测量物体靠近或远离。根据PSensor的这一特征,在计数以及自动化控制等领域都有使用近距离传感器(参考2,参考3)。目前,市面上主流的智能手机均包含了近距离传感器。PSensor检测到手机被遮挡则关闭屏幕,反之则点亮屏幕,一方面可以省电(LCD是耗电大户),另一方面可以防止用户近耳接听时触碰到屏幕引发误操作。

本文主要分析PSensor在整个通话过程中实现屏幕亮灭的控制原理。

本文来自 转载请务必注明出处

ProximitySensor初始化流程

在Android 4.4以后,Phone分为了TeleService和InCallUI两个部分,通话过程中PSensor的控制由packages/apps/InCallUI/src/com/android/incallui/ProximitySensor.java负责。在以前发布的文章《Android 4.4 Kitkat Phone工作流程浅析(七)__来电(MT)响铃流程》和《Android 4.4 Kitkat Phone工作流程浅析(九)__状态通知流程分析》中已经分析通话状态变更流程 ( Modem->RILC->RILJ->Framework->Telephony->InCallUI ) ,而TeleService与InCallUI建立连接是在通话状态变更之后。因此ProximitySensor的初始化建立在通话状态第一次变更后,整个流程如图1所示:

图 1 ProximitySensor初始化流程

InCallUI与TeleService通过两种方式进行通信,即Broadcast和AIDL。当MO流程发起时,InCallUI最终会通过广播的方式将MO请求传递给TeleService。而当通话状态返回以及通话控制命令发起时,InCallUI和TeleService之间将会通过AIDL的方式进行通信,即CallHandlerService和CallHandlerServiceProxy,以及CallCommandService和CallCommandClient。使用AIDL方式通信需要建立连接(bindService),而在建立连接的时候对ProximitySensor进行了初始化操作。

ProximitySensor初始化小结

1. ProximitySensor初始化是在InCallPresenter中完成;

在InCallPresenter中实例化了ProximitySensor对象并将其添加到Set<InCallStateListener>序列中,当通话状态变更时回调ProximitySensor的onStateChanged()方法。

2. ProximitySensor的初始化依赖于InCallUI中CallHandlerService的绑定;

当TeleService通过bindService连接CallHandlerServiceProxy和CallHandlerService时,经过逐步调用后完成ProximitySensor的初始化。而bindService的调用则依赖于Call State的改变,Call State的改变有两种情况:incoming 和 update,即当有来电或者通话状态改变时,会触发TeleService的bindService操作。

ProximitySensor使用流程

通话状态变更之后ProximitySensor完成初始化。ProximitySensor.java的关键方法如下:

// 构造方法,完成初始化// PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK是@hide的,普通app无法获取public ProximitySensor(Context context, AudioModeProvider audioModeProvider) {mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);if (mPowerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {mProximityWakeLock = mPowerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);} else {mProximityWakeLock = null;}mAccelerometerListener = new AccelerometerListener(context, this);mAudioModeProvider = audioModeProvider;mAudioModeProvider.addListener(this);}// 完成善后工作,初始化以及挂断电话后会被调用public void tearDown() {mAudioModeProvider.removeListener(this);mAccelerometerListener.enable(false);if (mProximityWakeLock != null && mProximityWakeLock.isHeld()) {mProximityWakeLock.release();}}// 当设备的方向改变之后会执行,用于判断是否启用PSensor(注:在灭屏状态下不会触发)// mOrientation的值包含:// ORIENTATION_UNKNOWN = 0// ORIENTATION_VERTICAL = 1 垂直摆放// ORIENTATION_HORIZONTAL = 2 水平摆放@Overridepublic void orientationChanged(int orientation) {mOrientation = orientation;updateProximitySensorMode();}// 当通话状态有改变则会通过InCallPresenter回调@Overridepublic void onStateChange(InCallState state, CallList callList) {// 如果是来电则无须启用PSensorboolean isOffhook = (InCallState.INCALL == state|| InCallState.OUTGOING == state);if (isOffhook != mIsPhoneOffhook) {mIsPhoneOffhook = isOffhook;mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN;mAccelerometerListener.enable(mIsPhoneOffhook);updateProximitySensorMode();}}//… …省略// 当通话过程中Audio模式有变化则执行,包括:// 1. 蓝牙耳机连接// 2. 有线耳机连接// 3. 开启扬声器@Overridepublic void onAudioMode(int mode) {updateProximitySensorMode();}// 拨号盘显示时回调,此时禁用PSensor以便用户输入public void onDialpadVisible(boolean visible) {mDialpadVisible = visible;updateProximitySensorMode();}// Config改变时回调,如开启物理键盘(如今许多设备都已不再配备物理键盘)public void onConfigurationChanged(Configuration newConfig) {mIsHardKeyboardOpen = newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;// Update the Proximity sensor based on keyboard stateupdateProximitySensorMode();}// InCallUI显示或隐藏时回调public void onInCallShowing(boolean showing) {if (showing) {mUiShowing = true;} else if (mPowerManager.isScreenOn()) {mUiShowing = false;}updateProximitySensorMode();}通过查看不难发现,以上方法(除了构造方法以外)均调用了updateProximitySensorMode方法,整个通话过程中正是由其触发PSensor的控制,关键代码如下:/** * 根据当前设备状态,使用wake lock锁来控制PSensor的行为 * On devices that have a proximity sensor, to avoid false touches * during a call, we hold a PROXIMITY_SCREEN_OFF_WAKE_LOCK wake lock * whenever the phone is off hook. (When held, that wake lock causes * the screen to turn off automatically when the sensor detects an * object close to the screen.) * 以上为google解释通话过程中PSensor的工作原理,即 * 在通话过程中持有PROXIMITY_SCREEN_OFF_WAKE_LOCK的wake lock,如果检测物体靠近 * 则关闭屏幕以防止用户误点击 * * 以下情况将会禁用PSensor的亮灭屏控制,即PSensor无效: * 1) 设备已连接蓝牙耳机 * 2) 设备已插入有线耳机 * 3) 设备开启扬声器 * 4) 设备开启物理键盘(如今许多设备都已不再配备物理键盘) */private void updateProximitySensorMode() {if (proximitySensorModeEnabled()) {synchronized (mProximityWakeLock) {final int audioMode = mAudioModeProvider.getAudioMode();// 以下情况将会禁用PSensor,是否禁用需根据update时具体状态决定:// 1. 插入有线耳机// 2. 接入蓝牙耳机// 3. 开启扬声器// 4. 开启物理键盘(如今许多设备都已不再配备物理键盘)// 5. 设备水平放置// screenOnImmediately = true表示禁用PSensor// screenOnImmediately = false表示启用PSensor,此时屏幕的亮灭则根据是否// 有物体靠近或远离PSensor决定,靠近则灭屏,远离则亮屏boolean screenOnImmediately = (AudioMode.WIRED_HEADSET == audioMode|| AudioMode.SPEAKER == audioMode|| AudioMode.BLUETOOTH == audioMode|| mIsHardKeyboardOpen);//… …省略// 当设备水平放置,且InCallActivity不在前台显示时,此时!mUiShowing && horizontal = true// 从而screenOnImmediately = true,并最终禁用PSensorfinal boolean horizontal =(mOrientation == AccelerometerListener.ORIENTATION_HORIZONTAL);screenOnImmediately |= !mUiShowing && horizontal;// 当设备水平放置且开启拨号盘时,screenOnImmediately = true,禁用PSensorscreenOnImmediately |= mDialpadVisible && horizontal;if (mIsPhoneOffhook && !screenOnImmediately) {final String logStr = "turning on proximity sensor: ";if (!mProximityWakeLock.isHeld()) {Log.i(this, logStr + "acquiring");// 如果没有执行过acquire方法则执行wakelock申请的动作// 该方法用于Enable PSensor的亮灭屏控制mProximityWakeLock.acquire();} else {// 无须再次acquireLog.i(this, logStr + "already acquired");}} else {final String logStr = "turning off proximity sensor: ";if (mProximityWakeLock.isHeld()) {Log.i(this, logStr + "releasing");// Wait until user has moved the phone away from his head if we are// releasing due to the phone call ending.// Qtherwise, turn screen on immediately// 禁用PSensor的亮灭屏控制包含两种情况:// 第一种:// 如果当前设备不再是OFFHOOK状态,比如已经挂断电话即 mIsPhoneOffhook = false// 此时screenOnImmediately有可能为false,即设备还处于灭屏的状态,这种情况下会根据// PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE 这个FLAG来release wake lock// 也就是说会立即释放锁,但需要等用户远离PSensor时才会禁用PSensor并点亮屏幕// 第二种:// 当update时发现需要禁用PSensor,比如在PSensor被遮挡设备灭屏的情况下插入有线耳机// 此时会导致Audio模式的改变从而调用updateProximitySensorMode,同时,在这种情况下// screenOnImmediately = true,则会立即禁用PSensor并release wake lock点亮屏幕int flags =(screenOnImmediately ? 0 : PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE);mProximityWakeLock.release(flags);} else {Log.i(this, logStr + "already released");}}}}}通过以上代码的分析可以知道,屏幕的亮灭有多种情况。在InCallUI中通过mProximityWakeLock.acquire()和mProximityWakeLock.release()申请/释放wake lock来 Enable/Unenable PSensor,从而让PSensor来控制屏幕的亮灭。从这一点也可以看出,通话过程中屏幕的亮灭的控制,实际上与Telephony没有多大关系。

ProximitySensor使用流程小结

1. 在InCallUI中通过ProximitySensor提供的public接口,调用updateProximitySensorMode()更新PSensor的Enable/Unenable状态。

public接口包括:

orientationChanged():设备方向改变后回调;

onStateChange():设备InCallState改变后回调;

onAudioMode():设备Audio模式改变后回调,Audio模式包括连接蓝牙耳机,插入有线耳机,,开启扬声器;

onDialpadVisible():设备通话时时Dialpad显示状态改变后回调;

onConfigurationChanged():设备Configuration改变后回调,如弹出物理键盘;

onInCallShowing():设备InCallActivity显示状态改变后回调;

2. PSensor的控制在ProximitySensor中通过如下方式完成:

肯承认错误则错已改了一半

PSensor工作流程

相关文章:

你感兴趣的文章:

标签云: