[置顶] Android系统移植与调试之------->如何修改Android手机NFC模块,使黑屏时候能够使用NFC

         我们都知道在不修改源代码的情况下,只能是解锁之后才能使用NFC功能。而在锁屏和黑屏2个状态下是没办法用NFC的,但是最近有个客户要求手机在黑屏状态下能够使用NFC,因此我们需要去修改Android源代码关于NFC模块。

       最开始可以通过查看分析源代码,找到到NfcService的相关代码,如下: packages\apps\Nfc\src\com\android\nfc\NfcService.java 


找到186行,这句是定义NFC能够使用的屏幕最小状态

// minimum screen state that enables NFC polling
    static final int NFC_POLLING_MODE = ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED;

这几个状态分别是:

SCREEN_STATE_OFF黑屏状态   

SCREEN_STATE_ON_LOCKED屏幕亮了,但是是锁屏状态

SCREEN_STATE_ON_UNLOCKED 屏幕亮了,并且是解锁状态

代码定义如下,在packages\apps\Nfc\src\com\android\nfc\ScreenStateHelper中定义

    static final int SCREEN_STATE_UNKNOWN = 0;
    static final int SCREEN_STATE_OFF = 1;
    static final int SCREEN_STATE_ON_LOCKED = 2;
    static final int SCREEN_STATE_ON_UNLOCKED = 3;

上面的这个最小状态在 NfcService.java的第1706行,computeDiscoveryParameters(int screenState)方法中被调用,用来判断的,方法代码如下:

private NfcDiscoveryParameters computeDiscoveryParameters(int screenState) {
        
        Log.d(TAG, "computeDiscoveryParameters() screenState:"+describeScreenState(screenState));

        if(screenState == ScreenStateHelper.SCREEN_STATE_ON_LOCKED)
            Log.d(TAG, "!!!! SCREEN_STATE_ON_LOCKED,, mNfcUnlockManager.isLockscreenPollingEnabled():"+mNfcUnlockManager.isLockscreenPollingEnabled());
        
        // Recompute discovery parameters based on screen state
        NfcDiscoveryParameters.Builder paramsBuilder = NfcDiscoveryParameters.newBuilder();
        // Polling
        if (screenState >= NFC_POLLING_MODE) {//这里被调用
            // Check if reader-mode is enabled
            if (mReaderModeParams != null) {
                int techMask = 0;
                if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_A) != 0)
                    techMask |= NFC_POLL_A;
                if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_B) != 0)
                    techMask |= NFC_POLL_B;
                if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_F) != 0)
                    techMask |= NFC_POLL_F;
                if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_V) != 0)
                    techMask |= NFC_POLL_ISO15693;
                if ((mReaderModeParams.flags & NfcAdapter.FLAG_READER_NFC_BARCODE) != 0)
                    techMask |= NFC_POLL_KOVIO;

                Log.d(TAG, " mReaderModeParams != null   paramsBuilder.setTechMask:"+techMask);

                paramsBuilder.setTechMask(techMask);
                paramsBuilder.setEnableReaderMode(true);
            } else {
            
            Log.d(TAG, " mReaderModeParams == null   paramsBuilder.setTechMask:"+NfcDiscoveryParameters.NFC_POLL_DEFAULT +"   NFC_POLL_DEFAULT");
                paramsBuilder.setTechMask(NfcDiscoveryParameters.NFC_POLL_DEFAULT);
                paramsBuilder.setEnableP2p(mIsNdefPushEnabled);
            }
        } else if (screenState == ScreenStateHelper.SCREEN_STATE_ON_LOCKED && mInProvisionMode) {
            paramsBuilder.setTechMask(NfcDiscoveryParameters.NFC_POLL_DEFAULT);
            // enable P2P for MFM/EDU/Corp provisioning
            paramsBuilder.setEnableP2p(true);
        } else if (screenState == ScreenStateHelper.SCREEN_STATE_ON_LOCKED &&
                mNfcUnlockManager.isLockscreenPollingEnabled()) {
            Log.d(TAG, "!!!! SCREEN_STATE_ON_LOCKED setTechMask ");

                
            // For lock-screen tags, no low-power polling
            paramsBuilder.setTechMask(mNfcUnlockManager.getLockscreenPollMask());
            paramsBuilder.setEnableLowPowerDiscovery(false);
            paramsBuilder.setEnableP2p(false);
        }

        if (mIsHceCapable && mScreenState >= ScreenStateHelper.SCREEN_STATE_ON_LOCKED) {
            // Host routing is always enabled at lock screen or later
            
            Log.d(TAG, "  >= SCREEN_STATE_ON_LOCKED  paramsBuilder.setEnableHostRouting(true) ");
            paramsBuilder.setEnableHostRouting(true);
        }

        return paramsBuilder.build();
    }
