diff options
author | Martijn Coenen <maco@google.com> | 2013-08-28 21:26:21 -0700 |
---|---|---|
committer | Martijn Coenen <maco@google.com> | 2013-08-29 11:05:57 -0700 |
commit | 451ba48faa87d78bfbec0597ff06af1747cf6acb (patch) | |
tree | e8fef45f995228d9a756cd58504f02677ded7085 | |
parent | c4e4277a71c70e96198cb760676ad3b40f9e0e3d (diff) | |
download | android_packages_apps_Nfc-451ba48faa87d78bfbec0597ff06af1747cf6acb.tar.gz android_packages_apps_Nfc-451ba48faa87d78bfbec0597ff06af1747cf6acb.tar.bz2 android_packages_apps_Nfc-451ba48faa87d78bfbec0597ff06af1747cf6acb.zip |
HCE: API updates, default handling, lock-screen.
- Use new selection mode APIs
- Fixed up default handling
- Check if service requires device unlock before dispatching
- Fixed isDefaultServiceForAid()
- Fixed a bug where we created two routing managers
Change-Id: Iba4a3ce7f599974ccedbf8d301cc46ff1e99c94e
9 files changed, 190 insertions, 138 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index aaeed4f9..0558d29f 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -58,7 +58,6 @@ /> <activity android:name=".cardemulation.AppChooserActivity" - android:theme="@*android:style/Theme.Holo.Dialog.Alert" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true" android:clearTaskOnLaunch="true" @@ -74,7 +73,6 @@ /> <activity android:name=".cardemulation.TapAgainDialog" - android:theme="@*android:style/Theme.Holo.Dialog.Alert" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true" android:clearTaskOnLaunch="true" diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java index 367ce977..fe34c06a 100755 --- a/src/com/android/nfc/NfcService.java +++ b/src/com/android/nfc/NfcService.java @@ -499,7 +499,7 @@ public class NfcService implements DeviceHostListener { mIsHceCapable = pm.hasSystemFeature(PackageManager.FEATURE_NFC_HCE); if (mIsHceCapable) { mAidRoutingManager = new AidRoutingManager(); - mAidCache = new RegisteredAidCache(mContext); + mAidCache = new RegisteredAidCache(mContext, mAidRoutingManager); mHostEmulationManager = new HostEmulationManager(mContext, mAidCache); } if (!mIsHceCapable || SE_BROADCASTS_WITH_HCE) { @@ -1122,7 +1122,7 @@ public class NfcService implements DeviceHostListener { mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR); validateUserId(userId); ComponentName defaultService = mAidCache.getDefaultServiceForCategory(userId, - category); + category, true); return (defaultService != null && defaultService.equals(service)); } diff --git a/src/com/android/nfc/cardemulation/AidRoutingManager.java b/src/com/android/nfc/cardemulation/AidRoutingManager.java index 3ad31a1e..12a13a23 100644 --- a/src/com/android/nfc/cardemulation/AidRoutingManager.java +++ b/src/com/android/nfc/cardemulation/AidRoutingManager.java @@ -28,7 +28,7 @@ import java.util.Set; public class AidRoutingManager { static final String TAG = "AidRoutingManager"; - // For Nexus device, just a static route to the eSE + // For Nexus devices, just a static route to the eSE // OEMs/Carriers could manually map off-host AIDs // to the correct eSE/UICC based on state they keep. static final int DEFAULT_OFFHOST_ROUTE = 0xF4; @@ -70,8 +70,8 @@ public class AidRoutingManager { int route; synchronized (mLock) { int currentRoute = getRouteForAidLocked(aid); - Log.d(TAG, "Set route for AID: " + aid + ", host: " + onHost + " , current:" + - Integer.toString(currentRoute)); + Log.d(TAG, "Set route for AID: " + aid + ", host: " + onHost + " , current: 0x" + + Integer.toHexString(currentRoute)); route = onHost ? 0 : DEFAULT_OFFHOST_ROUTE; if (route == currentRoute) return true; diff --git a/src/com/android/nfc/cardemulation/AppChooserActivity.java b/src/com/android/nfc/cardemulation/AppChooserActivity.java index 4846e1a6..1939a3bf 100644 --- a/src/com/android/nfc/cardemulation/AppChooserActivity.java +++ b/src/com/android/nfc/cardemulation/AppChooserActivity.java @@ -38,6 +38,8 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ImageView; @@ -61,8 +63,8 @@ public class AppChooserActivity extends AlertActivity protected void onCreate(Bundle savedInstanceState, String category, ArrayList<ComponentName> options, ComponentName failedComponent) { - setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert); super.onCreate(savedInstanceState); + setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert); if ((options == null || options.size() == 0) && failedComponent == null) { Log.e(TAG, "No components passed in."); @@ -118,6 +120,8 @@ public class AppChooserActivity extends AlertActivity setupAlert(); } + Window window = getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); } @Override diff --git a/src/com/android/nfc/cardemulation/DefaultRemovedActivity.java b/src/com/android/nfc/cardemulation/DefaultRemovedActivity.java index 31357426..67ca19e2 100644 --- a/src/com/android/nfc/cardemulation/DefaultRemovedActivity.java +++ b/src/com/android/nfc/cardemulation/DefaultRemovedActivity.java @@ -11,17 +11,14 @@ import com.android.internal.app.AlertController; public class DefaultRemovedActivity extends AlertActivity implements DialogInterface.OnClickListener { - public static final String EXTRA_DEFAULT_NAME = "default"; - @Override protected void onCreate(Bundle savedInstanceState) { setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert); super.onCreate(savedInstanceState); - CharSequence appName = getIntent().getCharSequenceExtra(EXTRA_DEFAULT_NAME); AlertController.AlertParams ap = mAlertParams; - ap.mMessage = appName + " was your preferred option for tap and pay. Choose another?"; + ap.mMessage = "Your preferred service for tap and pay was removed. Choose another?"; ap.mNegativeButtonText = getString(R.string.no); ap.mPositiveButtonText = getString(R.string.yes); ap.mPositiveButtonListener = this; diff --git a/src/com/android/nfc/cardemulation/HostEmulationManager.java b/src/com/android/nfc/cardemulation/HostEmulationManager.java index ff86d085..0a0cc429 100644 --- a/src/com/android/nfc/cardemulation/HostEmulationManager.java +++ b/src/com/android/nfc/cardemulation/HostEmulationManager.java @@ -17,6 +17,7 @@ package com.android.nfc.cardemulation; import android.app.ActivityManager; +import android.app.KeyguardManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -56,7 +57,7 @@ public class HostEmulationManager { final Context mContext; final RegisteredAidCache mAidCache; final Messenger mMessenger = new Messenger (new MessageHandler()); - + final KeyguardManager mKeyguard; final Object mLock; // All variables below protected by mLock @@ -95,6 +96,7 @@ public class HostEmulationManager { mLock = new Object(); mAidCache = aidCache; mState = STATE_IDLE; + mKeyguard = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); SettingsObserver observer = new SettingsObserver(mHandler); context.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT), @@ -160,6 +162,15 @@ public class HostEmulationManager { if (resolveInfo.defaultService != null) { // Resolve to default resolvedService = resolveInfo.defaultService.getComponent(); + // Check if resolvedService requires unlock + if (resolveInfo.defaultService.requiresUnlock() && + mKeyguard.isKeyguardLocked() && mKeyguard.isKeyguardSecure()) { + String category = mAidCache.getCategoryForAid(resolveInfo.aid); + // Just ignore all future APDUs until next tap + mState = STATE_W4_DEACTIVATE; + launchTapAgain(resolvedService, category); + return; + } // TODO also handle case where we prefer the bound service? } else { // We have no default, and either one or more services. @@ -309,7 +320,7 @@ public class HostEmulationManager { // on user switch. int userId = ActivityManager.getCurrentUser(); ComponentName paymentApp = mAidCache.getDefaultServiceForCategory(userId, - CardEmulationManager.CATEGORY_PAYMENT); + CardEmulationManager.CATEGORY_PAYMENT, true); if (paymentApp != null) { bindPaymentServiceLocked(userId, paymentApp); } else { @@ -347,6 +358,14 @@ public class HostEmulationManager { } } + void launchTapAgain(ComponentName service, String category) { + Intent dialogIntent = new Intent(mContext, TapAgainDialog.class); + dialogIntent.putExtra(TapAgainDialog.EXTRA_CATEGORY, category); + dialogIntent.putExtra(TapAgainDialog.EXTRA_COMPONENT, service); + dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + mContext.startActivityAsUser(dialogIntent, UserHandle.CURRENT); + } + void launchResolver(ArrayList<ComponentName> components, ComponentName failedComponent, String category) { Intent intent = new Intent(mContext, AppChooserActivity.class); diff --git a/src/com/android/nfc/cardemulation/RegisteredAidCache.java b/src/com/android/nfc/cardemulation/RegisteredAidCache.java index 770ca0fd..86c0b791 100644 --- a/src/com/android/nfc/cardemulation/RegisteredAidCache.java +++ b/src/com/android/nfc/cardemulation/RegisteredAidCache.java @@ -4,8 +4,6 @@ import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.database.ContentObserver; import android.net.Uri; import android.nfc.cardemulation.ApduServiceInfo; @@ -31,6 +29,8 @@ import java.util.TreeMap; public class RegisteredAidCache implements RegisteredServicesCache.Callback { static final String TAG = "RegisteredAidCache"; + static final boolean DBG = true; + // mAidServices is a tree that maps an AID to a list of handling services // on Android. It is only valid for the current user. final TreeMap<String, ArrayList<ApduServiceInfo>> mAidToServices = @@ -44,10 +44,7 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { final HashMap<String, AidResolveInfo> mAidCache = Maps.newHashMap(); - final HashMap<String, ComponentName> mCurrentDefaults = - Maps.newHashMap(); - - final HashMap<String, Boolean> mCurrentMode = + final HashMap<String, ComponentName> mCategoryDefaults = Maps.newHashMap(); final class AidResolveInfo { @@ -70,7 +67,6 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { final AidRoutingManager mRoutingManager; ComponentName mNextTapComponent = null; - List<ApduServiceInfo> mCurrentServices = null; private final class SettingsObserver extends ContentObserver { public SettingsObserver(Handler handler) { @@ -90,23 +86,20 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { generateAidCacheLocked(); updateRoutingLocked(); } else { - Log.d(TAG, "Not updating aid cache + routing: nothing changed."); + if (DBG) Log.d(TAG, "Not updating aid cache + routing: nothing changed."); } } } }; - public RegisteredAidCache(Context context) { + public RegisteredAidCache(Context context, AidRoutingManager routingManager) { SettingsObserver observer = new SettingsObserver(mHandler); context.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT), true, observer, UserHandle.USER_ALL); - context.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.NFC_PAYMENT_MODE), - true, observer, UserHandle.USER_ALL); mContext = context; mServiceCache = new RegisteredServicesCache(context, this); - mRoutingManager = new AidRoutingManager(); + mRoutingManager = routingManager; updateFromSettingsLocked(ActivityManager.getCurrentUser()); } @@ -147,11 +140,17 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { public boolean isDefaultServiceForAid(int userId, ComponentName service, String aid) { AidResolveInfo resolveInfo = mAidCache.get(aid); - if (resolveInfo.services == null || resolveInfo.services.size() == 0 - || resolveInfo.services.size() > 1) return false; - ApduServiceInfo serviceInfo = resolveInfo.services.get(0); + if (resolveInfo.services == null || resolveInfo.services.size() == 0) return false; - return service.equals(serviceInfo); + if (service.equals(resolveInfo.defaultService)) { + return true; + } else if (resolveInfo.services.size() == 1) { + ApduServiceInfo serviceInfo = resolveInfo.services.get(0); + return service.equals(serviceInfo); + } else { + // More than one service, not the default + return false; + } } public boolean setDefaultServiceForCategory(int userId, ComponentName service, @@ -164,13 +163,10 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { // TODO Not really nice to be writing to Settings.Secure here... // ideally we overlay our local changes over whatever is in // Settings.Secure - if (mServiceCache.hasService(userId, service)) { + if (service == null || mServiceCache.hasService(userId, service)) { Settings.Secure.putStringForUser(mContext.getContentResolver(), - Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, service.flattenToString(), - userId); - Settings.Secure.putStringForUser(mContext.getContentResolver(), - Settings.Secure.NFC_PAYMENT_MODE, CardEmulationManager.PAYMENT_MODE_AUTO, - userId); + Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, + service != null ? service.flattenToString() : null, userId); } else { Log.e(TAG, "Could not find default service to make default: " + service); } @@ -178,7 +174,8 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { return true; } - public ComponentName getDefaultServiceForCategory(int userId, String category) { + public ComponentName getDefaultServiceForCategory(int userId, String category, + boolean validateInstalled) { if (!CardEmulationManager.CATEGORY_PAYMENT.equals(category)) { Log.e(TAG, "Not allowing defaults for category " + category); return null; @@ -190,8 +187,11 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { userId); if (name != null) { ComponentName service = ComponentName.unflattenFromString(name); - return ((service != null && mServiceCache.hasService(userId, service)) ? - service : null); + if (!validateInstalled || service == null) { + return service; + } else { + return mServiceCache.hasService(userId, service) ? service : null; + } } else { return null; } @@ -221,7 +221,7 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { */ AidResolveInfo resolveAidLocked(List<ApduServiceInfo> resolvedServices, String aid) { if (resolvedServices == null || resolvedServices.size() == 0) { - Log.d(TAG, "Could not resolve AID " + aid + " to any service."); + if (DBG) Log.d(TAG, "Could not resolve AID " + aid + " to any service."); return null; } AidResolveInfo resolveInfo = new AidResolveInfo(); @@ -230,24 +230,26 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { resolveInfo.defaultService = null; ComponentName defaultComponent = mNextTapComponent; - Log.d(TAG, "resolveAidLocked: next tap component is " + defaultComponent); + if (DBG) Log.d(TAG, "resolveAidLocked: next tap component is " + defaultComponent); Set<String> paymentAids = mCategoryAids.get(CardEmulationManager.CATEGORY_PAYMENT); if (paymentAids != null && paymentAids.contains(aid)) { - Log.d(TAG, "resolveAidLocked: AID " + aid + " is a payment AID"); + if (DBG) Log.d(TAG, "resolveAidLocked: AID " + aid + " is a payment AID"); // This AID has been registered as a payment AID by at least one service. // Get default component for payment if no next tap default. if (defaultComponent == null) { - defaultComponent = mCurrentDefaults.get(CardEmulationManager.CATEGORY_PAYMENT); + defaultComponent = mCategoryDefaults.get(CardEmulationManager.CATEGORY_PAYMENT); } - Log.d(TAG, "resolveAidLocked: default payment component is " + defaultComponent); + // By default, store all resolved services + resolveInfo.services.addAll(resolvedServices); + if (DBG) Log.d(TAG, "resolveAidLocked: default payment component is " + + defaultComponent); if (resolvedServices.size() == 1) { ApduServiceInfo resolvedService = resolvedServices.get(0); Log.d(TAG, "resolveAidLocked: resolved single service " + resolvedService.getComponent()); if (resolvedService.equals(defaultComponent)) { - Log.d(TAG, "resolveAidLocked: DECISION: routing to (default) " + + if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: routing to (default) " + resolvedService.getComponent()); - resolveInfo.services.add(resolvedService); resolveInfo.defaultService = resolvedService; } else { // So..since we resolved to only one service, and this AID @@ -276,15 +278,13 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { } } if (!foundConflict) { - Log.d(TAG, "resolveAidLocked: DECISION: routing to " + + if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: routing to " + resolvedService.getComponent()); // Treat this as if it's the default for this AID - resolveInfo.services.add(resolvedService); resolveInfo.defaultService = resolvedService; } else { // Allow this service to handle, but don't set as default - resolveInfo.services.add(resolvedService); - Log.d(TAG, "resolveAidLocked: DECISION: routing AID " + aid + + if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: routing AID " + aid + " to " + resolvedService.getComponent() + ", but will ask confirmation because its AID group is contended."); } @@ -292,30 +292,28 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { } else if (resolvedServices.size() > 1) { // More services have registered. If there's a default and it // registered this AID, go with the default. Otherwise, add all. - Log.d(TAG, "resolveAidLocked: multiple services matched."); - resolveInfo.services.addAll(resolvedServices); + if (DBG) Log.d(TAG, "resolveAidLocked: multiple services matched."); if (defaultComponent != null) { for (ApduServiceInfo service : resolvedServices) { if (service.getComponent().equals(defaultComponent)) { - Log.d(TAG, "resolveAidLocked: DECISION: routing to (default) " + - service.getComponent()); + if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: routing to (default) " + + service.getComponent()); resolveInfo.defaultService = service; break; } } if (resolveInfo.defaultService == null) { - Log.d(TAG, "resolveAidLocked: DECISION: routing to all services"); + if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: routing to all services"); } } } // else -> should not hit, we checked for 0 before. } else { // This AID is not a payment AID, just return all components // that can handle it, but be mindful of (next tap) defaults. - resolveInfo.services.addAll(resolvedServices); for (ApduServiceInfo service : resolvedServices) { if (service.getComponent().equals(defaultComponent)) { - Log.d(TAG, "resolveAidLocked: DECISION: cat OTHER AID, routing to (default) " + - service.getComponent()); + if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: cat OTHER AID, " + + "routing to (default) " + service.getComponent()); resolveInfo.defaultService = service; break; } @@ -325,30 +323,23 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { // if there is only one. if (resolveInfo.services.size() == 1) { resolveInfo.defaultService = resolveInfo.services.get(0); - Log.d(TAG, "resolveAidLocked: DECISION: cat OTHER AID, routing to (default) " + - resolveInfo.defaultService.getComponent()); + if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: cat OTHER AID, " + + "routing to (default) " + resolveInfo.defaultService.getComponent()); } else { - Log.d(TAG, "resolveAidLocked: DECISION: cat OTHER AID, routing all"); + if (DBG) Log.d(TAG, "resolveAidLocked: DECISION: cat OTHER AID, routing all"); } } } return resolveInfo; } - void notifyDefaultServiceRemoved(CharSequence serviceName) { - Intent intent = new Intent(mContext, DefaultRemovedActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(DefaultRemovedActivity.EXTRA_DEFAULT_NAME, serviceName); - mContext.startActivityAsUser(intent, UserHandle.CURRENT); - } - - void generateAidTreeLocked() { + void generateAidTreeLocked(List<ApduServiceInfo> services) { // Easiest is to just build the entire tree again mAidToServices.clear(); - for (ApduServiceInfo service : mCurrentServices) { - Log.d(TAG, "generateAidTree component: " + service.getComponent()); + for (ApduServiceInfo service : services) { + if (DBG) Log.d(TAG, "generateAidTree component: " + service.getComponent()); for (String aid : service.getAids()) { - Log.d(TAG, "generateAidTree AID: " + aid); + if (DBG) Log.d(TAG, "generateAidTree AID: " + aid); // Check if a mapping exists for this AID if (mAidToServices.containsKey(aid)) { final ArrayList<ApduServiceInfo> aidServices = mAidToServices.get(aid); @@ -363,11 +354,11 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { } } - void generateAidCategoriesLocked() { + void generateAidCategoriesLocked(List<ApduServiceInfo> services) { // Trash existing mapping mCategoryAids.clear(); - for (ApduServiceInfo service : mCurrentServices) { + for (ApduServiceInfo service : services) { ArrayList<AidGroup> aidGroups = service.getAidGroups(); if (aidGroups == null) continue; for (AidGroup aidGroup : aidGroups) { @@ -390,20 +381,11 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { mContext.getContentResolver(), Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, userId); ComponentName newDefault = name != null ? ComponentName.unflattenFromString(name) : null; - ComponentName oldDefault = mCurrentDefaults.put(CardEmulationManager.CATEGORY_PAYMENT, + ComponentName oldDefault = mCategoryDefaults.put(CardEmulationManager.CATEGORY_PAYMENT, newDefault); changed |= newDefault != oldDefault; - Log.e(TAG, "Set default component to: " + (name != null ? + if (DBG) Log.d(TAG, "Updating default component to: " + (name != null ? ComponentName.unflattenFromString(name) : "null")); - - // Load payment mode from settings - String newModeString = Settings.Secure.getStringForUser(mContext.getContentResolver(), - Settings.Secure.NFC_PAYMENT_MODE, userId); - boolean newMode = !CardEmulationManager.PAYMENT_MODE_MANUAL.equals(newModeString); - Log.e(TAG, "Setting mode to: " + newMode); - Boolean oldMode = mCurrentMode.put(CardEmulationManager.CATEGORY_PAYMENT, newMode); - changed |= (oldMode == null) || (newMode != oldMode.booleanValue()); - return changed; } @@ -446,7 +428,7 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { Set<String> routedAids = mRoutingManager.getRoutedAids(); for (String aid : routedAids) { if (!handledAids.contains(aid)) { - Log.d(TAG, "Removing routing for AID " + aid + ", because " + + if (DBG) Log.d(TAG, "Removing routing for AID " + aid + ", because " + "there are no no interested services."); mRoutingManager.removeAid(aid); } @@ -455,38 +437,102 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { mRoutingManager.commitRouting(); } - @Override - public void onServiceRemoved(ComponentName service) { - // Can no longer load the label, since the app - // is likely gone from the filesystem. - notifyDefaultServiceRemoved(""); + public void showDefaultRemovedDialog() { + Intent intent = new Intent(mContext, DefaultRemovedActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivityAsUser(intent, UserHandle.CURRENT); } - @Override - public void onServiceCategoryRemoved(ApduServiceInfo service, String category) { - PackageManager pm; - try { - pm = mContext.createPackageContextAsUser("android", 0, - UserHandle.CURRENT).getPackageManager(); - } catch (NameNotFoundException e) { - Log.e(TAG, "Could not create user package context"); - return; + void onPaymentDefaultRemoved(int userId, List<ApduServiceInfo> services) { + int numPaymentServices = 0; + ComponentName lastFoundPaymentService = null; + for (ApduServiceInfo service : services) { + if (service.hasCategory(CardEmulationManager.CATEGORY_PAYMENT)) { + numPaymentServices++; + lastFoundPaymentService = service.getComponent(); + } + } + if (DBG) Log.d(TAG, "Number of payment services is " + + Integer.toString(numPaymentServices)); + if (numPaymentServices == 0) { + if (DBG) Log.d(TAG, "Default removed, no services left."); + // No payment services left, unset default and don't ask the user + setDefaultServiceForCategory(userId, null, + CardEmulationManager.CATEGORY_PAYMENT); + } else if (numPaymentServices == 1) { + // Only one left, automatically make it the default + if (DBG) Log.d(TAG, "Default removed, making remaining service default."); + setDefaultServiceForCategory(userId, lastFoundPaymentService, + CardEmulationManager.CATEGORY_PAYMENT); + } else if (numPaymentServices > 1) { + // More than one left, unset default and ask the user if he wants + // to set a new one + if (DBG) Log.d(TAG, "Default removed, asking user to pick."); + setDefaultServiceForCategory(userId, null, + CardEmulationManager.CATEGORY_PAYMENT); + showDefaultRemovedDialog(); + } + } + + void setDefaultIfNeededLocked(int userId, List<ApduServiceInfo> services) { + int numPaymentServices = 0; + ComponentName lastFoundPaymentService = null; + for (ApduServiceInfo service : services) { + if (service.hasCategory(CardEmulationManager.CATEGORY_PAYMENT)) { + numPaymentServices++; + lastFoundPaymentService = service.getComponent(); + } + } + if (numPaymentServices > 1) { + // More than one service left, leave default unset + if (DBG) Log.d(TAG, "No default set, more than one service left."); + } else if (numPaymentServices == 1) { + // Make single found payment service the default + if (DBG) Log.d(TAG, "No default set, making single service default."); + setDefaultServiceForCategory(userId, lastFoundPaymentService, + CardEmulationManager.CATEGORY_PAYMENT); + } else { + // No payment services left, leave default at null + if (DBG) Log.d(TAG, "No default set, last payment service removed."); + } + } + + void checkDefaultsLocked(int userId, List<ApduServiceInfo> services) { + ComponentName defaultPaymentService = + getDefaultServiceForCategory(userId, CardEmulationManager.CATEGORY_PAYMENT, false); + Log.d(TAG, "Current default: " + defaultPaymentService); + if (defaultPaymentService != null) { + // Validate the default is still installed and handling payment + ApduServiceInfo serviceInfo = mServiceCache.getService(userId, defaultPaymentService); + if (serviceInfo == null) { + Log.e(TAG, "Default payment service unexpectedly removed."); + onPaymentDefaultRemoved(userId, services); + } else if (!serviceInfo.hasCategory(CardEmulationManager.CATEGORY_PAYMENT)) { + if (DBG) Log.d(TAG, "Default payment service had payment category removed"); + onPaymentDefaultRemoved(userId, services); + } else { + // Default still exists and handles the category, nothing do + if (DBG) Log.d(TAG, "Default payment service still ok."); + } + } else { + // A payment service may have been removed, leaving only one; + // in that case, automatically set that app as default. + setDefaultIfNeededLocked(userId, services); } - notifyDefaultServiceRemoved(service.loadLabel(pm)); } @Override - public void onServicesUpdated(int userId) { + public void onServicesUpdated(int userId, List<ApduServiceInfo> services) { synchronized (mLock) { if (ActivityManager.getCurrentUser() == userId) { - mCurrentServices = mServiceCache.getServices(userId); - // Rebuild our internal data-structure - generateAidTreeLocked(); - generateAidCategoriesLocked(); + // Rebuild our internal data-structures + checkDefaultsLocked(userId, services); + generateAidTreeLocked(services); + generateAidCategoriesLocked(services); generateAidCacheLocked(); updateRoutingLocked(); } else { - Log.e(TAG, "Ignoring service update because it is not for the current user."); + if (DBG) Log.d(TAG, "Ignoring update because it's not for the current user."); } } } @@ -494,4 +540,4 @@ public class RegisteredAidCache implements RegisteredServicesCache.Callback { public void invalidateCache(int currentUser) { mServiceCache.invalidateCache(currentUser); } -}
\ No newline at end of file +} diff --git a/src/com/android/nfc/cardemulation/RegisteredServicesCache.java b/src/com/android/nfc/cardemulation/RegisteredServicesCache.java index 0c15e564..07fcb2d3 100644 --- a/src/com/android/nfc/cardemulation/RegisteredServicesCache.java +++ b/src/com/android/nfc/cardemulation/RegisteredServicesCache.java @@ -28,7 +28,6 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.nfc.cardemulation.ApduServiceInfo; -import android.nfc.cardemulation.CardEmulationManager; import android.nfc.cardemulation.HostApduService; import android.nfc.cardemulation.OffHostApduService; import android.os.UserHandle; @@ -39,6 +38,7 @@ import com.google.android.collect.Maps; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -60,9 +60,7 @@ public class RegisteredServicesCache { final Callback mCallback; public interface Callback { - void onServicesUpdated(int userId); - void onServiceRemoved(ComponentName service); - void onServiceCategoryRemoved(ApduServiceInfo service, String category); + void onServicesUpdated(int userId, final List<ApduServiceInfo> services); }; private static class UserServices { @@ -142,9 +140,13 @@ public class RegisteredServicesCache { } public boolean hasService(int userId, ComponentName service) { + return getService(userId, service) != null; + } + + public ApduServiceInfo getService(int userId, ComponentName service) { synchronized (mLock) { UserServices userServices = findOrCreateUserLocked(userId); - return userServices.services.containsKey(service); + return userServices.services.get(service); } } @@ -212,8 +214,6 @@ public class RegisteredServicesCache { } } - final ArrayList<ComponentName> removedServices = new ArrayList<ComponentName>(); - final ArrayList<ApduServiceInfo> categoryRemovedServices = new ArrayList<ApduServiceInfo>(); synchronized (mLock) { UserServices userServices = findOrCreateUserLocked(userId); @@ -225,33 +225,17 @@ public class RegisteredServicesCache { (Map.Entry<ComponentName, ApduServiceInfo>) it.next(); if (!containsServiceLocked(validServices, entry.getKey())) { Log.d(TAG, "Service removed: " + entry.getKey()); - removedServices.add(entry.getKey()); it.remove(); } } - for (ApduServiceInfo service : validServices) { - if (DEBUG) Log.d(TAG, "Processing service: " + service.getComponent() + + if (DEBUG) Log.d(TAG, "Adding service: " + service.getComponent() + " AIDs: " + service.getAids()); - ApduServiceInfo existingService = - userServices.services.put(service.getComponent(), service); - if (existingService != null && - existingService.hasCategory(CardEmulationManager.CATEGORY_PAYMENT) && - !service.hasCategory(CardEmulationManager.CATEGORY_PAYMENT)) { - categoryRemovedServices.add(service); - } + userServices.services.put(service.getComponent(), service); } - - } - - for (ComponentName service : removedServices) { - mCallback.onServiceRemoved(service); - } - for (ApduServiceInfo service : categoryRemovedServices) { - mCallback.onServiceCategoryRemoved(service, CardEmulationManager.CATEGORY_PAYMENT); } - mCallback.onServicesUpdated(userId); + mCallback.onServicesUpdated(userId, Collections.unmodifiableList(validServices)); dump(validServices); } -}
\ No newline at end of file +} diff --git a/src/com/android/nfc/cardemulation/TapAgainDialog.java b/src/com/android/nfc/cardemulation/TapAgainDialog.java index 0c5601d0..c55b349d 100644 --- a/src/com/android/nfc/cardemulation/TapAgainDialog.java +++ b/src/com/android/nfc/cardemulation/TapAgainDialog.java @@ -16,7 +16,6 @@ package com.android.nfc.cardemulation; -import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -28,6 +27,8 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.nfc.NfcAdapter; import android.nfc.cardemulation.CardEmulationManager; import android.os.Bundle; +import android.view.Window; +import android.view.WindowManager; import android.widget.TextView; import com.android.internal.R; @@ -52,9 +53,10 @@ public class TapAgainDialog extends AlertActivity { @Override protected void onCreate(Bundle savedInstanceState) { - setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert); super.onCreate(savedInstanceState); + setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert); + final NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); mCardEmuManager = CardEmulationManager.getInstance(adapter); Intent intent = getIntent(); @@ -80,6 +82,8 @@ public class TapAgainDialog extends AlertActivity { } catch (NameNotFoundException e) { finish(); } + Window window = getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); } @Override |