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