今天发现package中 NFC都没怎么好好看过,导致我写一个新的东西很难,所以决定今天把它剖析完。哈,发现找到我想要的东西就不想写全拉,嘿,果然还是很懒!!
因为新东西是跟NFC TAG有关,故从这里入手,需要看得为package下面的nfc文件夹里的东东
1. NfcService
这个是继承自Application. 应该是方便设一些全局变量。
里面有几个内部类NfcAdapterService,这个在下面介绍。
这其中还用到了keyguardManager 跟powerManager,表明有一段是跟屏幕有关。
定义了一个sharedPreference--NfcServicePrefs用来保存临时数据。
以NfcAdapter.STATE_OFF 或者 NfcAdapter.STATE_ON定义当前Nfc Adapter状态。
一句 mScreenState = checkScreenState(); 显而易见了PowerManager和KeyguardManager的意义,检查screen的状态
int checkScreenState() {
if (!mPowerManager.isScreenOn()) {
return SCREEN_STATE_OFF;//screen 完全不亮
} else if (mKeyguard.isKeyguardLocked()) {
return SCREEN_STATE_ON_LOCKED;
//点亮screen,但是有锁
} else {
return SCREEN_STATE_ON_UNLOCKED;//点亮screen并且无锁状态
}
}
注册了一个broadcastReceiver,该receiver filter很多,如下
IntentFilter filter = new IntentFilter(NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION);//那么这里NativeNfcManager呢就是一个native的接口然后加载libnfc库啊什么的 这个action也是通过监听发出的,比如其中onCardEmulationDeselected,一旦这个方法被回调了,那么间接的下面的receiver就会处理INTERNAL_TARGET_DESELECTED_ACTION
故而要了解下什么是deviceHost和deviceHostListener,应该是跟Nfc adapter有关啦
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(ACTION_MASTER_CLEAR_NOTIFICATION);
filter.addAction(Intent.ACTION_USER_PRESENT);
......
registerReceiver(mReceiver, filter);
......
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(
NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION)) {
// Perform applyRouting() in AsyncTask to serialize blocking calls
new ApplyRoutingTask().execute(); //这里可以看下ApplyRoutingTask干啥子了
} else if (action.equals(Intent.ACTION_SCREEN_ON)
|| action.equals(Intent.ACTION_SCREEN_OFF)
|| action.equals(Intent.ACTION_USER_PRESENT)) {//String · ACTION_USER_PRESENT,
Broadcast Action: Sent when the user is present after device wakes up (e.g when the keyguard is gone).
// Perform applyRouting() in AsyncTask to serialize blocking calls
int screenState = SCREEN_STATE_OFF;
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
screenState = SCREEN_STATE_OFF;
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
screenState = mKeyguard.isKeyguardLocked() ?
SCREEN_STATE_ON_LOCKED : SCREEN_STATE_ON_UNLOCKED;
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
screenState = SCREEN_STATE_ON_UNLOCKED;
}
new ApplyRoutingTask().execute(Integer.valueOf(screenState));
} else if (action.equals(ACTION_MASTER_CLEAR_NOTIFICATION)) {
EnableDisableTask eeWipeTask = new EnableDisableTask();
eeWipeTask.execute(TASK_EE_WIPE);
try {
eeWipeTask.get(); // blocks until EE wipe is complete
} catch (ExecutionException e) {
Log.w(TAG, "failed to wipe NFC-EE");
} catch (InterruptedException e) {
Log.w(TAG, "failed to wipe NFC-EE");
}
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
// Clear the NFCEE access cache in case a UID gets recycled
mNfceeAccessControl.invalidateCache();
boolean dataRemoved = intent.getBooleanExtra(Intent.EXTRA_DATA_REMOVED, false);
if (dataRemoved) {
Uri data = intent.getData();
if (data == null) return;
String packageName = data.getSchemeSpecificPart();
synchronized (NfcService.this) {
if (mSePackages.contains(packageName)) {
new EnableDisableTask().execute(TASK_EE_WIPE);
mSePackages.remove(packageName);
}
}
}
} else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
boolean isAirplaneModeOn = intent.getBooleanExtra("state", false);
// Query the airplane mode from Settings.System just to make sure that
// some random app is not sending this intent
if (isAirplaneModeOn != isAirplaneModeOn()) {
return;
}
if (!mIsAirplaneSensitive) {
return;
}
if (isAirplaneModeOn) {
new EnableDisableTask().execute(TASK_DISABLE);
} else if (!isAirplaneModeOn && mPrefs.getBoolean(PREF_NFC_ON, NFC_ON_DEFAULT)) {
new EnableDisableTask().execute(TASK_ENABLE);
}
}
}
};
以上是onCreate里做的主要的事。
2.NfcDispatcher
在NfcService中会通过实例化一个 NfcDispatcher mNfcDispatcher = new NfcDispatcher(this, mP2pLinkManager);
发送NFC 事件启动一些activity, 你可以追下dispatchTagInternal()这个方法就会发现NfcAdapter.ACTION_NDEF_DISCOVERED,NfcAdapter.ACTION_TECH_DISCOVERED,NfcAdapter.ACTION_TAG_DISCOVERED这些intent是从哪里来的
// Dispatch to either an override pending intent or a standard startActivity()
private boolean dispatchTagInternal(Tag tag, NdefMessage[] msgs,
PendingIntent overrideIntent, IntentFilter[] overrideFilters,
String[][] overrideTechLists)
throws CanceledException{
Intent intent;
//
// Try the NDEF content specific dispatch
//
if (msgs != null && msgs.length > 0) {
NdefMessage msg = msgs[0];
NdefRecord[] records = msg.getRecords();
if (records.length > 0) {
// Found valid NDEF data, try to dispatch that first
NdefRecord record = records[0];
intent = buildTagIntent(tag, msgs, NfcAdapter.ACTION_NDEF_DISCOVERED);
if (setTypeOrDataFromNdef(intent, record)) {
// The record contains filterable data, try to start a matching activity
if (startDispatchActivity(intent, overrideIntent, overrideFilters,
overrideTechLists, records)) {
// If an activity is found then skip further dispatching
return true;
} else {
if (DBG) Log.d(TAG, "No activities for NDEF handling of " + intent);
}
}
}
}
//
// Try the technology specific dispatch
//
String[] tagTechs = tag.getTechList();
Arrays.sort(tagTechs);
if (overrideIntent != null) {
// There are dispatch overrides in place
if (overrideTechLists != null) {
for (String[] filterTechs : overrideTechLists) {
if (filterMatch(tagTechs, filterTechs)) {
// An override matched, send it to the foreground activity.
intent = buildTagIntent(tag, msgs,
NfcAdapter.ACTION_TECH_DISCOVERED);
overrideIntent.send(mContext, Activity.RESULT_OK, intent);
return true;
}
}
}
} else {
// Standard tech dispatch path
ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
ArrayList<ComponentInfo> registered = mTechListFilters.getComponents();
// Check each registered activity to see if it matches
for (ComponentInfo info : registered) {
// Don't allow wild card matching
if (filterMatch(tagTechs, info.techs) &&
isComponentEnabled(mPackageManager, info.resolveInfo)) {
// Add the activity as a match if it's not already in the list
if (!matches.contains(info.resolveInfo)) {
matches.add(info.resolveInfo);
}
}
}
if (matches.size() == 1) {
// Single match, launch directly
intent = buildTagIntent(tag, msgs, NfcAdapter.ACTION_TECH_DISCOVERED);
ResolveInfo info = matches.get(0);
intent.setClassName(info.activityInfo.packageName, info.activityInfo.name);
if (startRootActivity(intent)) {
return true;
}
} else if (matches.size() > 1) {
// Multiple matches, show a custom activity chooser dialog
intent = new Intent(mContext, TechListChooserActivity.class);
intent.putExtra(Intent.EXTRA_INTENT,
buildTagIntent(tag, msgs, NfcAdapter.ACTION_TECH_DISCOVERED));
intent.putParcelableArrayListExtra(TechListChooserActivity.EXTRA_RESOLVE_INFOS,
matches);
if (startRootActivity(intent)) {
return true;
}
} else {
// No matches, move on
if (DBG) Log.w(TAG, "No activities for technology handling");
}
}
//
// Try the generic intent
//
intent = buildTagIntent(tag, msgs, NfcAdapter.ACTION_TAG_DISCOVERED);
if (startDispatchActivity(intent, overrideIntent, overrideFilters, overrideTechLists,
null)) {
return true;
} else {
Log.e(TAG, "No tag fallback activity found for " + intent);
return false;
}
}
3.NfcAdapterService
此在NfcService中的内部类,却大有用途,在NfcService 有通过ServiceManager将其添加到后台一直运行
mNfcAdapter = new NfcAdapterService();
ServiceManager.addService(SERVICE_NAME, mNfcAdapter);
4.ApplyRoutingTask
5.EnableDisableTask
引用源码中的介绍此类功能 Manages tasks that involve turning on/off the NFC controller.
这里我们先要了解下可能传递给它的参数值,分别有
*TASK_ENABLE--- enables the NFC adapter, without changing preferences
*TASK_DISABLE--- disables the NFC adapter, without changing
*TASK_BOOT---- does first boot work and may enable NFC
* TASK_EE_WIPE---- wipes the Execution Environment, and in the process may temporarily enable the NFC adapter
如果说当前 NfcAdapter处于STATE_TURNING_OFF或者STATE_TURNING_ON,那这个就不要往下做了,直接返回一个null回去
这里有三个函数要去看
enableInternal() --不做介绍
disableInternal()
--这里有介绍说要另起thread以防dviceHost deintialize的时候挂起,哎 为什么不用AsyncTask的处理方式,他说了,当NFC controller 停止响应的时候呢,ui线程和Async task 线程池也会挂起的。这个thread呢如下
class WatchDogThread extends Thread {
boolean mWatchDogCanceled = false;
@Override
public void run() {
boolean slept = false;
while (!slept) {
try {
Thread.sleep(10000);//哎,阻塞当前线程了
slept = true;
} catch (InterruptedException e) { }
}
synchronized (this) {
if (!mWatchDogCanceled) {
// Trigger watch-dog
Log.e(TAG, "Watch dog triggered");
mDeviceHost.doAbort();//啊,自己在这跑东西了,用到native方法来时NFC adpter为off
}
}
}
public synchronized void cancel() {
mWatchDogCanceled = true;
}
}
当然这个下面又说了,如果有tag出现的话要停止这个thread,那么要恰当的停止这个thread的话就要包含中断对tag的连接 。并且呢在避免tag再次被发现时要停止轮询(polling loop)
applyRouting(true); //这个是我比较关心的一个东东,哎
maybeDisconnectTarget();
当deinitialize devicehost 成功了,就要停止watchDog thread了,并且更新Nfc Adapter状态,broadcast出去
executeEeWipe()
当参数为TASK_EE_WIPE-就会调到这个函数,这里设计到NFC Execution Environment fields,干嘛的看最后的Tips,反正这个要在NFC Adapter处于on的状态下执行,具体要调用到native的方法了。
applyRouting()
/**
* Read mScreenState and apply NFC-C polling and NFC-EE routing
*/
void applyRouting(boolean force) {
synchronized (this) {
if (!isNfcEnabled() || mOpenEe != null) {
// PN544 cannot be reconfigured while EE is open
return;
}
//handle screen off, disable NFC discover tag
if (PN544_QUIRK_DISCONNECT_BEFORE_RECONFIGURE && mScreenState == SCREEN_STATE_OFF) {
/* TODO undo this after the LLCP stack is fixed.
* Use a different sequence when turning the screen off to
* workaround race conditions in pn544 libnfc. The race occurs
* when we change routing while there is a P2P target connect.
* The async LLCP callback will crash since the routing code
* is overwriting globals it relies on.
*/
if (POLLING_MODE > SCREEN_STATE_OFF) {
if (force || mNfcPollingEnabled) {
Log.d(TAG, "NFC-C OFF, disconnect");
mNfcPollingEnabled = false;
mDeviceHost.disableDiscovery();
maybeDisconnectTarget();
}
}
if (mEeRoutingState == ROUTE_ON_WHEN_SCREEN_ON) {
if (force || mNfceeRouteEnabled) {
Log.d(TAG, "NFC-EE OFF");
mNfceeRouteEnabled = false;
mDeviceHost.doDeselectSecureElement();
}
}
return;
}
// configure NFC-EE routing
if (mScreenState >= SCREEN_STATE_ON_LOCKED &&
mEeRoutingState == ROUTE_ON_WHEN_SCREEN_ON) {
if (force || !mNfceeRouteEnabled) {
Log.d(TAG, "NFC-EE ON");
mNfceeRouteEnabled = true;
mDeviceHost.doSelectSecureElement();
}
} else {
if (force || mNfceeRouteEnabled) {
Log.d(TAG, "NFC-EE OFF");
mNfceeRouteEnabled = false;
mDeviceHost.doDeselectSecureElement();
}
}
// configure NFC-C polling
//这里是要解锁了才能让NFC discover TAG,所以也许我可以改成mScreenState >= SCREEN_STATE_ON_LOCKED,
//那么不管有无锁都可以discover tag了~哎
if (mScreenState >= POLLING_MODE) {
if (force || !mNfcPollingEnabled) {
Log.d(TAG, "NFC-C ON");
mNfcPollingEnabled = true;
mDeviceHost.enableDiscovery();
}
} else {
if (force || mNfcPollingEnabled) {
Log.d(TAG, "NFC-C OFF");
mNfcPollingEnabled = false;
mDeviceHost.disableDiscovery();
}
}
}
}
TIPS: Card emulation(what google wallet does) and tag reading/writing are 2 different features of the NFC chip. On a phone with Google Wallet, the secure Element is enabled when the lock screen is displayed(in the logcat you will
see NFC-EE ON). However, NFC polling for tags is still turned off. It will only be turned on when you unlock the phone(logcat: NFC-C ON). Both are turned off when the screen is turned off. --此处引用我网搜来的,很好阿,促进我理解了NFC-EE和NFC-C
后续:
去面试,说了下实现nfc tag解锁 方法,大致是修改了nfcService.java中 nfcServiceHandler中处理MSG_NDEF_TAG的消息时braodcast出去一个自定义的字串,这样在PhoneWindowManager.java中init中注册接收,然后作解锁处理动作mKeyguardMediator.keyguardDone(true,true);
可是那个面试的人就说其实不用自定义这样的broadcast,系统有的,不过呢我回来还是没找着,对他表示怀疑~~改天再问下~