Android4.2距离感应器

来源:互联网 发布:奥登生涯数据 编辑:程序博客网 时间:2024/05/01 11:51

   先说现象,现象就是来电话,接通电话,把手机屏幕靠近脸部,遮挡住P-sensor,屏幕变黑了,不遮挡住P-sensor,屏幕就点亮了。接着我们来看看代码流程。

距离感应器与屏幕休眠可参考

http://blog.csdn.net/wds1181977/article/details/42125699

     先来说说靠近P-sensor,不灭屏的正常的现象:

  1. 插入耳机
  2. 打开扬声器
  3. 打开蓝牙耳机
  4. 链接蓝牙键盘

   步骤一: 在PhoneGlobals.java文件中onCreate()方法中:

。。。 。。。

[java] view plaincopyprint?
  1. // lock used to keep the processor awake, when we don't care for the display.  
  2.             mPartialWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK  
  3.                     | PowerManager.ON_AFTER_RELEASE, LOG_TAG);  
  4.             // Wake lock used to control proximity sensor behavior.  
  5.             if (mPowerManager.isWakeLockLevelSupported(  
  6.                     PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {  
  7.                 mProximityWakeLock = mPowerManager.newWakeLock(  
  8.                         PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, LOG_TAG);  
  9.             }  
。。。 。。。

  注意这个private PowerManager.WakeLock mProximityWakeLock;这个初始化变量,

这个mProximityWakeLock就是所说的P-Sensor锁,它是用来唤醒屏幕和使屏幕睡眠的锁。


步骤二:在PhoneGlobals.java文件中的onCreate()方法中:

[java] view plaincopyprint?
  1. // create mAccelerometerListener only if we are using the proximity sensor  
  2.             if (proximitySensorModeEnabled()) {  
  3.                 mAccelerometerListener = new AccelerometerListener(thisthis);  
  4.             }  
创建加速度感应器。


步骤三:在更新Phone的状态的时候确定这个加速度的P-sensor感应器起作用;

[java] view plaincopyprint?
  1. <span style="font-size:18px;">/** 
  2.      * Notifies the phone app when the phone state changes. 
  3.      * 
  4.      * This method will updates various states inside Phone app (e.g. proximity sensor mode, 
  5.      * accelerometer listener state, update-lock state, etc.) 
  6.      */  
  7.     /* package */ void updatePhoneState(PhoneConstants.State state) {  
  8.         if (state != mLastPhoneState) {  
  9.             mLastPhoneState = state;  
  10.             if (state == PhoneConstants.State.IDLE)  
  11.                 PhoneGlobals.getInstance().pokeUserActivity();  
  12.             updateProximitySensorMode(state);  
  13.   
  14.             // Try to acquire or release UpdateLock.  
  15.             //  
  16.             // Watch out: we don't release the lock here when the screen is still in foreground.  
  17.             // At that time InCallScreen will release it on onPause().  
  18.             if (state != PhoneConstants.State.IDLE) {  
  19.                 // UpdateLock is a recursive lock, while we may get "acquire" request twice and  
  20.                 // "release" request once for a single call (RINGING + OFFHOOK and IDLE).  
  21.                 // We need to manually ensure the lock is just acquired once for each (and this  
  22.                 // will prevent other possible buggy situations too).  
  23.                 if (!mUpdateLock.isHeld()) {  
  24.                     mUpdateLock.acquire();  
  25.                 }  
  26.             } else {  
  27.                 if (!isShowingCallScreen()) {  
  28.                     if (!mUpdateLock.isHeld()) {  
  29.                         mUpdateLock.release();  
  30.                     }  
  31.                 } else {  
  32.                     // For this case InCallScreen will take care of the release() call.  
  33.                 }  
  34.             }  
  35.   
  36.             if (mAccelerometerListener != null) {  
  37.                 // use accelerometer to augment proximity sensor when in call  
  38.                 mOrientation = AccelerometerListener.ORIENTATION_UNKNOWN;  
  39.                </span><span style="color:#ff0000;"><strong><span style="font-size:24px;"> </span><span style="font-size:18px;">mAccelerometerListener.enable(state == PhoneConstants.State.OFFHOOK);</span></strong></span><span style="font-size:18px;">  
  40.             }  
  41.             // clear our beginning call flag  
  42.             mBeginningCall = false;  
  43.             // While we are in call, the in-call screen should dismiss the keyguard.  
  44.             // This allows the user to press Home to go directly home without going through  
  45.             // an insecure lock screen.  
  46.             // But we do not want to do this if there is no active call so we do not  
  47.             // bypass the keyguard if the call is not answered or declined.  
  48.             if (mInCallScreen != null) {  
  49.         if (VDBG) Log.d(LOG_TAG, "updatePhoneState: state = " + state);  
  50.         if (!PhoneUtils.isDMLocked())  
  51.                     mInCallScreen.updateKeyguardPolicy(state == PhoneConstants.State.OFFHOOK);  
  52.             }  
  53.         }  
  54.     }</span>  

步骤四:用AccelerometerListener.java类中的监听事件来处理一些这个覆盖的改变,一共有2个状态,一个是

horizontal,一个是vertical的状态。在上述步骤三红色的调用部分注册这个监听事件:

[java] view plaincopyprint?
  1. public void enable(boolean enable) {  
  2.        if (DEBUG) Log.d(TAG, "enable(" + enable + ")");  
  3.        synchronized (this) {  
  4.            if (enable) {  
  5.                mOrientation = ORIENTATION_UNKNOWN;  
  6.                mPendingOrientation = ORIENTATION_UNKNOWN;  
  7.                mSensorManager.registerListener(mSensorListener, mSensor,  
  8.                        SensorManager.SENSOR_DELAY_NORMAL);  
  9.            } else {  
  10.                mSensorManager.unregisterListener(mSensorListener);  
  11.                mHandler.removeMessages(ORIENTATION_CHANGED);  
  12.            }  
  13.        }  
  14.    }  

步骤五:监听事件的相应的过程如下:

[java] view plaincopyprint?
  1. SensorEventListener mSensorListener = new SensorEventListener() {  
  2.         public void onSensorChanged(SensorEvent event) {  
  3.             onSensorEvent(event.values[0], event.values[1], event.values[2]);  
  4.         }  
  5.   
  6.         public void onAccuracyChanged(Sensor sensor, int accuracy) {  
  7.             // ignore  
  8.         }  
  9.     };  

[java] view plaincopyprint?
  1. private void onSensorEvent(double x, double y, double z) {  
  2.         if (VDEBUG) Log.d(TAG, "onSensorEvent(" + x + ", " + y + ", " + z + ")");  
  3.   
  4.         // If some values are exactly zero, then likely the sensor is not powered up yet.  
  5.         // ignore these events to avoid false horizontal positives.  
  6.         if (x == 0.0 || y == 0.0 || z == 0.0return;  
  7.   
  8.         // magnitude of the acceleration vector projected onto XY plane  
  9.         double xy = Math.sqrt(x*x + y*y);  
  10.         // compute the vertical angle  
  11.         double angle = Math.atan2(xy, z);  
  12.         // convert to degrees  
  13.         angle = angle * 180.0 / Math.PI;  
  14.         int orientation = (angle >  VERTICAL_ANGLE ? ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL);  
  15.         if (VDEBUG) Log.d(TAG, "angle: " + angle + " orientation: " + orientation);  
  16.         setOrientation(orientation);  
  17.     }  
  18.   
  19.   
  20. private void setOrientation(int orientation) {  
  21.         synchronized (this) {  
  22.             if (mPendingOrientation == orientation) {  
  23.                 // Pending orientation has not changed, so do nothing.  
  24.                 return;  
  25.             }  
  26.   
  27.             // Cancel any pending messages.  
  28.             // We will either start a new timer or cancel alltogether  
  29.             // if the orientation has not changed.  
  30.             mHandler.removeMessages(ORIENTATION_CHANGED);  
  31.   
  32.             if (mOrientation != orientation) {  
  33.                 // Set timer to send an event if the orientation has changed since its  
  34.                 // previously reported value.  
  35.                 mPendingOrientation = orientation;  
  36.                 Message m = mHandler.obtainMessage(ORIENTATION_CHANGED);  
  37.                 // set delay to our debounce timeout  
  38.                 int delay = (orientation == ORIENTATION_VERTICAL ? VERTICAL_DEBOUNCE  
  39.                                                                  : HORIZONTAL_DEBOUNCE);  
  40.                 mHandler.sendMessageDelayed(m, delay);  
  41.             } else {  
  42.                 // no message is pending  
  43.                 mPendingOrientation = ORIENTATION_UNKNOWN;  
  44.             }  
  45.         }  
  46.     }  
然后发送消息ORIENTATION_CHANGED这个改变的消息;这个消息会调用一个回调函数,然后根据状态判断,调用acquire和release()方法;

[java] view plaincopyprint?
  1. Handler mHandler = new Handler() {  
  2.         public void handleMessage(Message msg) {  
  3.             switch (msg.what) {  
  4.             case ORIENTATION_CHANGED:  
  5.                 synchronized (this) {  
  6.                     mOrientation = mPendingOrientation;  
  7.                     if (DEBUG) {  
  8.                         Log.d(TAG, "orientation: " +  
  9.                             (mOrientation == ORIENTATION_HORIZONTAL ? "horizontal"  
  10.                                 : (mOrientation == ORIENTATION_VERTICAL ? "vertical"  
  11.                                     : "unknown")));  
  12.                     }  
  13.                     mListener.orientationChanged(mOrientation);  
  14.                 }  
  15.                 break;  
  16.             }  
  17.         }  
  18.     };  

步骤五:回调到PhoneGlobals.java这个类的orientationChanged()

[java] view plaincopyprint?
  1. @Override  
  2.     public void orientationChanged(int orientation) {  
  3.         mOrientation = orientation;  
  4.         updateProximitySensorMode(mCM.getState());  
  5.     }  

[java] view plaincopyprint?
  1. <pre name="code" class="java">/** 
  2.      * Updates the wake lock used to control proximity sensor behavior, 
  3.      * based on the current state of the phone.  This method is called 
  4.      * from the CallNotifier on any phone state change. 
  5.      * 
  6.      * On devices that have a proximity sensor, to avoid false touches 
  7.      * during a call, we hold a PROXIMITY_SCREEN_OFF_WAKE_LOCK wake lock 
  8.      * whenever the phone is off hook.  (When held, that wake lock causes 
  9.      * the screen to turn off automatically when the sensor detects an 
  10.      * object close to the screen.) 
  11.      * 
  12.      * This method is a no-op for devices that don't have a proximity 
  13.      * sensor. 
  14.      * 
  15.      * Note this method doesn't care if the InCallScreen is the foreground 
  16.      * activity or not.  That's because we want the proximity sensor to be 
  17.      * enabled any time the phone is in use, to avoid false cheek events 
  18.      * for whatever app you happen to be running. 
  19.      * 
  20.      * Proximity wake lock will *not* be held if any one of the 
  21.      * conditions is true while on a call: 
  22.      * 1) If the audio is routed via Bluetooth 
  23.      * 2) If a wired headset is connected 
  24.      * 3) if the speaker is ON 
  25.      * 4) If the slider is open(i.e. the hardkeyboard is *not* hidden) 
  26.      * 
  27.      * @param state current state of the phone (see {@link Phone#State}) 
  28.      */  
  29.     /* package */ void updateProximitySensorMode(PhoneConstants.State state) {  
  30.       
  31.         boolean isRingingWhenActive = false;//MTK81281 add isRingingWhenActive for Cr:ALPS00117091  
  32.           
  33.         if (VDBG) Log.d(LOG_TAG, "updateProximitySensorMode: state = " + state);  
  34.   
  35.         if (proximitySensorModeEnabled()) {  
  36.             synchronized (mProximityWakeLock) {  
  37.                 // turn proximity sensor off and turn screen on immediately if  
  38.                 // we are using a headset, the keyboard is open, or the device  
  39.                 // is being held in a horizontal position.  
  40.                 boolean screenOnImmediately = (isHeadsetPlugged()  
  41.                                                || PhoneUtils.isSpeakerOn(this)  
  42.                                                || isBluetoothHeadsetAudioOn()  
  43.                                                || mIsHardKeyboardOpen);  
  44.   
  45.                 if (FeatureOption.MTK_VT3G324M_SUPPORT) {  
  46.                     screenOnImmediately = screenOnImmediately ||  
  47.                             ((!VTCallUtils.isVTIdle()) && (!VTCallUtils.isVTRinging()));  
  48.                 }  
  49.   
  50.                 // We do not keep the screen off when the user is outside in-call screen and we are  
  51.                 // horizontal, but we do not force it on when we become horizontal until the  
  52.                 // proximity sensor goes negative.  
  53.                   
  54.                 // this horizontal is not the same portrait.  
  55.                  boolean horizontal =  
  56.                         (mOrientation == AccelerometerListener.ORIENTATION_HORIZONTAL);  
  57.                  screenOnImmediately |= !isShowingCallScreenForProximity() && horizontal;  
  58.                 if (VDBG) Log.d(LOG_TAG, "updateProximitySensorMode: mBeginningCall = " + mBeginningCall);  
  59.                 if (VDBG) Log.d(LOG_TAG, "updateProximitySensorMode: screenOnImmediately = " + screenOnImmediately);  
  60.            //MTK81281 add isRingingWhenActive for Cr:ALPS00117091 start      
  61.            //when a call is activeand p-sensor turn off the screen,    
  62.            //another call or vtcall in we don't release the lock and acquire again  
  63.            //(the prowermanagerservice will turn on and off the screen and it's a problem)  
  64.            //instead ,we don't release the lock(prowermanagerservice will not turn on and off the screen)  
  65.                 isRingingWhenActive = (state == PhoneConstants.State.RINGING)  
  66.                     && (mCM.getActiveFgCallState() == Call.State.ACTIVE)  
  67.                     && (mCM.getFirstActiveRingingCall().getState() == Call.State.WAITING);  
  68.   
  69.                 if (VDBG) Log.d(LOG_TAG, "updateProximitySensorMode: isRingingWhenActive = " + isRingingWhenActive);  
  70.            //MTK81281 add  isRingingWhenActive for Cr:ALPS00117091 end  
  71.   
  72.                 //MTK81281 add isRingingWhenActive for Cr:ALPS00117091  
  73.                 if (((state == PhoneConstants.State.OFFHOOK) || mBeginningCall || isRingingWhenActive)  
  74.                         && !screenOnImmediately) {  
  75.                     // Phone is in use!  Arrange for the screen to turn off  
  76.                     // automatically when the sensor detects a close object.  
  77.                     if (!mProximityWakeLock.isHeld()) {  
  78.                         if (DBG) Log.d(LOG_TAG, "updateProximitySensorMode: acquiring...");  
  79.                         <span style="color:#ff0000;"><strong>mProximityWakeLock.acquire();</strong></span>  
  80.                     } else {  
  81.                         if (VDBG) Log.d(LOG_TAG, "updateProximitySensorMode: lock already held.");  
  82.                     }  
  83.                 } else {  
  84.                     // Phone is either idle, or ringing.  We don't want any  
  85.                     // special proximity sensor behavior in either case.  
  86.                     if (mProximityWakeLock.isHeld()) {  
  87.                         if (DBG) Log.d(LOG_TAG, "updateProximitySensorMode: releasing...");  
  88.                         // Wait until user has moved the phone away from his head if we are  
  89.                         // releasing due to the phone call ending.  
  90.                         // Qtherwise, turn screen on immediately  
  91.                         int flags =  
  92.                             (screenOnImmediately ? 0 : PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE);  
  93.                         <strong><span style="color:#ff0000;">mProximityWakeLock.release(flags);</span></strong>  
  94.                     } else {  
  95.                         if (VDBG) {  
  96.                             Log.d(LOG_TAG, "updateProximitySensorMode: lock already released.");  
  97.                         }  
  98.                     }  
  99.                 }  
  100.             }  
  101.         }  
  102.     }</pre><br><br>  

到这已经把Phone层的P-sensor的亮屏和灭屏说完了,回头再来屡屡这个mProximityWakeLock在framework层怎么具体实现的亮屏和灭屏的;敬请期待。。。 。。。


    更正:上述的讲解是不正确的,后来经过打log,发现,这个mProximityWakeLock.acquire();和mProximityWakeLock.release(flags);只是申请这个锁,和释放这个锁,防止其他的调用,


其实,在4.2以前Phone模块的P-Sensor在PowerManagerServer.java中处理的,而Android4.2的时候,谷歌对代码进行了一些调整,所以Phone的模块的P-Sensor的控制的类在DisplayPowerController.java

路径:framework/base/services/java/com/android/server/power/DisplayPowerController.java中的:

[java] view plaincopyprint?
  1. private final SensorEventListener mProximitySensorListener = new SensorEventListener() {  
  2.        @Override  
  3.        public void onSensorChanged(SensorEvent event) {  
  4.            if (mProximitySensorEnabled) {  
  5.                final long time = SystemClock.uptimeMillis();  
  6.                final float distance = event.values[0];  
  7.                boolean positive = distance >= 0.0f && distance < mProximityThreshold;  
  8.                if (DEBUG) {  
  9.                    Slog.d(TAG, "P-Sensor Changed: " + positive);  
  10.                }  
  11.                handleProximitySensorEvent(time, positive);  
  12.            }  
  13.        }  

[java] view plaincopyprint?
  1. <span style="color: rgb(255, 0, 0);"><strong></strong></span>  

这个onSensorChanged()就是距离感应器的;这个log:

P-Sensor Changed:true  表示:靠近手机,P-sensor被遮挡住;

P-Sensor Changed:false 表示:离开手机,P-sensor没有被遮挡住;

通过这个log也能帮我们分析一些问题;这个mProximitySensorListener的注册和反注册,大家可以自己在这个类中搜索就可以了;


       补充:在PowserManagerServerice.java这个类中,  private LightsService.Light mButtonLight;这个变量申请的就是按键灯,像:home,返回键,menu键的灯,申请这个灯的代码:

[java] view plaincopyprint?
  1. private LightsService mLightsService;  
  2. 。。。 。。。  
  3.   
  4. mButtonLight = mLightsService.getLight(LightsService.LIGHT_ID_BUTTONS);  
通过LightService可以获得键盘灯的实例,

打开灯的方法:mButtonLight.setBrightness(120);//120可以是(0~255)任意数字;

关闭灯的方法:mButtonLight.turnOff();


     需求:要想使Phone通话界面的P-Sensor亮灭屏幕和按键灯同步,当屏幕灭的时候,按键灯也灭,屏幕亮的时候,按键灯也亮;其实做法很简单了,在onSensorChanged()的时候处理,扩一些接口,

具体设计的类我列举一下:


  •   PowerManager.java
  •   PowerManagerService.java
  •   BridgetPowerManager.java
  •   IPowerManager.adil
  •   DisPlayPowerController.java

     具体代码就不详细赘述了,还是“觉知此事要躬行”;

     注意在

[java] view plaincopyprint?
  1. mLastUserActivityButtonTime = SystemClock.uptimeMillis();  
  2.         mButtonLight.setBrightness(102);  
在设置打开按键灯的时候,设置一下最后一次用户触摸按钮的时间;至于为什么??大家自已到时候打log就明白了。。。 。。。
[java] view plaincopyprint?
  1. mLastUserActivityButtonTime  
0 0