NfcAdapterExtras.java revision bb951c893973691554f49d2e725985125f866b27
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.content.Context;
20import android.nfc.INfcAdapterExtras;
21import android.nfc.NfcAdapter;
22import android.os.RemoteException;
23import android.util.Log;
24
25/**
26 * Provides additional methods on an {@link NfcAdapter} for Card Emulation
27 * and management of {@link NfcExecutionEnvironment}'s.
28 *
29 * There is a 1-1 relationship between an {@link NfcAdapterExtras} object and
30 * a {@link NfcAdapter} object.
31 */
32public final class NfcAdapterExtras {
33    private static final String TAG = "NfcAdapterExtras";
34
35    /**
36     * Broadcast Action: an RF field ON has been detected.
37     *
38     * <p class="note">This is an unreliable signal, and will be removed.
39     * <p class="note">
40     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission
41     * to receive.
42     */
43    public static final String ACTION_RF_FIELD_ON_DETECTED =
44            "com.android.nfc_extras.action.RF_FIELD_ON_DETECTED";
45
46    /**
47     * Broadcast Action: an RF field OFF has been detected.
48     *
49     * <p class="note">This is an unreliable signal, and will be removed.
50     * <p class="note">
51     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission
52     * to receive.
53     */
54    public static final String ACTION_RF_FIELD_OFF_DETECTED =
55            "com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED";
56
57    // protected by NfcAdapterExtras.class, and final after first construction,
58    // except for attemptDeadServiceRecovery() when NFC crashes - we accept a
59    // best effort recovery
60    private static NfcAdapter sAdapter;
61    private static INfcAdapterExtras sService;
62    private static final CardEmulationRoute ROUTE_OFF =
63            new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null);
64
65    private final NfcExecutionEnvironment mEmbeddedEe;
66    private final CardEmulationRoute mRouteOnWhenScreenOn;
67
68    final Context mContext;
69    final String mPackageName;
70
71    /** get service handles */
72    private static void initService() {
73        final INfcAdapterExtras service = sAdapter.getNfcAdapterExtrasInterface();
74        if (service != null) {
75            // Leave stale rather than receive a null value.
76            sService = service;
77        }
78    }
79
80    /**
81     * Get the {@link NfcAdapterExtras} for the given {@link NfcAdapter}.
82     *
83     * <p class="note">
84     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
85     *
86     * @param adapter a {@link NfcAdapter}, must not be null
87     * @return the {@link NfcAdapterExtras} object for the given {@link NfcAdapter}
88     */
89    public static NfcAdapterExtras get(NfcAdapter adapter) {
90        Context context = adapter.getContext();
91        if (context == null) {
92            throw new UnsupportedOperationException(
93                    "You must pass a context to your NfcAdapter to use the NFC extras APIs");
94        }
95
96        synchronized (NfcAdapterExtras.class) {
97            if (sService == null) {
98                try {
99                    sAdapter = adapter;
100                    initService();
101                } finally {
102                    if (sService == null) {
103                        sAdapter = null;
104                    }
105                }
106            }
107        }
108
109        return new NfcAdapterExtras(context);
110    }
111
112    private NfcAdapterExtras(Context context) {
113        mContext = context.getApplicationContext();
114        mPackageName = context.getPackageName();
115        mEmbeddedEe = new NfcExecutionEnvironment(this);
116        mRouteOnWhenScreenOn = new CardEmulationRoute(CardEmulationRoute.ROUTE_ON_WHEN_SCREEN_ON,
117                mEmbeddedEe);
118    }
119
120    /**
121     * Immutable data class that describes a card emulation route.
122     */
123    public final static class CardEmulationRoute {
124        /**
125         * Card Emulation is turned off on this NfcAdapter.
126         * <p>This is the default routing state after boot.
127         */
128        public static final int ROUTE_OFF = 1;
129
130        /**
131         * Card Emulation is routed to {@link #nfcEe} only when the screen is on,
132         * otherwise it is turned off.
133         */
134        public static final int ROUTE_ON_WHEN_SCREEN_ON = 2;
135
136        /**
137         * A route such as {@link #ROUTE_OFF} or {@link #ROUTE_ON_WHEN_SCREEN_ON}.
138         */
139        public final int route;
140
141        /**
142         * The {@link NFcExecutionEnvironment} that is Card Emulation is routed to.
143         * <p>null if {@link #route} is {@link #ROUTE_OFF}, otherwise not null.
144         */
145        public final NfcExecutionEnvironment nfcEe;
146
147        public CardEmulationRoute(int route, NfcExecutionEnvironment nfcEe) {
148            if (route == ROUTE_OFF && nfcEe != null) {
149                throw new IllegalArgumentException("must not specifiy a NFC-EE with ROUTE_OFF");
150            } else if (route != ROUTE_OFF && nfcEe == null) {
151                throw new IllegalArgumentException("must specifiy a NFC-EE for this route");
152            }
153            this.route = route;
154            this.nfcEe = nfcEe;
155        }
156    }
157
158    /**
159     * NFC service dead - attempt best effort recovery
160     */
161    void attemptDeadServiceRecovery(Exception e) {
162        Log.e(TAG, "NFC Adapter Extras dead - attempting to recover");
163        sAdapter.attemptDeadServiceRecovery(e);
164        initService();
165    }
166
167    INfcAdapterExtras getService() {
168        return sService;
169    }
170
171    /**
172     * Get the routing state of this NFC EE.
173     *
174     * <p class="note">
175     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
176     */
177    public CardEmulationRoute getCardEmulationRoute() {
178        try {
179            int route = sService.getCardEmulationRoute(mPackageName);
180            return route == CardEmulationRoute.ROUTE_OFF ?
181                    ROUTE_OFF :
182                    mRouteOnWhenScreenOn;
183        } catch (RemoteException e) {
184            attemptDeadServiceRecovery(e);
185            return ROUTE_OFF;
186        }
187    }
188
189    /**
190     * Set the routing state of this NFC EE.
191     *
192     * <p>This routing state is not persisted across reboot.
193     *
194     * <p class="note">
195     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
196     *
197     * @param route a {@link CardEmulationRoute}
198     */
199    public void setCardEmulationRoute(CardEmulationRoute route) {
200        try {
201            sService.setCardEmulationRoute(mPackageName, route.route);
202        } catch (RemoteException e) {
203            attemptDeadServiceRecovery(e);
204        }
205    }
206
207    /**
208     * Get the {@link NfcExecutionEnvironment} that is embedded with the
209     * {@link NfcAdapter}.
210     *
211     * <p class="note">
212     * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
213     *
214     * @return a {@link NfcExecutionEnvironment}, or null if there is no embedded NFC-EE
215     */
216    public NfcExecutionEnvironment getEmbeddedExecutionEnvironment() {
217        return mEmbeddedEe;
218    }
219
220    /**
221     * Authenticate the client application.
222     *
223     * Some implementations of NFC Adapter Extras may require applications
224     * to authenticate with a token, before using other methods.
225     *
226     * @param token a implementation specific token
227     * @throws java.lang.SecurityException if authentication failed
228     */
229    public void authenticate(byte[] token) {
230        try {
231            sService.authenticate(mPackageName, token);
232        } catch (RemoteException e) {
233            attemptDeadServiceRecovery(e);
234        }
235    }
236}
237