前置文章:
《 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 水平摆放
@Override
public void orientationChanged(int orientation) {
mOrientation = orientation;
updateProximitySensorMode();
}
// 当通话状态有改变则会通过InCallPresenter回调
@Override
public void onStateChange(InCallState state, CallList callList) {
// 如果是来电则无须启用PSensor
boolean isOffhook = (InCallState.INCALL == state
|| InCallState.OUTGOING == state);
if (isOffhook != mIsPhoneOffhook) {
mIsPhoneOffhook = isOffhook;
mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN;
mAccelerometerListener.enable(mIsPhoneOffhook);
updateProximitySensorMode();
}
}
//... ...省略
// 当通话过程中Audio模式有变化则执