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