1a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen/*
2a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * Copyright (C) 2015 The Android Open Source Project
3a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen *
4a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * Licensed under the Apache License, Version 2.0 (the "License");
5a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * you may not use this file except in compliance with the License.
6a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * You may obtain a copy of the License at
7a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen *
8a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen *      http://www.apache.org/licenses/LICENSE-2.0
9a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen *
10a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * Unless required by applicable law or agreed to in writing, software
11a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * distributed under the License is distributed on an "AS IS" BASIS,
12a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * See the License for the specific language governing permissions and
14a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen * limitations under the License.
15a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen */
16a1ec95882901ebf386cc794122b36b18ee845554Martijn Coenen
175eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenpackage android.nfc.cardemulation;
185eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
195eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenimport android.annotation.SdkConstant;
205eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenimport android.annotation.SdkConstant.SdkConstantType;
215eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenimport android.app.Service;
225eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenimport android.content.Intent;
2335bf6288527b177a04100585321a1266f020004aMartijn Coenenimport android.content.pm.PackageManager;
245eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenimport android.os.Bundle;
255eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenimport android.os.Handler;
265eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenimport android.os.IBinder;
275eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenimport android.os.Message;
285eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenimport android.os.Messenger;
295eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenimport android.os.RemoteException;
305eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenimport android.util.Log;
315eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
325eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen/**
3335bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>HostApduService is a convenience {@link Service} class that can be
3435bf6288527b177a04100585321a1266f020004aMartijn Coenen * extended to emulate an NFC card inside an Android
3535bf6288527b177a04100585321a1266f020004aMartijn Coenen * service component.
365eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen *
3735bf6288527b177a04100585321a1266f020004aMartijn Coenen * <div class="special reference">
3835bf6288527b177a04100585321a1266f020004aMartijn Coenen * <h3>Developer Guide</h3>
39682c24e22811d4ee17ae1cd61bf255c3f7e722b7Trevor Johns * For a general introduction to card emulation, see
40682c24e22811d4ee17ae1cd61bf255c3f7e722b7Trevor Johns * <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html">
41682c24e22811d4ee17ae1cd61bf255c3f7e722b7Trevor Johns * Host-based Card Emulation</a>.</p>
4235bf6288527b177a04100585321a1266f020004aMartijn Coenen * </div>
4335bf6288527b177a04100585321a1266f020004aMartijn Coenen *
4435bf6288527b177a04100585321a1266f020004aMartijn Coenen * <h3>NFC Protocols</h3>
4535bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>Cards emulated by this class are based on the NFC-Forum ISO-DEP
4635bf6288527b177a04100585321a1266f020004aMartijn Coenen * protocol (based on ISO/IEC 14443-4) and support processing
4735bf6288527b177a04100585321a1266f020004aMartijn Coenen * command Application Protocol Data Units (APDUs) as
4835bf6288527b177a04100585321a1266f020004aMartijn Coenen * defined in the ISO/IEC 7816-4 specification.
4935bf6288527b177a04100585321a1266f020004aMartijn Coenen *
5035bf6288527b177a04100585321a1266f020004aMartijn Coenen * <h3>Service selection</h3>
5135bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>When a remote NFC device wants to talk to your
5235bf6288527b177a04100585321a1266f020004aMartijn Coenen * service, it sends a so-called
5335bf6288527b177a04100585321a1266f020004aMartijn Coenen * "SELECT AID" APDU as defined in the ISO/IEC 7816-4 specification.
5435bf6288527b177a04100585321a1266f020004aMartijn Coenen * The AID is an application identifier defined in ISO/IEC 7816-4.
5535bf6288527b177a04100585321a1266f020004aMartijn Coenen *
5635bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>The registration procedure for AIDs is defined in the
5735bf6288527b177a04100585321a1266f020004aMartijn Coenen * ISO/IEC 7816-5 specification. If you don't want to register an
5835bf6288527b177a04100585321a1266f020004aMartijn Coenen * AID, you are free to use AIDs in the proprietary range:
5935bf6288527b177a04100585321a1266f020004aMartijn Coenen * bits 8-5 of the first byte must each be set to '1'. For example,
6035bf6288527b177a04100585321a1266f020004aMartijn Coenen * "0xF00102030405" is a proprietary AID. If you do use proprietary
6135bf6288527b177a04100585321a1266f020004aMartijn Coenen * AIDs, it is recommended to choose an AID of at least 6 bytes,
6235bf6288527b177a04100585321a1266f020004aMartijn Coenen * to reduce the risk of collisions with other applications that
6335bf6288527b177a04100585321a1266f020004aMartijn Coenen * might be using proprietary AIDs as well.
6435bf6288527b177a04100585321a1266f020004aMartijn Coenen *
6535bf6288527b177a04100585321a1266f020004aMartijn Coenen * <h3>AID groups</h3>
6635bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>In some cases, a service may need to register multiple AIDs
6735bf6288527b177a04100585321a1266f020004aMartijn Coenen * to implement a certain application, and it needs to be sure
6835bf6288527b177a04100585321a1266f020004aMartijn Coenen * that it is the default handler for all of these AIDs (as opposed
6935bf6288527b177a04100585321a1266f020004aMartijn Coenen * to some AIDs in the group going to another service).
7035bf6288527b177a04100585321a1266f020004aMartijn Coenen *
7135bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>An AID group is a list of AIDs that should be considered as
7235bf6288527b177a04100585321a1266f020004aMartijn Coenen * belonging together by the OS. For all AIDs in an AID group, the
7335bf6288527b177a04100585321a1266f020004aMartijn Coenen * OS will guarantee one of the following:
7435bf6288527b177a04100585321a1266f020004aMartijn Coenen * <ul>
7535bf6288527b177a04100585321a1266f020004aMartijn Coenen * <li>All AIDs in the group are routed to this service
7635bf6288527b177a04100585321a1266f020004aMartijn Coenen * <li>No AIDs in the group are routed to this service
7735bf6288527b177a04100585321a1266f020004aMartijn Coenen * </ul>
7835bf6288527b177a04100585321a1266f020004aMartijn Coenen * In other words, there is no in-between state, where some AIDs
7935bf6288527b177a04100585321a1266f020004aMartijn Coenen * in the group can be routed to this service, and some to another.
8035bf6288527b177a04100585321a1266f020004aMartijn Coenen * <h3>AID groups and categories</h3>
8135bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>Each AID group can be associated with a category. This allows
8235bf6288527b177a04100585321a1266f020004aMartijn Coenen * the Android OS to classify services, and it allows the user to
8335bf6288527b177a04100585321a1266f020004aMartijn Coenen * set defaults at the category level instead of the AID level.
8435bf6288527b177a04100585321a1266f020004aMartijn Coenen *
8535bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>You can use
8635bf6288527b177a04100585321a1266f020004aMartijn Coenen * {@link CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String)}
8735bf6288527b177a04100585321a1266f020004aMartijn Coenen * to determine if your service is the default handler for a category.
8835bf6288527b177a04100585321a1266f020004aMartijn Coenen *
8935bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>In this version of the platform, the only known categories
9035bf6288527b177a04100585321a1266f020004aMartijn Coenen * are {@link CardEmulation#CATEGORY_PAYMENT} and {@link CardEmulation#CATEGORY_OTHER}.
9135bf6288527b177a04100585321a1266f020004aMartijn Coenen * AID groups without a category, or with a category that is not recognized
9235bf6288527b177a04100585321a1266f020004aMartijn Coenen * by the current platform version, will automatically be
9335bf6288527b177a04100585321a1266f020004aMartijn Coenen * grouped into the {@link CardEmulation#CATEGORY_OTHER} category.
9435bf6288527b177a04100585321a1266f020004aMartijn Coenen * <h3>Service AID registration</h3>
9535bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>To tell the platform which AIDs groups
9635bf6288527b177a04100585321a1266f020004aMartijn Coenen * are requested by this service, a {@link #SERVICE_META_DATA}
975eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen * entry must be included in the declaration of the service. An
9835bf6288527b177a04100585321a1266f020004aMartijn Coenen * example of a HostApduService manifest declaration is shown below:
9935bf6288527b177a04100585321a1266f020004aMartijn Coenen * <pre> &lt;service android:name=".MyHostApduService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"&gt;
1005eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen *     &lt;intent-filter&gt;
10135bf6288527b177a04100585321a1266f020004aMartijn Coenen *         &lt;action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/&gt;
1025eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen *     &lt;/intent-filter&gt;
10335bf6288527b177a04100585321a1266f020004aMartijn Coenen *     &lt;meta-data android:name="android.nfc.cardemulation.host_apdu_ervice" android:resource="@xml/apduservice"/&gt;
1045eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen * &lt;/service&gt;</pre>
10535bf6288527b177a04100585321a1266f020004aMartijn Coenen *
10635bf6288527b177a04100585321a1266f020004aMartijn Coenen * This meta-data tag points to an apduservice.xml file.
10735bf6288527b177a04100585321a1266f020004aMartijn Coenen * An example of this file with a single AID group declaration is shown below:
10835bf6288527b177a04100585321a1266f020004aMartijn Coenen * <pre>
10935bf6288527b177a04100585321a1266f020004aMartijn Coenen * &lt;host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
11035bf6288527b177a04100585321a1266f020004aMartijn Coenen *           android:description="@string/servicedesc" android:requireDeviceUnlock="false"&gt;
11135bf6288527b177a04100585321a1266f020004aMartijn Coenen *       &lt;aid-group android:description="@string/aiddescription" android:category="other">
11235bf6288527b177a04100585321a1266f020004aMartijn Coenen *           &lt;aid-filter android:name="F0010203040506"/&gt;
11335bf6288527b177a04100585321a1266f020004aMartijn Coenen *           &lt;aid-filter android:name="F0394148148100"/&gt;
11435bf6288527b177a04100585321a1266f020004aMartijn Coenen *       &lt;/aid-group&gt;
11535bf6288527b177a04100585321a1266f020004aMartijn Coenen * &lt;/host-apdu-service&gt;
11635bf6288527b177a04100585321a1266f020004aMartijn Coenen * </pre>
11735bf6288527b177a04100585321a1266f020004aMartijn Coenen *
11835bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>The {@link android.R.styleable#HostApduService &lt;host-apdu-service&gt;} is required
11935bf6288527b177a04100585321a1266f020004aMartijn Coenen * to contain a
12035bf6288527b177a04100585321a1266f020004aMartijn Coenen * {@link android.R.styleable#HostApduService_description &lt;android:description&gt;}
12135bf6288527b177a04100585321a1266f020004aMartijn Coenen * attribute that contains a user-friendly description of the service that may be shown in UI.
12235bf6288527b177a04100585321a1266f020004aMartijn Coenen * The
12335bf6288527b177a04100585321a1266f020004aMartijn Coenen * {@link android.R.styleable#HostApduService_requireDeviceUnlock &lt;requireDeviceUnlock&gt;}
12435bf6288527b177a04100585321a1266f020004aMartijn Coenen * attribute can be used to specify that the device must be unlocked before this service
12535bf6288527b177a04100585321a1266f020004aMartijn Coenen * can be invoked to handle APDUs.
12635bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>The {@link android.R.styleable#HostApduService &lt;host-apdu-service&gt;} must
12735bf6288527b177a04100585321a1266f020004aMartijn Coenen * contain one or more {@link android.R.styleable#AidGroup &lt;aid-group&gt;} tags.
12835bf6288527b177a04100585321a1266f020004aMartijn Coenen * Each {@link android.R.styleable#AidGroup &lt;aid-group&gt;} must contain one or
12935bf6288527b177a04100585321a1266f020004aMartijn Coenen * more {@link android.R.styleable#AidFilter &lt;aid-filter&gt;} tags, each of which
13035bf6288527b177a04100585321a1266f020004aMartijn Coenen * contains a single AID. The AID must be specified in hexadecimal format, and contain
13135bf6288527b177a04100585321a1266f020004aMartijn Coenen * an even number of characters.
13235bf6288527b177a04100585321a1266f020004aMartijn Coenen * <h3>AID conflict resolution</h3>
13335bf6288527b177a04100585321a1266f020004aMartijn Coenen * Multiple HostApduServices may be installed on a single device, and the same AID
13435bf6288527b177a04100585321a1266f020004aMartijn Coenen * can be registered by more than one service. The Android platform resolves AID
13535bf6288527b177a04100585321a1266f020004aMartijn Coenen * conflicts depending on which category an AID belongs to. Each category may
13635bf6288527b177a04100585321a1266f020004aMartijn Coenen * have a different conflict resolution policy. For example, for some categories
13735bf6288527b177a04100585321a1266f020004aMartijn Coenen * the user may be able to select a default service in the Android settings UI.
13835bf6288527b177a04100585321a1266f020004aMartijn Coenen * For other categories, to policy may be to always ask the user which service
13935bf6288527b177a04100585321a1266f020004aMartijn Coenen * is to be invoked in case of conflict.
14035bf6288527b177a04100585321a1266f020004aMartijn Coenen *
14135bf6288527b177a04100585321a1266f020004aMartijn Coenen * To query the conflict resolution policy for a certain category, see
14235bf6288527b177a04100585321a1266f020004aMartijn Coenen * {@link CardEmulation#getSelectionModeForCategory(String)}.
14335bf6288527b177a04100585321a1266f020004aMartijn Coenen *
14435bf6288527b177a04100585321a1266f020004aMartijn Coenen * <h3>Data exchange</h3>
14535bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p>Once the platform has resolved a "SELECT AID" command APDU to a specific
14635bf6288527b177a04100585321a1266f020004aMartijn Coenen * service component, the "SELECT AID" command APDU and all subsequent
14735bf6288527b177a04100585321a1266f020004aMartijn Coenen * command APDUs will be sent to that service through
14835bf6288527b177a04100585321a1266f020004aMartijn Coenen * {@link #processCommandApdu(byte[], Bundle)}, until either:
14935bf6288527b177a04100585321a1266f020004aMartijn Coenen * <ul>
15035bf6288527b177a04100585321a1266f020004aMartijn Coenen * <li>The NFC link is broken</li>
15135bf6288527b177a04100585321a1266f020004aMartijn Coenen * <li>A "SELECT AID" APDU is received which resolves to another service</li>
15235bf6288527b177a04100585321a1266f020004aMartijn Coenen * </ul>
15335bf6288527b177a04100585321a1266f020004aMartijn Coenen * These two scenarios are indicated by a call to {@link #onDeactivated(int)}.
15435bf6288527b177a04100585321a1266f020004aMartijn Coenen *
15535bf6288527b177a04100585321a1266f020004aMartijn Coenen * <p class="note">Use of this class requires the
15635bf6288527b177a04100585321a1266f020004aMartijn Coenen * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present
15735bf6288527b177a04100585321a1266f020004aMartijn Coenen * on the device.
15835bf6288527b177a04100585321a1266f020004aMartijn Coenen *
1595eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen */
1605eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenenpublic abstract class HostApduService extends Service {
1615eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    /**
16235bf6288527b177a04100585321a1266f020004aMartijn Coenen     * The {@link Intent} action that must be declared as handled by the service.
1635eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     */
1645eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    @SdkConstant(SdkConstantType.SERVICE_ACTION)
1655eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    public static final String SERVICE_INTERFACE =
16652246087f4e2b5ad62b9cd6ea8c2cb58f624d4e7Martijn Coenen            "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
16752246087f4e2b5ad62b9cd6ea8c2cb58f624d4e7Martijn Coenen
16852246087f4e2b5ad62b9cd6ea8c2cb58f624d4e7Martijn Coenen    /**
16952246087f4e2b5ad62b9cd6ea8c2cb58f624d4e7Martijn Coenen     * The name of the meta-data element that contains
17052246087f4e2b5ad62b9cd6ea8c2cb58f624d4e7Martijn Coenen     * more information about this service.
17152246087f4e2b5ad62b9cd6ea8c2cb58f624d4e7Martijn Coenen     */
17252246087f4e2b5ad62b9cd6ea8c2cb58f624d4e7Martijn Coenen    public static final String SERVICE_META_DATA =
17352246087f4e2b5ad62b9cd6ea8c2cb58f624d4e7Martijn Coenen            "android.nfc.cardemulation.host_apdu_service";
17452246087f4e2b5ad62b9cd6ea8c2cb58f624d4e7Martijn Coenen
17552246087f4e2b5ad62b9cd6ea8c2cb58f624d4e7Martijn Coenen    /**
1765eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * Reason for {@link #onDeactivated(int)}.
1775eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * Indicates deactivation was due to the NFC link
1785eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * being lost.
1795eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     */
1805eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    public static final int DEACTIVATION_LINK_LOSS = 0;
1815eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
1825eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    /**
1835eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * Reason for {@link #onDeactivated(int)}.
1845eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     *
1855eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * <p>Indicates deactivation was due to a different AID
1865eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * being selected (which implicitly deselects the AID
1875eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * currently active on the logical channel).
1885eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     *
1895eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * <p>Note that this next AID may still be resolved to this
190c85fb576b817aae2d853b6f5cb6effb924b892edMartijn Coenen     * service, in which case {@link #processCommandApdu(byte[], Bundle)}
1915eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * will be called again.
1925eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     */
1935eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    public static final int DEACTIVATION_DESELECTED = 1;
1945eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
1955eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    static final String TAG = "ApduService";
1965eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
1975eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    /**
1985eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * MSG_COMMAND_APDU is sent by NfcService when
1995eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * a 7816-4 command APDU has been received.
2005eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     *
2015eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * @hide
2025eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     */
2035eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    public static final int MSG_COMMAND_APDU = 0;
2045eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
2055eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    /**
2065eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * MSG_RESPONSE_APDU is sent to NfcService to send
2075eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * a response APDU back to the remote device.
2085eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     *
2095eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * @hide
2105eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     */
2115eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    public static final int MSG_RESPONSE_APDU = 1;
2125eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
2135eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    /**
2145eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * MSG_DEACTIVATED is sent by NfcService when
2155eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * the current session is finished; either because
2165eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * another AID was selected that resolved to
2175eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * another service, or because the NFC link
2185eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * was deactivated.
2195eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     *
2205eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * @hide
2215eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     */
2225eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    public static final int MSG_DEACTIVATED = 2;
2235eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
2245eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    /**
225583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     *
226583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * @hide
227583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     */
228583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen    public static final int MSG_UNHANDLED = 3;
229583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen
230583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen    /**
2315eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * @hide
2325eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     */
2335eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    public static final String KEY_DATA = "data";
2345eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
2355eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    /**
2365eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * Messenger interface to NfcService for sending responses.
2375eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * Only accessed on main thread by the message handler.
238583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     *
239583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * @hide
2405eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     */
2415eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    Messenger mNfcService = null;
2425eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
2435eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    final Messenger mMessenger = new Messenger(new MsgHandler());
2445eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
2455eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    final class MsgHandler extends Handler {
2465eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen        @Override
2475eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen        public void handleMessage(Message msg) {
2485eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen            switch (msg.what) {
2495eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen            case MSG_COMMAND_APDU:
2505eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                Bundle dataBundle = msg.getData();
2515eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                if (dataBundle == null) {
2525eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                    return;
2535eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                }
2545eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                if (mNfcService == null) mNfcService = msg.replyTo;
2555eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
2565eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                byte[] apdu = dataBundle.getByteArray(KEY_DATA);
2575eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                if (apdu != null) {
258fb2f8337f6fca9809156ddbaf0a7fe468bc1329fMartijn Coenen                    byte[] responseApdu = processCommandApdu(apdu, null);
2595eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                    if (responseApdu != null) {
2605eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                        if (mNfcService == null) {
2615eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                            Log.e(TAG, "Response not sent; service was deactivated.");
2625eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                            return;
2635eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                        }
2645eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                        Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU);
2655eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                        Bundle responseBundle = new Bundle();
2665eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                        responseBundle.putByteArray(KEY_DATA, responseApdu);
2675eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                        responseMsg.setData(responseBundle);
268583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                        responseMsg.replyTo = mMessenger;
2695eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                        try {
2705eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                            mNfcService.send(responseMsg);
2715eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                        } catch (RemoteException e) {
2725eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                            Log.e("TAG", "Response not sent; RemoteException calling into " +
2735eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                                    "NfcService.");
2745eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                        }
2755eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                    }
2765eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                } else {
2775eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                    Log.e(TAG, "Received MSG_COMMAND_APDU without data.");
2785eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                }
2795eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                break;
2805eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen            case MSG_RESPONSE_APDU:
2815eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                if (mNfcService == null) {
2825eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                    Log.e(TAG, "Response not sent; service was deactivated.");
2835eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                    return;
2845eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                }
2855eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                try {
286583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                    msg.replyTo = mMessenger;
2875eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                    mNfcService.send(msg);
2885eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                } catch (RemoteException e) {
2895eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                    Log.e(TAG, "RemoteException calling into NfcService.");
2905eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                }
2915eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                break;
2925eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen            case MSG_DEACTIVATED:
2935eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                // Make sure we won't call into NfcService again
2945eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                mNfcService = null;
2955eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                onDeactivated(msg.arg1);
2965eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                break;
297583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen            case MSG_UNHANDLED:
298583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                if (mNfcService == null) {
299583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                    Log.e(TAG, "notifyUnhandled not sent; service was deactivated.");
300583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                    return;
301583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                }
302583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                try {
303583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                    msg.replyTo = mMessenger;
304583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                    mNfcService.send(msg);
305583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                } catch (RemoteException e) {
306583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                    Log.e(TAG, "RemoteException calling into NfcService.");
307583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                }
308583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen                break;
3095eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen            default:
3105eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen                super.handleMessage(msg);
3115eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen            }
3125eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen        }
3135eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    }
3145eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
3155eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    @Override
3165eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    public final IBinder onBind(Intent intent) {
3175eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen        return mMessenger.getBinder();
3185eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    }
3195eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
3205eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    /**
3215eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * Sends a response APDU back to the remote device.
3225eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     *
3235eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * <p>Note: this method may be called from any thread and will not block.
3245eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * @param responseApdu A byte-array containing the reponse APDU.
3255eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     */
3265eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    public final void sendResponseApdu(byte[] responseApdu) {
3275eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen        Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU);
3285eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen        Bundle dataBundle = new Bundle();
3295eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen        dataBundle.putByteArray(KEY_DATA, responseApdu);
3305eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen        responseMsg.setData(dataBundle);
3315eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen        try {
3325eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen            mMessenger.send(responseMsg);
3335eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen        } catch (RemoteException e) {
3345eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen            Log.e("TAG", "Local messenger has died.");
3355eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen        }
3365eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    }
3375eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
3385eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    /**
339583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * Calling this method allows the service to tell the OS
340583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * that it won't be able to complete this transaction -
341583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * for example, because it requires data connectivity
342583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * that is not present at that moment.
343583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     *
344583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * The OS may use this indication to give the user a list
345583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * of alternative applications that can handle the last
346583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * AID that was selected. If the user would select an
347583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * application from the list, that action by itself
348583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * will not cause the default to be changed; the selected
349583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * application will be invoked for the next tap only.
350583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     *
351583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * If there are no other applications that can handle
352583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * this transaction, the OS will show an error dialog
353583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * indicating your service could not complete the
354583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * transaction.
355583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     *
356583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * <p>Note: this method may be called anywhere between
357c85fb576b817aae2d853b6f5cb6effb924b892edMartijn Coenen     *    the first {@link #processCommandApdu(byte[], Bundle)}
358583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     *    call and a {@link #onDeactivated(int)} call.
359583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     */
360583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen    public final void notifyUnhandled() {
361583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen        Message unhandledMsg = Message.obtain(null, MSG_UNHANDLED);
362583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen        try {
363583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen            mMessenger.send(unhandledMsg);
364583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen        } catch (RemoteException e) {
365583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen            Log.e("TAG", "Local messenger has died.");
366583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen        }
367583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen    }
368583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen
369583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen
370583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen    /**
371583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * <p>This method will be called when a command APDU has been received
372583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * from a remote device. A response APDU can be provided directly
373583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * by returning a byte-array in this method. Note that in general
374583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * response APDUs must be sent as quickly as possible, given the fact
375583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * that the user is likely holding his device over an NFC reader
376583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * when this method is called.
377583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     *
378583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * <p class="note">If there are multiple services that have registered for the same
379583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * AIDs in their meta-data entry, you will only get called if the user has
380583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * explicitly selected your service, either as a default or just for the next tap.
381583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     *
382583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * <p class="note">This method is running on the main thread of your application.
383583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * If you cannot return a response APDU immediately, return null
384583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * and use the {@link #sendResponseApdu(byte[])} method later.
385583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     *
38635bf6288527b177a04100585321a1266f020004aMartijn Coenen     * @param commandApdu The APDU that was received from the remote device
387583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * @param extras A bundle containing extra data. May be null.
388583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     * @return a byte-array containing the response APDU, or null if no
389583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     *         response APDU can be sent at this point.
390583fe9d22b21aa77fad3096bfdad3020d6088230Martijn Coenen     */
39151b6322197da054715e53d02754bc81caa8fd456Martijn Coenen    public abstract byte[] processCommandApdu(byte[] commandApdu, Bundle extras);
3925eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen
3935eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    /**
3945eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * This method will be called in two possible scenarios:
3955eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * <li>The NFC link has been deactivated or lost
3965eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * <li>A different AID has been selected and was resolved to a different
3975eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     *     service component
3985eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     * @param reason Either {@link #DEACTIVATION_LINK_LOSS} or {@link #DEACTIVATION_DESELECTED}
3995eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen     */
4005eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen    public abstract void onDeactivated(int reason);
4015eaa8970aa86fa32a6cbb47e0681000ed2d22c57Martijn Coenen}
402