NfcAdapterExtras.java revision cc9ee72bd42bb40b1852f907f58305adde12ecc2
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.nfc_extras;
18
19import android.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
21import android.nfc.ApduList;
22import android.nfc.INfcAdapterExtras;
23import android.nfc.NfcAdapter;
24import android.os.RemoteException;
25import android.util.Log;
26
27/**
28 * Provides additional methods on an {@link NfcAdapter} for Card Emulation
29 * and management of {@link NfcExecutionEnvironment}'s.
30 *
31 * There is a 1-1 relationship between an {@link NfcAdapterExtras} object and
32 * a {@link NfcAdapter} object.
33 */
34public final class NfcAdapterExtras {
35    private static final String TAG = "NfcAdapterExtras";
36
37    /**
38     * Broadcast Action: an RF field ON has been detected.
39     *
40     * <p class="note">This is an unreliable signal, and will be removed.
41     * <p class="note">
42     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission
43     * to receive.
44     */
45    public static final String ACTION_RF_FIELD_ON_DETECTED =
46            "com.android.nfc_extras.action.RF_FIELD_ON_DETECTED";
47
48    /**
49     * Broadcast Action: an RF field OFF has been detected.
50     *
51     * <p class="note">This is an unreliable signal, and will be removed.
52     * <p class="note">
53     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission
54     * to receive.
55     */
56    public static final String ACTION_RF_FIELD_OFF_DETECTED =
57            "com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED";
58
59    // protected by NfcAdapterExtras.class, and final after first construction,
60    // except for attemptDeadServiceRecovery() when NFC crashes - we accept a
61    // best effort recovery
62    private static NfcAdapter sAdapter;
63    private static INfcAdapterExtras sService;
64    private static NfcAdapterExtras sSingleton;
65    private static NfcExecutionEnvironment sEmbeddedEe;
66    private static CardEmulationRoute sRouteOff;
67    private static CardEmulationRoute sRouteOnWhenScreenOn;
68
69    /** get service handles */
70    private static void initService() {
71        sService = sAdapter.getNfcAdapterExtrasInterface();
72    }
73
74    /**
75     * Get the {@link NfcAdapterExtras} for the given {@link NfcAdapter}.
76     *
77     * <p class="note">
78     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
79     *
80     * @param adapter a {@link NfcAdapter}, must not be null
81     * @return the {@link NfcAdapterExtras} object for the given {@link NfcAdapter}
82     */
83    public static NfcAdapterExtras get(NfcAdapter adapter) {
84        synchronized(NfcAdapterExtras.class) {
85            if (sSingleton == null) {
86                try {
87                    sAdapter = adapter;
88                    sRouteOff = new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null);
89                    sSingleton = new NfcAdapterExtras();
90                    sEmbeddedEe = new NfcExecutionEnvironment(sSingleton);
91                    sRouteOnWhenScreenOn = new CardEmulationRoute(
92                            CardEmulationRoute.ROUTE_ON_WHEN_SCREEN_ON, sEmbeddedEe);
93                    initService();
94                } finally {
95                    if (sSingleton == null) {
96                        sService = null;
97                        sEmbeddedEe = null;
98                        sRouteOff = null;
99                        sRouteOnWhenScreenOn = null;
100                    }
101                }
102            }
103            return sSingleton;
104        }
105    }
106
107    private NfcAdapterExtras() {}
108
109    /**
110     * Immutable data class that describes a card emulation route.
111     */
112    public final static class CardEmulationRoute {
113        /**
114         * Card Emulation is turned off on this NfcAdapter.
115         * <p>This is the default routing state after boot.
116         */
117        public static final int ROUTE_OFF = 1;
118
119        /**
120         * Card Emulation is routed to {@link #nfcEe} only when the screen is on,
121         * otherwise it is turned off.
122         */
123        public static final int ROUTE_ON_WHEN_SCREEN_ON = 2;
124
125        /**
126         * A route such as {@link #ROUTE_OFF} or {@link #ROUTE_ON_WHEN_SCREEN_ON}.
127         */
128        public final int route;
129
130        /**
131         * The {@link NFcExecutionEnvironment} that is Card Emulation is routed to.
132         * <p>null if {@link #route} is {@link #ROUTE_OFF}, otherwise not null.
133         */
134        public final NfcExecutionEnvironment nfcEe;
135
136        public CardEmulationRoute(int route, NfcExecutionEnvironment nfcEe) {
137            if (route == ROUTE_OFF && nfcEe != null) {
138                throw new IllegalArgumentException("must not specifiy a NFC-EE with ROUTE_OFF");
139            } else if (route != ROUTE_OFF && nfcEe == null) {
140                throw new IllegalArgumentException("must specifiy a NFC-EE for this route");
141            }
142            this.route = route;
143            this.nfcEe = nfcEe;
144        }
145    }
146
147    /**
148     * NFC service dead - attempt best effort recovery
149     */
150    void attemptDeadServiceRecovery(Exception e) {
151        Log.e(TAG, "NFC Adapter Extras dead - attempting to recover");
152        sAdapter.attemptDeadServiceRecovery(e);
153        initService();
154    }
155
156    INfcAdapterExtras getService() {
157        return sService;
158    }
159
160    /**
161     * Get the routing state of this NFC EE.
162     *
163     * <p class="note">
164     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
165     *
166     * @return
167     */
168    public CardEmulationRoute getCardEmulationRoute() {
169        try {
170            int route = sService.getCardEmulationRoute();
171            return route == CardEmulationRoute.ROUTE_OFF ?
172                    sRouteOff :
173                    sRouteOnWhenScreenOn;
174        } catch (RemoteException e) {
175            attemptDeadServiceRecovery(e);
176            return sRouteOff;
177        }
178    }
179
180    /**
181     * Set the routing state of this NFC EE.
182     *
183     * <p>This routing state is not persisted across reboot.
184     *
185     * <p class="note">
186     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
187     *
188     * @param route a {@link #CardEmulationRoute}
189     */
190    public void setCardEmulationRoute(CardEmulationRoute route) {
191        try {
192            sService.setCardEmulationRoute(route.route);
193        } catch (RemoteException e) {
194            attemptDeadServiceRecovery(e);
195        }
196    }
197
198    /**
199     * Get the {@link NfcExecutionEnvironment} that is embedded with the
200     * {@link NFcAdapter}.
201     *
202     * <p class="note">
203     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
204     *
205     * @return a {@link NfcExecutionEnvironment}, or null if there is no embedded NFC-EE
206     */
207    public NfcExecutionEnvironment getEmbeddedExecutionEnvironment() {
208        return sEmbeddedEe;
209    }
210
211    public void registerTearDownApdus(String packageName, ApduList apdus) {
212        try {
213            sService.registerTearDownApdus(packageName, apdus);
214        } catch (RemoteException e) {
215            attemptDeadServiceRecovery(e);
216        }
217    }
218
219    public void unregisterTearDownApdus(String packageName) {
220        try {
221            sService.unregisterTearDownApdus(packageName);
222        } catch (RemoteException e) {
223            attemptDeadServiceRecovery(e);
224        }
225    }
226}
227