summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp4
-rwxr-xr-xAndroidManifest.xml10
-rw-r--r--nci/jni/Android.bp5
-rw-r--r--nci/jni/HciEventManager.cpp34
-rw-r--r--nci/jni/NativeLlcpConnectionlessSocket.cpp2
-rw-r--r--nci/jni/NativeNfcManager.cpp281
-rw-r--r--nci/jni/NativeNfcTag.cpp11
-rw-r--r--nci/jni/PeerToPeer.cpp2
-rw-r--r--[-rwxr-xr-x]nci/jni/RoutingManager.cpp52
-rwxr-xr-xnci/jni/RoutingManager.h6
-rwxr-xr-xnci/src/com/android/nfc/dhimpl/NativeNfcManager.java18
-rw-r--r--res/values-as/strings.xml2
-rw-r--r--res/values-hy/strings.xml2
-rw-r--r--res/values-iw/strings.xml2
-rw-r--r--res/values-ta/strings.xml2
-rw-r--r--res/values/config.xml1
-rw-r--r--src/com/android/nfc/DeviceHost.java14
-rw-r--r--src/com/android/nfc/NfcDispatcher.java2
-rw-r--r--src/com/android/nfc/NfcPermissions.java16
-rw-r--r--src/com/android/nfc/NfcRootActivity.java1
-rw-r--r--src/com/android/nfc/NfcService.java345
-rw-r--r--src/com/android/nfc/NfcWifiProtectedSetup.java2
-rw-r--r--src/com/android/nfc/beam/BeamManager.java10
-rw-r--r--src/com/android/nfc/beam/BeamTransferManager.java13
-rw-r--r--src/com/android/nfc/beam/SendUi.java22
-rw-r--r--src/com/android/nfc/cardemulation/AidRoutingManager.java86
-rw-r--r--src/com/android/nfc/cardemulation/CardEmulationManager.java11
-rw-r--r--src/com/android/nfc/cardemulation/HostEmulationManager.java25
-rw-r--r--src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java12
-rw-r--r--src/com/android/nfc/cardemulation/PreferredServices.java2
-rw-r--r--src/com/android/nfc/cardemulation/RegisteredAidCache.java74
-rw-r--r--src/com/android/nfc/handover/BluetoothPeripheralHandover.java3
-rw-r--r--src/com/android/nfc/handover/HandoverDataParser.java59
-rw-r--r--src/com/android/nfc/snep/SnepMessenger.java5
-rw-r--r--tests/Android.bp27
-rw-r--r--tests/AndroidManifest.xml37
-rw-r--r--tests/src/com/android/nfc/MockLlcpSocket.java105
-rw-r--r--tests/src/com/android/nfc/snep/SnepBasicTests.java299
-rw-r--r--tests/src/com/android/nfc/snep/SnepCustomClientTests.java95
-rw-r--r--tests/src/com/android/nfc/snep/SnepDefaultClientTests.java91
-rw-r--r--tests/src/com/android/nfc/snep/SnepValidationClientTests.java201
-rw-r--r--tests/src/com/android/nfc/snep/SnepValidationServerTests.java135
-rw-r--r--tests/unit/Android.bp32
-rw-r--r--tests/unit/AndroidManifest.xml34
-rw-r--r--tests/unit/AndroidTest.xml30
-rw-r--r--tests/unit/src/com/android/nfc/NfcFeatureFlagTest.java85
-rw-r--r--tests/unit/src/com/android/nfc/NfcStateTest.java358
47 files changed, 1450 insertions, 1215 deletions
diff --git a/Android.bp b/Android.bp
index 8c68554d..6c3fefd7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
genrule {
name: "statslog-Nfc-java-gen",
tools: ["stats-log-api-gen"],
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index fba2509d..66a4a8a0 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,8 +20,11 @@
>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.NFC" />
+ <uses-permission android:name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON" />
<uses-permission android:name="android.permission.BIND_NFC_SERVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.STATUS_BAR" />
@@ -47,7 +50,7 @@
<uses-permission android:name="android.permission.SET_ACTIVITY_WATCHER" />
<uses-permission android:name="android.permission.NFC_HANDOVER_STATUS" />
<uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" />
- <uses-permission android:name="com.android.permission.WHITELIST_BLUETOOTH_DEVICE" />
+ <uses-permission android:name="com.android.permission.ALLOWLIST_BLUETOOTH_DEVICE" />
<uses-permission android:name="android.permission.DISPATCH_NFC_MESSAGE" />
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
@@ -71,6 +74,7 @@
android:usesCleartextTraffic="false"
android:supportsRtl="true"
android:hardwareAccelerated="false"
+ android:memtagMode="async"
>
<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAIbiKKs0wlimxeJ9y8iRIaBOH6aeb2IurmZyBHvg" />
@@ -121,6 +125,7 @@
android:theme="@android:style/Theme.Translucent"
android:label="@string/android_beam"
android:noHistory="true"
+ android:exported="true"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.SEND" />
@@ -147,7 +152,8 @@
android:process=":beam"
/>
- <receiver android:name=".NfcBootCompletedReceiver">
+ <receiver android:name=".NfcBootCompletedReceiver"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
diff --git a/nci/jni/Android.bp b/nci/jni/Android.bp
index 65d065de..def76fc7 100644
--- a/nci/jni/Android.bp
+++ b/nci/jni/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_library_shared {
name: "libnfc_nci_jni",
@@ -48,5 +52,6 @@ cc_library_shared {
sanitize: {
integer_overflow: true,
misc_undefined: ["bounds"],
+ scs: true,
},
}
diff --git a/nci/jni/HciEventManager.cpp b/nci/jni/HciEventManager.cpp
index 0921f539..4aeaa6b0 100644
--- a/nci/jni/HciEventManager.cpp
+++ b/nci/jni/HciEventManager.cpp
@@ -16,6 +16,7 @@
#include "HciEventManager.h"
#include <android-base/stringprintf.h>
#include <base/logging.h>
+#include <log/log.h>
#include <nativehelper/ScopedLocalRef.h>
#include "JavaClassConstants.h"
#include "NfcJniUtil.h"
@@ -148,7 +149,7 @@ void HciEventManager::nfaHciCallback(tNFA_HCI_EVT event,
} else if (eventData->rcvd_evt.pipe == sSimPipe) {
evtSrc = "SIM1";
} else {
- LOG(ERROR) << "Incorrect Pipe Id";
+ LOG(WARNING) << "Incorrect Pipe Id";
return;
}
@@ -159,20 +160,25 @@ void HciEventManager::nfaHciCallback(tNFA_HCI_EVT event,
if (event == NFA_HCI_EVENT_RCVD_EVT &&
eventData->rcvd_evt.evt_code == NFA_HCI_EVT_TRANSACTION &&
buffLength > 3 && event_buff[0] == 0x81) {
- int aidlen = event_buff[1];
- std::vector<uint8_t> aid(event_buff.begin() + 2,
- event_buff.begin() + aidlen + 2);
-
- int32_t berTlvStart = aidlen + 2 + 1;
- int32_t berTlvLen = buffLength - berTlvStart;
- std::vector<uint8_t> data;
- if (berTlvLen > 0 && event_buff[2 + aidlen] == 0x82) {
- std::vector<uint8_t> berTlv(event_buff.begin() + berTlvStart,
- event_buff.end());
- // BERTLV decoding here, to support extended data length for params.
- data = getInstance().getDataFromBerTlv(berTlv);
+ uint32_t aidlen = event_buff[1];
+ if (aidlen < (buffLength - 1)) {
+ std::vector<uint8_t> aid(event_buff.begin() + 2,
+ event_buff.begin() + aidlen + 2);
+
+ int32_t berTlvStart = aidlen + 2 + 1;
+ int32_t berTlvLen = buffLength - berTlvStart;
+ std::vector<uint8_t> data;
+ if (berTlvLen > 0 && event_buff[2 + aidlen] == 0x82) {
+ std::vector<uint8_t> berTlv(event_buff.begin() + berTlvStart,
+ event_buff.end());
+ // BERTLV decoding here, to support extended data length for params.
+ data = getInstance().getDataFromBerTlv(berTlv);
+ }
+ getInstance().notifyTransactionListenersOfAid(aid, data, evtSrc);
+ } else {
+ android_errorWriteLog(0x534e4554, "181346545");
+ LOG(ERROR) << StringPrintf("error: aidlen(%d) is too big", aidlen);
}
- getInstance().notifyTransactionListenersOfAid(aid, data, evtSrc);
}
}
diff --git a/nci/jni/NativeLlcpConnectionlessSocket.cpp b/nci/jni/NativeLlcpConnectionlessSocket.cpp
index e43dbff3..7506d80e 100644
--- a/nci/jni/NativeLlcpConnectionlessSocket.cpp
+++ b/nci/jni/NativeLlcpConnectionlessSocket.cpp
@@ -108,7 +108,7 @@ void nativeLlcpConnectionlessSocket_receiveData(uint8_t* data, uint32_t len,
<< StringPrintf("%s: waiting for data = %d, len = %d", __func__,
sConnlessRecvWaitingForData, len);
- // Sanity...
+ // Quick check...
if (sConnlessRecvLen < len) {
len = sConnlessRecvLen;
}
diff --git a/nci/jni/NativeNfcManager.cpp b/nci/jni/NativeNfcManager.cpp
index 5048262c..28ae464a 100644
--- a/nci/jni/NativeNfcManager.cpp
+++ b/nci/jni/NativeNfcManager.cpp
@@ -18,6 +18,7 @@
#include <base/logging.h>
#include <cutils/properties.h>
#include <errno.h>
+#include <nativehelper/JNIPlatformHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
@@ -84,6 +85,8 @@ bool gActivated = false;
SyncEvent gDeactivatedEvent;
SyncEvent sNfaSetPowerSubState;
bool legacy_mfc_reader = true;
+int recovery_option = 0;
+int nfcee_power_and_link_conf = 0;
namespace android {
jmethodID gCachedNfcManagerNotifyNdefMessageListeners;
@@ -97,6 +100,7 @@ jmethodID gCachedNfcManagerNotifyHostEmuDeactivated;
jmethodID gCachedNfcManagerNotifyRfFieldActivated;
jmethodID gCachedNfcManagerNotifyRfFieldDeactivated;
jmethodID gCachedNfcManagerNotifyEeUpdated;
+jmethodID gCachedNfcManagerNotifyHwErrorReported;
const char* gNativeP2pDeviceClassName =
"com/android/nfc/dhimpl/NativeP2pDevice";
const char* gNativeLlcpServiceSocketClassName =
@@ -141,6 +145,7 @@ static bool sP2pActive = false; // whether p2p was last active
static bool sAbortConnlessWait = false;
static jint sLfT3tMax = 0;
static bool sRoutingInitialized = false;
+static bool sIsRecovering = false;
#define CONFIG_UPDATE_TECH_MASK (1 << 1)
#define DEFAULT_TECH_MASK \
@@ -191,13 +196,28 @@ void initializeGlobalDebugEnabledFlag() {
}
void initializeMfcReaderOption() {
legacy_mfc_reader =
- (NfcConfig::getUnsigned(NAME_LEGACY_MIFARE_READER, 0) != 0) ? true
- : false;
+ (NfcConfig::getUnsigned(NAME_LEGACY_MIFARE_READER, 0) != 0) ? true : false;
DLOG_IF(INFO, nfc_debug_enabled)
<< __func__ <<": mifare reader option=" << legacy_mfc_reader;
}
+void initializeRecoveryOption() {
+ recovery_option = NfcConfig::getUnsigned(NAME_RECOVERY_OPTION, 0);
+
+ DLOG_IF(INFO, nfc_debug_enabled)
+ << __func__ << ": recovery option=" << recovery_option;
+}
+
+void initializeNfceePowerAndLinkConf() {
+ nfcee_power_and_link_conf =
+ NfcConfig::getUnsigned(NAME_ALWAYS_ON_SET_EE_POWER_AND_LINK_CONF, 0);
+
+ DLOG_IF(INFO, nfc_debug_enabled)
+ << __func__ << ": Always on set NFCEE_POWER_AND_LINK_CONF="
+ << nfcee_power_and_link_conf;
+}
+
} // namespace
/*******************************************************************************
@@ -660,6 +680,8 @@ static void nfaConnectionCallback(uint8_t connEvent,
static jboolean nfcManager_initNativeStruc(JNIEnv* e, jobject o) {
initializeGlobalDebugEnabledFlag();
initializeMfcReaderOption();
+ initializeRecoveryOption();
+ initializeNfceePowerAndLinkConf();
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: enter", __func__);
nfc_jni_native_data* nat =
@@ -712,6 +734,9 @@ static jboolean nfcManager_initNativeStruc(JNIEnv* e, jobject o) {
gCachedNfcManagerNotifyEeUpdated =
e->GetMethodID(cls.get(), "notifyEeUpdated", "()V");
+ gCachedNfcManagerNotifyHwErrorReported =
+ e->GetMethodID(cls.get(), "notifyHwErrorReported", "()V");
+
if (nfc_jni_cache_object(e, gNativeNfcTagClassName, &(nat->cached_NfcTag)) ==
-1) {
LOG(ERROR) << StringPrintf("%s: fail cache NativeNfcTag", __func__);
@@ -822,46 +847,97 @@ void nfaDeviceManagementCallback(uint8_t dmEvent,
LOG(ERROR) << StringPrintf("%s: NFA_DM_NFCC_TRANSPORT_ERR_EVT; abort",
__func__);
- nativeNfcTag_abortWaits();
- NfcTag::getInstance().abort();
- sAbortConnlessWait = true;
- nativeLlcpConnectionlessSocket_abortWait();
- {
- DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
- "%s: aborting sNfaEnableDisablePollingEvent", __func__);
- SyncEventGuard guard(sNfaEnableDisablePollingEvent);
- sNfaEnableDisablePollingEvent.notifyOne();
- }
- {
- DLOG_IF(INFO, nfc_debug_enabled)
- << StringPrintf("%s: aborting sNfaEnableEvent", __func__);
- SyncEventGuard guard(sNfaEnableEvent);
- sNfaEnableEvent.notifyOne();
- }
- {
- DLOG_IF(INFO, nfc_debug_enabled)
- << StringPrintf("%s: aborting sNfaDisableEvent", __func__);
- SyncEventGuard guard(sNfaDisableEvent);
- sNfaDisableEvent.notifyOne();
- }
- sDiscoveryEnabled = false;
- sPollingEnabled = false;
- PowerSwitch::getInstance().abort();
-
- if (!sIsDisabling && sIsNfaEnabled) {
- EXTNS_Close();
- NFA_Disable(FALSE);
- sIsDisabling = true;
+ if (recovery_option) {
+ struct nfc_jni_native_data* nat = getNative(NULL, NULL);
+ JNIEnv* e = NULL;
+ ScopedAttach attach(nat->vm, &e);
+ if (e == NULL) {
+ LOG(ERROR) << StringPrintf("jni env is null");
+ return;
+ }
+ LOG(ERROR) << StringPrintf("%s: toggle NFC state to recovery nfc",
+ __func__);
+ sIsRecovering = true;
+ e->CallVoidMethod(nat->manager,
+ android::gCachedNfcManagerNotifyHwErrorReported);
+ {
+ DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+ "%s: aborting sNfaEnableDisablePollingEvent", __func__);
+ SyncEventGuard guard(sNfaEnableDisablePollingEvent);
+ sNfaEnableDisablePollingEvent.notifyOne();
+ }
+ {
+ DLOG_IF(INFO, nfc_debug_enabled)
+ << StringPrintf("%s: aborting sNfaEnableEvent", __func__);
+ SyncEventGuard guard(sNfaEnableEvent);
+ sNfaEnableEvent.notifyOne();
+ }
+ {
+ DLOG_IF(INFO, nfc_debug_enabled)
+ << StringPrintf("%s: aborting sNfaDisableEvent", __func__);
+ SyncEventGuard guard(sNfaDisableEvent);
+ sNfaDisableEvent.notifyOne();
+ }
+ {
+ DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+ "%s: aborting sNfaSetPowerSubState", __func__);
+ SyncEventGuard guard(sNfaSetPowerSubState);
+ sNfaSetPowerSubState.notifyOne();
+ }
+ {
+ DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+ "%s: aborting sNfaSetConfigEvent", __func__);
+ SyncEventGuard guard(sNfaSetConfigEvent);
+ sNfaSetConfigEvent.notifyOne();
+ }
+ {
+ DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+ "%s: aborting sNfaGetConfigEvent", __func__);
+ SyncEventGuard guard(sNfaGetConfigEvent);
+ sNfaGetConfigEvent.notifyOne();
+ }
} else {
- sIsNfaEnabled = false;
- sIsDisabling = false;
+ nativeNfcTag_abortWaits();
+ NfcTag::getInstance().abort();
+ sAbortConnlessWait = true;
+ nativeLlcpConnectionlessSocket_abortWait();
+ {
+ DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+ "%s: aborting sNfaEnableDisablePollingEvent", __func__);
+ SyncEventGuard guard(sNfaEnableDisablePollingEvent);
+ sNfaEnableDisablePollingEvent.notifyOne();
+ }
+ {
+ DLOG_IF(INFO, nfc_debug_enabled)
+ << StringPrintf("%s: aborting sNfaEnableEvent", __func__);
+ SyncEventGuard guard(sNfaEnableEvent);
+ sNfaEnableEvent.notifyOne();
+ }
+ {
+ DLOG_IF(INFO, nfc_debug_enabled)
+ << StringPrintf("%s: aborting sNfaDisableEvent", __func__);
+ SyncEventGuard guard(sNfaDisableEvent);
+ sNfaDisableEvent.notifyOne();
+ }
+ sDiscoveryEnabled = false;
+ sPollingEnabled = false;
+ PowerSwitch::getInstance().abort();
+
+ if (!sIsDisabling && sIsNfaEnabled) {
+ EXTNS_Close();
+ NFA_Disable(FALSE);
+ sIsDisabling = true;
+ } else {
+ sIsNfaEnabled = false;
+ sIsDisabling = false;
+ }
+ PowerSwitch::getInstance().initialize(PowerSwitch::UNKNOWN_LEVEL);
+ LOG(ERROR) << StringPrintf("%s: crash NFC service", __func__);
+ //////////////////////////////////////////////
+ // crash the NFC service process so it can restart automatically
+ abort();
+ //////////////////////////////////////////////
}
- PowerSwitch::getInstance().initialize(PowerSwitch::UNKNOWN_LEVEL);
- LOG(ERROR) << StringPrintf("%s: crash NFC service", __func__);
- //////////////////////////////////////////////
- // crash the NFC service process so it can restart automatically
- abort();
- //////////////////////////////////////////////
} break;
case NFA_DM_PWR_MODE_CHANGE_EVT:
@@ -917,7 +993,7 @@ static jboolean nfcManager_sendRawFrame(JNIEnv* e, jobject, jbyteArray data) {
**
*******************************************************************************/
static jboolean nfcManager_routeAid(JNIEnv* e, jobject, jbyteArray aid,
- jint route, jint aidInfo) {
+ jint route, jint aidInfo, jint power) {
uint8_t* buf;
size_t bufLen;
@@ -925,13 +1001,13 @@ static jboolean nfcManager_routeAid(JNIEnv* e, jobject, jbyteArray aid,
buf = NULL;
bufLen = 0;
return RoutingManager::getInstance().addAidRouting(buf, bufLen, route,
- aidInfo);
+ aidInfo, power);
}
ScopedByteArrayRO bytes(e, aid);
buf = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(&bytes[0]));
bufLen = bytes.size();
return RoutingManager::getInstance().addAidRouting(buf, bufLen, route,
- aidInfo);
+ aidInfo, power);
}
/*******************************************************************************
@@ -1068,6 +1144,7 @@ static jint nfcManager_getLfT3tMax(JNIEnv*, jobject) {
static jboolean nfcManager_doInitialize(JNIEnv* e, jobject o) {
initializeGlobalDebugEnabledFlag();
tNFA_STATUS stat = NFA_STATUS_OK;
+ sIsRecovering = false;
PowerSwitch& powerSwitch = PowerSwitch::getInstance();
@@ -1296,6 +1373,20 @@ static void nfcManager_enableDiscovery(JNIEnv* e, jobject o,
}
}
} else {
+ /* enable_p2p=> request to enable p2p, P2pEnabled=> current state of p2p */
+ if (enable_p2p && !sP2pEnabled) {
+ sP2pEnabled = true;
+ DLOG_IF(INFO, nfc_debug_enabled)
+ << StringPrintf("%s: Enable p2pListening", __func__);
+ PeerToPeer::getInstance().enableP2pListening(true);
+ NFA_ResumeP2p();
+ } else if (!enable_p2p && sP2pEnabled) {
+ sP2pEnabled = false;
+ DLOG_IF(INFO, nfc_debug_enabled)
+ << StringPrintf("%s: Disable p2pListening", __func__);
+ PeerToPeer::getInstance().enableP2pListening(false);
+ NFA_PauseP2p();
+ }
// No technologies configured, stop polling
stopPolling_rfDiscoveryDisabled();
}
@@ -1472,7 +1563,9 @@ static jboolean nfcManager_doDeinitialize(JNIEnv*, jobject) {
sIsDisabling = true;
- RoutingManager::getInstance().onNfccShutdown();
+ if (!recovery_option || !sIsRecovering) {
+ RoutingManager::getInstance().onNfccShutdown();
+ }
PowerSwitch::getInstance().initialize(PowerSwitch::UNKNOWN_LEVEL);
HciEventManager::getInstance().finalize();
@@ -1811,6 +1904,13 @@ static void nfcManager_doSetScreenState(JNIEnv* e, jobject o,
prevScreenState = state;
return;
}
+
+ // skip remaining SetScreenState tasks when trying to silent recover NFCC
+ if (recovery_option && sIsRecovering) {
+ prevScreenState = state;
+ return;
+ }
+
if (prevScreenState == NFA_SCREEN_STATE_OFF_LOCKED ||
prevScreenState == NFA_SCREEN_STATE_OFF_UNLOCKED ||
prevScreenState == NFA_SCREEN_STATE_ON_LOCKED) {
@@ -1825,6 +1925,12 @@ static void nfcManager_doSetScreenState(JNIEnv* e, jobject o,
}
}
+ // skip remaining SetScreenState tasks when trying to silent recover NFCC
+ if (recovery_option && sIsRecovering) {
+ prevScreenState = state;
+ return;
+ }
+
if (state == NFA_SCREEN_STATE_OFF_LOCKED ||
state == NFA_SCREEN_STATE_OFF_UNLOCKED) {
// disable poll and enable listen on DH 0x00
@@ -1857,6 +1963,12 @@ static void nfcManager_doSetScreenState(JNIEnv* e, jobject o,
return;
}
+ // skip remaining SetScreenState tasks when trying to silent recover NFCC
+ if (recovery_option && sIsRecovering) {
+ prevScreenState = state;
+ return;
+ }
+
if (prevScreenState == NFA_SCREEN_STATE_ON_UNLOCKED) {
SyncEventGuard guard(sNfaSetPowerSubState);
status = NFA_SetPowerSubStateForScreenState(state);
@@ -1867,10 +1979,18 @@ static void nfcManager_doSetScreenState(JNIEnv* e, jobject o,
sNfaSetPowerSubState.wait();
}
}
+
+ // skip remaining SetScreenState tasks when trying to silent recover NFCC
+ if (recovery_option && sIsRecovering) {
+ prevScreenState = state;
+ return;
+ }
+
if ((state == NFA_SCREEN_STATE_OFF_LOCKED ||
state == NFA_SCREEN_STATE_OFF_UNLOCKED) &&
- prevScreenState == NFA_SCREEN_STATE_ON_UNLOCKED && (!sP2pActive) &&
- (!sSeRfActive)) {
+ (prevScreenState == NFA_SCREEN_STATE_ON_UNLOCKED ||
+ prevScreenState == NFA_SCREEN_STATE_ON_LOCKED) &&
+ (!sP2pActive) && (!sSeRfActive)) {
// screen turns off, disconnect tag if connected
nativeNfcTag_doDisconnect(NULL, NULL);
}
@@ -1976,6 +2096,23 @@ static jint nfcManager_getAidTableSize(JNIEnv*, jobject) {
return NFA_GetAidTableSize();
}
+/*******************************************************************************
+**
+** Function: nfcManager_doStartStopPolling
+**
+** Description: Start or stop NFC RF polling
+** e: JVM environment.
+** o: Java object.
+** start: start or stop RF polling
+**
+** Returns: None
+**
+*******************************************************************************/
+static void nfcManager_doStartStopPolling(JNIEnv* e, jobject o,
+ jboolean start) {
+ startStopPolling(start);
+}
+
static jboolean nfcManager_doSetNfcSecure(JNIEnv* e, jobject o,
jboolean enable) {
RoutingManager& routingManager = RoutingManager::getInstance();
@@ -1996,6 +2133,17 @@ static jstring nfcManager_doGetNfaStorageDir(JNIEnv* e, jobject o) {
string nfaStorageDir = NfcConfig::getString(NAME_NFA_STORAGE, "/data/nfc");
return e->NewStringUTF(nfaStorageDir.c_str());
}
+
+static void nfcManager_doSetNfceePowerAndLinkCtrl(JNIEnv* e, jobject o,
+ jboolean enable) {
+ RoutingManager& routingManager = RoutingManager::getInstance();
+ if (enable) {
+ routingManager.eeSetPwrAndLinkCtrl((uint8_t)nfcee_power_and_link_conf);
+ } else {
+ routingManager.eeSetPwrAndLinkCtrl(0);
+ }
+}
+
/*****************************************************************************
**
** JNI functions for android-4.0.1_r1
@@ -2012,7 +2160,7 @@ static JNINativeMethod gMethods[] = {
{"sendRawFrame", "([B)Z", (void*)nfcManager_sendRawFrame},
- {"routeAid", "([BII)Z", (void*)nfcManager_routeAid},
+ {"routeAid", "([BIII)Z", (void*)nfcManager_routeAid},
{"unrouteAid", "([B)Z", (void*)nfcManager_unrouteAid},
@@ -2028,6 +2176,8 @@ static JNINativeMethod gMethods[] = {
{"doEnableDiscovery", "(IZZZZZ)V", (void*)nfcManager_enableDiscovery},
+ {"doStartStopPolling", "(Z)V", (void*)nfcManager_doStartStopPolling},
+
{"doCheckLlcp", "()Z", (void*)nfcManager_doCheckLlcp},
{"doActivateLlcp", "()Z", (void*)nfcManager_doActivateLlcp},
@@ -2086,6 +2236,9 @@ static JNINativeMethod gMethods[] = {
{"getNfaStorageDir", "()Ljava/lang/String;",
(void*)nfcManager_doGetNfaStorageDir},
+
+ {"doSetNfceePowerAndLinkCtrl", "(Z)V",
+ (void*)nfcManager_doSetNfceePowerAndLinkCtrl},
};
/*******************************************************************************
@@ -2204,16 +2357,36 @@ bool nfcManager_isNfcActive() { return sIsNfaEnabled; }
**
*******************************************************************************/
void startStopPolling(bool isStartPolling) {
+ tNFA_STATUS status = NFA_STATUS_FAILED;
+ uint8_t discovry_param = 0;
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("%s: enter; isStart=%u", __func__, isStartPolling);
- startRfDiscovery(false);
- if (isStartPolling)
- startPolling_rfDiscoveryDisabled(0);
- else
- stopPolling_rfDiscoveryDisabled();
-
- startRfDiscovery(true);
+ if (NFC_GetNCIVersion() >= NCI_VERSION_2_0) {
+ SyncEventGuard guard(sNfaSetConfigEvent);
+ if (isStartPolling) {
+ discovry_param =
+ NCI_LISTEN_DH_NFCEE_ENABLE_MASK | NCI_POLLING_DH_ENABLE_MASK;
+ } else {
+ discovry_param =
+ NCI_LISTEN_DH_NFCEE_ENABLE_MASK | NCI_POLLING_DH_DISABLE_MASK;
+ }
+ status = NFA_SetConfig(NCI_PARAM_ID_CON_DISCOVERY_PARAM,
+ NCI_PARAM_LEN_CON_DISCOVERY_PARAM, &discovry_param);
+ if (status == NFA_STATUS_OK) {
+ sNfaSetConfigEvent.wait();
+ } else {
+ LOG(ERROR) << StringPrintf("%s: Failed to update CON_DISCOVER_PARAM",
+ __FUNCTION__);
+ }
+ } else {
+ startRfDiscovery(false);
+ if (isStartPolling)
+ startPolling_rfDiscoveryDisabled(0);
+ else
+ stopPolling_rfDiscoveryDisabled();
+ startRfDiscovery(true);
+ }
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%s: exit", __func__);
}
diff --git a/nci/jni/NativeNfcTag.cpp b/nci/jni/NativeNfcTag.cpp
index ddae94e9..cc167e67 100644
--- a/nci/jni/NativeNfcTag.cpp
+++ b/nci/jni/NativeNfcTag.cpp
@@ -581,11 +581,14 @@ static jint nativeNfcTag_doConnect(JNIEnv*, jobject, jint targetHandle) {
if (sCurrentConnectedTargetType == TARGET_TYPE_ISO14443_3A ||
sCurrentConnectedTargetType == TARGET_TYPE_ISO14443_3B) {
- DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+
+ if (sCurrentConnectedTargetProtocol != NFC_PROTOCOL_MIFARE) {
+ DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"%s: switching to tech: %d need to switch rf intf to frame", __func__,
sCurrentConnectedTargetType);
- retCode = switchRfInterface(NFA_INTERFACE_FRAME) ? NFA_STATUS_OK
- : NFA_STATUS_FAILED;
+ retCode = switchRfInterface(NFA_INTERFACE_FRAME) ? NFA_STATUS_OK
+ : NFA_STATUS_FAILED;
+ }
} else if (sCurrentConnectedTargetType == TARGET_TYPE_MIFARE_CLASSIC) {
retCode = switchRfInterface(NFA_INTERFACE_MIFARE) ? NFA_STATUS_OK
: NFA_STATUS_FAILED;
@@ -863,7 +866,7 @@ jboolean nativeNfcTag_doDisconnect(JNIEnv*, jobject) {
NfcTag::getInstance().resetAllTransceiveTimeouts();
if (NfcTag::getInstance().getActivationState() != NfcTag::Active) {
- LOG(ERROR) << StringPrintf("%s: tag already deactivated", __func__);
+ LOG(WARNING) << StringPrintf("%s: tag already deactivated", __func__);
goto TheEnd;
}
diff --git a/nci/jni/PeerToPeer.cpp b/nci/jni/PeerToPeer.cpp
index ac51dd93..75027d87 100644
--- a/nci/jni/PeerToPeer.cpp
+++ b/nci/jni/PeerToPeer.cpp
@@ -973,7 +973,7 @@ bool PeerToPeer::disconnectConnOriented(tJNI_HANDLE jniHandle) {
return (false);
}
- // If this is a client, he may not be connected yet, so unblock him just in
+ // If this is a client, it may not be connected yet, so unblock it just in
// case
if (((pClient = findClient(jniHandle)) != NULL) && (pClient->mIsConnecting)) {
SyncEventGuard guard(pClient->mConnectingEvent);
diff --git a/nci/jni/RoutingManager.cpp b/nci/jni/RoutingManager.cpp
index 93ba9e06..4ea3cf0d 100755..100644
--- a/nci/jni/RoutingManager.cpp
+++ b/nci/jni/RoutingManager.cpp
@@ -299,12 +299,17 @@ void RoutingManager::disableRoutingToHost() {
}
bool RoutingManager::addAidRouting(const uint8_t* aid, uint8_t aidLen,
- int route, int aidInfo) {
+ int route, int aidInfo, int power) {
static const char fn[] = "RoutingManager::addAidRouting";
DLOG_IF(INFO, nfc_debug_enabled) << fn << ": enter";
uint8_t powerState = 0x01;
if (!mSecureNfcEnabled) {
- powerState = (route != 0x00) ? mOffHostAidRoutingPowerState : 0x11;
+ if (power == 0x00) {
+ powerState = (route != 0x00) ? mOffHostAidRoutingPowerState : 0x11;
+ } else {
+ powerState =
+ (route != 0x00) ? mOffHostAidRoutingPowerState & power : power;
+ }
}
SyncEventGuard guard(mRoutingEvent);
mAidRoutingConfigured = false;
@@ -335,7 +340,7 @@ bool RoutingManager::removeAidRouting(const uint8_t* aid, uint8_t aidLen) {
DLOG_IF(INFO, nfc_debug_enabled) << fn << ": removed AID";
return true;
} else {
- LOG(ERROR) << fn << ": failed to remove AID";
+ LOG(WARNING) << fn << ": failed to remove AID";
return false;
}
}
@@ -886,6 +891,13 @@ void RoutingManager::nfaEeCallback(tNFA_EE_EVT event,
routingManager.mEeUpdateEvent.notifyOne();
} break;
+ case NFA_EE_PWR_AND_LINK_CTRL_EVT: {
+ DLOG_IF(INFO, nfc_debug_enabled)
+ << StringPrintf("%s: NFA_EE_PWR_AND_LINK_CTRL_EVT", fn);
+ SyncEventGuard guard(routingManager.mEePwrAndLinkCtrlEvent);
+ routingManager.mEePwrAndLinkCtrlEvent.notifyOne();
+ } break;
+
default:
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("%s: unknown event=%u ????", fn, event);
@@ -1040,6 +1052,40 @@ bool RoutingManager::setNfcSecure(bool enable) {
return true;
}
+/*******************************************************************************
+**
+** Function: eeSetPwrAndLinkCtrl
+**
+** Description: Programs the NCI command NFCEE_POWER_AND_LINK_CTRL_CMD
+**
+** Returns: None
+**
+*******************************************************************************/
+void RoutingManager::eeSetPwrAndLinkCtrl(uint8_t config) {
+ static const char fn[] = "RoutingManager::eeSetPwrAndLinkCtrl";
+ tNFA_STATUS status = NFA_STATUS_OK;
+
+ if (mOffHostRouteEse.size() > 0) {
+ DLOG_IF(INFO, nfc_debug_enabled)
+ << StringPrintf("%s - nfceeId: 0x%02X, config: 0x%02X", fn,
+ mOffHostRouteEse[0], config);
+
+ SyncEventGuard guard(mEePwrAndLinkCtrlEvent);
+ status =
+ NFA_EePowerAndLinkCtrl(
+ ((uint8_t)mOffHostRouteEse[0] | NFA_HANDLE_GROUP_EE), config);
+ if (status != NFA_STATUS_OK) {
+ LOG(ERROR) << StringPrintf("%s: fail NFA_EePowerAndLinkCtrl; error=0x%X",
+ __FUNCTION__, status);
+ return;
+ } else {
+ mEePwrAndLinkCtrlEvent.wait();
+ }
+ } else {
+ LOG(ERROR) << StringPrintf("%s: No ESE specified", __FUNCTION__);
+ }
+}
+
void RoutingManager::deinitialize() {
onNfccShutdown();
NFA_EeDeregister(nfaEeCallback);
diff --git a/nci/jni/RoutingManager.h b/nci/jni/RoutingManager.h
index 51ef4f42..92815bd8 100755
--- a/nci/jni/RoutingManager.h
+++ b/nci/jni/RoutingManager.h
@@ -36,8 +36,8 @@ class RoutingManager {
void deinitialize();
void enableRoutingToHost();
void disableRoutingToHost();
- bool addAidRouting(const uint8_t* aid, uint8_t aidLen, int route,
- int aidInfo);
+ bool addAidRouting(const uint8_t* aid, uint8_t aidLen, int route, int aidInfo,
+ int power);
bool removeAidRouting(const uint8_t* aid, uint8_t aidLen);
bool commitRouting();
int registerT3tIdentifier(uint8_t* t3tId, uint8_t t3tIdLen);
@@ -46,6 +46,7 @@ class RoutingManager {
int registerJniFunctions(JNIEnv* e);
bool setNfcSecure(bool enable);
void updateRoutingTable();
+ void eeSetPwrAndLinkCtrl(uint8_t config);
private:
RoutingManager();
@@ -121,4 +122,5 @@ class RoutingManager {
SyncEvent mEeUpdateEvent;
SyncEvent mEeInfoEvent;
SyncEvent mEeSetModeEvent;
+ SyncEvent mEePwrAndLinkCtrlEvent;
};
diff --git a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
index 027f4ff0..af9484cf 100755
--- a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
+++ b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
@@ -130,7 +130,7 @@ public class NativeNfcManager implements DeviceHost {
public native boolean sendRawFrame(byte[] data);
@Override
- public native boolean routeAid(byte[] aid, int route, int aidInfo);
+ public native boolean routeAid(byte[] aid, int route, int aidInfo, int power);
@Override
public native boolean unrouteAid(byte[] aid);
@@ -392,6 +392,18 @@ public class NativeNfcManager implements DeviceHost {
@Override
public native String getNfaStorageDir();
+ private native void doStartStopPolling(boolean start);
+ @Override
+ public void startStopPolling(boolean start) {
+ doStartStopPolling(start);
+ }
+
+ private native void doSetNfceePowerAndLinkCtrl(boolean enable);
+ @Override
+ public void setNfceePowerAndLinkCtrl(boolean enable) {
+ doSetNfceePowerAndLinkCtrl(enable);
+ }
+
/**
* Notifies Ndef Message (TODO: rename into notifyTargetDiscovered)
*/
@@ -447,4 +459,8 @@ public class NativeNfcManager implements DeviceHost {
private void notifyEeUpdated() {
mListener.onEeUpdated();
}
+
+ private void notifyHwErrorReported() {
+ mListener.onHwErrorReported();
+ }
}
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index bfa8b03f..884a68c1 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -42,7 +42,7 @@
<string name="prompt_connect_to_network" msgid="8511683573657516114">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> নেটৱৰ্কৰ লগত সংযোগ কৰিবনে?"</string>
<string name="beam_requires_nfc_enabled" msgid="2800366967218600534">"Android বীমে NFC সক্ষম কৰিব লাগিব৷ আপুনি ইয়াক সক্ষম কৰিবলৈ বিচাৰেনে?"</string>
<string name="android_beam" msgid="1666446406999492763">"Android বীম"</string>
- <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"এপ্লিকেশ্বনটোৰ বাহ্যিক সঞ্চয়াগাৰ ব্যৱহাৰ কৰাৰ অনুমতি নাই৷ এই ফাইলটো বীম কৰিবলৈ এয়া প্ৰয়োজন।"</string>
+ <string name="beam_requires_external_storage_permission" msgid="8798337545702206901">"এপ্লিকেশ্বনটোৰ বাহ্যিক ষ্ট’ৰেজ ব্যৱহাৰ কৰাৰ অনুমতি নাই৷ এই ফাইলটো বীম কৰিবলৈ এয়া প্ৰয়োজন।"</string>
<string name="title_confirm_url_open" msgid="8069968913244794478">"লিংক খুলিবনে?"</string>
<string name="summary_confirm_url_open" product="tablet" msgid="3353502750736192055">"আপোনাৰ টেবলেটে NFC যোগে এই লিংক পাইছে:"</string>
<string name="summary_confirm_url_open" product="default" msgid="1246398412196449226">"আপোনাৰ ফ\'নে NFC যোগে এই লিংক পাইছে:"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 0c4b54e5..7151f0ea 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -21,7 +21,7 @@
<string name="connect_peripheral_failed" msgid="7925702596242839275">"Չհաջողվեց կապակցել <xliff:g id="DEVICE_NAME">%1$s</xliff:g> սարքը"</string>
<string name="disconnecting_peripheral" msgid="1443699384809097200">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի կապախզում"</string>
<string name="disconnected_peripheral" msgid="4470578100296504366">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> սարքը կապախզված է"</string>
- <string name="pairing_peripheral" msgid="6983626861540899365">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի զուգավորում"</string>
+ <string name="pairing_peripheral" msgid="6983626861540899365">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի զուգակցում"</string>
<string name="pairing_peripheral_failed" msgid="6087643307743264679">"Չհաջողվեց զուգակցել <xliff:g id="DEVICE_NAME">%1$s</xliff:g> սարքը"</string>
<string name="failed_to_enable_bt" msgid="7229153323594758077">"Չհաջողվեց միացնել Bluetooth-ը"</string>
<string name="confirm_pairing" msgid="4112568077038265363">"Իսկապե՞ս ուզում եք զուգակցել <xliff:g id="DEVICE_NAME">%1$s</xliff:g> Bluetooth սարքը:"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index e0c77612..0fca22c0 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -33,7 +33,7 @@
<string name="could_not_use_app" msgid="8137587876138569083">"לא ניתן היה להשתמש ב-<xliff:g id="APP">%1$s</xliff:g>."</string>
<string name="pay_with" msgid="5531545488795798945">"תשלום באמצעות"</string>
<string name="complete_with" msgid="6797459104103012992">"השלמה באמצעות"</string>
- <string name="default_pay_app_removed" msgid="4108250545457437360">"השירות המועדף עליך עבור \'הקשה לתשלום\' הוסר. האם לבחור אחר?"</string>
+ <string name="default_pay_app_removed" msgid="4108250545457437360">"השירות המועדף עליך עבור \'תשלום בקליק\' הוסר. האם לבחור אחר?"</string>
<string name="ask_nfc_tap" msgid="2925239870458286340">"יש להקיש על מכשיר אחר להשלמה"</string>
<string name="wifi_connect" msgid="6250727951843550671">"התחברות"</string>
<string name="status_unable_to_connect" msgid="9183908200295307657">"לא ניתן להתחבר לרשת"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 6c5b066e..3081a349 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -12,7 +12,7 @@
<string name="beam_canceled" msgid="5425192751826544741">"பீம் ரத்து செய்யப்பட்டது"</string>
<string name="cancel" msgid="61873902552555096">"ரத்துசெய்"</string>
<string name="beam_tap_to_view" msgid="7430394753262448349">"பார்க்க, தட்டவும்"</string>
- <string name="beam_handover_not_supported" msgid="4083165921751489015">"பெறுநரின் சாதனம் பீம் வழியான பெரிய கோப்புப் பரிமாற்றத்தை ஆதரிக்கவில்லை."</string>
+ <string name="beam_handover_not_supported" msgid="4083165921751489015">"பெறுநரின் சாதனம் பீம் வழியான பெரிய ஃபைல் பரிமாற்றத்தை ஆதரிக்கவில்லை."</string>
<string name="beam_try_again" msgid="3364677301009783455">"மீண்டும் சாதனங்களை ஒன்றாகக் கொண்டுவரவும்"</string>
<string name="beam_busy" msgid="5253335587620612576">"பீம் தற்போது பணிமிகுதியில் உள்ளது. முந்தைய இடமாற்றம் முடியும்போது, மீண்டும் முயலவும்."</string>
<string name="device" msgid="4459621591392478151">"சாதனம்"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index c3501816..e60d3162 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -7,6 +7,7 @@
<bool name="enable_notify_read_failed">false</bool>
<bool name="enable_antenna_blocked_alert">false</bool>
<bool name="polling_disable_allowed">false</bool>
+ <bool name="nfcc_always_on_allowed">false</bool>
<integer name="max_antenna_blocked_failure_count">10</integer>
<integer name="toast_debounce_time_ms">3000</integer>
<integer name="unknown_tag_polling_delay">2000</integer>
diff --git a/src/com/android/nfc/DeviceHost.java b/src/com/android/nfc/DeviceHost.java
index 43722ab6..4ffbe824 100644
--- a/src/com/android/nfc/DeviceHost.java
+++ b/src/com/android/nfc/DeviceHost.java
@@ -51,6 +51,8 @@ public interface DeviceHost {
public void onNfcTransactionEvent(byte[] aid, byte[] data, String seName);
public void onEeUpdated();
+
+ public void onHwErrorReported();
}
public interface TagEndpoint {
@@ -188,7 +190,7 @@ public interface DeviceHost {
public boolean sendRawFrame(byte[] data);
- public boolean routeAid(byte[] aid, int route, int aidInfo);
+ public boolean routeAid(byte[] aid, int route, int aidInfo, int power);
public boolean unrouteAid(byte[] aid);
@@ -260,4 +262,14 @@ public interface DeviceHost {
public boolean setNfcSecure(boolean enable);
public String getNfaStorageDir();
+
+ /**
+ * Start or stop RF polling
+ */
+ void startStopPolling(boolean enable);
+
+ /**
+ * Set NFCC power state by sending NFCEE_POWER_AND_LINK_CNTRL_CMD
+ */
+ void setNfceePowerAndLinkCtrl(boolean enable);
}
diff --git a/src/com/android/nfc/NfcDispatcher.java b/src/com/android/nfc/NfcDispatcher.java
index 0f46087d..b22d49d9 100644
--- a/src/com/android/nfc/NfcDispatcher.java
+++ b/src/com/android/nfc/NfcDispatcher.java
@@ -338,7 +338,7 @@ class NfcDispatcher {
// We only allow NDEF-message dispatch in provisioning mode
return DISPATCH_FAIL;
}
- // Restrict to mime-types in whitelist.
+ // Restrict to mime-types in allowlist.
String ndefMimeType = message.getRecords()[0].toMimeType();
if (provisioningMimes == null ||
!(Arrays.asList(provisioningMimes).contains(ndefMimeType))) {
diff --git a/src/com/android/nfc/NfcPermissions.java b/src/com/android/nfc/NfcPermissions.java
index c4528a99..6ebb939f 100644
--- a/src/com/android/nfc/NfcPermissions.java
+++ b/src/com/android/nfc/NfcPermissions.java
@@ -26,6 +26,14 @@ public class NfcPermissions {
private static final String NFC_PREFERRED_PAYMENT_INFO_PERM_ERROR =
"NFC_PREFERRED_PAYMENT_INFO permission required";
+ /**
+ * NFC SET CONTROLLER ALWAYS ON permission
+ */
+ static final String NFC_SET_CONTROLLER_ALWAYS_ON =
+ android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON;
+ private static final String NFC_SET_CONTROLLER_ALWAYS_ON_ERROR =
+ "NFC_SET_CONTROLLER_ALWAYS_ON permission required";
+
public static void validateUserId(int userId) {
if (userId != UserHandle.getCallingUserId()) {
throw new SecurityException("userId passed in is not the calling user.");
@@ -45,4 +53,12 @@ public class NfcPermissions {
context.enforceCallingOrSelfPermission(NFC_PREFERRED_PAYMENT_INFO_PERMISSION,
NFC_PREFERRED_PAYMENT_INFO_PERM_ERROR);
}
+
+ /**
+ * Permission check for android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON
+ */
+ public static void enforceSetControllerAlwaysOnPermissions(Context context) {
+ context.enforceCallingOrSelfPermission(NFC_SET_CONTROLLER_ALWAYS_ON,
+ NFC_SET_CONTROLLER_ALWAYS_ON_ERROR);
+ }
}
diff --git a/src/com/android/nfc/NfcRootActivity.java b/src/com/android/nfc/NfcRootActivity.java
index cc216f2e..b0b340f5 100644
--- a/src/com/android/nfc/NfcRootActivity.java
+++ b/src/com/android/nfc/NfcRootActivity.java
@@ -37,6 +37,7 @@ public class NfcRootActivity extends Activity {
try {
startActivityAsUser(launchIntent, UserHandle.CURRENT);
} catch (ActivityNotFoundException e) {
+ } catch (SecurityException e) {
}
}
}
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index 837940cf..633b8f46 100644
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -46,6 +46,7 @@ import android.nfc.IAppCallback;
import android.nfc.INfcAdapter;
import android.nfc.INfcAdapterExtras;
import android.nfc.INfcCardEmulation;
+import android.nfc.INfcControllerAlwaysOnListener;
import android.nfc.INfcDta;
import android.nfc.INfcFCardEmulation;
import android.nfc.INfcTag;
@@ -106,11 +107,14 @@ import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Scanner;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@@ -120,6 +124,8 @@ public class NfcService implements DeviceHostListener {
public static final String SERVICE_NAME = "nfc";
+ private static final String SYSTEM_UI = "com.android.systemui";
+
public static final String PREF = "NfcServicePrefs";
static final String PREF_NFC_ON = "nfc_on";
@@ -139,6 +145,7 @@ public class NfcService implements DeviceHostListener {
static final String TRON_NFC_TAG = "nfc_tag";
static final String NATIVE_LOG_FILE_NAME = "native_crash_logs";
+ static final String NATIVE_LOG_FILE_PATH = "/data/misc/nfc/logs";
static final int NATIVE_CRASH_FILE_SIZE = 1024 * 1024;
static final int MSG_NDEF_TAG = 0;
@@ -161,6 +168,9 @@ public class NfcService implements DeviceHostListener {
static final int MSG_TRANSACTION_EVENT = 17;
static final int MSG_PREFERRED_PAYMENT_CHANGED = 18;
static final int MSG_TOAST_DEBOUNCE_EVENT = 19;
+ static final int MSG_DELAY_POLLING = 20;
+
+ static final String MSG_ROUTE_AID_PARAM_TAG = "power";
// Negative value for NO polling delay
static final int NO_POLL_DELAY = -1;
@@ -174,6 +184,8 @@ public class NfcService implements DeviceHostListener {
static final int TASK_ENABLE = 1;
static final int TASK_DISABLE = 2;
static final int TASK_BOOT = 3;
+ static final int TASK_ENABLE_ALWAYS_ON = 4;
+ static final int TASK_DISABLE_ALWAYS_ON = 5;
// Polling technology masks
static final int NFC_POLL_A = 0x01;
@@ -262,6 +274,7 @@ public class NfcService implements DeviceHostListener {
private int mUserId;
boolean mPollingPaused;
+ boolean mPollingDelayed;
// True if nfc notification message already shown
boolean mAntennaBlockedMessageShown;
@@ -286,12 +299,16 @@ public class NfcService implements DeviceHostListener {
// and the default AsyncTask thread so it is read unprotected from that
// thread
int mState; // one of NfcAdapter.STATE_ON, STATE_TURNING_ON, etc
+ // mAlwaysOnState is protected by this, however it is only modified in onCreate()
+ // and the default AsyncTask thread so it is read unprotected from that thread
+ int mAlwaysOnState; // one of NfcAdapter.STATE_ON, STATE_TURNING_ON, etc
// fields below are final after onCreate()
Context mContext;
private DeviceHost mDeviceHost;
private SharedPreferences mPrefs;
private SharedPreferences.Editor mPrefsEditor;
private PowerManager.WakeLock mRoutingWakeLock;
+ private PowerManager.WakeLock mRequireUnlockWakeLock;
int mStartSound;
int mEndSound;
@@ -306,6 +323,8 @@ public class NfcService implements DeviceHostListener {
boolean mIsHceFCapable;
boolean mIsBeamCapable;
boolean mIsSecureNfcCapable;
+ boolean mIsRequestUnlockShowed;
+ boolean mIsRecovering;
int mPollDelay;
boolean mNotifyDispatchFailed;
@@ -332,6 +351,10 @@ public class NfcService implements DeviceHostListener {
private IVrManager vrManager;
boolean mIsVrModeEnabled;
+ private final boolean mIsAlwaysOnSupported;
+ private final Set<INfcControllerAlwaysOnListener> mAlwaysOnListeners =
+ Collections.synchronizedSet(new HashSet<>());
+
public static NfcService getInstance() {
return sService;
}
@@ -418,6 +441,14 @@ public class NfcService implements DeviceHostListener {
new ApplyRoutingTask().execute();
}
+ @Override
+ public void onHwErrorReported() {
+ mContext.unregisterReceiver(mReceiver);
+ mIsRecovering = true;
+ new EnableDisableTask().execute(TASK_DISABLE);
+ new EnableDisableTask().execute(TASK_ENABLE);
+ }
+
final class ReaderModeParams {
public int flags;
public IAppCallback callback;
@@ -461,6 +492,7 @@ public class NfcService implements DeviceHostListener {
mPrefsEditor = mPrefs.edit();
mState = NfcAdapter.STATE_OFF;
+ mAlwaysOnState = NfcAdapter.STATE_OFF;
mIsDebugBuild = "userdebug".equals(Build.TYPE) || "eng".equals(Build.TYPE);
@@ -469,6 +501,10 @@ public class NfcService implements DeviceHostListener {
mRoutingWakeLock = mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "NfcService:mRoutingWakeLock");
+ mRequireUnlockWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
+ | PowerManager.ACQUIRE_CAUSES_WAKEUP
+ | PowerManager.ON_AFTER_RELEASE, "NfcService:mRequireUnlockWakeLock");
+
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
@@ -561,6 +597,9 @@ public class NfcService implements DeviceHostListener {
// Make sure this is only called when object construction is complete.
ServiceManager.addService(SERVICE_NAME, mNfcAdapter);
+ mIsAlwaysOnSupported =
+ mContext.getResources().getBoolean(R.bool.nfcc_always_on_allowed);
+
new EnableDisableTask().execute(TASK_BOOT); // do blocking boot tasks
mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATS, STATS_UPDATE_INTERVAL_MS);
@@ -665,7 +704,7 @@ public class NfcService implements DeviceHostListener {
class EnableDisableTask extends AsyncTask<Integer, Void, Void> {
@Override
protected Void doInBackground(Integer... params) {
- // Sanity check mState
+ // Quick check mState
switch (mState) {
case NfcAdapter.STATE_TURNING_OFF:
case NfcAdapter.STATE_TURNING_ON:
@@ -711,6 +750,12 @@ public class NfcService implements DeviceHostListener {
SystemProperties.set("nfc.initialized", "true");
}
break;
+ case TASK_ENABLE_ALWAYS_ON:
+ enableAlwaysOnInternal();
+ break;
+ case TASK_DISABLE_ALWAYS_ON:
+ disableAlwaysOnInternal();
+ break;
}
// Restore default AsyncTask priority
@@ -736,8 +781,19 @@ public class NfcService implements DeviceHostListener {
try {
mRoutingWakeLock.acquire();
try {
- if (!mDeviceHost.initialize()) {
- Log.w(TAG, "Error enabling NFC");
+ if (!mIsAlwaysOnSupported || mIsRecovering
+ || mAlwaysOnState != NfcAdapter.STATE_ON
+ || mAlwaysOnState != NfcAdapter.STATE_TURNING_OFF) {
+ if (!mDeviceHost.initialize()) {
+ Log.w(TAG, "Error enabling NFC");
+ updateState(NfcAdapter.STATE_OFF);
+ return false;
+ }
+ } else if (mAlwaysOnState == NfcAdapter.STATE_ON
+ || mAlwaysOnState == NfcAdapter.STATE_TURNING_OFF) {
+ Log.i(TAG, "Already initialized");
+ } else {
+ Log.e(TAG, "Unexptected bad state " + mAlwaysOnState);
updateState(NfcAdapter.STATE_OFF);
return false;
}
@@ -779,9 +835,24 @@ public class NfcService implements DeviceHostListener {
sToast_debounce = false;
- /* Start polling loop */
+ /* Skip applyRouting if always on state is switching */
+ if (!mIsAlwaysOnSupported
+ || mAlwaysOnState != NfcAdapter.STATE_TURNING_ON
+ || mAlwaysOnState != NfcAdapter.STATE_TURNING_OFF) {
+ /* Start polling loop */
+ applyRouting(true);
+ }
+
+ if (mIsRecovering) {
+ // 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);
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null);
+ mIsRecovering = false;
+ }
- applyRouting(true);
return true;
}
@@ -813,6 +884,10 @@ public class NfcService implements DeviceHostListener {
mP2pLinkManager.enableDisable(false, false);
}
+ // Disable delay polling when disabling
+ mPollingDelayed = false;
+ mHandler.removeMessages(MSG_DELAY_POLLING);
+
// Stop watchdog if tag present
// A convenient way to stop the watchdog properly consists of
// disconnecting the tag. The polling loop shall be stopped before
@@ -821,9 +896,17 @@ public class NfcService implements DeviceHostListener {
mNfcDispatcher.setForegroundDispatch(null, null, null);
-
- boolean result = mDeviceHost.deinitialize();
- if (DBG) Log.d(TAG, "mDeviceHost.deinitialize() = " + result);
+ boolean result;
+ if (!mIsAlwaysOnSupported || mIsRecovering
+ || (mAlwaysOnState == NfcAdapter.STATE_OFF)
+ || (mAlwaysOnState == NfcAdapter.STATE_TURNING_OFF)) {
+ result = mDeviceHost.deinitialize();
+ if (DBG) Log.d(TAG, "mDeviceHost.deinitialize() = " + result);
+ } else {
+ mDeviceHost.disableDiscovery();
+ result = true;
+ Log.i(TAG, "AlwaysOn set, disableDiscovery()");
+ }
watchDog.cancel();
@@ -837,6 +920,69 @@ public class NfcService implements DeviceHostListener {
return result;
}
+ /**
+ * Enable always on feature.
+ */
+ void enableAlwaysOnInternal() {
+ if (mAlwaysOnState == NfcAdapter.STATE_ON) {
+ return;
+ } else if (mState == NfcAdapter.STATE_TURNING_ON
+ || mAlwaysOnState == NfcAdapter.STATE_TURNING_OFF) {
+ Log.e(TAG, "Processing enableAlwaysOnInternal() from bad state");
+ return;
+ } else if (mState == NfcAdapter.STATE_ON) {
+ updateAlwaysOnState(NfcAdapter.STATE_TURNING_ON);
+ mDeviceHost.setNfceePowerAndLinkCtrl(true);
+ updateAlwaysOnState(NfcAdapter.STATE_ON);
+ } else if (mState == NfcAdapter.STATE_OFF) {
+ /* Special case when NFCC is OFF without initialize.
+ * Temperatorily enable NfcAdapter but don't applyRouting.
+ * Then disable NfcAdapter without deinitialize to keep the NFCC stays initialized.
+ * mState will switch back to OFF in the end.
+ * And the NFCC stays initialized.
+ */
+ updateAlwaysOnState(NfcAdapter.STATE_TURNING_ON);
+ if (!enableInternal()) {
+ updateAlwaysOnState(NfcAdapter.STATE_OFF);
+ return;
+ }
+ disableInternal();
+ mDeviceHost.setNfceePowerAndLinkCtrl(true);
+ updateAlwaysOnState(NfcAdapter.STATE_ON);
+ }
+ }
+
+ /**
+ * Disable always on feature.
+ */
+ void disableAlwaysOnInternal() {
+ if (mAlwaysOnState == NfcAdapter.STATE_OFF) {
+ return;
+ } else if (mState == NfcAdapter.STATE_TURNING_ON
+ || mAlwaysOnState == NfcAdapter.STATE_TURNING_OFF) {
+ Log.e(TAG, "Processing disableAlwaysOnInternal() from bad state");
+ return;
+ } else if (mState == NfcAdapter.STATE_ON) {
+ updateAlwaysOnState(NfcAdapter.STATE_TURNING_OFF);
+ mDeviceHost.setNfceePowerAndLinkCtrl(false);
+ updateAlwaysOnState(NfcAdapter.STATE_OFF);
+ } else if (mState == NfcAdapter.STATE_OFF) {
+ /* Special case when mState is OFF but NFCC is already initialized.
+ * Temperatorily enable NfcAdapter without initialize NFCC and applyRouting.
+ * And disable NfcAdapter normally with deinitialize.
+ * All state will switch back to OFF in the end.
+ */
+ updateAlwaysOnState(NfcAdapter.STATE_TURNING_OFF);
+ mDeviceHost.setNfceePowerAndLinkCtrl(false);
+ if (!enableInternal()) {
+ updateAlwaysOnState(NfcAdapter.STATE_OFF);
+ return;
+ }
+ disableInternal();
+ updateAlwaysOnState(NfcAdapter.STATE_OFF);
+ }
+ }
+
void updateState(int newState) {
synchronized (NfcService.this) {
if (newState == mState) {
@@ -849,6 +995,39 @@ public class NfcService implements DeviceHostListener {
mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
}
}
+
+ void updateAlwaysOnState(int newState) {
+ synchronized (NfcService.this) {
+ if (newState == mAlwaysOnState) {
+ return;
+ }
+ mAlwaysOnState = newState;
+ if (mAlwaysOnState == NfcAdapter.STATE_OFF
+ || mAlwaysOnState == NfcAdapter.STATE_ON) {
+ synchronized (mAlwaysOnListeners) {
+ for (INfcControllerAlwaysOnListener listener
+ : mAlwaysOnListeners) {
+ try {
+ listener.onControllerAlwaysOnChanged(
+ mAlwaysOnState == NfcAdapter.STATE_ON);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in updateAlwaysOnState");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ int getAlwaysOnState() {
+ synchronized (NfcService.this) {
+ if (!mIsAlwaysOnSupported) {
+ return NfcAdapter.STATE_OFF;
+ } else {
+ return mAlwaysOnState;
+ }
+ }
+ }
}
void saveNfcOnSetting(boolean on) {
@@ -901,7 +1080,7 @@ public class NfcService implements DeviceHostListener {
}
try {
mIpm.setComponentEnabledSetting(new ComponentName(
- BeamShareActivity.class.getPackageName$(),
+ BeamShareActivity.class.getPackageName(),
BeamShareActivity.class.getName()),
isActiveForUser ?
PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
@@ -1255,7 +1434,7 @@ public class NfcService implements DeviceHostListener {
int callingPid = Binder.getCallingPid();
// Allow non-foreground callers with system uid or systemui
boolean privilegedCaller = (callingUid == Process.SYSTEM_UID
- || getPackageNameFromUid(callingUid).equals("com.android.systemui"));
+ || getPackageNameFromUid(callingUid).equals(SYSTEM_UI));
if (!privilegedCaller && !mForegroundUtils.isInForeground(callingUid)) {
Log.e(TAG, "setReaderMode: Caller is not in foreground and is not system process.");
return;
@@ -1428,6 +1607,50 @@ public class NfcService implements DeviceHostListener {
: DEFAULT_PRESENCE_CHECK_DELAY;
}
}
+
+ @Override
+ public boolean setControllerAlwaysOn(boolean value) throws RemoteException {
+ NfcPermissions.enforceSetControllerAlwaysOnPermissions(mContext);
+ if (!mIsAlwaysOnSupported) {
+ return false;
+ }
+ if (value) {
+ new EnableDisableTask().execute(TASK_ENABLE_ALWAYS_ON);
+ } else {
+ new EnableDisableTask().execute(TASK_DISABLE_ALWAYS_ON);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isControllerAlwaysOn() throws RemoteException {
+ NfcPermissions.enforceSetControllerAlwaysOnPermissions(mContext);
+ return mIsAlwaysOnSupported && mAlwaysOnState == NfcAdapter.STATE_ON;
+ }
+
+ @Override
+ public boolean isControllerAlwaysOnSupported() throws RemoteException {
+ NfcPermissions.enforceSetControllerAlwaysOnPermissions(mContext);
+ return mIsAlwaysOnSupported;
+ }
+
+ @Override
+ public void registerControllerAlwaysOnListener(
+ INfcControllerAlwaysOnListener listener) throws RemoteException {
+ NfcPermissions.enforceSetControllerAlwaysOnPermissions(mContext);
+ if (!mIsAlwaysOnSupported) return;
+
+ mAlwaysOnListeners.add(listener);
+ }
+
+ @Override
+ public void unregisterControllerAlwaysOnListener(
+ INfcControllerAlwaysOnListener listener) throws RemoteException {
+ NfcPermissions.enforceSetControllerAlwaysOnPermissions(mContext);
+ if (!mIsAlwaysOnSupported) return;
+
+ mAlwaysOnListeners.remove(listener);
+ }
}
final class ReaderModeDeathRecipient implements IBinder.DeathRecipient {
@@ -2141,12 +2364,17 @@ public class NfcService implements DeviceHostListener {
sendMessage(MSG_MOCK_NDEF, msg);
}
- public void routeAids(String aid, int route, int aidInfo) {
+ public void routeAids(String aid, int route, int aidInfo, int power) {
Message msg = mHandler.obtainMessage();
msg.what = MSG_ROUTE_AID;
msg.arg1 = route;
msg.obj = aid;
msg.arg2 = aidInfo;
+
+ Bundle aidPowerState = new Bundle();
+ aidPowerState.putInt(MSG_ROUTE_AID_PARAM_TAG, power);
+ msg.setData(aidPowerState);
+
mHandler.sendMessage(msg);
}
@@ -2220,7 +2448,14 @@ public class NfcService implements DeviceHostListener {
int route = msg.arg1;
int aidInfo = msg.arg2;
String aid = (String) msg.obj;
- mDeviceHost.routeAid(hexStringToBytes(aid), route, aidInfo);
+
+ int power = 0x00;
+ Bundle bundle = msg.getData();
+ if (bundle != null) {
+ power = bundle.getInt(MSG_ROUTE_AID_PARAM_TAG);
+ }
+
+ mDeviceHost.routeAid(hexStringToBytes(aid), route, aidInfo, power);
// Restart polling config
break;
}
@@ -2428,11 +2663,22 @@ public class NfcService implements DeviceHostListener {
break;
case MSG_RF_FIELD_ACTIVATED:
Intent fieldOnIntent = new Intent(ACTION_RF_FIELD_ON_DETECTED);
- sendNfcEeAccessProtectedBroadcast(fieldOnIntent);
+ sendNfcPermissionProtectedBroadcast(fieldOnIntent);
+ if (!mIsRequestUnlockShowed
+ && mIsSecureNfcEnabled && mKeyguard.isKeyguardLocked()) {
+ if (DBG) Log.d(TAG, "Request unlock");
+ mIsRequestUnlockShowed = true;
+ mRequireUnlockWakeLock.acquire();
+ Intent requireUnlockIntent =
+ new Intent(NfcAdapter.ACTION_REQUIRE_UNLOCK_FOR_NFC);
+ requireUnlockIntent.setPackage(SYSTEM_UI);
+ mContext.sendBroadcast(requireUnlockIntent);
+ mRequireUnlockWakeLock.release();
+ }
break;
case MSG_RF_FIELD_DEACTIVATED:
Intent fieldOffIntent = new Intent(ACTION_RF_FIELD_OFF_DETECTED);
- sendNfcEeAccessProtectedBroadcast(fieldOffIntent);
+ sendNfcPermissionProtectedBroadcast(fieldOffIntent);
break;
case MSG_RESUME_POLLING:
mNfcAdapter.resumePolling();
@@ -2475,6 +2721,10 @@ public class NfcService implements DeviceHostListener {
mScreenState = (Integer)msg.obj;
Log.d(TAG, "MSG_APPLY_SCREEN_STATE " + mScreenState);
+ // Disable delay polling when screen state changed
+ mPollingDelayed = false;
+ mHandler.removeMessages(MSG_DELAY_POLLING);
+
// If NFC is turning off, we shouldn't need any changes here
synchronized (NfcService.this) {
if (mState == NfcAdapter.STATE_TURNING_OFF)
@@ -2482,7 +2732,8 @@ public class NfcService implements DeviceHostListener {
}
if (mScreenState == ScreenStateHelper.SCREEN_STATE_ON_UNLOCKED) {
- applyRouting(false);
+ applyRouting(false);
+ mIsRequestUnlockShowed = false;
}
int screen_state_mask = (mNfcUnlockManager.isLockscreenPollingEnabled()) ?
(ScreenStateHelper.SCREEN_POLLING_TAG_MASK | mScreenState) : mScreenState;
@@ -2513,6 +2764,16 @@ public class NfcService implements DeviceHostListener {
sToast_debounce = false;
break;
+ case MSG_DELAY_POLLING:
+ synchronized (NfcService.this) {
+ if (!mPollingDelayed) {
+ return;
+ }
+ mPollingDelayed = false;
+ mDeviceHost.startStopPolling(true);
+ }
+ if (DBG) Log.d(TAG, "Polling is started");
+ break;
default:
Log.e(TAG, "Unknown message received");
break;
@@ -2610,35 +2871,14 @@ public class NfcService implements DeviceHostListener {
return packages;
}
- private void sendNfcEeAccessProtectedBroadcast(Intent intent) {
+ private void sendNfcPermissionProtectedBroadcast(Intent intent) {
+ if (mNfcEventInstalledPackages.isEmpty()) {
+ return;
+ }
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
- // Resume app switches so the receivers can start activites without delay
- mNfcDispatcher.resumeAppSwitches();
- synchronized (this) {
- ArrayList<String> SEPackages = getSEAccessAllowedPackages();
- if (SEPackages!= null && !SEPackages.isEmpty()) {
- for (String packageName : SEPackages) {
- intent.setPackage(packageName);
- mContext.sendBroadcast(intent);
- }
- }
- PackageManager pm = mContext.getPackageManager();
- for (String packageName : mNfcEventInstalledPackages) {
- try {
- PackageInfo info = pm.getPackageInfo(packageName, 0);
- if (SEPackages != null && SEPackages.contains(packageName)) {
- continue;
- }
- if (info.applicationInfo != null &&
- ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ||
- (info.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0)) {
- intent.setPackage(packageName);
- mContext.sendBroadcast(intent);
- }
- } catch (Exception e) {
- Log.e(TAG, "Exception in getPackageInfo " + e);
- }
- }
+ for (String packageName : mNfcEventInstalledPackages) {
+ intent.setPackage(packageName);
+ mContext.sendBroadcast(intent);
}
}
@@ -2821,7 +3061,11 @@ public class NfcService implements DeviceHostListener {
unregisterObject(tagEndpoint.getHandle());
if (mPollDelay > NO_POLL_DELAY) {
tagEndpoint.stopPresenceChecking();
- mNfcAdapter.pausePolling(mPollDelay);
+ mDeviceHost.startStopPolling(false);
+ mPollingDelayed = true;
+ if (DBG) Log.d(TAG, "Polling delayed");
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_DELAY_POLLING), mPollDelay);
} else {
Log.e(TAG, "Keep presence checking.");
}
@@ -2919,7 +3163,7 @@ public class NfcService implements DeviceHostListener {
IPackageManager mIpm = IPackageManager.Stub.asInterface(
ServiceManager.getService("package"));
beamSetting = mIpm.getComponentEnabledSetting(new ComponentName(
- BeamShareActivity.class.getPackageName$(),
+ BeamShareActivity.class.getPackageName(),
BeamShareActivity.class.getName()),
userId);
} catch(RemoteException e) {
@@ -2964,6 +3208,9 @@ public class NfcService implements DeviceHostListener {
updatePackageCache();
} else if (action.equals(Intent.ACTION_SHUTDOWN)) {
if (DBG) Log.d(TAG, "Device is shutting down.");
+ if (mIsAlwaysOnSupported && mAlwaysOnState == NfcAdapter.STATE_ON) {
+ new EnableDisableTask().execute(TASK_DISABLE_ALWAYS_ON);
+ }
if (isNfcEnabled()) {
mDeviceHost.shutdown();
}
@@ -3031,7 +3278,7 @@ public class NfcService implements DeviceHostListener {
private void copyNativeCrashLogsIfAny(PrintWriter pw) {
try {
- File file = new File(getNfaStorageDir(), NATIVE_LOG_FILE_NAME);
+ File file = new File(NATIVE_LOG_FILE_PATH, NATIVE_LOG_FILE_NAME);
if (!file.exists()) {
return;
}
@@ -3050,7 +3297,7 @@ public class NfcService implements DeviceHostListener {
private void storeNativeCrashLogs() {
try {
- File file = new File(getNfaStorageDir(), NATIVE_LOG_FILE_NAME);
+ File file = new File(NATIVE_LOG_FILE_PATH, NATIVE_LOG_FILE_NAME);
if (file.length() >= NATIVE_CRASH_FILE_SIZE) {
file.createNewFile();
}
@@ -3086,9 +3333,11 @@ public class NfcService implements DeviceHostListener {
synchronized (this) {
pw.println("mState=" + stateToString(mState));
+ pw.println("mAlwaysOnState=" + stateToString(mAlwaysOnState));
pw.println("mIsZeroClickRequested=" + mIsNdefPushEnabled);
pw.println("mScreenState=" + ScreenStateHelper.screenStateToString(mScreenState));
pw.println("mIsSecureNfcEnabled=" + mIsSecureNfcEnabled);
+ pw.println("mIsAlwaysOnSupported=" + mIsAlwaysOnSupported);
pw.println(mCurrentDiscoveryParameters);
if (mIsBeamCapable) {
mP2pLinkManager.dump(fd, pw, args);
@@ -3150,7 +3399,7 @@ public class NfcService implements DeviceHostListener {
proto.end(token);
// Dump native crash logs if any
- File file = new File(getNfaStorageDir(), NATIVE_LOG_FILE_NAME);
+ File file = new File(NATIVE_LOG_FILE_PATH, NATIVE_LOG_FILE_NAME);
if (!file.exists()) {
return;
}
diff --git a/src/com/android/nfc/NfcWifiProtectedSetup.java b/src/com/android/nfc/NfcWifiProtectedSetup.java
index af4dd6f6..a078efcd 100644
--- a/src/com/android/nfc/NfcWifiProtectedSetup.java
+++ b/src/com/android/nfc/NfcWifiProtectedSetup.java
@@ -121,7 +121,7 @@ public final class NfcWifiProtectedSetup {
short fieldId = payload.getShort();
int fieldSize = payload.getShort() & 0xFFFF;
- // sanity check
+ // Quick check
if (payload.position() + fieldSize > startPosition + size) {
return null;
}
diff --git a/src/com/android/nfc/beam/BeamManager.java b/src/com/android/nfc/beam/BeamManager.java
index cf5f3342..a6f2abbf 100644
--- a/src/com/android/nfc/beam/BeamManager.java
+++ b/src/com/android/nfc/beam/BeamManager.java
@@ -38,7 +38,7 @@ public class BeamManager implements Handler.Callback {
private static final String TAG = "BeamManager";
private static final boolean DBG = false;
- private static final String ACTION_WHITELIST_DEVICE =
+ private static final String ACTION_ALLOWLIST_DEVICE =
"android.btopp.intent.action.WHITELIST_DEVICE";
public static final int MSG_BEAM_COMPLETE = 0;
@@ -89,7 +89,7 @@ public class BeamManager implements Handler.Callback {
receiveIntent.putExtra(BeamReceiveService.EXTRA_BEAM_TRANSFER_RECORD, transferRecord);
receiveIntent.putExtra(BeamReceiveService.EXTRA_BEAM_COMPLETE_CALLBACK,
new Messenger(mCallback));
- whitelistOppDevice(context, handoverData.device);
+ allowlistOppDevice(context, handoverData.device);
context.startServiceAsUser(receiveIntent, UserHandle.CURRENT);
return true;
}
@@ -133,9 +133,9 @@ public class BeamManager implements Handler.Callback {
return false;
}
- void whitelistOppDevice(Context context, BluetoothDevice device) {
- if (DBG) Log.d(TAG, "Whitelisting " + device + " for BT OPP");
- Intent intent = new Intent(ACTION_WHITELIST_DEVICE);
+ void allowlistOppDevice(Context context, BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "Allowlist " + device + " for BT OPP");
+ Intent intent = new Intent(ACTION_ALLOWLIST_DEVICE);
intent.setPackage(context.getString(R.string.bluetooth_package));
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
diff --git a/src/com/android/nfc/beam/BeamTransferManager.java b/src/com/android/nfc/beam/BeamTransferManager.java
index d728864b..cb59c280 100644
--- a/src/com/android/nfc/beam/BeamTransferManager.java
+++ b/src/com/android/nfc/beam/BeamTransferManager.java
@@ -105,7 +105,7 @@ public class BeamTransferManager implements Handler.Callback,
static final String BEAM_NOTIFICATION_CHANNEL = "beam_notification_channel";
- static final String ACTION_WHITELIST_DEVICE =
+ static final String ACTION_ALLOWLIST_DEVICE =
"android.btopp.intent.action.WHITELIST_DEVICE";
static final String ACTION_STOP_BLUETOOTH_TRANSFER =
@@ -180,9 +180,9 @@ public class BeamTransferManager implements Handler.Callback,
mNotificationManager.createNotificationChannel(notificationChannel);
}
- void whitelistOppDevice(BluetoothDevice device) {
- if (DBG) Log.d(TAG, "Whitelisting " + device + " for BT OPP");
- Intent intent = new Intent(ACTION_WHITELIST_DEVICE);
+ void allowlistOppDevice(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "Allowlist " + device + " for BT OPP");
+ Intent intent = new Intent(ACTION_ALLOWLIST_DEVICE);
intent.setPackage(mContext.getString(R.string.bluetooth_package));
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -212,8 +212,8 @@ public class BeamTransferManager implements Handler.Callback,
this.mProgress = progress;
// We're still receiving data from this device - keep it in
- // the whitelist for a while longer
- if (mIncoming && mRemoteDevice != null) whitelistOppDevice(mRemoteDevice);
+ // the allowlist for a while longer
+ if (mIncoming && mRemoteDevice != null) allowlistOppDevice(mRemoteDevice);
updateStateAndNotification(STATE_IN_PROGRESS);
}
@@ -554,4 +554,3 @@ public class BeamTransferManager implements Handler.Callback,
return newFile;
}
}
-
diff --git a/src/com/android/nfc/beam/SendUi.java b/src/com/android/nfc/beam/SendUi.java
index 49f893c2..22457295 100644
--- a/src/com/android/nfc/beam/SendUi.java
+++ b/src/com/android/nfc/beam/SendUi.java
@@ -18,9 +18,6 @@ package com.android.nfc.beam;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import com.android.nfc.R;
-import com.android.nfc.beam.FireflyRenderer;
-
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
@@ -42,6 +39,7 @@ import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.IBinder;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ActionMode;
@@ -52,7 +50,6 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
-import com.android.internal.policy.PhoneWindow;
import android.view.SearchEvent;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -68,7 +65,9 @@ import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
-
+import com.android.internal.policy.PhoneWindow;
+import com.android.nfc.R;
+import com.android.nfc.beam.FireflyRenderer;
import java.util.List;
/**
@@ -613,8 +612,6 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
mDisplayMetrics.heightPixels);
float smallestWidthDp = smallestWidth / (mDisplayMetrics.densityDpi / 160f);
- int rot = mDisplay.getRotation();
-
// TODO this is somewhat device-specific; need generic solution.
// The starting crop for the screenshot is the fullscreen without the status bar, which
// is always on top. The conditional check will determine how to crop the navbar,
@@ -639,7 +636,16 @@ public class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
int height = crop.height();
// Take the screenshot. SurfaceControl will generate a hardware bitmap in the correct
// orientation and size.
- Bitmap bitmap = SurfaceControl.screenshot(crop, width, height, rot);
+ IBinder displayToken = SurfaceControl.getInternalDisplayToken();
+ final SurfaceControl.DisplayCaptureArgs captureArgs =
+ new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
+ .setSourceCrop(crop)
+ .setSize(width, height)
+ .build();
+ SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ SurfaceControl.captureDisplay(captureArgs);
+ final Bitmap bitmap = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
+
// Bail if we couldn't take the screenshot
if (bitmap == null) {
return null;
diff --git a/src/com/android/nfc/cardemulation/AidRoutingManager.java b/src/com/android/nfc/cardemulation/AidRoutingManager.java
index b1e67130..3dc9f578 100644
--- a/src/com/android/nfc/cardemulation/AidRoutingManager.java
+++ b/src/com/android/nfc/cardemulation/AidRoutingManager.java
@@ -85,6 +85,7 @@ public class AidRoutingManager {
String offHostSE;
int route;
int aidInfo;
+ int power;
}
public AidRoutingManager() {
@@ -168,9 +169,9 @@ public class AidRoutingManager {
NfcService.getInstance().unrouteAids(aid);
}
- if(NfcService.getInstance().getNciVersion() != NfcService.getInstance().NCI_VERSION_1_0) {
- // unRoute EmptyAid
- NfcService.getInstance().unrouteAids("");
+ if (NfcService.getInstance().getNciVersion() >= NfcService.getInstance().NCI_VERSION_2_0) {
+ // unRoute EmptyAid
+ NfcService.getInstance().unrouteAids("");
}
}
@@ -334,25 +335,62 @@ public class AidRoutingManager {
}
}
- if(mDefaultRoute != mDefaultIsoDepRoute) {
- if(NfcService.getInstance().getNciVersion() != NfcService.getInstance().NCI_VERSION_1_0) {
- String emptyAid = "";
- AidEntry entry = new AidEntry();
- entry.route = mDefaultRoute;
- if(mDefaultRoute==ROUTE_HOST) {
- entry.isOnHost = true;
- } else{
- entry.isOnHost = false;
- }
- entry.aidInfo = RegisteredAidCache.AID_ROUTE_QUAL_PREFIX;
- aidRoutingTableCache.put(emptyAid, entry);
- if (DBG) Log.d(TAG, "Add emptyAid into AidRoutingTable");
- }
+ // register default route in below cases:
+ // 1. mDefaultRoute is different with mDefaultIsoDepRoute
+ // 2. mDefaultRoute and mDefaultIsoDepRoute all equal to ROUTE_HOST
+ // , which is used for screen off HCE scenarios
+ if (mDefaultRoute != mDefaultIsoDepRoute || mDefaultIsoDepRoute == ROUTE_HOST) {
+ if (NfcService.getInstance().getNciVersion()
+ >= NfcService.getInstance().NCI_VERSION_2_0) {
+ String emptyAid = "";
+ AidEntry entry = new AidEntry();
+ int default_route_power_state;
+ entry.route = mDefaultRoute;
+ if (mDefaultRoute == ROUTE_HOST) {
+ entry.isOnHost = true;
+ default_route_power_state = RegisteredAidCache.POWER_STATE_SWITCH_ON
+ | RegisteredAidCache.POWER_STATE_SCREEN_ON_LOCKED;
+ Set<String> aidsForDefaultRoute = mAidRoutingTable.get(mDefaultRoute);
+ if (aidsForDefaultRoute != null) {
+ for (String aid : aidsForDefaultRoute) {
+ default_route_power_state |= aidMap.get(aid).power;
+ }
+ }
+ } else {
+ entry.isOnHost = false;
+ default_route_power_state = RegisteredAidCache.POWER_STATE_ALL;
+ }
+ entry.aidInfo = RegisteredAidCache.AID_ROUTE_QUAL_PREFIX;
+ entry.power = default_route_power_state;
+
+ aidRoutingTableCache.put(emptyAid, entry);
+ if (DBG) Log.d(TAG, "Add emptyAid into AidRoutingTable");
+ }
+ }
+
+ // Register additional offhost AIDs when their support power states are
+ // differernt from the default route entry
+ if (mDefaultRoute != ROUTE_HOST) {
+ int default_route_power_state = RegisteredAidCache.POWER_STATE_ALL;
+ if (NfcService.getInstance().getNciVersion()
+ < NfcService.getInstance().NCI_VERSION_2_0) {
+ default_route_power_state =
+ RegisteredAidCache.POWER_STATE_ALL_NCI_VERSION_1_0;
+ }
+
+ Set<String> aidsForDefaultRoute = mAidRoutingTable.get(mDefaultRoute);
+ if (aidsForDefaultRoute != null) {
+ for (String aid : aidsForDefaultRoute) {
+ if (aidMap.get(aid).power != default_route_power_state) {
+ aidRoutingTableCache.put(aid, aidMap.get(aid));
+ }
+ }
+ }
}
- if( calculateAidRouteSize(aidRoutingTableCache) <= mMaxAidRoutingTableSize) {
- aidRouteResolved = true;
- break;
+ if (calculateAidRouteSize(aidRoutingTableCache) <= mMaxAidRoutingTableSize) {
+ aidRouteResolved = true;
+ break;
}
}
@@ -375,9 +413,13 @@ public class AidRoutingManager {
int route = aidEntry.getValue().route;
int aidType = aidEntry.getValue().aidInfo;
String aid = aidEntry.getKey();
- if (DBG) Log.d (TAG, "commit aid:"+aid+"route:"+route+"aidtype:"+aidType);
+ int power = aidEntry.getValue().power;
+ if (DBG) {
+ Log.d(TAG, "commit aid:" + aid + ",route:" + route
+ + ",aidtype:" + aidType + ", power state:" + power);
+ }
- NfcService.getInstance().routeAids(aid, route, aidType);
+ NfcService.getInstance().routeAids(aid, route, aidType, power);
}
}
diff --git a/src/com/android/nfc/cardemulation/CardEmulationManager.java b/src/com/android/nfc/cardemulation/CardEmulationManager.java
index a7b7e9dc..f6353942 100644
--- a/src/com/android/nfc/cardemulation/CardEmulationManager.java
+++ b/src/com/android/nfc/cardemulation/CardEmulationManager.java
@@ -259,7 +259,7 @@ public class CardEmulationManager implements RegisteredServicesCache.Callback,
void verifyDefaults(int userId, List<ApduServiceInfo> services) {
ComponentName defaultPaymentService =
- getDefaultServiceForCategory(userId, CardEmulation.CATEGORY_PAYMENT, false);
+ getDefaultServiceForCategory(userId, CardEmulation.CATEGORY_PAYMENT, true);
if (DBG) Log.d(TAG, "Current default: " + defaultPaymentService);
if (defaultPaymentService == null) {
// A payment service may have been removed, leaving only one;
@@ -275,6 +275,7 @@ public class CardEmulationManager implements RegisteredServicesCache.Callback,
if (numPaymentServices > 1) {
// More than one service left, leave default unset
if (DBG) Log.d(TAG, "No default set, more than one service left.");
+ setDefaultServiceForCategoryChecked(userId, null, CardEmulation.CATEGORY_PAYMENT);
} else if (numPaymentServices == 1) {
// Make single found payment service the default
if (DBG) Log.d(TAG, "No default set, making single service default.");
@@ -283,6 +284,7 @@ public class CardEmulationManager implements RegisteredServicesCache.Callback,
} else {
// No payment services left, leave default at null
if (DBG) Log.d(TAG, "No default set, last payment service removed.");
+ setDefaultServiceForCategoryChecked(userId, null, CardEmulation.CATEGORY_PAYMENT);
}
}
}
@@ -532,6 +534,13 @@ public class CardEmulationManager implements RegisteredServicesCache.Callback,
NfcPermissions.enforcePreferredPaymentInfoPermissions(mContext);
return mServiceCache.getService(userId, mAidCache.getPreferredService());
}
+
+ @Override
+ public boolean isDefaultPaymentRegistered() throws RemoteException {
+ String defaultComponent = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
+ return defaultComponent != null ? true : false;
+ }
}
/**
diff --git a/src/com/android/nfc/cardemulation/HostEmulationManager.java b/src/com/android/nfc/cardemulation/HostEmulationManager.java
index 2d763f93..e26f6f2b 100644
--- a/src/com/android/nfc/cardemulation/HostEmulationManager.java
+++ b/src/com/android/nfc/cardemulation/HostEmulationManager.java
@@ -31,6 +31,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
@@ -72,6 +73,7 @@ public class HostEmulationManager {
final Messenger mMessenger = new Messenger (new MessageHandler());
final KeyguardManager mKeyguard;
final Object mLock;
+ final PowerManager mPowerManager;
// All variables below protected by mLock
@@ -106,6 +108,7 @@ public class HostEmulationManager {
mAidCache = aidCache;
mState = STATE_IDLE;
mKeyguard = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+ mPowerManager = context.getSystemService(PowerManager.class);
}
public void onPreferredPaymentServiceChanged(final ComponentName service) {
@@ -181,6 +184,11 @@ public class HostEmulationManager {
launchTapAgain(resolveInfo.defaultService, resolveInfo.category);
return;
}
+ if (defaultServiceInfo.requiresScreenOn() && !mPowerManager.isScreenOn()) {
+ // Just ignore all future APDUs
+ mState = STATE_W4_DEACTIVATE;
+ return;
+ }
// In no circumstance should this be an OffHostService -
// we should never get this AID on the host in the first place
if (!defaultServiceInfo.isOnHost()) {
@@ -309,12 +317,15 @@ public class HostEmulationManager {
unbindServiceIfNeededLocked();
Intent aidIntent = new Intent(HostApduService.SERVICE_INTERFACE);
aidIntent.setComponent(service);
- if (mContext.bindServiceAsUser(aidIntent, mConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
- UserHandle.CURRENT)) {
- mServiceBound = true;
- } else {
- Log.e(TAG, "Could not bind service.");
+ try {
+ mServiceBound = mContext.bindServiceAsUser(aidIntent, mConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
+ UserHandle.CURRENT);
+ if (!mServiceBound) {
+ Log.e(TAG, "Could not bind service.");
+ }
+ } catch (SecurityException e) {
+ Log.e(TAG, "Could not bind service due to security exception.");
}
return null;
}
@@ -464,6 +475,7 @@ public class HostEmulationManager {
}
mService = new Messenger(service);
mServiceName = name;
+ mServiceBound = true;
Log.d(TAG, "Service bound");
mState = STATE_XFER;
// Send pending select APDU
@@ -479,6 +491,7 @@ public class HostEmulationManager {
synchronized (mLock) {
Log.d(TAG, "Service unbound");
mService = null;
+ mServiceName = null;
mServiceBound = false;
}
}
diff --git a/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java b/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
index 8b9e7dee..e2f85541 100644
--- a/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
+++ b/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
@@ -231,10 +231,14 @@ public class HostNfcFEmulationManager {
unbindServiceIfNeededLocked();
Intent bindIntent = new Intent(HostNfcFService.SERVICE_INTERFACE);
bindIntent.setComponent(service);
- if (mContext.bindServiceAsUser(bindIntent, mConnection,
- Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
- } else {
- Log.e(TAG, "Could not bind service.");
+ try {
+ mServiceBound = mContext.bindServiceAsUser(bindIntent, mConnection,
+ Context.BIND_AUTO_CREATE, UserHandle.CURRENT);
+ if (!mServiceBound) {
+ Log.e(TAG, "Could not bind service.");
+ }
+ } catch (SecurityException e) {
+ Log.e(TAG, "Could not bind service due to security exception.");
}
return null;
}
diff --git a/src/com/android/nfc/cardemulation/PreferredServices.java b/src/com/android/nfc/cardemulation/PreferredServices.java
index 7b0a6e66..b82c8009 100644
--- a/src/com/android/nfc/cardemulation/PreferredServices.java
+++ b/src/com/android/nfc/cardemulation/PreferredServices.java
@@ -235,7 +235,7 @@ public class PreferredServices implements com.android.nfc.ForegroundUtils.Callba
Log.d(TAG, "Requested foreground service unexpectedly removed");
return false;
}
- // Do some sanity checking
+ // Do some quick checking
if (!mPaymentDefaults.preferForeground) {
// Foreground apps are not allowed to override payment default
// Check if this app registers payment AIDs, in which case we'll fail anyway
diff --git a/src/com/android/nfc/cardemulation/RegisteredAidCache.java b/src/com/android/nfc/cardemulation/RegisteredAidCache.java
index 7618d38c..2e66e3cc 100644
--- a/src/com/android/nfc/cardemulation/RegisteredAidCache.java
+++ b/src/com/android/nfc/cardemulation/RegisteredAidCache.java
@@ -24,6 +24,7 @@ import android.nfc.cardemulation.CardEmulation;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import com.android.nfc.NfcService;
import com.google.android.collect.Maps;
import java.util.Collections;
import java.io.FileDescriptor;
@@ -46,6 +47,19 @@ public class RegisteredAidCache {
static final int AID_ROUTE_QUAL_SUBSET = 0x20;
static final int AID_ROUTE_QUAL_PREFIX = 0x10;
+ static final int POWER_STATE_SWITCH_ON = 0x1;
+ static final int POWER_STATE_SWITCH_OFF = 0x2;
+ static final int POWER_STATE_BATTERY_OFF = 0x4;
+ static final int POWER_STATE_SCREEN_OFF_UNLOCKED = 0x8;
+ static final int POWER_STATE_SCREEN_ON_LOCKED = 0x10;
+ static final int POWER_STATE_SCREEN_OFF_LOCKED = 0x20;
+ static final int POWER_STATE_ALL = POWER_STATE_SWITCH_ON | POWER_STATE_SWITCH_OFF
+ | POWER_STATE_BATTERY_OFF | POWER_STATE_SCREEN_OFF_UNLOCKED
+ | POWER_STATE_SCREEN_ON_LOCKED | POWER_STATE_SCREEN_OFF_LOCKED;
+ static final int POWER_STATE_ALL_NCI_VERSION_1_0 = POWER_STATE_SWITCH_ON
+ | POWER_STATE_SWITCH_OFF
+ | POWER_STATE_BATTERY_OFF;
+
// mAidServices maps AIDs to services that have registered them.
// It's a TreeMap in order to be able to quickly select subsets
// of AIDs that conflict with each other.
@@ -779,6 +793,33 @@ public class RegisteredAidCache {
updateRoutingLocked(false);
}
+ private int computeAidPowerState(boolean isOnHost, boolean requiresScreenOn,
+ boolean requiresUnlock) {
+ int power = POWER_STATE_ALL;
+ if (NfcService.getInstance().getNciVersion() < NfcService.getInstance().NCI_VERSION_2_0) {
+ power = POWER_STATE_ALL_NCI_VERSION_1_0;
+ }
+
+ if (isOnHost) {
+ power &= ~(POWER_STATE_SWITCH_OFF | POWER_STATE_BATTERY_OFF);
+ } else {
+ if (requiresUnlock) {
+ power &= ~POWER_STATE_SCREEN_ON_LOCKED;
+ }
+ }
+
+ if (requiresScreenOn) {
+ power &= ~(POWER_STATE_SWITCH_OFF | POWER_STATE_BATTERY_OFF
+ | POWER_STATE_SCREEN_OFF_UNLOCKED | POWER_STATE_SCREEN_OFF_LOCKED);
+ }
+ if (requiresUnlock) {
+ power &= ~(POWER_STATE_SWITCH_OFF | POWER_STATE_BATTERY_OFF
+ | POWER_STATE_SCREEN_OFF_LOCKED);
+ }
+
+ return power;
+ }
+
void updateRoutingLocked(boolean force) {
if (!mNfcEnabled) {
if (DBG) Log.d(TAG, "Not updating routing table because NFC is off.");
@@ -812,6 +853,12 @@ public class RegisteredAidCache {
aidType.offHostSE =
resolveInfo.defaultService.getOffHostSecureElement();
}
+
+ boolean requiresUnlock = resolveInfo.defaultService.requiresUnlock();
+ boolean requiresScreenOn = resolveInfo.defaultService.requiresScreenOn();
+ aidType.power =
+ computeAidPowerState(aidType.isOnHost, requiresScreenOn, requiresUnlock);
+
routingEntries.put(aid, aidType);
} else if (resolveInfo.services.size() == 1) {
// Only one service, but not the default, must route to host
@@ -826,12 +873,20 @@ public class RegisteredAidCache {
resolveInfo.services.get(0).getOffHostSecureElement();
}
}
+
+ boolean requiresUnlock = resolveInfo.services.get(0).requiresUnlock();
+ boolean requiresScreenOn = resolveInfo.services.get(0).requiresScreenOn();
+ aidType.power =
+ computeAidPowerState(aidType.isOnHost, requiresScreenOn, requiresUnlock);
+
routingEntries.put(aid, aidType);
} else if (resolveInfo.services.size() > 1) {
// Multiple services if all the services are routing to same
// offhost then the service should be routed to off host.
boolean onHost = false;
String offHostSE = null;
+ boolean requiresUnlock = false;
+ boolean requiresScreenOn = true;
for (ApduServiceInfo service : resolveInfo.services) {
// In case there is at least one service which routes to host
// Route it to host for user to select which service to use
@@ -839,18 +894,37 @@ public class RegisteredAidCache {
if (!onHost) {
if (offHostSE == null) {
offHostSE = service.getOffHostSecureElement();
+ requiresUnlock = service.requiresUnlock();
+ requiresScreenOn = service.requiresScreenOn();
} else if (!offHostSE.equals(
service.getOffHostSecureElement())) {
// There are registerations to different SEs, route this
// to host and have user choose a service for this AID
offHostSE = null;
onHost = true;
+ requiresUnlock = false;
+ requiresScreenOn = true;
+ break;
+ } else if (requiresUnlock != service.requiresUnlock()
+ || requiresScreenOn != service.requiresScreenOn()) {
+ // There are registrations to the same SE with differernt supported
+ // power states, route this to host and have user choose a service
+ // for this AID
+ offHostSE = null;
+ onHost = true;
+ requiresUnlock = false;
+ requiresScreenOn = true;
break;
}
}
}
aidType.isOnHost = onHost;
aidType.offHostSE = onHost ? null : offHostSE;
+ requiresUnlock = onHost ? false : requiresUnlock;
+ requiresScreenOn = onHost ? true : requiresScreenOn;
+
+ aidType.power = computeAidPowerState(onHost, requiresScreenOn, requiresUnlock);
+
routingEntries.put(aid, aidType);
}
}
diff --git a/src/com/android/nfc/handover/BluetoothPeripheralHandover.java b/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
index 98d40578..45b00ae1 100644
--- a/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
+++ b/src/com/android/nfc/handover/BluetoothPeripheralHandover.java
@@ -453,7 +453,8 @@ public class BluetoothPeripheralHandover implements BluetoothProfile.ServiceList
toast(getToastString(R.string.pairing_peripheral));
}
if (mOobData != null) {
- if (!mDevice.createBondOutOfBand(mTransport, mOobData)) {
+ if (!mDevice.createBondOutOfBand(mTransport, /* p192 not implemented for LE */ null,
+ mOobData)) {
toast(getToastString(R.string.pairing_peripheral_failed));
complete(false);
}
diff --git a/src/com/android/nfc/handover/HandoverDataParser.java b/src/com/android/nfc/handover/HandoverDataParser.java
index 09bad095..0e04fdf5 100644
--- a/src/com/android/nfc/handover/HandoverDataParser.java
+++ b/src/com/android/nfc/handover/HandoverDataParser.java
@@ -16,15 +16,6 @@
package com.android.nfc.handover;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.nio.charset.Charset;
-import java.util.Arrays;
-import java.util.Random;
-
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -38,6 +29,14 @@ import android.nfc.NdefRecord;
import android.os.ParcelUuid;
import android.os.UserHandle;
import android.util.Log;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Random;
/**
* Manages handover of NFC to other technologies.
@@ -462,18 +461,20 @@ public class HandoverDataParser {
try {
+ byte[] bdaddr = null;
+ byte role = 0xF; // invalid default
+ byte[] leScC = null;
+ byte[] leScR = null;
+ byte[] nameBytes = null;
+ byte[] securityManagerTK = null;
while (payload.remaining() > 0) {
int len = payload.get();
int type = payload.get();
switch (type) {
case BT_HANDOVER_TYPE_MAC: // mac address
-
int startpos = payload.position();
- byte[] bdaddr = new byte[7]; // 6 bytes for mac, 1 for addres type
+ bdaddr = new byte[7]; // 6 bytes for mac, 1 for address type
payload.get(bdaddr);
- if (result.oobData == null)
- result.oobData = new OobData();
- result.oobData.setLeBluetoothDeviceAddress(bdaddr);
payload.position(startpos);
byte[] address = parseMacFromBluetoothRecord(payload);
@@ -481,19 +482,22 @@ public class HandoverDataParser {
result.device = mBluetoothAdapter.getRemoteDevice(address);
result.valid = true;
break;
+
case BT_HANDOVER_TYPE_LE_ROLE:
- byte role = payload.get();
+ role = payload.get();
if (role == BT_HANDOVER_LE_ROLE_CENTRAL_ONLY) {
// only central role supported, can't pair
result.valid = false;
return result;
}
break;
+
case BT_HANDOVER_TYPE_LONG_LOCAL_NAME:
- byte[] nameBytes = new byte[len - 1];
+ nameBytes = new byte[len - 1];
payload.get(nameBytes);
result.name = new String(nameBytes, StandardCharsets.UTF_8);
break;
+
case BT_HANDOVER_TYPE_SECURITY_MANAGER_TK:
if (len-1 != SECURITY_MANAGER_TK_SIZE) {
Log.i(TAG, "BT OOB: invalid size of SM TK, should be " +
@@ -501,12 +505,8 @@ public class HandoverDataParser {
break;
}
- byte[] securityManagerTK = new byte[len - 1];
+ securityManagerTK = new byte[len - 1];
payload.get(securityManagerTK);
-
- if (result.oobData == null)
- result.oobData = new OobData();
- result.oobData.setSecurityManagerTk(securityManagerTK);
break;
case BT_HANDOVER_TYPE_LE_SC_CONFIRMATION:
@@ -516,12 +516,8 @@ public class HandoverDataParser {
break;
}
- byte[] leScC = new byte[len - 1];
+ leScC = new byte[len - 1];
payload.get(leScC);
-
- if (result.oobData == null)
- result.oobData = new OobData();
- result.oobData.setLeSecureConnectionsConfirmation(leScC);
break;
case BT_HANDOVER_TYPE_LE_SC_RANDOM:
@@ -531,12 +527,8 @@ public class HandoverDataParser {
break;
}
- byte[] leScR = new byte[len - 1];
+ leScR = new byte[len - 1];
payload.get(leScR);
-
- if (result.oobData == null)
- result.oobData = new OobData();
- result.oobData.setLeSecureConnectionsRandom(leScR);
break;
default:
@@ -544,6 +536,11 @@ public class HandoverDataParser {
break;
}
}
+ result.oobData = new OobData.LeBuilder(leScC, bdaddr, (int)(role & 0xFF))
+ .setRandomizerHash(leScR)
+ .setDeviceName(nameBytes)
+ .setLeTemporaryKey(securityManagerTK)
+ .build();
} catch (IllegalArgumentException e) {
Log.i(TAG, "BLE OOB: error parsing OOB data", e);
} catch (BufferUnderflowException e) {
diff --git a/src/com/android/nfc/snep/SnepMessenger.java b/src/com/android/nfc/snep/SnepMessenger.java
index 5078fe8c..51ebe03b 100644
--- a/src/com/android/nfc/snep/SnepMessenger.java
+++ b/src/com/android/nfc/snep/SnepMessenger.java
@@ -202,9 +202,10 @@ public class SnepMessenger {
}
if (NfcService.sIsDtaMode) {
- if (!mIsClient && (requestField == SnepMessage.RESPONSE_CONTINUE)|| // added for TC_S_BIT_B1_01_X
+ // added for TC_S_BIT_B1_01_X
+ if (!mIsClient && (requestField == SnepMessage.RESPONSE_CONTINUE ||
requestField == SnepMessage.RESPONSE_SUCCESS ||
- requestField == SnepMessage.RESPONSE_NOT_FOUND) {
+ requestField == SnepMessage.RESPONSE_NOT_FOUND)) {
if (DBG) Log.d(TAG, "errorneous response received, disconnecting client");
close();
}
diff --git a/tests/Android.bp b/tests/Android.bp
deleted file mode 100644
index ce42a9b7..00000000
--- a/tests/Android.bp
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2011, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-android_test {
- name: "NfcTests",
- libs: [
- "android.test.runner",
- "android.test.base",
- ],
- static_libs: ["junit"],
- // Include all test java files.
- srcs: ["src/**/*.java"],
- platform_apis: true,
- certificate: "platform",
- instrumentation_for: "NfcNci",
-}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
deleted file mode 100644
index fa2380cc..00000000
--- a/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.nfc.tests">
-
- <!-- We add an application tag here just so that we can indicate that
- this package needs to link against the android.test library,
- which is needed when building test cases. -->
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <!--
- This declares that this app uses the instrumentation test runner targeting
- the package of com.android.nfc3. To run the tests use the command:
- "adb shell am instrument -w com.android.nfc.tests/android.test.InstrumentationTestRunner"
- -->
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.nfc"
- android:label="Tests for Nfc."/>
-
-</manifest>
diff --git a/tests/src/com/android/nfc/MockLlcpSocket.java b/tests/src/com/android/nfc/MockLlcpSocket.java
deleted file mode 100644
index 95279a73..00000000
--- a/tests/src/com/android/nfc/MockLlcpSocket.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.nfc;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.util.LinkedList;
-import java.util.List;
-
-public class MockLlcpSocket implements DeviceHost.LlcpSocket {
- private static final String TAG = "mockLlcpSocket";
- private MockLlcpSocket mPairedSocket;
- private List<byte[]> mReceivedPackets = new LinkedList<byte[]>();
- private boolean mClosed = false;
-
- @Override
- public void close() throws IOException {
- mClosed = true;
- mPairedSocket.mClosed = true;
- }
-
- @Override
- public void connectToSap(int sap) throws IOException {
- throw new UnsupportedOperationException("Use MockLlcpSocket.bind(client, server)");
- }
-
- @Override
- public void send(byte[] data) throws IOException {
- if (mClosed || mPairedSocket == null) {
- throw new IOException("Socket not connected");
- }
- synchronized (mPairedSocket.mReceivedPackets) {
- Log.d(TAG, "sending packet");
- mPairedSocket.mReceivedPackets.add(data);
- mPairedSocket.mReceivedPackets.notify();
- }
- }
-
- @Override
- public int receive(byte[] receiveBuffer) throws IOException {
- synchronized (mReceivedPackets) {
- while (!mClosed && mReceivedPackets.size() == 0) {
- try {
- mReceivedPackets.wait(1000);
- } catch (InterruptedException e) {}
- }
- if (mClosed) {
- throw new IOException("Socket closed.");
- }
- byte[] arr = mReceivedPackets.remove(0);
- System.arraycopy(arr, 0, receiveBuffer, 0, arr.length);
- return arr.length;
- }
- }
-
- public static void bind(MockLlcpSocket client, MockLlcpSocket server) {
- client.mPairedSocket = server;
- server.mPairedSocket = client;
- }
-
- @Override
- public void connectToService(String serviceName) throws IOException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getRemoteMiu() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getRemoteRw() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getLocalSap() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getLocalMiu() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getLocalRw() {
- throw new UnsupportedOperationException();
- }
-} \ No newline at end of file
diff --git a/tests/src/com/android/nfc/snep/SnepBasicTests.java b/tests/src/com/android/nfc/snep/SnepBasicTests.java
deleted file mode 100644
index 32b5e645..00000000
--- a/tests/src/com/android/nfc/snep/SnepBasicTests.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.nfc.snep;
-
-import com.android.nfc.MockLlcpSocket;
-
-import android.nfc.NdefMessage;
-import android.nfc.NdefRecord;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.io.IOException;
-
-/**
- * Tests the SNEP cleint/server interfaces using a mock LLCP socket.
- */
-public class SnepBasicTests extends AndroidTestCase {
- private static final String TAG = "snepBasicTests";
- private static final int MIU = 250;
- private static final int ACCEPTABLE_LENGTH = 2*1024;
-
- public void testGetSmallNdef() throws IOException {
- MockLlcpSocket clientSocket = new MockLlcpSocket();
- MockLlcpSocket serverSocket = new MockLlcpSocket();
- MockLlcpSocket.bind(clientSocket, serverSocket);
-
- final SnepMessenger client = new SnepMessenger(true, clientSocket, MIU);
- final SnepMessenger server = new SnepMessenger(false, serverSocket, MIU);
-
- new Thread() {
- @Override
- public void run() {
- try {
- SnepServer.handleRequest(server, mCallback);
- } catch (Exception e) {
- Log.e(TAG, "error getting message", e);
- }
- };
- }.start();
-
- SnepMessage response = null;
- try {
- client.sendMessage(SnepMessage.getGetRequest(ACCEPTABLE_LENGTH, getSmallNdef()));
- response = client.getMessage();
- } catch (SnepException e) {
- throw new IOException("Failed to retrieve SNEP message", e);
- }
-
- assertNotNull(response);
- assertEquals(SnepMessage.RESPONSE_SUCCESS, response.getField());
- }
-
- public void testGetLargeNdef() throws IOException {
- MockLlcpSocket clientSocket = new MockLlcpSocket();
- MockLlcpSocket serverSocket = new MockLlcpSocket();
- MockLlcpSocket.bind(clientSocket, serverSocket);
-
- final SnepMessenger client = new SnepMessenger(true, clientSocket, MIU);
- final SnepMessenger server = new SnepMessenger(false, serverSocket, MIU);
-
- new Thread() {
- @Override
- public void run() {
- try {
- SnepServer.handleRequest(server, mCallback);
- } catch (Exception e) {
- Log.e(TAG, "error getting message", e);
- }
- };
- }.start();
-
- SnepMessage response = null;
- try {
- client.sendMessage(SnepMessage.getGetRequest(ACCEPTABLE_LENGTH, getNdef(900)));
- response = client.getMessage();
- } catch (SnepException e) {
- throw new IOException("Failed to retrieve SNEP message", e);
- }
-
- assertNotNull(response);
- assertEquals(SnepMessage.RESPONSE_SUCCESS, response.getField());
- }
-
- public void testGetExcessiveNdef() throws IOException {
- MockLlcpSocket clientSocket = new MockLlcpSocket();
- MockLlcpSocket serverSocket = new MockLlcpSocket();
- MockLlcpSocket.bind(clientSocket, serverSocket);
-
- final SnepMessenger client = new SnepMessenger(true, clientSocket, MIU);
- final SnepMessenger server = new SnepMessenger(false, serverSocket, MIU);
-
- new Thread() {
- @Override
- public void run() {
- try {
- SnepServer.handleRequest(server, mCallback);
- } catch (Exception e) {
- Log.e(TAG, "error getting message", e);
- }
- };
- }.start();
-
- SnepMessage response = null;
- try {
- client.sendMessage(SnepMessage.getGetRequest(10, getSmallNdef()));
- response = client.getMessage();
- } catch (SnepException e) {
- throw new IOException("Failed to retrieve SNEP message", e);
- }
-
- assertNotNull(response);
- assertEquals(SnepMessage.RESPONSE_EXCESS_DATA, response.getField());
- }
-
- public void testPutSmallNdef() throws IOException {
- MockLlcpSocket clientSocket = new MockLlcpSocket();
- MockLlcpSocket serverSocket = new MockLlcpSocket();
- MockLlcpSocket.bind(clientSocket, serverSocket);
-
- final SnepMessenger client = new SnepMessenger(true, clientSocket, MIU);
- final SnepMessenger server = new SnepMessenger(false, serverSocket, MIU);
-
- new Thread() {
- @Override
- public void run() {
- try {
- SnepServer.handleRequest(server, mCallback);
- } catch (Exception e) {
- Log.e(TAG, "error getting message", e);
- }
- };
- }.start();
-
- SnepMessage response = null;
- try {
- client.sendMessage(SnepMessage.getPutRequest(getSmallNdef()));
- response = client.getMessage();
- } catch (SnepException e) {
- throw new IOException("Failed to retrieve SNEP message", e);
- }
-
- assertNotNull(response);
- assertEquals(SnepMessage.RESPONSE_SUCCESS, response.getField());
- }
-
- public void testPutLargeNdef() throws IOException {
- MockLlcpSocket clientSocket = new MockLlcpSocket();
- MockLlcpSocket serverSocket = new MockLlcpSocket();
- MockLlcpSocket.bind(clientSocket, serverSocket);
-
- final SnepMessenger client = new SnepMessenger(true, clientSocket, MIU);
- final SnepMessenger server = new SnepMessenger(false, serverSocket, MIU);
-
- new Thread() {
- @Override
- public void run() {
- try {
- SnepServer.handleRequest(server, mCallback);
- } catch (Exception e) {
- Log.e(TAG, "error getting message", e);
- }
- };
- }.start();
-
- SnepMessage response = null;
- try {
- client.sendMessage(SnepMessage.getPutRequest(getNdef(900)));
- response = client.getMessage();
- } catch (SnepException e) {
- throw new IOException("Failed to retrieve SNEP message", e);
- }
-
- assertNotNull(response);
- assertEquals(SnepMessage.RESPONSE_SUCCESS, response.getField());
- }
-
- public void testUnsupportedVersion() throws IOException {
- MockLlcpSocket clientSocket = new MockLlcpSocket();
- MockLlcpSocket serverSocket = new MockLlcpSocket();
- MockLlcpSocket.bind(clientSocket, serverSocket);
-
- final SnepMessenger client = new SnepMessenger(true, clientSocket, MIU);
- final SnepMessenger server = new SnepMessenger(false, serverSocket, MIU);
-
- new Thread() {
- @Override
- public void run() {
- try {
- SnepServer.handleRequest(server, mCallback);
- } catch (Exception e) {
- Log.e(TAG, "error getting message", e);
- }
- };
- }.start();
-
- SnepMessage response = null;
- try {
- NdefMessage ndef = getSmallNdef();
- SnepMessage request = new SnepMessage(
- (byte)2, SnepMessage.REQUEST_PUT, ndef.toByteArray().length, 0, ndef);
- client.sendMessage(request);
- response = client.getMessage();
- } catch (SnepException e) {
- throw new IOException("Failed to retrieve SNEP message", e);
- }
-
- assertNotNull(response);
- assertEquals(SnepMessage.RESPONSE_UNSUPPORTED_VERSION, response.getField());
- }
-
- public void testDifferentMinorVersion() throws IOException {
- MockLlcpSocket clientSocket = new MockLlcpSocket();
- MockLlcpSocket serverSocket = new MockLlcpSocket();
- MockLlcpSocket.bind(clientSocket, serverSocket);
-
- final SnepMessenger client = new SnepMessenger(true, clientSocket, MIU);
- final SnepMessenger server = new SnepMessenger(false, serverSocket, MIU);
-
- new Thread() {
- @Override
- public void run() {
- try {
- SnepServer.handleRequest(server, mCallback);
- } catch (Exception e) {
- Log.e(TAG, "error getting message", e);
- }
- };
- }.start();
-
- byte version = (0xF0 & (SnepMessage.VERSION_MAJOR << 4)) |
- (0x0F & (SnepMessage.VERSION_MINOR + 1));
- SnepMessage response = null;
- try {
- NdefMessage ndef = getSmallNdef();
- SnepMessage request = new SnepMessage(
- version, SnepMessage.REQUEST_PUT, ndef.toByteArray().length, 0, ndef);
- client.sendMessage(request);
- response = client.getMessage();
- } catch (SnepException e) {
- throw new IOException("Failed to retrieve SNEP message", e);
- }
-
- assertNotNull(response);
- assertEquals(SnepMessage.RESPONSE_SUCCESS, response.getField());
- }
-
- NdefMessage getSmallNdef() {
- NdefRecord rec = new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, NdefRecord.RTD_URI,
- new byte[0], "http://android.com".getBytes());
- return new NdefMessage(new NdefRecord[] { rec });
- }
-
- NdefMessage getNdef(int size) {
- StringBuffer string = new StringBuffer(size);
- for (int i = 0; i < size; i++) {
- string.append('A' + (i % 26));
- }
- NdefRecord rec = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, "text/plain".getBytes(),
- new byte[0], string.toString().getBytes());
- return new NdefMessage(new NdefRecord[] { rec });
- }
-
- /**
- * A SNEP Server implementation that accepts PUT requests for all ndef
- * messages and responds to GET requests with acceptable length greater
- * than or equal to 1024.
- */
- final SnepServer.Callback mCallback = new SnepServer.Callback() {
- private static final int GET_LENGTH = 1024;
-
- @Override
- public SnepMessage doPut(NdefMessage msg) {
- return SnepMessage.getSuccessResponse(null);
- }
-
- @Override
- public SnepMessage doGet(int acceptableLength, NdefMessage msg) {
- if (GET_LENGTH <= acceptableLength) {
- return SnepMessage.getSuccessResponse(getSmallNdef());
- } else {
- return SnepMessage.getMessage(SnepMessage.RESPONSE_EXCESS_DATA);
- }
- }
- };
-} \ No newline at end of file
diff --git a/tests/src/com/android/nfc/snep/SnepCustomClientTests.java b/tests/src/com/android/nfc/snep/SnepCustomClientTests.java
deleted file mode 100644
index 4f96aef2..00000000
--- a/tests/src/com/android/nfc/snep/SnepCustomClientTests.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.nfc.snep;
-
-import java.io.IOException;
-
-import com.android.nfc.snep.SnepClient;
-import com.android.nfc.snep.SnepMessage;
-import com.android.nfc.snep.SnepServer;
-
-import android.nfc.NdefMessage;
-import android.nfc.NdefRecord;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.lang.StringBuffer;
-
-/**
- * Tests connectivity to a custom SNEP server, using a physical NFC device.
- */
-public class SnepCustomClientTests extends AndroidTestCase {
- private static final String TAG = "nfcTest";
-
- public void setUp() {
- Log.d(TAG, "Waiting for service to restart...");
- try {
- Thread.sleep(6000);
- } catch (InterruptedException e) {}
- Log.d(TAG, "Running test.");
- }
-
- public void testPutSmall() throws IOException {
- SnepClient client = getSnepClient();
- client.connect();
- client.put(getSmallNdef());
- client.close();
- }
-
- public void testPutLarge() throws IOException {
- SnepClient client = getSnepClient();
- client.connect();
- client.put(getLargeNdef());
- client.close();
- }
-
- public void testPutTwice() throws IOException {
- SnepClient client = getSnepClient();
- client.connect();
- client.put(getSmallNdef());
- client.put(getSmallNdef());
- client.close();
- }
-
- public void testGetSmall() throws IOException {
- SnepClient client = getSnepClient();
- client.connect();
- SnepMessage response = client.get(getSmallNdef());
- assertEquals(SnepMessage.RESPONSE_SUCCESS, response.getField());
- }
-
- private NdefMessage getSmallNdef() {
- NdefRecord rec = new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, NdefRecord.RTD_URI,
- new byte[0], "http://android.com".getBytes());
- return new NdefMessage(new NdefRecord[] { rec });
- }
-
- private NdefMessage getLargeNdef() {
- int size = 3000;
- StringBuffer string = new StringBuffer(size);
- for (int i = 0; i < size; i++) {
- string.append('A' + (i % 26));
- }
- NdefRecord rec = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, "text/plain".getBytes(),
- new byte[0], string.toString().getBytes());
- return new NdefMessage(new NdefRecord[] { rec });
- }
-
- private SnepClient getSnepClient() {
- return new SnepClient(SnepValidationServerTests.SERVICE_NAME);
- }
-}
diff --git a/tests/src/com/android/nfc/snep/SnepDefaultClientTests.java b/tests/src/com/android/nfc/snep/SnepDefaultClientTests.java
deleted file mode 100644
index b8849435..00000000
--- a/tests/src/com/android/nfc/snep/SnepDefaultClientTests.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.nfc.snep;
-
-import java.io.IOException;
-
-import com.android.nfc.snep.SnepClient;
-import com.android.nfc.snep.SnepMessage;
-import com.android.nfc.snep.SnepServer;
-
-import android.nfc.NdefMessage;
-import android.nfc.NdefRecord;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.lang.StringBuffer;
-
-/**
- * Tests connectivity to a default SNEP server, using a physical NFC device.
- */
-public class SnepDefaultClientTests extends AndroidTestCase {
- private static final String TAG = "nfcTest";
-
- public void setUp() {
- Log.d(TAG, "Waiting for service to restart...");
- try {
- Thread.sleep(6000);
- } catch (InterruptedException e) {}
- Log.d(TAG, "Running test.");
- }
-
- public void testPutSmallToDefaultServer() throws IOException {
- SnepClient client = new SnepClient();
- client.connect();
- client.put(getSmallNdef());
- client.close();
- }
-
- public void testPutLargeToDefaultServer() throws IOException {
- SnepClient client = new SnepClient();
- client.connect();
- client.put(getLargeNdef());
- client.close();
- }
-
- public void testPutTwiceToDefaultServer() throws IOException {
- SnepClient client = new SnepClient();
- client.connect();
- client.put(getSmallNdef());
- client.put(getSmallNdef());
- client.close();
- }
-
- public void testGetFail() throws IOException {
- SnepClient client = new SnepClient();
- client.connect();
- SnepMessage response = client.get(getSmallNdef());
- assertEquals(SnepMessage.RESPONSE_NOT_IMPLEMENTED, response.getField());
- }
-
- private NdefMessage getSmallNdef() {
- NdefRecord rec = new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, NdefRecord.RTD_URI,
- new byte[0], "http://android.com".getBytes());
- return new NdefMessage(new NdefRecord[] { rec });
- }
-
- private NdefMessage getLargeNdef() {
- int size = 3000;
- StringBuffer string = new StringBuffer(size);
- for (int i = 0; i < size; i++) {
- string.append('A' + (i % 26));
- }
- NdefRecord rec = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, "text/plain".getBytes(),
- new byte[0], string.toString().getBytes());
- return new NdefMessage(new NdefRecord[] { rec });
- }
-}
diff --git a/tests/src/com/android/nfc/snep/SnepValidationClientTests.java b/tests/src/com/android/nfc/snep/SnepValidationClientTests.java
deleted file mode 100644
index 1b0cedf0..00000000
--- a/tests/src/com/android/nfc/snep/SnepValidationClientTests.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.nfc.snep;
-
-import java.io.IOException;
-
-import com.android.nfc.snep.SnepClient;
-import com.android.nfc.snep.SnepMessage;
-import com.android.nfc.snep.SnepServer;
-
-import android.nfc.NdefMessage;
-import android.nfc.NdefRecord;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.lang.StringBuffer;
-
-/**
- * Tests connectivity to a custom SNEP server, using a physical NFC device.
- */
-public class SnepValidationClientTests extends AndroidTestCase {
- private static final String TAG = "nfcTest";
-
- private static final int FRAGMENT_LENGTH = 50;
-
- public static final String SERVICE_NAME = SnepValidationServerTests.SERVICE_NAME;
-
- public void setUp() {
- Log.d(TAG, "Waiting for service to restart...");
- try {
- Thread.sleep(8000);
- } catch (InterruptedException e) {
- }
- Log.d(TAG, "Running test.");
- }
-
- public void testNonFragmented() throws IOException {
- try {
- SnepClient client = getSnepClient();
- NdefMessage msg = getSmallNdef();
- Log.d(TAG, "Connecting to service " + SERVICE_NAME + "...");
- client.connect();
- Log.d(TAG, "Putting ndef message...");
- client.put(msg);
-
- Log.d(TAG, "Getting ndef message...");
- byte[] responseBytes = client.get(msg).getNdefMessage().toByteArray();
- int i = 0;
- byte[] msgBytes = msg.toByteArray();
- Log.d(TAG, "Done. Checking " + msgBytes.length + " bytes.");
- for (byte b : msgBytes) {
- assertEquals(b, responseBytes[i++]);
- }
- Log.d(TAG, "Closing client.");
- client.close();
- } catch (IOException e) {
- Log.d(TAG, "Test failed.", e);
- throw e;
- }
- }
-
- public void testFragmented() throws IOException {
- try {
- SnepClient client = getSnepClient();
- NdefMessage msg = getLargeNdef();
- Log.d(TAG, "Connecting to service " + SERVICE_NAME + "...");
- client.connect();
- Log.d(TAG, "Putting ndef message of size " + msg.toByteArray().length + "...");
- client.put(msg);
-
- Log.d(TAG, "Getting ndef message...");
- byte[] responseBytes = client.get(msg).getNdefMessage().toByteArray();
- int i = 0;
- byte[] msgBytes = msg.toByteArray();
- Log.d(TAG, "Done. Checking " + msgBytes.length + " bytes.");
- for (byte b : msgBytes) {
- assertEquals(b, responseBytes[i++]);
- }
- client.close();
- } catch (IOException e) {
- Log.d(TAG, "Error running fragmented", e);
- throw e;
- }
- }
-
- public void testMultipleNdef() throws IOException {
- try {
- SnepClient client = getSnepClient();
- Log.d(TAG, "Connecting to service " + SERVICE_NAME + "...");
- client.connect();
-
- NdefMessage msgA = getSmallNdef();
- NdefMessage msgB = getLargeNdef();
-
- Log.d(TAG, "Putting ndef message A...");
- client.put(msgA);
- Log.d(TAG, "Putting ndef message B...");
- client.put(msgB);
-
- byte[] responseBytes;
- byte[] msgBytes;
- int i;
-
- Log.d(TAG, "Getting ndef message A...");
- responseBytes = client.get(msgA).getNdefMessage().toByteArray();
- i = 0;
- msgBytes = msgA.toByteArray();
- Log.d(TAG, "Done. Checking " + msgBytes.length + " bytes.");
- for (byte b : msgBytes) {
- assertEquals(b, responseBytes[i++]);
- }
-
- Log.d(TAG, "Getting ndef message B...");
- responseBytes = client.get(msgB).getNdefMessage().toByteArray();
- i = 0;
- msgBytes = msgB.toByteArray();
- Log.d(TAG, "Done. Checking " + msgBytes.length + " bytes.");
- for (byte b : msgBytes) {
- assertEquals(b, responseBytes[i++]);
- }
-
- Log.d(TAG, "Closing client.");
- client.close();
- } catch (IOException e) {
- Log.d(TAG, "Test failed.", e);
- throw e;
- }
- }
-
- public void testUnavailable() throws IOException {
- try {
- SnepClient client = getSnepClient();
- NdefMessage msg = getSmallNdef();
- Log.d(TAG, "Connecting to service " + SERVICE_NAME + "...");
- client.connect();
-
- Log.d(TAG, "Getting ndef message...");
- SnepMessage response = client.get(msg);
- assertEquals(SnepMessage.RESPONSE_NOT_FOUND, response.getField());
- Log.d(TAG, "Closing client.");
- client.close();
- } catch (IOException e) {
- Log.d(TAG, "Test failed.", e);
- throw e;
- }
- }
-
- public void testUndeliverable() throws IOException {
- try {
- SnepClient client = new SnepClient(SERVICE_NAME, 100, FRAGMENT_LENGTH);
- NdefMessage msg = getLargeNdef();
- Log.d(TAG, "Connecting to service " + SERVICE_NAME + "...");
- client.connect();
- Log.d(TAG, "Putting ndef message of size " + msg.toByteArray().length + "...");
- client.put(msg);
-
- Log.d(TAG, "Getting ndef message...");
- SnepMessage response = client.get(msg);
- assertEquals(SnepMessage.RESPONSE_EXCESS_DATA, response.getField());
- client.close();
- } catch (IOException e) {
- Log.d(TAG, "Error running fragmented", e);
- throw e;
- }
- }
-
- private NdefMessage getSmallNdef() {
- NdefRecord rec = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI,
- new byte[] { 'A' }, "http://android.com".getBytes());
- return new NdefMessage(new NdefRecord[] { rec });
- }
-
- private NdefMessage getLargeNdef() {
- int size = 500;
- StringBuffer string = new StringBuffer(size);
- for (int i = 0; i < size; i++) {
- string.append('A' + (i % 26));
- }
- NdefRecord rec = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, "text/plain".getBytes(),
- new byte[] { 'B' }, string.toString().getBytes());
- return new NdefMessage(new NdefRecord[] { rec });
- }
-
- private SnepClient getSnepClient() {
- return new SnepClient(SERVICE_NAME, FRAGMENT_LENGTH);
- }
-}
diff --git a/tests/src/com/android/nfc/snep/SnepValidationServerTests.java b/tests/src/com/android/nfc/snep/SnepValidationServerTests.java
deleted file mode 100644
index 1a35764b..00000000
--- a/tests/src/com/android/nfc/snep/SnepValidationServerTests.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.nfc.snep;
-
-import java.io.IOException;
-
-import com.android.nfc.snep.SnepClient;
-import com.android.nfc.snep.SnepMessage;
-import com.android.nfc.snep.SnepServer;
-
-import android.nfc.NdefMessage;
-import android.nfc.NdefRecord;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.lang.StringBuffer;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Uses the Android testing instrumentation framework to run a listening SNEP
- * server.
- */
-public class SnepValidationServerTests extends AndroidTestCase implements SnepServer.Callback {
- static final int SERVICE_SAP = 0x11;
- static final String SERVICE_NAME = "urn:nfc:xsn:nfc-forum.org:snep-validation";
- private static final String TAG = "nfcTest";
-
- private Map<ByteArrayWrapper, NdefMessage> mStoredNdef =
- new HashMap<ByteArrayWrapper, NdefMessage>();
- private static final ByteArrayWrapper DEFAULT_NDEF = new ByteArrayWrapper(new byte[] {});
-
- public void setUp() {
- Log.d(TAG, "Waiting for service to restart...");
- try {
- Thread.sleep(6000);
- } catch (InterruptedException e) {
- }
- Log.d(TAG, "Running test.");
- }
-
- public void testStartSnepServer() throws IOException {
- SnepServer server = new SnepServer(SERVICE_NAME, SERVICE_SAP, this);
- server.start();
-
- try {
- Thread.sleep(24 * 60 * 60 * 1000);
- } catch (InterruptedException e) {
-
- }
- }
-
- private NdefMessage getSmallNdef() {
- NdefRecord rec = new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, NdefRecord.RTD_URI,
- new byte[0], "http://android.com".getBytes());
- return new NdefMessage(new NdefRecord[] { rec });
- }
-
- private NdefMessage getLargeNdef() {
- int size = 3000;
- StringBuffer string = new StringBuffer(size);
- for (int i = 0; i < size; i++) {
- string.append('A' + (i % 26));
- }
- NdefRecord rec = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, "text/plain".getBytes(),
- new byte[0], string.toString().getBytes());
- return new NdefMessage(new NdefRecord[] { rec });
- }
-
- static class ByteArrayWrapper {
- private final byte[] data;
-
- public ByteArrayWrapper(byte[] data) {
- if (data == null) {
- throw new NullPointerException();
- }
- this.data = data;
- }
-
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof ByteArrayWrapper)) {
- return false;
- }
- return Arrays.equals(data, ((ByteArrayWrapper) other).data);
- }
-
- @Override
- public int hashCode() {
- return Arrays.hashCode(data);
- }
- }
-
- @Override
- public SnepMessage doPut(NdefMessage msg) {
- Log.d(TAG, "doPut()");
- NdefRecord record = msg.getRecords()[0];
- ByteArrayWrapper id = (record.getId().length > 0) ?
- new ByteArrayWrapper(record.getId()) : DEFAULT_NDEF;
- mStoredNdef.put(id, msg);
- return SnepMessage.getMessage(SnepMessage.RESPONSE_SUCCESS);
- }
-
- @Override
- public SnepMessage doGet(int acceptableLength, NdefMessage msg) {
- Log.d(TAG, "doGet()");
- NdefRecord record = msg.getRecords()[0];
- ByteArrayWrapper id = (record.getId().length > 0) ?
- new ByteArrayWrapper(record.getId()) : DEFAULT_NDEF;
- NdefMessage result = mStoredNdef.get(id);
-
- if (result == null) {
- return SnepMessage.getMessage(SnepMessage.RESPONSE_NOT_FOUND);
- }
- if (acceptableLength < result.toByteArray().length) {
- return SnepMessage.getMessage(SnepMessage.RESPONSE_EXCESS_DATA);
- }
- return SnepMessage.getSuccessResponse(result);
- }
-}
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
new file mode 100644
index 00000000..d1556ddf
--- /dev/null
+++ b/tests/unit/Android.bp
@@ -0,0 +1,32 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "NfcNciInstrumentationTests",
+
+ certificate: "platform",
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "mockito-target",
+ "truth-prebuilt",
+ ],
+
+ // Include all test java files.
+ srcs: ["src/**/*.java"],
+
+ platform_apis: true,
+
+ test_suites: ["device-tests"],
+
+ instrumentation_for: "NfcNci",
+}
diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml
new file mode 100644
index 00000000..cf8895dc
--- /dev/null
+++ b/tests/unit/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.nfc.tests.unit">
+
+ <uses-permission android:name="android.permission.NFC" />
+ <uses-permission android:name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.nfc"
+ android:label="NfcNci Test Cases">
+ </instrumentation>
+
+</manifest>
diff --git a/tests/unit/AndroidTest.xml b/tests/unit/AndroidTest.xml
new file mode 100644
index 00000000..976f2dc8
--- /dev/null
+++ b/tests/unit/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs NfcNci Test Cases.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="NfcNciInstrumentationTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="NfcNciInstrumentationTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.nfc.tests.unit" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/tests/unit/src/com/android/nfc/NfcFeatureFlagTest.java b/tests/unit/src/com/android/nfc/NfcFeatureFlagTest.java
new file mode 100644
index 00000000..348b86a2
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/NfcFeatureFlagTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.util.ArrayUtils;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public final class NfcFeatureFlagTest {
+
+ private static final String TAG = NfcFeatureFlagTest.class.getSimpleName();
+ private Context mContext;
+ private NfcAdapter mNfcAdapter;
+ private boolean mNfcSupported;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ PackageManager pm = mContext.getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_ANY)) {
+ mNfcSupported = false;
+ return;
+ }
+ mNfcSupported = true;
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext);
+ Assert.assertNotNull(mNfcAdapter);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void testIsSecureNfcSupported() {
+ if (!mNfcSupported) return;
+ String[] skuList = mContext.getResources().getStringArray(
+ R.array.config_skuSupportsSecureNfc);
+ String sku = SystemProperties.get("ro.boot.hardware.sku");
+ if (TextUtils.isEmpty(sku) || !ArrayUtils.contains(skuList, sku)) {
+ assertFalse(mNfcAdapter.isSecureNfcSupported());
+ } else {
+ assertTrue(mNfcAdapter.isSecureNfcSupported());
+ }
+ }
+
+ @Test
+ public void testIsControllerAlwaysOnSupported() {
+ if (!mNfcSupported) return;
+ assertThat(mContext.getResources()
+ .getBoolean(R.bool.nfcc_always_on_allowed))
+ .isEqualTo(mNfcAdapter.isControllerAlwaysOnSupported());
+ }
+}
diff --git a/tests/unit/src/com/android/nfc/NfcStateTest.java b/tests/unit/src/com/android/nfc/NfcStateTest.java
new file mode 100644
index 00000000..26f93a4b
--- /dev/null
+++ b/tests/unit/src/com/android/nfc/NfcStateTest.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.ControllerAlwaysOnListener;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+public final class NfcStateTest {
+
+ private static final String TAG = NfcStateTest.class.getSimpleName();
+ private static final int MAX_TIMEOUT_MS = 20000;
+ private Context mContext;
+ private NfcAdapter mNfcAdapter;
+ private BroadcastReceiver mAdapterStateChangedReceiver;
+ private int mState;
+ private boolean mIsAlwaysOnEnabled;
+ private boolean mNfcSupported;
+ private ControllerAlwaysOnListener mListener;
+
+ class SynchronousExecutor implements Executor {
+ public void execute(Runnable r) {
+ r.run();
+ }
+ }
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
+ mAdapterStateChangedReceiver = new AdapterStateChangedReceiver();
+ mContext.registerReceiver(mAdapterStateChangedReceiver, filter);
+ PackageManager pm = mContext.getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_ANY)) {
+ mNfcSupported = false;
+ return;
+ }
+ mNfcSupported = true;
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext);
+ Assert.assertNotNull(mNfcAdapter);
+ if (mNfcAdapter.isEnabled()) {
+ mState = NfcAdapter.STATE_ON;
+ } else {
+ mState = NfcAdapter.STATE_OFF;
+ }
+ if (mNfcAdapter.isControllerAlwaysOnSupported()) {
+ mListener = new AlwaysOnStateListener();
+ mNfcAdapter.registerControllerAlwaysOnListener(new SynchronousExecutor(),
+ mListener);
+ mIsAlwaysOnEnabled = mNfcAdapter.isControllerAlwaysOn();
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mContext.unregisterReceiver(mAdapterStateChangedReceiver);
+ if (mNfcAdapter.isControllerAlwaysOnSupported()) {
+ mNfcAdapter.unregisterControllerAlwaysOnListener(mListener);
+ }
+ }
+
+ @Test
+ public void testSetControllerAlwaysOnTrueFromFalseWhenDisabled() {
+ if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
+ if (mNfcAdapter.isControllerAlwaysOn()) {
+ mNfcAdapter.setControllerAlwaysOn(false);
+ wait_for_always_on(false);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
+ }
+ if (mNfcAdapter.isEnabled()) {
+ mNfcAdapter.disable();
+ wait_for_state(NfcAdapter.STATE_OFF);
+ }
+ mNfcAdapter.setControllerAlwaysOn(true);
+ wait_for_always_on(true);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
+ }
+
+ @Test
+ public void testSetControllerAlwaysOnFalseFromTrueWhenDisabled() {
+ if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
+ if (!mNfcAdapter.isControllerAlwaysOn()) {
+ mNfcAdapter.setControllerAlwaysOn(true);
+ wait_for_always_on(true);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
+ }
+ if (mNfcAdapter.isEnabled()) {
+ mNfcAdapter.disable();
+ wait_for_state(NfcAdapter.STATE_OFF);
+ }
+ mNfcAdapter.setControllerAlwaysOn(false);
+ wait_for_always_on(false);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
+ }
+
+ @Test
+ public void testSetControllerAlwaysOnFalseFromFalseWhenDisabled() {
+ if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
+ if (!mNfcAdapter.isControllerAlwaysOn()) {
+ mNfcAdapter.setControllerAlwaysOn(false);
+ wait_for_always_on(false);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
+ }
+ if (mNfcAdapter.isEnabled()) {
+ mNfcAdapter.disable();
+ wait_for_state(NfcAdapter.STATE_OFF);
+ }
+ mNfcAdapter.setControllerAlwaysOn(false);
+ wait_for_always_on(false);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
+ }
+
+ @Test
+ public void testSetControllerAlwaysOnTrueFromTrueWhenDisabled() {
+ if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
+ if (!mNfcAdapter.isControllerAlwaysOn()) {
+ mNfcAdapter.setControllerAlwaysOn(true);
+ wait_for_always_on(true);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
+ }
+ if (mNfcAdapter.isEnabled()) {
+ mNfcAdapter.disable();
+ wait_for_state(NfcAdapter.STATE_OFF);
+ }
+ mNfcAdapter.setControllerAlwaysOn(true);
+ wait_for_always_on(true);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
+ }
+
+ @Test
+ public void testSetControllerAlwaysOnTrueFromFalseWhenEnabled() {
+ if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
+ if (mNfcAdapter.isControllerAlwaysOn()) {
+ mNfcAdapter.setControllerAlwaysOn(false);
+ wait_for_always_on(false);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
+ }
+ if (!mNfcAdapter.isEnabled()) {
+ mNfcAdapter.enable();
+ wait_for_state(NfcAdapter.STATE_ON);
+ }
+ mNfcAdapter.setControllerAlwaysOn(true);
+ wait_for_always_on(true);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
+ }
+
+ @Test
+ public void testSetAlwaysOnFalseFromTrueWhenEnabled() {
+ if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
+ if (!mNfcAdapter.isControllerAlwaysOn()) {
+ mNfcAdapter.setControllerAlwaysOn(true);
+ wait_for_always_on(true);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
+ }
+ if (!mNfcAdapter.isEnabled()) {
+ mNfcAdapter.enable();
+ wait_for_state(NfcAdapter.STATE_ON);
+ }
+ mNfcAdapter.setControllerAlwaysOn(false);
+ wait_for_always_on(false);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
+ }
+
+ @Test
+ public void testSetControllerAlwaysOnFalseFromFalseWhenEnabled() {
+ if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
+ if (!mNfcAdapter.isControllerAlwaysOn()) {
+ mNfcAdapter.setControllerAlwaysOn(false);
+ wait_for_always_on(false);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
+ }
+ if (!mNfcAdapter.isEnabled()) {
+ mNfcAdapter.enable();
+ wait_for_state(NfcAdapter.STATE_ON);
+ }
+ mNfcAdapter.setControllerAlwaysOn(false);
+ wait_for_always_on(false);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
+ }
+
+ @Test
+ public void testSetControllerAlwaysOnTrueFromTrueWhenEnabled() {
+ if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
+ if (!mNfcAdapter.isControllerAlwaysOn()) {
+ mNfcAdapter.setControllerAlwaysOn(true);
+ wait_for_always_on(true);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
+ }
+ if (!mNfcAdapter.isEnabled()) {
+ mNfcAdapter.enable();
+ wait_for_state(NfcAdapter.STATE_ON);
+ }
+ mNfcAdapter.setControllerAlwaysOn(true);
+ wait_for_always_on(true);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
+ }
+
+ @Test
+ public void testEnableWhenSetControllerAlwaysOnTrueAndDisabled() {
+ if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
+ if (!mNfcAdapter.isControllerAlwaysOn()) {
+ mNfcAdapter.setControllerAlwaysOn(true);
+ wait_for_always_on(true);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
+ }
+ if (mNfcAdapter.isEnabled()) {
+ mNfcAdapter.disable();
+ wait_for_state(NfcAdapter.STATE_OFF);
+ assertThat(mNfcAdapter.isEnabled()).isEqualTo(false);
+ }
+ mNfcAdapter.enable();
+ wait_for_state(NfcAdapter.STATE_ON);
+ assertThat(mState).isEqualTo(NfcAdapter.STATE_ON);
+ assertThat(mNfcAdapter.isEnabled()).isEqualTo(true);
+ }
+
+ @Test
+ public void testDisableWhenSetControllerAlwaysOnTrueAndEnabled() {
+ if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
+ if (!mNfcAdapter.isControllerAlwaysOn()) {
+ mNfcAdapter.setControllerAlwaysOn(true);
+ wait_for_always_on(true);
+ assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
+ }
+ if (!mNfcAdapter.isEnabled()) {
+ mNfcAdapter.enable();
+ wait_for_state(NfcAdapter.STATE_ON);
+ assertThat(mNfcAdapter.isEnabled()).isEqualTo(true);
+ }
+ mNfcAdapter.disable();
+ wait_for_state(NfcAdapter.STATE_OFF);
+ assertThat(mState).isEqualTo(NfcAdapter.STATE_OFF);
+ assertThat(mNfcAdapter.isEnabled()).isEqualTo(false);
+ }
+
+ @Test
+ public void testDisableWhenEnabled() {
+ if (!mNfcSupported) return;
+ if (!mNfcAdapter.isEnabled()) {
+ mNfcAdapter.enable();
+ wait_for_state(NfcAdapter.STATE_ON);
+ }
+ mNfcAdapter.disable();
+ wait_for_state(NfcAdapter.STATE_OFF);
+ assertThat(mState).isEqualTo(NfcAdapter.STATE_OFF);
+ assertThat(mNfcAdapter.isEnabled()).isEqualTo(false);
+ }
+
+ @Test
+ public void testEnableWhenDisabled() {
+ if (!mNfcSupported) return;
+ if (mNfcAdapter.isEnabled()) {
+ mNfcAdapter.disable();
+ wait_for_state(NfcAdapter.STATE_OFF);
+ }
+ mNfcAdapter.enable();
+ wait_for_state(NfcAdapter.STATE_ON);
+ assertThat(mState).isEqualTo(NfcAdapter.STATE_ON);
+ assertThat(mNfcAdapter.isEnabled()).isEqualTo(true);
+ }
+
+ @Test
+ public void testDisableWhenDisabled() {
+ if (!mNfcSupported) return;
+ if (mNfcAdapter.isEnabled()) {
+ mNfcAdapter.disable();
+ wait_for_state(NfcAdapter.STATE_OFF);
+ }
+ mNfcAdapter.disable();
+ wait_for_state(NfcAdapter.STATE_OFF);
+ assertThat(mState).isEqualTo(NfcAdapter.STATE_OFF);
+ assertThat(mNfcAdapter.isEnabled()).isEqualTo(false);
+ }
+
+ @Test
+ public void testEnableWhenEnabled() {
+ if (!mNfcSupported) return;
+ if (!mNfcAdapter.isEnabled()) {
+ mNfcAdapter.enable();
+ wait_for_state(NfcAdapter.STATE_ON);
+ }
+ mNfcAdapter.enable();
+ wait_for_state(NfcAdapter.STATE_ON);
+ assertThat(mState).isEqualTo(NfcAdapter.STATE_ON);
+ assertThat(mNfcAdapter.isEnabled()).isEqualTo(true);
+ }
+
+ private class AdapterStateChangedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (NfcAdapter.ACTION_ADAPTER_STATE_CHANGED.equals(intent.getAction())) {
+ mState = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE,
+ NfcAdapter.STATE_OFF);
+ Log.i(TAG, "mState = " + mState);
+ }
+ }
+ }
+ private class AlwaysOnStateListener implements ControllerAlwaysOnListener {
+ @Override
+ public void onControllerAlwaysOnChanged(boolean isEnabled) {
+ Log.i(TAG, "onControllerAlwaysOnChanged, mIsAlwaysOnEnabled = " + isEnabled);
+ mIsAlwaysOnEnabled = isEnabled;
+ }
+ }
+ private void wait_for_state(int targetState) {
+ int duration = 100;
+ for (int i = 0; i < MAX_TIMEOUT_MS / duration; i++) {
+ msleep(duration);
+ if (mState == targetState) break;
+ }
+ }
+ private void wait_for_always_on(boolean isEnabled) {
+ int duration = 1000;
+ for (int i = 0; i < MAX_TIMEOUT_MS / duration; i++) {
+ msleep(duration);
+ if (isEnabled == mIsAlwaysOnEnabled) break;
+ }
+ }
+
+ private void msleep(int millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ }
+ }
+}