Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ca12d455 authored by Martijn Coenen's avatar Martijn Coenen Committed by Android Git Automerger
Browse files

am dd592e2a: Merge "HCE API review." into klp-dev

* commit 'dd592e2a':
  HCE API review.
parents 85d8eb69 dd592e2a
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -15264,12 +15264,12 @@ package android.nfc {
package android.nfc.cardemulation {
  public final class CardEmulationManager {
    method public static synchronized android.nfc.cardemulation.CardEmulationManager getInstance(android.nfc.NfcAdapter);
  public final class CardEmulation {
    method public static synchronized android.nfc.cardemulation.CardEmulation 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";
    field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
    field public static final java.lang.String CATEGORY_OTHER = "other";
    field public static final java.lang.String CATEGORY_PAYMENT = "payment";
    field public static final java.lang.String EXTRA_CATEGORY = "category";
@@ -15289,15 +15289,15 @@ package android.nfc.cardemulation {
    method public final void sendResponseApdu(byte[]);
    field public static final int DEACTIVATION_DESELECTED = 1; // 0x1
    field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
    field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.HostApduService";
    field public static final java.lang.String SERVICE_META_DATA = "android.nfc.HostApduService";
    field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
    field public static final java.lang.String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service";
  }
  public abstract class OffHostApduService extends android.app.Service {
    ctor public OffHostApduService();
    method public abstract android.os.IBinder onBind(android.content.Intent);
    field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.OffHostApduService";
    field public static final java.lang.String SERVICE_META_DATA = "android.nfc.OffHostApduService";
    field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE";
    field public static final java.lang.String SERVICE_META_DATA = "android.nfc.cardemulation.off_host_apdu_service";
  }
}
+9 −5
Original line number Diff line number Diff line
@@ -104,10 +104,14 @@ public final class ApduServiceInfo implements Parcelable {
        try {
            if (onHost) {
                parser = si.loadXmlMetaData(pm, HostApduService.SERVICE_META_DATA);
                if (parser == null) {
                    Log.d(TAG, "Didn't find service meta-data, trying legacy.");
                    parser = si.loadXmlMetaData(pm, HostApduService.OLD_SERVICE_META_DATA);
                    if (parser == null) {
                        throw new XmlPullParserException("No " + HostApduService.SERVICE_META_DATA +
                                " meta-data");
                    }
                }
            } else {
                parser = si.loadXmlMetaData(pm, OffHostApduService.SERVICE_META_DATA);
                if (parser == null) {
@@ -170,12 +174,12 @@ public final class ApduServiceInfo implements Parcelable {
                            com.android.internal.R.styleable.AidGroup_description);
                    String groupCategory = groupAttrs.getString(
                            com.android.internal.R.styleable.AidGroup_category);
                    if (!CardEmulationManager.CATEGORY_PAYMENT.equals(groupCategory)) {
                        groupCategory = CardEmulationManager.CATEGORY_OTHER;
                    if (!CardEmulation.CATEGORY_PAYMENT.equals(groupCategory)) {
                        groupCategory = CardEmulation.CATEGORY_OTHER;
                    }
                    currentGroup = mCategoryToGroup.get(groupCategory);
                    if (currentGroup != null) {
                        if (!CardEmulationManager.CATEGORY_OTHER.equals(groupCategory)) {
                        if (!CardEmulation.CATEGORY_OTHER.equals(groupCategory)) {
                            Log.e(TAG, "Not allowing multiple aid-groups in the " +
                                    groupCategory + " category");
                            currentGroup = null;
+343 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 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 android.nfc.cardemulation;

import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
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;
import java.util.List;

public final class CardEmulation {
    static final String TAG = "CardEmulation";

    /**
     * Activity action: ask the user to change the default
     * card emulation service for a certain category. This will
     * show a dialog that asks the user whether he wants to
     * replace the current default service with the service
     * identified with the ComponentName specified in
     * {@link #EXTRA_SERVICE_COMPONENT}, for the category
     * specified in {@link #EXTRA_CATEGORY}
     */
    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    public static final String ACTION_CHANGE_DEFAULT =
            "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";

    /**
     * The category extra for {@link #ACTION_CHANGE_DEFAULT}
     *
     * @see #ACTION_CHANGE_DEFAULT
     */
    public static final String EXTRA_CATEGORY = "category";

    /**
     * The ComponentName object passed in as a parcelable
     * extra for {@link #ACTION_CHANGE_DEFAULT}
     *
     * @see #ACTION_CHANGE_DEFAULT
     */
    public static final String EXTRA_SERVICE_COMPONENT = "component";

    /**
     * The payment category can be used to indicate that an AID
     * represents a payment application.
     */
    public static final String CATEGORY_PAYMENT = "payment";

    /**
     * If an AID group does not contain a category, or the
     * specified category is not defined by the platform version
     * that is parsing the AID group, all AIDs in the group will
     * automatically be categorized under the {@link #CATEGORY_OTHER}
     * category.
     */
    public static final String CATEGORY_OTHER = "other";

    /**
     * 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;

    /**
     * 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 int SELECTION_MODE_ALWAYS_ASK = 1;

    /**
     * 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 int SELECTION_MODE_ASK_IF_CONFLICT = 2;

    static boolean sIsInitialized = false;
    static HashMap<Context, CardEmulation> sCardEmus = new HashMap();
    static INfcCardEmulation sService;

    final Context mContext;

    private CardEmulation(Context context, INfcCardEmulation service) {
        mContext = context.getApplicationContext();
        sService = service;
    }

    public static synchronized CardEmulation getInstance(NfcAdapter adapter) {
        if (adapter == null) throw new NullPointerException("NfcAdapter is null");
        Context context = adapter.getContext();
        if (context == null) {
            Log.e(TAG, "NfcAdapter context is null.");
            throw new UnsupportedOperationException();
        }
        if (!sIsInitialized) {
            IPackageManager pm = ActivityThread.getPackageManager();
            if (pm == null) {
                Log.e(TAG, "Cannot get PackageManager");
                throw new UnsupportedOperationException();
            }
            try {
                if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
                    Log.e(TAG, "This device does not support card emulation");
                    throw new UnsupportedOperationException();
                }
            } catch (RemoteException e) {
                Log.e(TAG, "PackageManager query failed.");
                throw new UnsupportedOperationException();
            }
            sIsInitialized = true;
        }
        CardEmulation manager = sCardEmus.get(context);
        if (manager == null) {
            // Get card emu service
            INfcCardEmulation service = adapter.getCardEmulationService();
            manager = new CardEmulation(context, service);
            sCardEmus.put(context, manager);
        }
        return manager;
    }

    /**
     * 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.
     */
    public boolean isDefaultServiceForCategory(ComponentName service, String category) {
        try {
            return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service, category);
        } 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);
            } catch (RemoteException ee) {
                Log.e(TAG, "Failed to recover CardEmulationService.");
                return false;
            }
        }
    }

    /**
     *
     * Allows an application to query whether a service is currently
     * the default handler for a specified ISO7816-4 Application ID.
     *
     * @param service The ComponentName of the service
     * @param aid The ISO7816-4 Application ID
     * @return
     */
    public boolean isDefaultServiceForAid(ComponentName service, String aid) {
        try {
            return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
        } catch (RemoteException e) {
            // Try one more time
            recoverService();
            if (sService == null) {
                Log.e(TAG, "Failed to recover CardEmulationService.");
                return false;
            }
            try {
                return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
            } catch (RemoteException ee) {
                Log.e(TAG, "Failed to reach CardEmulationService.");
                return false;
            }
        }
    }

    /**
     * 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) {
        try {
            return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service, category);
        } catch (RemoteException e) {
            // Try one more time
            recoverService();
            if (sService == null) {
                Log.e(TAG, "Failed to recover CardEmulationService.");
                return false;
            }
            try {
                return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service,
                        category);
            } catch (RemoteException ee) {
                Log.e(TAG, "Failed to reach CardEmulationService.");
                return false;
            }
        }
    }

    /**
     * @hide
     */
    public boolean setDefaultForNextTap(ComponentName service) {
        try {
            return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
        } catch (RemoteException e) {
            // Try one more time
            recoverService();
            if (sService == null) {
                Log.e(TAG, "Failed to recover CardEmulationService.");
                return false;
            }
            try {
                return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
            } catch (RemoteException ee) {
                Log.e(TAG, "Failed to reach CardEmulationService.");
                return false;
            }
        }
    }
    /**
     * @hide
     */
    public List<ApduServiceInfo> getServices(String category) {
        try {
            return sService.getServices(UserHandle.myUserId(), category);
        } catch (RemoteException e) {
            // Try one more time
            recoverService();
            if (sService == null) {
                Log.e(TAG, "Failed to recover CardEmulationService.");
                return null;
            }
            try {
                return sService.getServices(UserHandle.myUserId(), category);
            } catch (RemoteException ee) {
                Log.e(TAG, "Failed to reach CardEmulationService.");
                return null;
            }
        }
    }

    void recoverService() {
        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
        sService = adapter.getCardEmulationService();
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -33,6 +33,10 @@ import android.util.Log;
import java.util.HashMap;
import java.util.List;

/**
 * TODO Remove when calling .apks are upgraded
 * @hide
 */
public final class CardEmulationManager {
    static final String TAG = "CardEmulationManager";

+19 −1
Original line number Diff line number Diff line
@@ -40,13 +40,31 @@ public abstract class HostApduService extends Service {
     */
    @SdkConstant(SdkConstantType.SERVICE_ACTION)
    public static final String SERVICE_INTERFACE =
            "android.nfc.cardemulation.action.HOST_APDU_SERVICE";

    /**
     * The name of the meta-data element that contains
     * more information about this service.
     */
    public static final String SERVICE_META_DATA =
            "android.nfc.cardemulation.host_apdu_service";

    /**
     * The {@link Intent} that must be declared as handled by the service.
     * TODO Remove
     * @hide
     */
    public static final String OLD_SERVICE_INTERFACE =
            "android.nfc.HostApduService";

    /**
     * The name of the meta-data element that contains
     * more information about this service.
     *
     * TODO Remove
     * @hide
     */
    public static final String SERVICE_META_DATA = "android.nfc.HostApduService";
    public static final String OLD_SERVICE_META_DATA = "android.nfc.HostApduService";

    /**
     * Reason for {@link #onDeactivated(int)}.
Loading