blob: 2c730564fb7bb3abb64d387598b6fac038d1307f [file] [log] [blame]
Nick Pellyc84c89a2011-08-22 22:27:11 -07001/*
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
17package android.nfc;
18
19import android.app.Activity;
Nick Pelly8ce7a272012-03-21 15:14:09 -070020import android.app.Application;
21import android.os.Bundle;
Nick Pellyc84c89a2011-08-22 22:27:11 -070022import android.os.RemoteException;
23import android.util.Log;
24
Nick Pelly8ce7a272012-03-21 15:14:09 -070025import java.util.ArrayList;
26import java.util.LinkedList;
27import java.util.List;
Nick Pellyc84c89a2011-08-22 22:27:11 -070028
29/**
30 * Manages NFC API's that are coupled to the life-cycle of an Activity.
31 *
Nick Pelly8ce7a272012-03-21 15:14:09 -070032 * <p>Uses {@link Application#registerActivityLifecycleCallbacks} to hook
33 * into activity life-cycle events such as onPause() and onResume().
Nick Pellyc84c89a2011-08-22 22:27:11 -070034 *
35 * @hide
36 */
Nick Pelly8ce7a272012-03-21 15:14:09 -070037public final class NfcActivityManager extends INdefPushCallback.Stub
38 implements Application.ActivityLifecycleCallbacks {
Nick Pellyc84c89a2011-08-22 22:27:11 -070039 static final String TAG = NfcAdapter.TAG;
40 static final Boolean DBG = false;
41
42 final NfcAdapter mAdapter;
Nick Pelly8ce7a272012-03-21 15:14:09 -070043 final NfcEvent mDefaultEvent; // cached NfcEvent (its currently always the same)
44
45 // All objects in the lists are protected by this
46 final List<NfcApplicationState> mApps; // Application(s) that have NFC state. Usually one
47 final List<NfcActivityState> mActivities; // Activities that have NFC state
48
49 /**
50 * NFC State associated with an {@link Application}.
51 */
52 class NfcApplicationState {
53 int refCount = 0;
54 final Application app;
55 public NfcApplicationState(Application app) {
56 this.app = app;
57 }
58 public void register() {
59 refCount++;
60 if (refCount == 1) {
61 this.app.registerActivityLifecycleCallbacks(NfcActivityManager.this);
62 }
63 }
64 public void unregister() {
65 refCount--;
66 if (refCount == 0) {
67 this.app.unregisterActivityLifecycleCallbacks(NfcActivityManager.this);
68 } else if (refCount < 0) {
69 Log.e(TAG, "-ve refcount for " + app);
70 }
71 }
72 }
73
74 NfcApplicationState findAppState(Application app) {
75 for (NfcApplicationState appState : mApps) {
76 if (appState.app == app) {
77 return appState;
78 }
79 }
80 return null;
81 }
82
83 void registerApplication(Application app) {
84 NfcApplicationState appState = findAppState(app);
85 if (appState == null) {
86 appState = new NfcApplicationState(app);
87 mApps.add(appState);
88 }
89 appState.register();
90 }
91
92 void unregisterApplication(Application app) {
93 NfcApplicationState appState = findAppState(app);
94 if (appState == null) {
95 Log.e(TAG, "app was not registered " + app);
96 return;
97 }
98 appState.unregister();
99 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700100
101 /**
102 * NFC state associated with an {@link Activity}
103 */
104 class NfcActivityState {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700105 boolean resumed = false;
106 Activity activity;
107 NdefMessage ndefMessage = null; // static NDEF message
108 NfcAdapter.CreateNdefMessageCallback ndefMessageCallback = null;
109 NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
110 public NfcActivityState(Activity activity) {
111 if (activity.getWindow().isDestroyed()) {
112 throw new IllegalStateException("activity is already destroyed");
113 }
114 this.activity = activity;
115 registerApplication(activity.getApplication());
116 }
117 public void destroy() {
118 unregisterApplication(activity.getApplication());
119 resumed = false;
120 activity = null;
121 ndefMessage = null;
122 ndefMessageCallback = null;
123 onNdefPushCompleteCallback = null;
124 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700125 @Override
126 public String toString() {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700127 StringBuilder s = new StringBuilder("[").append(" ");
Nick Pellyc84c89a2011-08-22 22:27:11 -0700128 s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
129 s.append(onNdefPushCompleteCallback).append("]");
130 return s.toString();
131 }
132 }
133
Nick Pelly8ce7a272012-03-21 15:14:09 -0700134 /** find activity state from mActivities */
135 synchronized NfcActivityState findActivityState(Activity activity) {
136 for (NfcActivityState state : mActivities) {
137 if (state.activity == activity) {
138 return state;
139 }
140 }
141 return null;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700142 }
143
Nick Pelly8ce7a272012-03-21 15:14:09 -0700144 /** find or create activity state from mActivities */
145 synchronized NfcActivityState getActivityState(Activity activity) {
146 NfcActivityState state = findActivityState(activity);
147 if (state == null) {
148 state = new NfcActivityState(activity);
149 mActivities.add(state);
Nick Pellyc84c89a2011-08-22 22:27:11 -0700150 }
151 return state;
152 }
153
Nick Pelly8ce7a272012-03-21 15:14:09 -0700154 synchronized NfcActivityState findResumedActivityState() {
155 for (NfcActivityState state : mActivities) {
156 if (state.resumed) {
157 return state;
158 }
159 }
160 return null;
161 }
162
163 synchronized void destroyActivityState(Activity activity) {
164 NfcActivityState activityState = findActivityState(activity);
165 if (activityState != null) {
166 activityState.destroy();
167 mActivities.remove(activityState);
168 }
169 }
170
171 public NfcActivityManager(NfcAdapter adapter) {
172 mAdapter = adapter;
173 mActivities = new LinkedList<NfcActivityState>();
174 mApps = new ArrayList<NfcApplicationState>(1); // Android VM usually has 1 app
175 mDefaultEvent = new NfcEvent(mAdapter);
176 }
177
178 public void setNdefPushMessage(Activity activity, NdefMessage message) {
179 boolean isResumed;
180 synchronized (NfcActivityManager.this) {
181 NfcActivityState state = getActivityState(activity);
182 state.ndefMessage = message;
183 isResumed = state.resumed;
184 }
185 if (isResumed) {
186 requestNfcServiceCallback(true);
187 }
188 }
189
190 public void setNdefPushMessageCallback(Activity activity,
191 NfcAdapter.CreateNdefMessageCallback callback) {
192 boolean isResumed;
193 synchronized (NfcActivityManager.this) {
194 NfcActivityState state = getActivityState(activity);
195 state.ndefMessageCallback = callback;
196 isResumed = state.resumed;
197 }
198 if (isResumed) {
199 requestNfcServiceCallback(true);
200 }
201 }
202
203 public void setOnNdefPushCompleteCallback(Activity activity,
204 NfcAdapter.OnNdefPushCompleteCallback callback) {
205 boolean isResumed;
206 synchronized (NfcActivityManager.this) {
207 NfcActivityState state = getActivityState(activity);
208 state.onNdefPushCompleteCallback = callback;
209 isResumed = state.resumed;
210 }
211 if (isResumed) {
212 requestNfcServiceCallback(true);
Nick Pellyc84c89a2011-08-22 22:27:11 -0700213 }
214 }
215
216 /**
Nick Pelly8ce7a272012-03-21 15:14:09 -0700217 * Request or unrequest NFC service callbacks for NDEF push.
218 * Makes IPC call - do not hold lock.
219 * TODO: Do not do IPC on every onPause/onResume
Nick Pellyc84c89a2011-08-22 22:27:11 -0700220 */
Nick Pelly8ce7a272012-03-21 15:14:09 -0700221 void requestNfcServiceCallback(boolean request) {
Nick Pellyc84c89a2011-08-22 22:27:11 -0700222 try {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700223 NfcAdapter.sService.setNdefPushCallback(request ? this : null);
Nick Pellyc84c89a2011-08-22 22:27:11 -0700224 } catch (RemoteException e) {
225 mAdapter.attemptDeadServiceRecovery(e);
226 }
227 }
228
Nick Pelly8ce7a272012-03-21 15:14:09 -0700229 /** Callback from NFC service, usually on binder thread */
Nick Pellyc84c89a2011-08-22 22:27:11 -0700230 @Override
231 public NdefMessage createMessage() {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700232 NfcAdapter.CreateNdefMessageCallback callback;
233 NdefMessage message;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700234 synchronized (NfcActivityManager.this) {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700235 NfcActivityState state = findResumedActivityState();
236 if (state == null) return null;
237
238 callback = state.ndefMessageCallback;
239 message = state.ndefMessage;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700240 }
241
Nick Pelly8ce7a272012-03-21 15:14:09 -0700242 // Make callback without lock
Nick Pellyc84c89a2011-08-22 22:27:11 -0700243 if (callback != null) {
244 return callback.createNdefMessage(mDefaultEvent);
Nick Pelly8ce7a272012-03-21 15:14:09 -0700245 } else {
246 return message;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700247 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700248 }
249
Nick Pelly8ce7a272012-03-21 15:14:09 -0700250 /** Callback from NFC service, usually on binder thread */
Nick Pellyc84c89a2011-08-22 22:27:11 -0700251 @Override
252 public void onNdefPushComplete() {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700253 NfcAdapter.OnNdefPushCompleteCallback callback;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700254 synchronized (NfcActivityManager.this) {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700255 NfcActivityState state = findResumedActivityState();
256 if (state == null) return;
257
258 callback = state.onNdefPushCompleteCallback;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700259 }
260
Nick Pelly8ce7a272012-03-21 15:14:09 -0700261 // Make callback without lock
Nick Pellyc84c89a2011-08-22 22:27:11 -0700262 if (callback != null) {
263 callback.onNdefPushComplete(mDefaultEvent);
264 }
265 }
Martijn Coenen3433a8a2011-09-01 19:18:02 -0700266
Nick Pelly8ce7a272012-03-21 15:14:09 -0700267 /** Callback from Activity life-cycle, on main thread */
268 @Override
269 public void onActivityCreated(Activity activity, Bundle savedInstanceState) { /* NO-OP */ }
270
271 /** Callback from Activity life-cycle, on main thread */
272 @Override
273 public void onActivityStarted(Activity activity) { /* NO-OP */ }
274
275 /** Callback from Activity life-cycle, on main thread */
276 @Override
277 public void onActivityResumed(Activity activity) {
278 synchronized (NfcActivityManager.this) {
279 NfcActivityState state = findActivityState(activity);
280 if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
281 if (state == null) return;
282 state.resumed = true;
283 }
284 requestNfcServiceCallback(true);
285 }
286
287 /** Callback from Activity life-cycle, on main thread */
288 @Override
289 public void onActivityPaused(Activity activity) {
290 synchronized (NfcActivityManager.this) {
291 NfcActivityState state = findActivityState(activity);
292 if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
293 if (state == null) return;
294 state.resumed = false;
295 }
296 requestNfcServiceCallback(false);
297 }
298
299 /** Callback from Activity life-cycle, on main thread */
300 @Override
301 public void onActivityStopped(Activity activity) { /* NO-OP */ }
302
303 /** Callback from Activity life-cycle, on main thread */
304 @Override
305 public void onActivitySaveInstanceState(Activity activity, Bundle outState) { /* NO-OP */ }
306
307 /** Callback from Activity life-cycle, on main thread */
308 @Override
309 public void onActivityDestroyed(Activity activity) {
310 synchronized (NfcActivityManager.this) {
311 NfcActivityState state = findActivityState(activity);
312 if (DBG) Log.d(TAG, "onDestroy() for " + activity + " " + state);
313 if (state != null) {
314 // release all associated references
315 destroyActivityState(activity);
316 }
317 }
318 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700319}