OSDN Git Service

Merge "Add an API for querying HCE service selection mode." into klp-dev
authorMartijn Coenen <maco@google.com>
Thu, 29 Aug 2013 21:01:16 +0000 (21:01 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Thu, 29 Aug 2013 21:01:16 +0000 (21:01 +0000)
api/current.txt
core/java/android/nfc/cardemulation/ApduServiceInfo.java
core/java/android/nfc/cardemulation/CardEmulationManager.java
core/java/android/nfc/cardemulation/HostApduService.java
core/java/android/provider/Settings.java

index f013062..680cb70 100644 (file)
@@ -15261,6 +15261,7 @@ package android.nfc.cardemulation {
 
   public final class CardEmulationManager {
     method public static synchronized android.nfc.cardemulation.CardEmulationManager getInstance(android.nfc.NfcAdapter);
+    method public int getSelectionModeForCategory(java.lang.String);
     method public boolean isDefaultServiceForAid(android.content.ComponentName, java.lang.String);
     method public boolean isDefaultServiceForCategory(android.content.ComponentName, java.lang.String);
     field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.ACTION_CHANGE_DEFAULT";
@@ -15268,6 +15269,9 @@ package android.nfc.cardemulation {
     field public static final java.lang.String CATEGORY_PAYMENT = "payment";
     field public static final java.lang.String EXTRA_CATEGORY = "category";
     field public static final java.lang.String EXTRA_SERVICE_COMPONENT = "component";
+    field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1
+    field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2
+    field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
   }
 
   public abstract class HostApduService extends android.app.Service {
index 3f7e3ef..b83911a 100644 (file)
@@ -75,16 +75,22 @@ public final class ApduServiceInfo implements Parcelable {
     final HashMap<String, AidGroup> mCategoryToGroup;
 
     /**
+     * Whether this service should only be started when the device is unlocked.
+     */
+    final boolean mRequiresDeviceUnlock;
+
+    /**
      * @hide
      */
     public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
-            ArrayList<AidGroup> aidGroups) {
+            ArrayList<AidGroup> aidGroups, boolean requiresUnlock) {
         this.mService = info;
         this.mDescription = description;
         this.mAidGroups = aidGroups;
         this.mAids = new ArrayList<String>();
         this.mCategoryToGroup = new HashMap<String, AidGroup>();
         this.mOnHost = onHost;
+        this.mRequiresDeviceUnlock = requiresUnlock;
         for (AidGroup aidGroup : aidGroups) {
             this.mCategoryToGroup.put(aidGroup.category, aidGroup);
             this.mAids.addAll(aidGroup.aids);
@@ -132,12 +138,16 @@ public final class ApduServiceInfo implements Parcelable {
                 mService = info;
                 mDescription = sa.getString(
                         com.android.internal.R.styleable.HostApduService_description);
+                mRequiresDeviceUnlock = sa.getBoolean(
+                        com.android.internal.R.styleable.HostApduService_requireDeviceUnlock,
+                        false);
             } else {
                 TypedArray sa = res.obtainAttributes(attrs,
                         com.android.internal.R.styleable.OffHostApduService);
                 mService = info;
                 mDescription = sa.getString(
                         com.android.internal.R.styleable.OffHostApduService_description);
+                mRequiresDeviceUnlock = false;
             }
 
             mAidGroups = new ArrayList<AidGroup>();
@@ -226,6 +236,10 @@ public final class ApduServiceInfo implements Parcelable {
         return mOnHost;
     }
 
+    public boolean requiresUnlock() {
+        return mRequiresDeviceUnlock;
+    }
+
     public CharSequence loadLabel(PackageManager pm) {
         return mService.loadLabel(pm);
     }
@@ -287,6 +301,7 @@ public final class ApduServiceInfo implements Parcelable {
         if (mAidGroups.size() > 0) {
             dest.writeTypedList(mAidGroups);
         }
+        dest.writeInt(mRequiresDeviceUnlock ? 1 : 0);
     };
 
     public static final Parcelable.Creator<ApduServiceInfo> CREATOR =
@@ -301,7 +316,8 @@ public final class ApduServiceInfo implements Parcelable {
             if (numGroups > 0) {
                 source.readTypedList(aidGroups, AidGroup.CREATOR);
             }
-            return new ApduServiceInfo(info, onHost, description, aidGroups);
+            boolean requiresUnlock = (source.readInt() != 0) ? true : false;
+            return new ApduServiceInfo(info, onHost, description, aidGroups, requiresUnlock);
         }
 
         @Override
index 537fded..9d60c73 100644 (file)
@@ -27,6 +27,7 @@ import android.nfc.INfcCardEmulation;
 import android.nfc.NfcAdapter;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Log;
 
 import java.util.HashMap;
@@ -78,19 +79,68 @@ public final class CardEmulationManager {
      */
     public static final String CATEGORY_OTHER = "other";
 
-    static boolean sIsInitialized = false;
-    static HashMap<Context, CardEmulationManager> sCardEmuManagers = new HashMap();
-    static INfcCardEmulation sService;
+    /**
+     * Return value for {@link #getSelectionModeForCategory(String)}.
+     *
+     * <p>In this mode, the user has set a default service for this
+     *    AID category. If a remote reader selects any of the AIDs
+     *    that the default service has registered in this category,
+     *    that service will automatically be bound to to handle
+     *    the transaction.
+     *
+     * <p>There are still cases where a service that is
+     *    not the default for a category can selected:
+     *    <p>
+     *    If a remote reader selects an AID in this category
+     *    that is not handled by the default service, and there is a set
+     *    of other services {S} that do handle this AID, the
+     *    user is asked if he wants to use any of the services in
+     *    {S} instead.
+     *    <p>
+     *    As a special case, if the size of {S} is one, containing a single service X,
+     *    and all AIDs X has registered in this category are not
+     *    registered by any other service, then X will be
+     *    selected automatically without asking the user.
+     *    <p>Example:
+     *    <ul>
+     *    <li>Service A registers AIDs "1", "2" and "3" in the category
+     *    <li>Service B registers AIDs "3" and "4" in the category
+     *    <li>Service C registers AIDs "5" and "6" in the category
+     *    </ul>
+     *    In this case, the following will happen when service A
+     *    is the default:
+     *    <ul>
+     *    <li>Reader selects AID "1", "2" or "3": service A is invoked automatically
+     *    <li>Reader selects AID "4": the user is asked to confirm he
+     *        wants to use service B, because its AIDs overlap with service A.
+     *    <li>Reader selects AID "5" or "6": service C is invoked automatically,
+     *        because all AIDs it has asked for are only registered by C,
+     *        and there is no overlap.
+     *    </ul>
+     *
+     */
+    public static final int SELECTION_MODE_PREFER_DEFAULT = 0;
 
     /**
-     * @hide
+     * Return value for {@link #getSelectionModeForCategory(String)}.
+     *
+     * <p>In this mode, whenever an AID of this category is selected,
+     *    the user is asked which service he wants to use to handle
+     *    the transaction, even if there is only one matching service.
      */
-    public static final String PAYMENT_MODE_AUTO = "auto";
+    public static final int SELECTION_MODE_ALWAYS_ASK = 1;
 
     /**
-     * @hide
+     * Return value for {@link #getSelectionModeForCategory(String)}.
+     *
+     * <p>In this mode, the user will only be asked to select a service
+     *    if the selected AID has been registered by multiple applications.
      */
-    public static final String PAYMENT_MODE_MANUAL = "manual";
+    public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2;
+
+    static boolean sIsInitialized = false;
+    static HashMap<Context, CardEmulationManager> sCardEmuManagers = new HashMap();
+    static INfcCardEmulation sService;
 
     final Context mContext;
 
@@ -113,7 +163,7 @@ public final class CardEmulationManager {
                 throw new UnsupportedOperationException();
             }
             try {
-                if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HCE)) {
+                if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
                     Log.e(TAG, "This device does not support card emulation");
                     throw new UnsupportedOperationException();
                 }
@@ -137,6 +187,10 @@ public final class CardEmulationManager {
      * Allows an application to query whether a service is currently
      * the default service to handle a card emulation category.
      *
+     * <p>Note that if {@link #getSelectionModeForCategory(String)}
+     * returns {@link #SELECTION_MODE_ALWAYS_ASK}, this method will always
+     * return false.
+     *
      * @param service The ComponentName of the service
      * @param category The category
      * @return whether service is currently the default service for the category.
@@ -147,6 +201,10 @@ public final class CardEmulationManager {
         } catch (RemoteException e) {
             // Try one more time
             recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
             try {
                 return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service,
                         category);
@@ -186,6 +244,33 @@ public final class CardEmulationManager {
     }
 
     /**
+     * Returns the application selection mode for the passed in category.
+     * Valid return values are:
+     * <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default
+     *    application for this category, which will be preferred.
+     * <p>{@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked
+     *    every time what app he would like to use in this category.
+     * <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked
+     *    to pick a service if there is a conflict.
+     * @param category The category, for example {@link #CATEGORY_PAYMENT}
+     * @return
+     */
+    public int getSelectionModeForCategory(String category) {
+        if (CATEGORY_PAYMENT.equals(category)) {
+            String defaultComponent = Settings.Secure.getString(mContext.getContentResolver(),
+                    Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
+            if (defaultComponent != null) {
+                return SELECTION_MODE_PREFER_DEFAULT;
+            } else {
+                return SELECTION_MODE_ALWAYS_ASK;
+            }
+        } else {
+            // All other categories are in "only ask if conflict" mode
+            return SELECTION_MODE_ASK_IF_CONFLICT;
+        }
+    }
+
+    /**
      * @hide
      */
     public boolean setDefaultServiceForCategory(ComponentName service, String category) {
index cdc4adb..ae94b2f 100644 (file)
@@ -4,13 +4,11 @@ import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.app.Service;
 import android.content.Intent;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Messenger;
-import android.os.Parcel;
 import android.os.RemoteException;
 import android.util.Log;
 
index 24dbf0e..802bedf 100644 (file)
@@ -4285,12 +4285,6 @@ public final class Settings {
         public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
 
         /**
-         * Whether to automatically invoke NFC payment app or manually select on tap.
-         * @hide
-         */
-        public static final String NFC_PAYMENT_MODE = "nfc_payment_mode";
-
-        /**
          * Name of a package that the current user has explicitly allowed to see all of that
          * user's notifications.
          *