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

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

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




相关文章
|
2月前
|
Android开发
基于android-11.0.0_r39,系统应用的手动签名方法和过程
本文介绍了基于Android 11.0.0_r39版本进行系统应用手动签名的方法和解决签名过程中遇到的错误,包括处理`no conscrypt_openjdk_jni-linux-x86_64`和`RegisterNatives failed`的问题。
112 2
|
2月前
|
JavaScript 前端开发 Java
[Android][Framework]系统jar包,sdk的制作及引用
[Android][Framework]系统jar包,sdk的制作及引用
47 0
|
5天前
|
Ubuntu Linux Android开发
termux+anlinux+Rvnc viewer来使安卓手机(平板)变成linux服务器
本文介绍了如何在Android设备上安装Termux和AnLinux,并通过这些工具运行Ubuntu系统和桌面环境。
29 2
termux+anlinux+Rvnc viewer来使安卓手机(平板)变成linux服务器
|
1天前
|
Web App开发 Android开发
利用firefox调试安卓手机端web
该教程详细介绍如何通过Firefox浏览器实现手机与电脑的远程调试。手机端需安装最新版Firefox,并按指定步骤设置完成;电脑端则需安装15版及以上Firefox。设置完成后,通过工具栏中的“远程调试”选项,输入手机IP地址即可连接。连接确认后,即可使用电脑端Firefox调试器调试手机上的Web信息。注意,调试前手机需提前打开目标网页。
11 2
|
3天前
|
IDE Android开发 iOS开发
探索安卓与iOS系统的技术差异:开发者的视角
本文深入分析了安卓(Android)与苹果iOS两大移动操作系统在技术架构、开发环境、用户体验和市场策略方面的主要差异。通过对比这两种系统的不同特点,旨在为移动应用开发者提供有价值的见解,帮助他们在不同平台上做出更明智的开发决策。
|
3天前
|
Ubuntu Shell API
Ubuntu 64系统编译android arm64-v8a 的openssl静态库libssl.a和libcrypto.a
Ubuntu 64系统编译android arm64-v8a 的openssl静态库libssl.a和libcrypto.a
|
21天前
|
监控 Android开发 iOS开发
深入探索安卓与iOS的系统架构差异:理解两大移动平台的技术根基在移动技术日新月异的今天,安卓和iOS作为市场上最为流行的两个操作系统,各自拥有独特的技术特性和庞大的用户基础。本文将深入探讨这两个平台的系统架构差异,揭示它们如何支撑起各自的生态系统,并影响着全球数亿用户的使用体验。
本文通过对比分析安卓和iOS的系统架构,揭示了这两个平台在设计理念、安全性、用户体验和技术生态上的根本区别。不同于常规的技术综述,本文以深入浅出的方式,带领读者理解这些差异是如何影响应用开发、用户选择和市场趋势的。通过梳理历史脉络和未来展望,本文旨在为开发者、用户以及行业分析师提供有价值的见解,帮助大家更好地把握移动技术发展的脉络。
|
18天前
|
Dart 开发工具 Android开发
在 Android 系统上搭建 Flutter 环境的具体步骤是什么?
在 Android 系统上搭建 Flutter 环境的具体步骤是什么?
|
1月前
|
Android开发 UED 开发者
Android经典实战之WindowManager和创建系统悬浮窗
本文详细介绍了Android系统服务`WindowManager`,包括其主要功能和工作原理,并提供了创建系统悬浮窗的完整步骤。通过示例代码,展示了如何添加权限、请求权限、实现悬浮窗口及最佳实践,帮助开发者轻松掌握悬浮窗开发技巧。
84 1
|
2月前
|
Java 物联网 Android开发
移动应用与系统:技术演进与未来展望探索安卓应用开发:从新手到专家的旅程
【8月更文挑战第28天】本文将深入探讨移动应用开发的技术演进、移动操作系统的发展历程以及未来的发展趋势。我们将通过实例和代码示例,展示如何利用最新的技术和工具来开发高效、可靠的移动应用。无论你是初学者还是经验丰富的开发者,这篇文章都将为你提供有价值的信息和见解。 【8月更文挑战第28天】在这个数字时代,掌握安卓应用的开发技能不仅是技术人员的追求,也成为了许多人实现创意和梦想的途径。本文将通过深入浅出的方式,带领读者从零基础开始,一步步走进安卓开发的奇妙世界。我们将探讨如何配置开发环境,理解安卓应用的核心组件,以及如何通过实际编码来构建一个功能完整的应用。无论你是编程新手还是希望提升自己的开发者