我们都知道在不修改源代码的情况下,只能是解锁之后才能使用NFC功能。而在锁屏和黑屏2个状态下是没办法用NFC的,但是最近有个客户要求手机在黑屏状态下能够使用NFC,因此我们需要去修改Android源代码关于NFC模块。
最开始可以通过查看分析源代码,找到到NfcService的相关代码,如下: packagesappsNfcsrccomandroid fcNfcService.java
找到186行,这句是定义NFC能够使用的屏幕最小状态
1
2
|
// 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 屏幕亮了,并且是解锁状态
代码定义如下,在packagesappsNfcsrccomandroid fcScreenStateHelper中定义
1
2
3
4
|
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)方法中被调用,用来判断的,方法代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
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();
}
|
ScreenStateHelper.SCREEN_STATE_OFF即可,代码如下:
1
2
3
|
// 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;处开始修改,修改代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// 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行
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// 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行代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
//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行代码如下:
1
2
3
|
// if (screenState >= NFC_POLLING_MODE) {
//edited by ouyang [2015-10-19 11:13:17]
if
(screenState >= getNFC_POLLING_MODE()) {
|