因此如果要改成黑屏状态下可以使用NFC的话,只要将变量NFC_POLLING_MODE改成
ScreenStateHelper.SCREEN_STATE_OFF即可,代码如下:

    // minimum screen state that enables NFC polling
    //static final int NFC_POLLING_MODE = ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED;
    static final int NFC_POLLING_MODE = ScreenStateHelper.SCREEN_STATE_OFF;

      但是这样的话,手机会一直不休眠,观察电池的电流和电压发现,一直在跳动,这样在黑屏状态下,手机不会休眠,会很耗电,因此还要优化。

      客户的要求是:当双击物理按键Camera键的时候,可以在黑屏状态下使用NFC十分钟,十分钟之类,差不多关于NFC的工作完成了,之后将状态改回来,即:只能在解锁状态下使用NFC,这样的话就可以黑屏使用NFC又节电。

因此,思路如下:

1、接收物理按键Camera键发送的广播,来判断是双击,并将NFC_POLLING_MODE的最小模式改为ScreenStateHelper.SCREEN_STATE_OFF。

2、需要写一个定时器来处理十分钟之后将NFC_POLLING_MODE的最小模式改为会原来的ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED。

      

因此,首先先定义几个常量,从第185行static final int NFC_POLLING_MODE= ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED;处开始修改,修改代码如下:

 // minimum screen state that enables NFC polling
    // edited by ouyang [2015-10-19] start
//    static final int NFC_POLLING_MODE= ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED;
    //默认为要解锁才能使用NFC
	static final int NFC_POLLING_MODE_DEFALUT = ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED;
	//在黑屏情况下也可以使用NFC
    static final int NFC_POLLING_MODE_SCREEN_OFF = ScreenStateHelper.SCREEN_STATE_OFF;
    //默认能使用NFC时的屏幕状态
    static int NFC_POLLING_MODE = ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED;
	
    public static int getNFC_POLLING_MODE() {
		return NFC_POLLING_MODE;
	}
    
	public static void setNFC_POLLING_MODE(int mNFC_POLLING_MODE) {
		NFC_POLLING_MODE = mNFC_POLLING_MODE;
	}
	//是否是双击Camera键
	static boolean isDoublePress=false;
	//从黑屏可用NFC恢复到要解锁才能用NFC的时间
	static final int TIME_TO_Restore_Default_Values=(60*1000)*10;//10分钟  10*1000*60
    // edited by ouyang [2015-10-19] end

第二步:写一个广播接收者来处理物理按键Camera,按下和松开时发出的广播。

因为是要判断双击Camera,所以这里只要接收松开Camera键时发出的广播即可。这个广播是公司自己定义的,定义的广播为:"com.runbo.camera.key.up"。所以现在处理这个广播。因为代码中本来就动态注册了一个广播接收者,因此我们在这个广播接收者种再注册一个Intent即可。代码如下:在第450行

// Intents for all users
        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_USER_PRESENT);
        filter.addAction(Intent.ACTION_USER_SWITCHED);
        
        //added by ouyang start [2015-10-19]
        //Camera物理键按下后松开  发出的广播
        filter.addAction("com.runbo.camera.key.up");
        //added by ouyang end [2015-10-19] 
       
        registerForAirplaneMode(filter);
        mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null);

