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 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