Home | History | Annotate | Download | only in nfc
      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 android.nfc;
     18 
     19 import android.app.Activity;
     20 import android.os.RemoteException;
     21 import android.util.Log;
     22 
     23 import java.util.WeakHashMap;
     24 
     25 /**
     26  * Manages NFC API's that are coupled to the life-cycle of an Activity.
     27  *
     28  * <p>Uses a fragment to hook into onPause() and onResume() of the host
     29  * activities.
     30  *
     31  * <p>Ideally all of this management would be done in the NFC Service,
     32  * but right now it is much easier to do it in the application process.
     33  *
     34  * @hide
     35  */
     36 public final class NfcActivityManager extends INdefPushCallback.Stub {
     37     static final String TAG = NfcAdapter.TAG;
     38     static final Boolean DBG = false;
     39 
     40     final NfcAdapter mAdapter;
     41     final WeakHashMap<Activity, NfcActivityState> mNfcState;  // contents protected by this
     42     final NfcEvent mDefaultEvent;  // can re-use one NfcEvent because it just contains adapter
     43 
     44     /**
     45      * NFC state associated with an {@link Activity}
     46      */
     47     class NfcActivityState {
     48         boolean resumed = false;  // is the activity resumed
     49         NdefMessage ndefMessage;
     50         NfcAdapter.CreateNdefMessageCallback ndefMessageCallback;
     51         NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback;
     52         @Override
     53         public String toString() {
     54             StringBuilder s = new StringBuilder("[").append(resumed).append(" ");
     55             s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
     56             s.append(onNdefPushCompleteCallback).append("]");
     57             return s.toString();
     58         }
     59     }
     60 
     61     public NfcActivityManager(NfcAdapter adapter) {
     62         mAdapter = adapter;
     63         mNfcState = new WeakHashMap<Activity, NfcActivityState>();
     64         mDefaultEvent = new NfcEvent(mAdapter);
     65     }
     66 
     67     /**
     68      * onResume hook from fragment attached to activity
     69      */
     70     public synchronized void onResume(Activity activity) {
     71         NfcActivityState state = mNfcState.get(activity);
     72         if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
     73         if (state != null) {
     74             state.resumed = true;
     75             updateNfcService(state);
     76         }
     77     }
     78 
     79     /**
     80      * onPause hook from fragment attached to activity
     81      */
     82     public synchronized void onPause(Activity activity) {
     83         NfcActivityState state = mNfcState.get(activity);
     84         if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
     85         if (state != null) {
     86             state.resumed = false;
     87             updateNfcService(state);
     88         }
     89     }
     90 
     91     /**
     92      * onDestroy hook from fragment attached to activity
     93      */
     94     public void onDestroy(Activity activity) {
     95         mNfcState.remove(activity);
     96     }
     97 
     98     public synchronized void setNdefPushMessage(Activity activity, NdefMessage message) {
     99         NfcActivityState state = getOrCreateState(activity, message != null);
    100         if (state == null || state.ndefMessage == message) {
    101             return;  // nothing more to do;
    102         }
    103         state.ndefMessage = message;
    104         if (message == null) {
    105             maybeRemoveState(activity, state);
    106         }
    107         if (state.resumed) {
    108             updateNfcService(state);
    109         }
    110     }
    111 
    112     public synchronized void setNdefPushMessageCallback(Activity activity,
    113             NfcAdapter.CreateNdefMessageCallback callback) {
    114         NfcActivityState state = getOrCreateState(activity, callback != null);
    115         if (state == null || state.ndefMessageCallback == callback) {
    116             return;  // nothing more to do;
    117         }
    118         state.ndefMessageCallback = callback;
    119         if (callback == null) {
    120             maybeRemoveState(activity, state);
    121         }
    122         if (state.resumed) {
    123             updateNfcService(state);
    124         }
    125     }
    126 
    127     public synchronized void setOnNdefPushCompleteCallback(Activity activity,
    128             NfcAdapter.OnNdefPushCompleteCallback callback) {
    129         NfcActivityState state = getOrCreateState(activity, callback != null);
    130         if (state == null || state.onNdefPushCompleteCallback == callback) {
    131             return;  // nothing more to do;
    132         }
    133         state.onNdefPushCompleteCallback = callback;
    134         if (callback == null) {
    135             maybeRemoveState(activity, state);
    136         }
    137         if (state.resumed) {
    138             updateNfcService(state);
    139         }
    140     }
    141 
    142     /**
    143      * Get the NfcActivityState for the specified Activity.
    144      * If create is true, then create it if it doesn't already exist,
    145      * and ensure the NFC fragment is attached to the activity.
    146      */
    147     synchronized NfcActivityState getOrCreateState(Activity activity, boolean create) {
    148         if (DBG) Log.d(TAG, "getOrCreateState " + activity + " " + create);
    149         NfcActivityState state = mNfcState.get(activity);
    150         if (state == null && create) {
    151             state = new NfcActivityState();
    152             mNfcState.put(activity, state);
    153             NfcFragment.attach(activity);
    154         }
    155         return state;
    156     }
    157 
    158     /**
    159      * If the NfcActivityState is empty then remove it, and
    160      * detach it from the Activity.
    161      */
    162     synchronized void maybeRemoveState(Activity activity, NfcActivityState state) {
    163         if (state.ndefMessage == null && state.ndefMessageCallback == null &&
    164                 state.onNdefPushCompleteCallback == null) {
    165             NfcFragment.remove(activity);
    166             mNfcState.remove(activity);
    167         }
    168     }
    169 
    170     /**
    171      * Register NfcActivityState with the NFC service.
    172      */
    173     synchronized void updateNfcService(NfcActivityState state) {
    174         boolean serviceCallbackNeeded = state.ndefMessageCallback != null ||
    175                 state.onNdefPushCompleteCallback != null;
    176 
    177         try {
    178             NfcAdapter.sService.setForegroundNdefPush(state.resumed ? state.ndefMessage : null,
    179                     state.resumed && serviceCallbackNeeded ? this : null);
    180         } catch (RemoteException e) {
    181             mAdapter.attemptDeadServiceRecovery(e);
    182         }
    183     }
    184 
    185     /**
    186      * Callback from NFC service
    187      */
    188     @Override
    189     public NdefMessage createMessage() {
    190         NfcAdapter.CreateNdefMessageCallback callback = null;
    191         synchronized (NfcActivityManager.this) {
    192             for (NfcActivityState state : mNfcState.values()) {
    193                 if (state.resumed) {
    194                     callback = state.ndefMessageCallback;
    195                 }
    196             }
    197         }
    198 
    199         // drop lock before making callback
    200         if (callback != null) {
    201             return callback.createNdefMessage(mDefaultEvent);
    202         }
    203         return null;
    204     }
    205 
    206     /**
    207      * Callback from NFC service
    208      */
    209     @Override
    210     public void onNdefPushComplete() {
    211         NfcAdapter.OnNdefPushCompleteCallback callback = null;
    212         synchronized (NfcActivityManager.this) {
    213             for (NfcActivityState state : mNfcState.values()) {
    214                 if (state.resumed) {
    215                     callback = state.onNdefPushCompleteCallback;
    216                 }
    217             }
    218         }
    219 
    220         // drop lock before making callback
    221         if (callback != null) {
    222             callback.onNdefPushComplete(mDefaultEvent);
    223         }
    224     }
    225 
    226 }
    227