这样我们处理这个双击 Camera键可以在mReceiver中处理了,在mReceiver中的onReceive方法中,判断action是否是"com.runbo.camera.key.up",2295行代码如下:

 //added by ouyang start [2015-10-19] Camera物理键按下后松开
            else if (action.equals("com.runbo.camera.key.up")) {
				Log.d("oyp", "<----com.runbo.camera.key.up---->");
				Handler checkHandler=new Handler();
				Handler restoreHandler=new Handler();
				
				//单击
				if (!isDoublePress) {
					isDoublePress=true;//500ms之类再单击Camera键的话,就是双击了,直接进入  else语句块
					//500ms后触发该线程,查看是单击还是双击
					Runnable CheckDoubleRunnable=new Runnable(){  
						   @Override  
						   public void run() {  
							if (isDoublePress) {
								Log.i("oyp", "<----Single Press the Camera Key---->");  //显示为单击
							}else{
								Log.i("oyp", "<----Double Press the Camera Key---->");  //显示为双击
							}
							isDoublePress=false;//500ms后在单击Camera键的话 依旧是单击 ,还是进入了 if语句块
						   }   
						};  
					checkHandler.postDelayed(CheckDoubleRunnable, 500);// 500ms内两次单击,触发双击  
				}
				// 500ms内两次单击,触发双击  
				else{
					isDoublePress=false;//双击后,将该值设为false,下次在单击Camera键的话 依旧是单击 ,还是进入了 if语句块
					//设置在屏幕关闭情况下仍然可以使用NFC
					setNFC_POLLING_MODE(NFC_POLLING_MODE_SCREEN_OFF);
					applyRouting(true);
					Log.d("oyp", "2、NFC_POLLING_MODE="+getNFC_POLLING_MODE());
					//10分钟后触发该线程,恢复原来值 
					Runnable RestoreDefaultValues=new Runnable(){  
						   @Override  
						   public void run() {  
							//设置在屏幕解锁情况下可以使用NFC
							setNFC_POLLING_MODE(NFC_POLLING_MODE_DEFALUT);
							applyRouting(true);
							Log.d("oyp", "3、NFC_POLLING_MODE="+getNFC_POLLING_MODE());
						   }   
						};  
					restoreHandler.removeCallbacks(RestoreDefaultValues);//先取消定时器
					restoreHandler.postDelayed(RestoreDefaultValues, TIME_TO_Restore_Default_Values);//10分钟后恢复原来值 
				}
			}
            //added by ouyang end [2015-10-19]

还要将computeDiscoveryParameters()方法中的判断语句改掉,1733行代码如下:

