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