Home | History | Annotate | Download | only in nfc_extras
      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 java.util.HashMap;
     20 
     21 import android.content.Context;
     22 import android.nfc.INfcAdapterExtras;
     23 import android.nfc.NfcAdapter;
     24 import android.os.RemoteException;
     25 import 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  */
     34 public 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 INfcAdapterExtras sService;
     63     private static final CardEmulationRoute ROUTE_OFF =
     64             new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null);
     65 
     66     // contents protected by NfcAdapterExtras.class
     67     private static final HashMap<NfcAdapter, NfcAdapterExtras> sNfcExtras = new HashMap();
     68 
     69     private final NfcExecutionEnvironment mEmbeddedEe;
     70     private final CardEmulationRoute mRouteOnWhenScreenOn;
     71 
     72     private final NfcAdapter mAdapter;
     73     final String mPackageName;
     74 
     75     /** get service handles */
     76     private static void initService(NfcAdapter adapter) {
     77         final INfcAdapterExtras service = adapter.getNfcAdapterExtrasInterface();
     78         if (service != null) {
     79             // Leave stale rather than receive a null value.
     80             sService = service;
     81         }
     82     }
     83 
     84     /**
     85      * Get the {@link NfcAdapterExtras} for the given {@link NfcAdapter}.
     86      *
     87      * <p class="note">
     88      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
     89      *
     90      * @param adapter a {@link NfcAdapter}, must not be null
     91      * @return the {@link NfcAdapterExtras} object for the given {@link NfcAdapter}
     92      */
     93     public static NfcAdapterExtras get(NfcAdapter adapter) {
     94         Context context = adapter.getContext();
     95         if (context == null) {
     96             throw new UnsupportedOperationException(
     97                     "You must pass a context to your NfcAdapter to use the NFC extras APIs");
     98         }
     99 
    100         synchronized (NfcAdapterExtras.class) {
    101             if (sService == null) {
    102                 initService(adapter);
    103             }
    104             NfcAdapterExtras extras = sNfcExtras.get(adapter);
    105             if (extras == null) {
    106                 extras = new NfcAdapterExtras(adapter);
    107                 sNfcExtras.put(adapter,  extras);
    108             }
    109             return extras;
    110         }
    111     }
    112 
    113     private NfcAdapterExtras(NfcAdapter adapter) {
    114         mAdapter = adapter;
    115         mPackageName = adapter.getContext().getPackageName();
    116         mEmbeddedEe = new NfcExecutionEnvironment(this);
    117         mRouteOnWhenScreenOn = new CardEmulationRoute(CardEmulationRoute.ROUTE_ON_WHEN_SCREEN_ON,
    118                 mEmbeddedEe);
    119     }
    120 
    121     /**
    122      * Immutable data class that describes a card emulation route.
    123      */
    124     public final static class CardEmulationRoute {
    125         /**
    126          * Card Emulation is turned off on this NfcAdapter.
    127          * <p>This is the default routing state after boot.
    128          */
    129         public static final int ROUTE_OFF = 1;
    130 
    131         /**
    132          * Card Emulation is routed to {@link #nfcEe} only when the screen is on,
    133          * otherwise it is turned off.
    134          */
    135         public static final int ROUTE_ON_WHEN_SCREEN_ON = 2;
    136 
    137         /**
    138          * A route such as {@link #ROUTE_OFF} or {@link #ROUTE_ON_WHEN_SCREEN_ON}.
    139          */
    140         public final int route;
    141 
    142         /**
    143          * The {@link NFcExecutionEnvironment} that is Card Emulation is routed to.
    144          * <p>null if {@link #route} is {@link #ROUTE_OFF}, otherwise not null.
    145          */
    146         public final NfcExecutionEnvironment nfcEe;
    147 
    148         public CardEmulationRoute(int route, NfcExecutionEnvironment nfcEe) {
    149             if (route == ROUTE_OFF && nfcEe != null) {
    150                 throw new IllegalArgumentException("must not specifiy a NFC-EE with ROUTE_OFF");
    151             } else if (route != ROUTE_OFF && nfcEe == null) {
    152                 throw new IllegalArgumentException("must specifiy a NFC-EE for this route");
    153             }
    154             this.route = route;
    155             this.nfcEe = nfcEe;
    156         }
    157     }
    158 
    159     /**
    160      * NFC service dead - attempt best effort recovery
    161      */
    162     void attemptDeadServiceRecovery(Exception e) {
    163         Log.e(TAG, "NFC Adapter Extras dead - attempting to recover");
    164         mAdapter.attemptDeadServiceRecovery(e);
    165         initService(mAdapter);
    166     }
    167 
    168     INfcAdapterExtras getService() {
    169         return sService;
    170     }
    171 
    172     /**
    173      * Get the routing state of this NFC EE.
    174      *
    175      * <p class="note">
    176      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
    177      */
    178     public CardEmulationRoute getCardEmulationRoute() {
    179         try {
    180             int route = sService.getCardEmulationRoute(mPackageName);
    181             return route == CardEmulationRoute.ROUTE_OFF ?
    182                     ROUTE_OFF :
    183                     mRouteOnWhenScreenOn;
    184         } catch (RemoteException e) {
    185             attemptDeadServiceRecovery(e);
    186             return ROUTE_OFF;
    187         }
    188     }
    189 
    190     /**
    191      * Set the routing state of this NFC EE.
    192      *
    193      * <p>This routing state is not persisted across reboot.
    194      *
    195      * <p class="note">
    196      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
    197      *
    198      * @param route a {@link CardEmulationRoute}
    199      */
    200     public void setCardEmulationRoute(CardEmulationRoute route) {
    201         try {
    202             sService.setCardEmulationRoute(mPackageName, route.route);
    203         } catch (RemoteException e) {
    204             attemptDeadServiceRecovery(e);
    205         }
    206     }
    207 
    208     /**
    209      * Get the {@link NfcExecutionEnvironment} that is embedded with the
    210      * {@link NfcAdapter}.
    211      *
    212      * <p class="note">
    213      * Requires the {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
    214      *
    215      * @return a {@link NfcExecutionEnvironment}, or null if there is no embedded NFC-EE
    216      */
    217     public NfcExecutionEnvironment getEmbeddedExecutionEnvironment() {
    218         return mEmbeddedEe;
    219     }
    220 
    221     /**
    222      * Authenticate the client application.
    223      *
    224      * Some implementations of NFC Adapter Extras may require applications
    225      * to authenticate with a token, before using other methods.
    226      *
    227      * @param token a implementation specific token
    228      * @throws java.lang.SecurityException if authentication failed
    229      */
    230     public void authenticate(byte[] token) {
    231         try {
    232             sService.authenticate(mPackageName, token);
    233         } catch (RemoteException e) {
    234             attemptDeadServiceRecovery(e);
    235         }
    236     }
    237 
    238     /**
    239      * Returns the name of this adapter's driver.
    240      *
    241      * <p>Different NFC adapters may use different drivers.  This value is
    242      * informational and should not be parsed.
    243      *
    244      * @return the driver name, or empty string if unknown
    245      */
    246     public String getDriverName() {
    247         try {
    248             return sService.getDriverName(mPackageName);
    249         } catch (RemoteException e) {
    250             attemptDeadServiceRecovery(e);
    251             return "";
    252         }
    253     }
    254 }
    255