summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartijn Coenen <maco@google.com>2014-09-08 21:34:22 -0700
committerMartijn Coenen <maco@google.com>2014-09-08 21:37:59 -0700
commitc19c065b6d0e1a088780c4dca27a1404d5926765 (patch)
tree80fc306e820caea226517ab2c9aa4454db61d2d9
parentd0393d4d0f421623fb52328fda5ab6a3d93a1a84 (diff)
downloadandroid_packages_apps_Nfc-c19c065b6d0e1a088780c4dca27a1404d5926765.tar.gz
android_packages_apps_Nfc-c19c065b6d0e1a088780c4dca27a1404d5926765.tar.bz2
android_packages_apps_Nfc-c19c065b6d0e1a088780c4dca27a1404d5926765.zip
Reinstate RF field events.
These are generic enough to maintain in AOSP, and can still be protected by nfcee_access.xml like they used to be. Bug: 16653617 Change-Id: Id604fcc45d4263b24fea2f16ca6a24e25f8580c3
-rw-r--r--etc/sample_nfcee_access.xml45
-rwxr-xr-xnci/jni/NativeNfcManager.cpp22
-rwxr-xr-xnci/src/com/android/nfc/dhimpl/NativeNfcManager.java9
-rw-r--r--nxp/jni/com_android_nfc_NativeNfcManager.cpp10
-rwxr-xr-xnxp/src/com/android/nfc/dhimpl/NativeNfcManager.java8
-rw-r--r--src/com/android/nfc/DeviceHost.java4
-rwxr-xr-xsrc/com/android/nfc/NfcService.java105
-rw-r--r--src/com/android/nfc/NfceeAccessControl.java289
8 files changed, 484 insertions, 8 deletions
diff --git a/etc/sample_nfcee_access.xml b/etc/sample_nfcee_access.xml
new file mode 100644
index 00000000..d6c8707d
--- /dev/null
+++ b/etc/sample_nfcee_access.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- The built in list of signatures and package names that are allowed
+ access to the NFCEE (Secure Element).
+
+ Format:
+ <signer android:signature="SIGNATURE">
+ <package android:name="PACKAGE_NAME" />
+ ...
+ </signer>
+ ...
+
+ Rules:
+ SIGNATURE is a hex encoded X.509 certificate
+ See Debug section to generate hex encoded certificates.
+ PACKAGE_NAME is a Android package name
+ Zero or more signer tags are allowed.
+ Each signer tag must have one android:signature.
+ Zero or more package tags are allowed per signer.
+ Each package tag must have one android:name.
+ If a signer has zero package tags, then ANY application with the
+ specified certificate is granted NFCEE access.
+ If a signer has one or more package tags, then an application signed
+ with the specified certificate must have a package name that is an
+ exact match of one of the specified package names to be granted
+ NFCEE access.
+ Duplicate signer tags are not allowed.
+ Duplicate package tags for a single signer are not allowed.
+
+ Example:
+ In this example, any application from the first signer is granted
+ access, but only applications with the specified package names
+ are granted access for the second signer.
+ <signer android:signature="308201c53082012ea00302010202044ebb27cc300d06092a864886f70d01010505003026310f300d060355040a1306476f6f676c65311330110603550403130a4e69636b2050656c6c793020170d3131313131303031323432385a180f32313131313031373031323432385a3026310f300d060355040a1306476f6f676c65311330110603550403130a4e69636b2050656c6c7930819f300d06092a864886f70d010101050003818d00308189028181008d43e546b3f5572707a095ced120d8f06781fa162bcf0ffa0ed0ecb48eb90ed009f65a5a1afd69fb4d38cf24e931b69b061741b8c7ca9f785ba59509e883f5a308f5e2da3c496bb362a2229da8f95f08a92f7f94c829c56e78a34e5147d138d0be0671cb5b7caceaffae6199ba544496a7645e7df3c9f02c5ac156eb0501584f0203010001300d06092a864886f70d0101050500038181003511bcb73651a7927db71ad76e4f6dc5ba121c941ae0fd4dfe519aae8775520b204a5e3cdad2c61ad41aff2c510dbe3376a7578d8aba00f35633e1ae72c91ec83d80eac6f5f081fb8361c6c30c47b0a17932d859d7e991e02cba410a82b35234b2b1bc859e50fe308bf9b64b71a8046777300c07ead159287c187b8865e23f23" />
+ <signer android:signature="3082044c30820334a003020102020900de7695041d7650c0300d06092a864886f70d01010505003077310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e205669657731143012060355040a130b476f6f676c6520496e632e3110300e060355040b1307416e64726f6964311330110603550403130a476f6f676c65204e4643301e170d3131303332343031303332345a170d3338303830393031303332345a3077310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e205669657731143012060355040a130b476f6f676c6520496e632e3110300e060355040b1307416e64726f6964311330110603550403130a476f6f676c65204e464330820120300d06092a864886f70d01010105000382010d00308201080282010100e6ff3defe92aa10d71eb0fa6408bc036b7e243eeed68a6a4763dc7a52a31757cdac61fe510bb73c716e4000104265b347fcecef4c42bf1e1379dd0a876f028227fbbc1f9bdd5d713b2f6a935a379d2cba9c96f92d2d0787c11f1eb19548008a6a072b34b91836cfa0ae1276780e9007530166986a11c9cef46cef7c704806dde9431fb60284d120ab0e7de1d633f07687d468c51139afffdc6bc9a207ca904b8be1da0aa7b4e97756f43606488be5cae3c68e8bb7942cdf51607c930a2fcda655b75d0759cba89ad06e739bd0ba29b1f404296c2c0a85a847f5ab0d067c6c3ec9c49212042ac63a7e53b546c65b46080b4e3e680e23e1f77cfe7f6de744b1a65020103a381dc3081d9301d0603551d0e04160414a2e89064b05d08865c34db930a9d840050117aec3081a90603551d230481a130819e8014a2e89064b05d08865c34db930a9d840050117aeca17ba4793077310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e205669657731143012060355040a130b476f6f676c6520496e632e3110300e060355040b1307416e64726f6964311330110603550403130a476f6f676c65204e4643820900de7695041d7650c0300c0603551d13040530030101ff300d06092a864886f70d010105050003820101003771870ce87c3c52ea84899230c6e962d94b4d5f1293c25d88261541fd90b5555d1285cef3b8312c3f5df691a8aae04cb981b305e427fd1d2d9e1987e1d29078f13c8452990f1821980263d8d4bd36519348d8d8ba26d8b99fbf09f5fd3ebb0ea3c2f0c9376f1e1fca76f3a6a405429d081b752a7a90b756e9ab44da41abc8e1e8f88ac2758da743fb73e650719a57840ccb6b7add21b99fc681e456e1872c223d5c074adf55f6abda268c2d8b64ea0a8845eecd968f92b493127e75c753c3ff30cbc678b51c9f52961472f17da20a0dc6274aa2463434c1a9b614df697d8ff5ca8101e7a25c7db3fb055d65569c04b01d389cabba57b3a1703ec2e74a88d334">
+ <package android:name="com.foo.my.awesome.wallet" />
+ <package android:name="com.foo.my.awesome.wallet.tests" />
+ </signer>
+
+ Debug:
+ Including the tag <debug /> will print to logcat the
+ hex encoded signature of applications that are denied NFCEE
+ access, for easy cut-and-paste into this file.
+ -->
+</resources>
diff --git a/nci/jni/NativeNfcManager.cpp b/nci/jni/NativeNfcManager.cpp
index fa1b2d21..57dcb809 100755
--- a/nci/jni/NativeNfcManager.cpp
+++ b/nci/jni/NativeNfcManager.cpp
@@ -84,6 +84,8 @@ namespace android
jmethodID gCachedNfcManagerNotifyHostEmuActivated;
jmethodID gCachedNfcManagerNotifyHostEmuData;
jmethodID gCachedNfcManagerNotifyHostEmuDeactivated;
+ jmethodID gCachedNfcManagerNotifyRfFieldActivated;
+ jmethodID gCachedNfcManagerNotifyRfFieldDeactivated;
const char* gNativeP2pDeviceClassName = "com/android/nfc/dhimpl/NativeP2pDevice";
const char* gNativeLlcpServiceSocketClassName = "com/android/nfc/dhimpl/NativeLlcpServiceSocket";
const char* gNativeLlcpConnectionlessSocketClassName = "com/android/nfc/dhimpl/NativeLlcpConnectionlessSocket";
@@ -562,6 +564,11 @@ static jboolean nfcManager_initNativeStruc (JNIEnv* e, jobject o)
gCachedNfcManagerNotifyHostEmuDeactivated = e->GetMethodID(cls.get(),
"notifyHostEmuDeactivated", "()V");
+ gCachedNfcManagerNotifyRfFieldActivated = e->GetMethodID(cls.get(),
+ "notifyRfFieldActivated", "()V");
+ gCachedNfcManagerNotifyRfFieldDeactivated = e->GetMethodID(cls.get(),
+ "notifyRfFieldDeactivated", "()V");
+
if (nfc_jni_cache_object(e, gNativeNfcTagClassName, &(nat->cached_NfcTag)) == -1)
{
ALOGE ("%s: fail cache NativeNfcTag", __FUNCTION__);
@@ -647,6 +654,21 @@ void nfaDeviceManagementCallback (UINT8 dmEvent, tNFA_DM_CBACK_DATA* eventData)
case NFA_DM_RF_FIELD_EVT:
ALOGD ("%s: NFA_DM_RF_FIELD_EVT; status=0x%X; field status=%u", __FUNCTION__,
eventData->rf_field.status, eventData->rf_field.rf_field_status);
+ if (!sP2pActive && eventData->rf_field.status == NFA_STATUS_OK)
+ {
+ struct nfc_jni_native_data *nat = getNative(NULL, NULL);
+ JNIEnv* e = NULL;
+ ScopedAttach attach(nat->vm, &e);
+ if (e == NULL)
+ {
+ ALOGE ("jni env is null");
+ return;
+ }
+ if (eventData->rf_field.rf_field_status == NFA_DM_RF_FIELD_ON)
+ e->CallVoidMethod (nat->manager, android::gCachedNfcManagerNotifyRfFieldActivated);
+ else
+ e->CallVoidMethod (nat->manager, android::gCachedNfcManagerNotifyRfFieldDeactivated);
+ }
break;
case NFA_DM_NFCC_TRANSPORT_ERR_EVT:
diff --git a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
index 080bca01..d327f62a 100755
--- a/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
+++ b/nci/src/com/android/nfc/dhimpl/NativeNfcManager.java
@@ -335,4 +335,13 @@ public class NativeNfcManager implements DeviceHost {
private void notifyHostEmuDeactivated() {
mListener.onHostCardEmulationDeactivated();
}
+
+ private void notifyRfFieldActivated() {
+ mListener.onRemoteFieldActivated();
+ }
+
+ private void notifyRfFieldDeactivated() {
+ mListener.onRemoteFieldDeactivated();
+ }
+
}
diff --git a/nxp/jni/com_android_nfc_NativeNfcManager.cpp b/nxp/jni/com_android_nfc_NativeNfcManager.cpp
index 489a615b..c80141e6 100644
--- a/nxp/jni/com_android_nfc_NativeNfcManager.cpp
+++ b/nxp/jni/com_android_nfc_NativeNfcManager.cpp
@@ -50,6 +50,8 @@ static jmethodID cached_NfcManager_notifyLlcpLinkActivation;
static jmethodID cached_NfcManager_notifyLlcpLinkDeactivated;
static jmethodID cached_NfcManager_notifyTargetDeselected;
+static jmethodID cached_NfcManager_notifyRfFieldActivated;
+static jmethodID cached_NfcManager_notifyRfFieldDeactivated;
namespace android {
phLibNfc_Handle storedHandle = 0;
@@ -1169,11 +1171,13 @@ static void nfc_jni_transaction_callback(void *context,
case phLibNfc_eSE_EvtFieldOn:
{
TRACE("> SE EVT_FIELD_ON");
+ e->CallVoidMethod(nat->manager, cached_NfcManager_notifyRfFieldActivated);
}break;
case phLibNfc_eSE_EvtFieldOff:
{
TRACE("> SE EVT_FIELD_OFF");
+ e->CallVoidMethod(nat->manager, cached_NfcManager_notifyRfFieldDeactivated);
}break;
default:
@@ -1605,6 +1609,12 @@ static jboolean com_android_nfc_NfcManager_init_native_struc(JNIEnv *e, jobject
cached_NfcManager_notifyLlcpLinkDeactivated = e->GetMethodID(cls,
"notifyLlcpLinkDeactivated","(Lcom/android/nfc/dhimpl/NativeP2pDevice;)V");
+ cached_NfcManager_notifyRfFieldActivated = e->GetMethodID(cls,
+ "notifyRfFieldActivated", "()V");
+
+ cached_NfcManager_notifyRfFieldDeactivated = e->GetMethodID(cls,
+ "notifyRfFieldDeactivated", "()V");
+
if(nfc_jni_cache_object(e,"com/android/nfc/dhimpl/NativeNfcTag",&(nat->cached_NfcTag)) == -1)
{
ALOGD("Native Structure initialization failed");
diff --git a/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java b/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java
index 18336233..cc73db3b 100755
--- a/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java
+++ b/nxp/src/com/android/nfc/dhimpl/NativeNfcManager.java
@@ -373,4 +373,12 @@ public class NativeNfcManager implements DeviceHost {
private void notifyLlcpLinkDeactivated(NativeP2pDevice device) {
mListener.onLlcpLinkDeactivated(device);
}
+
+ private void notifyRfFieldActivated() {
+ mListener.onRemoteFieldActivated();
+ }
+
+ private void notifyRfFieldDeactivated() {
+ mListener.onRemoteFieldDeactivated();
+ }
}
diff --git a/src/com/android/nfc/DeviceHost.java b/src/com/android/nfc/DeviceHost.java
index 58b20696..07710119 100644
--- a/src/com/android/nfc/DeviceHost.java
+++ b/src/com/android/nfc/DeviceHost.java
@@ -43,6 +43,10 @@ public interface DeviceHost {
public void onLlcpLinkDeactivated(NfcDepEndpoint device);
public void onLlcpFirstPacketReceived(NfcDepEndpoint device);
+
+ public void onRemoteFieldActivated();
+
+ public void onRemoteFieldDeactivated();
}
public interface TagEndpoint {
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index a646c246..0b110c77 100755
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -27,6 +27,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources.NotFoundException;
import android.media.AudioManager;
@@ -78,6 +79,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -98,14 +100,16 @@ public class NfcService implements DeviceHostListener {
static final String PREF_AIRPLANE_OVERRIDE = "airplane_override";
static final int MSG_NDEF_TAG = 0;
- static final int MSG_LLCP_LINK_ACTIVATION = 2;
- static final int MSG_LLCP_LINK_DEACTIVATED = 3;
- static final int MSG_MOCK_NDEF = 7;
- static final int MSG_LLCP_LINK_FIRST_PACKET = 15;
- static final int MSG_ROUTE_AID = 16;
- static final int MSG_UNROUTE_AID = 17;
- static final int MSG_COMMIT_ROUTING = 18;
- static final int MSG_INVOKE_BEAM = 19;
+ static final int MSG_LLCP_LINK_ACTIVATION = 1;
+ static final int MSG_LLCP_LINK_DEACTIVATED = 2;
+ static final int MSG_MOCK_NDEF = 3;
+ static final int MSG_LLCP_LINK_FIRST_PACKET = 4;
+ static final int MSG_ROUTE_AID = 5;
+ static final int MSG_UNROUTE_AID = 6;
+ static final int MSG_COMMIT_ROUTING = 7;
+ static final int MSG_INVOKE_BEAM = 8;
+ static final int MSG_RF_FIELD_ACTIVATED = 9;
+ static final int MSG_RF_FIELD_DEACTIVATED = 10;
static final int TASK_ENABLE = 1;
static final int TASK_DISABLE = 2;
@@ -138,6 +142,12 @@ public class NfcService implements DeviceHostListener {
// the Beam animation when called through the share menu.
static final int INVOKE_BEAM_DELAY_MS = 1000;
+ // RF field events as defined in NFC extras
+ public static final String ACTION_RF_FIELD_ON_DETECTED =
+ "com.android.nfc_extras.action.RF_FIELD_ON_DETECTED";
+ public static final String ACTION_RF_FIELD_OFF_DETECTED =
+ "com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED";
+
// for use with playSound()
public static final int SOUND_START = 0;
public static final int SOUND_END = 1;
@@ -157,6 +167,10 @@ public class NfcService implements DeviceHostListener {
new ReaderModeDeathRecipient();
private final NfcUnlockManager mNfcUnlockManager;
+ private final NfceeAccessControl mNfceeAccessControl;
+
+ List<PackageInfo> mInstalledPackages; // cached version of installed packages
+
// fields below are used in multiple threads and protected by synchronized(this)
final HashMap<Integer, Object> mObjectMap = new HashMap<Integer, Object>();
int mScreenState;
@@ -260,6 +274,15 @@ public class NfcService implements DeviceHostListener {
sendMessage(NfcService.MSG_LLCP_LINK_FIRST_PACKET, device);
}
+ @Override
+ public void onRemoteFieldActivated() {
+ sendMessage(NfcService.MSG_RF_FIELD_ACTIVATED, null);
+ }
+
+ public void onRemoteFieldDeactivated() {
+ sendMessage(NfcService.MSG_RF_FIELD_DEACTIVATED, null);
+ }
+
final class ReaderModeParams {
public int flags;
public IAppCallback callback;
@@ -305,6 +328,8 @@ public class NfcService implements DeviceHostListener {
mPrefs = mContext.getSharedPreferences(PREF, Context.MODE_PRIVATE);
mPrefsEditor = mPrefs.edit();
+ mNfceeAccessControl = new NfceeAccessControl(mContext);
+
mState = NfcAdapter.STATE_OFF;
mIsNdefPushEnabled = mPrefs.getBoolean(PREF_NDEF_PUSH_ON, NDEF_PUSH_ON_DEFAULT);
setBeamShareActivityState(mIsNdefPushEnabled);
@@ -331,6 +356,18 @@ public class NfcService implements DeviceHostListener {
registerForAirplaneMode(filter);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null);
+ IntentFilter ownerFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ ownerFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ mContext.registerReceiver(mOwnerReceiver, ownerFilter);
+
+ ownerFilter = new IntentFilter();
+ ownerFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ ownerFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ ownerFilter.addDataScheme("package");
+ mContext.registerReceiver(mOwnerReceiver, ownerFilter);
+
+ updatePackageCache();
+
PackageManager pm = mContext.getPackageManager();
mIsHceCapable = pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION);
if (mIsHceCapable) {
@@ -376,6 +413,14 @@ public class NfcService implements DeviceHostListener {
}
}
+ void updatePackageCache() {
+ PackageManager pm = mContext.getPackageManager();
+ List<PackageInfo> packages = pm.getInstalledPackages(0, UserHandle.USER_OWNER);
+ synchronized (this) {
+ mInstalledPackages = packages;
+ }
+ }
+
/**
* Manages tasks that involve turning on/off the NFC controller.
* <p/>
@@ -1664,12 +1709,37 @@ public class NfcService implements DeviceHostListener {
case MSG_LLCP_LINK_FIRST_PACKET:
mP2pLinkManager.onLlcpFirstPacketReceived();
break;
+ case MSG_RF_FIELD_ACTIVATED:
+ Intent fieldOnIntent = new Intent(ACTION_RF_FIELD_ON_DETECTED);
+ sendNfcEeAccessProtectedBroadcast(fieldOnIntent);
+ break;
+ case MSG_RF_FIELD_DEACTIVATED:
+ Intent fieldOffIntent = new Intent(ACTION_RF_FIELD_OFF_DETECTED);
+ sendNfcEeAccessProtectedBroadcast(fieldOffIntent);
+ break;
default:
Log.e(TAG, "Unknown message received");
break;
}
}
+ private void sendNfcEeAccessProtectedBroadcast(Intent intent) {
+ intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ // Resume app switches so the receivers can start activites without delay
+ mNfcDispatcher.resumeAppSwitches();
+
+ synchronized (this) {
+ for (PackageInfo pkg : mInstalledPackages) {
+ if (pkg != null && pkg.applicationInfo != null) {
+ if (mNfceeAccessControl.check(pkg.applicationInfo)) {
+ intent.setPackage(pkg.packageName);
+ mContext.sendBroadcast(intent);
+ }
+ }
+ }
+ }
+ }
+
private boolean llcpActivated(NfcDepEndpoint device) {
Log.d(TAG, "LLCP Activation message");
@@ -1832,6 +1902,25 @@ public class NfcService implements DeviceHostListener {
}
};
+ private final BroadcastReceiver mOwnerReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_PACKAGE_REMOVED) ||
+ action.equals(Intent.ACTION_PACKAGE_ADDED) ||
+ action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE) ||
+ action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
+ updatePackageCache();
+
+ if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
+ // Clear the NFCEE access cache in case a UID gets recycled
+ mNfceeAccessControl.invalidateCache();
+ }
+ }
+ }
+ };
+
+
/**
* Returns true if airplane mode is currently on
*/
diff --git a/src/com/android/nfc/NfceeAccessControl.java b/src/com/android/nfc/NfceeAccessControl.java
new file mode 100644
index 00000000..e872d284
--- /dev/null
+++ b/src/com/android/nfc/NfceeAccessControl.java
@@ -0,0 +1,289 @@
+/*
+ * 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 java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Environment;
+import android.util.Log;
+
+public class NfceeAccessControl {
+ static final String TAG = "NfceeAccess";
+ static final boolean DBG = true;
+
+ public static final String NFCEE_ACCESS_PATH = "/etc/nfcee_access.xml";
+
+ /**
+ * Map of signatures to valid packages names, as read from nfcee_access.xml.
+ * An empty list of package names indicates that any package
+ * with this signature is allowed.
+ */
+ final HashMap<Signature, String[]> mNfceeAccess; // contents final after onCreate()
+
+ /**
+ * Map from UID to NFCEE access, used as a cache.
+ * Note: if a UID contains multiple packages they must all be
+ * signed with the same certificate so in effect UID == certificate
+ * used to sign the package.
+ */
+ final HashMap<Integer, Boolean> mUidCache; // contents guarded by this
+
+ final Context mContext;
+ final boolean mDebugPrintSignature;
+
+ NfceeAccessControl(Context context) {
+ mContext = context;
+ mNfceeAccess = new HashMap<Signature, String[]>();
+ mUidCache = new HashMap<Integer, Boolean>();
+ mDebugPrintSignature = parseNfceeAccess();
+ }
+
+ /**
+ * Check if the {uid, pkg} combination may use NFCEE.
+ * Also verify with package manager that this {uid, pkg} combination
+ * is valid if it is not cached.
+ */
+ public boolean check(int uid, String pkg) {
+ synchronized (this) {
+ Boolean cached = mUidCache.get(uid);
+ if (cached != null) {
+ return cached;
+ }
+
+ boolean access = false;
+
+ // Ensure the claimed package is present in the calling UID
+ PackageManager pm = mContext.getPackageManager();
+ String[] pkgs = pm.getPackagesForUid(uid);
+ for (String uidPkg : pkgs) {
+ if (uidPkg.equals(pkg)) {
+ // Ensure the package has access permissions
+ if (checkPackageNfceeAccess(pkg)) {
+ access = true;
+ }
+ break;
+ }
+ }
+
+ mUidCache.put(uid, access);
+ return access;
+ }
+ }
+
+ /**
+ * Check if the given ApplicationInfo may use the NFCEE.
+ * Assumes ApplicationInfo came from package manager,
+ * so no need to confirm {uid, pkg} is valid.
+ */
+ public boolean check(ApplicationInfo info) {
+ synchronized (this) {
+ Boolean access = mUidCache.get(info.uid);
+ if (access == null) {
+ access = checkPackageNfceeAccess(info.packageName);
+ mUidCache.put(info.uid, access);
+ }
+ return access;
+ }
+ }
+
+ public void invalidateCache() {
+ synchronized (this) {
+ mUidCache.clear();
+ }
+ }
+
+ /**
+ * Check with package manager if the pkg may use NFCEE.
+ * Does not use cache.
+ */
+ boolean checkPackageNfceeAccess(String pkg) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
+ if (info.signatures == null) {
+ return false;
+ }
+
+ for (Signature s : info.signatures){
+ if (s == null) {
+ continue;
+ }
+ String[] packages = mNfceeAccess.get(s);
+ if (packages == null) {
+ continue;
+ }
+ if (packages.length == 0) {
+ // wildcard access
+ if (DBG) Log.d(TAG, "Granted NFCEE access to " + pkg + " (wildcard)");
+ return true;
+ }
+ for (String p : packages) {
+ if (pkg.equals(p)) {
+ // explicit package access
+ if (DBG) Log.d(TAG, "Granted access to " + pkg + " (explicit)");
+ return true;
+ }
+ }
+ }
+
+ if (mDebugPrintSignature) {
+ Log.w(TAG, "denied NFCEE access for " + pkg + " with signature:");
+ for (Signature s : info.signatures) {
+ if (s != null) {
+ Log.w(TAG, s.toCharsString());
+ }
+ }
+ }
+ } catch (NameNotFoundException e) {
+ // ignore
+ }
+ return false;
+ }
+
+ /**
+ * Parse nfcee_access.xml, populate mNfceeAccess
+ * Policy is to ignore unexpected XML elements and continue processing,
+ * except for obvious errors within a <signer> group since they might cause
+ * package names to by ignored and therefore wildcard access granted
+ * by mistake. Those errors invalidate the entire <signer> group.
+ */
+ boolean parseNfceeAccess() {
+ File file = new File(Environment.getRootDirectory(), NFCEE_ACCESS_PATH);
+ FileReader reader = null;
+ boolean debug = false;
+ try {
+ reader = new FileReader(file);
+ XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+ XmlPullParser parser = factory.newPullParser();
+ parser.setInput(reader);
+
+ int event;
+ ArrayList<String> packages = new ArrayList<String>();
+ Signature signature = null;
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+ while (true) {
+ event = parser.next();
+ String tag = parser.getName();
+ if (event == XmlPullParser.START_TAG && "signer".equals(tag)) {
+ signature = null;
+ packages.clear();
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ if ("android:signature".equals(parser.getAttributeName(i))) {
+ signature = new Signature(parser.getAttributeValue(i));
+ break;
+ }
+ }
+ if (signature == null) {
+ Log.w(TAG, "signer tag is missing android:signature attribute, igorning");
+ continue;
+ }
+ if (mNfceeAccess.containsKey(signature)) {
+ Log.w(TAG, "duplicate signature, ignoring");
+ signature = null;
+ continue;
+ }
+ } else if (event == XmlPullParser.END_TAG && "signer".equals(tag)) {
+ if (signature == null) {
+ Log.w(TAG, "mis-matched signer tag");
+ continue;
+ }
+ mNfceeAccess.put(signature, packages.toArray(new String[0]));
+ packages.clear();
+ } else if (event == XmlPullParser.START_TAG && "package".equals(tag)) {
+ if (signature == null) {
+ Log.w(TAG, "ignoring unnested packge tag");
+ continue;
+ }
+ String name = null;
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ if ("android:name".equals(parser.getAttributeName(i))) {
+ name = parser.getAttributeValue(i);
+ break;
+ }
+ }
+ if (name == null) {
+ Log.w(TAG, "package missing android:name, ignoring signer group");
+ signature = null; // invalidate signer
+ continue;
+ }
+ // check for duplicate package names
+ if (packages.contains(name)) {
+ Log.w(TAG, "duplicate package name in signer group, ignoring");
+ continue;
+ }
+ packages.add(name);
+ } else if (event == XmlPullParser.START_TAG && "debug".equals(tag)) {
+ debug = true;
+ } else if (event == XmlPullParser.END_DOCUMENT) {
+ break;
+ }
+ }
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "failed to load NFCEE access list", e);
+ mNfceeAccess.clear(); // invalidate entire access list
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "could not find " + NFCEE_ACCESS_PATH + ", no NFCEE access allowed");
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to load NFCEE access list", e);
+ mNfceeAccess.clear(); // invalidate entire access list
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e2) { }
+ }
+ }
+ Log.i(TAG, "read " + mNfceeAccess.size() + " signature(s) for NFCEE access");
+ return debug;
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("mNfceeAccess=");
+ for (Signature s : mNfceeAccess.keySet()) {
+ pw.printf("\t%s [", s.toCharsString());
+ String[] ps = mNfceeAccess.get(s);
+ for (String p : ps) {
+ pw.printf("%s, ", p);
+ }
+ pw.println("]");
+ }
+ synchronized (this) {
+ pw.println("mNfceeUidCache=");
+ for (Integer uid : mUidCache.keySet()) {
+ Boolean b = mUidCache.get(uid);
+ pw.printf("\t%d %s\n", uid, b);
+ }
+ }
+ }
+}