//        if (screenState >= NFC_POLLING_MODE) {
        //edited by ouyang [2015-10-19 11:13:17]
        if (screenState >= getNFC_POLLING_MODE()) {

====================================================================================

上面代码用Handler做十分钟定时器的时候,时间不准确,改用AlarmManager做定时器,下面是修改的代码

//added by ouyang start [2015-11-4] 40分钟后恢复NFC默认值的广播
            else if (action.equals("com.runbo.camera.nfc.restore")) {
            	//设置在屏幕解锁情况下可以使用NFC
            	Log.d("oyp", "<----com.runbo.camera.nfc.restore---->");
				setNFC_POLLING_MODE(NFC_POLLING_MODE_DEFALUT);
				applyRouting(true);
				Log.d("oyp", "3、NFC_POLLING_MODE="+getNFC_POLLING_MODE());

                SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date date=new Date();
                String time=sdf.format(date);
                Log.d("oyp", "time after="+time);
            }
            //added by ouyang start [2015-10-19] Camera物理键按下后松开
            else if (action.equals("com.runbo.camera.key.up")) {
				Log.d("oyp", "<----com.runbo.camera.key.up---->");
				Handler checkHandler=new Handler();
				Handler restoreHandler=new Handler();
				
				//单击
				if (!isDoublePress) {
					isDoublePress=true;//500ms之类再单击Camera键的话,就是双击了,直接进入  else语句块
					//500ms后触发该线程,查看是单击还是双击
					Runnable CheckDoubleRunnable=new Runnable(){  
						   @Override  
						   public void run() {  
							if (isDoublePress) {
								Log.i("oyp", "<----Single Press the Camera Key---->");  //显示为单击
							}else{
								Log.i("oyp", "<----Double Press the Camera Key---->");  //显示为双击
							}
							isDoublePress=false;//500ms后在单击Camera键的话 依旧是单击 ,还是进入了 if语句块
						   }   
						};  
					checkHandler.postDelayed(CheckDoubleRunnable, 500);// 500ms内两次单击,触发双击  
				}
				// 500ms内两次单击,触发双击  
				else{
					isDoublePress=false;//双击后,将该值设为false,下次在单击Camera键的话 依旧是单击 ,还是进入了 if语句块
					//设置在屏幕关闭情况下仍然可以使用NFC
					setNFC_POLLING_MODE(NFC_POLLING_MODE_SCREEN_OFF);
					applyRouting(true);
					Log.d("oyp", "2、NFC_POLLING_MODE="+getNFC_POLLING_MODE());
					//使用AlarmManager来做定时
					AlarmManager aManager = (AlarmManager)context.getSystemService(Service.ALARM_SERVICE);
					// 指定启动AlarmActivity组件
					Intent nfcRestoreIntent = new Intent("com.runbo.camera.nfc.restore");
					// 创建PendingIntent对象
					PendingIntent pi = PendingIntent.getBroadcast(context, 0, nfcRestoreIntent, 0);
					//设定一个40分钟后的时间
					Calendar calendar=Calendar.getInstance();
					calendar.setTimeInMillis(System.currentTimeMillis());
					calendar.add(Calendar.MINUTE,TIME_TO_Restore_Default_Values);
					//先取消定时器
					//aManager.cancel(pi);
					// 设置AlarmManager将在Calendar对应的时间启动指定组件
					aManager.set(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(), pi);	
                
                    SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Date date=new Date();
                    String time=sdf.format(date);
                    Log.d("oyp", "time before="+time);
					
//					//10分钟后触发该线程,恢复原来值 
//					Runnable RestoreDefaultValues=new Runnable(){  
//						   @Override  
//						   public void run() {  
//							//设置在屏幕解锁情况下可以使用NFC
//							setNFC_POLLING_MODE(NFC_POLLING_MODE_DEFALUT);
//							applyRouting(true);
//							Log.d("oyp", "3、NFC_POLLING_MODE="+getNFC_POLLING_MODE());
//						   }   
//						};  
//					restoreHandler.removeCallbacks(RestoreDefaultValues);//先取消定时器
//					restoreHandler.postDelayed(RestoreDefaultValues, TIME_TO_Restore_Default_Values);//10分钟后恢复原来值
				}
			}
            //added by ouyang end [2015-10-19]


   将Handler的代码注释掉了,改用AlarmManager来做定时器,到达指定的时间后,发送一个“com.runbo.camera.nfc.restore”广播,这个广播也让该广播接收者来接收,因此动态注册广播的代码改成:

      //如果是Hanbang的定制软件
        if (android.os.SystemProperties.isHanbangVersion()) {
            //接收Camera物理键按下后松开,发出的广播 
            filter.addAction("com.runbo.camera.key.up");
            //接收NFC恢复默认值的广播
            filter.addAction("com.runbo.camera.nfc.restore");
        }
        //added by ouyang end [2015-10-19] 


因为之前使用秒来计时,现在使用分钟来计时,因此TIME_TO_Restore_Default_Values改成40,即40分钟

 //从黑屏可用NFC恢复到要解锁才能用NFC的时间
static final int TIME_TO_Restore_Default_Values=40; //40分钟


 

====================================================================================

下面呈上修改后的代码和没修改的代码,经验证,完美!

修改之后的代码如下:



修改之前的代码如下:


               ====================================================================================

  作者:欧阳鹏  欢迎转载,与人分享是进步的源泉!

  转载请保留原文地址:http://blog.csdn.net/ouyang_peng

====================================================================================




你可能感兴趣的:([置顶] Android系统移植与调试之------->如何修改Android手机NFC模块,使黑屏时候能够使用NFC)