blob: 83354595eeb418884c5868377238b3e8a2de252c [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;
Nick Pelly1d7e9062012-04-03 17:46:00 -070021import android.net.Uri;
Nick Pelly8ce7a272012-03-21 15:14:09 -070022import android.os.Bundle;
Nick Pellyc84c89a2011-08-22 22:27:11 -070023import android.os.RemoteException;
24import android.util.Log;
25
Nick Pelly8ce7a272012-03-21 15:14:09 -070026import java.util.ArrayList;
27import java.util.LinkedList;
28import java.util.List;
Nick Pellyc84c89a2011-08-22 22:27:11 -070029
30/**
31 * Manages NFC API's that are coupled to the life-cycle of an Activity.
32 *
Nick Pelly8ce7a272012-03-21 15:14:09 -070033 * <p>Uses {@link Application#registerActivityLifecycleCallbacks} to hook
34 * into activity life-cycle events such as onPause() and onResume().
Nick Pellyc84c89a2011-08-22 22:27:11 -070035 *
36 * @hide
37 */
Nick Pelly8ce7a272012-03-21 15:14:09 -070038public final class NfcActivityManager extends INdefPushCallback.Stub
39 implements Application.ActivityLifecycleCallbacks {
Nick Pellyc84c89a2011-08-22 22:27:11 -070040 static final String TAG = NfcAdapter.TAG;
41 static final Boolean DBG = false;
42
43 final NfcAdapter mAdapter;
Nick Pelly8ce7a272012-03-21 15:14:09 -070044 final NfcEvent mDefaultEvent; // cached NfcEvent (its currently always the same)
45
46 // All objects in the lists are protected by this
47 final List<NfcApplicationState> mApps; // Application(s) that have NFC state. Usually one
48 final List<NfcActivityState> mActivities; // Activities that have NFC state
49
50 /**
51 * NFC State associated with an {@link Application}.
52 */
53 class NfcApplicationState {
54 int refCount = 0;
55 final Application app;
56 public NfcApplicationState(Application app) {
57 this.app = app;
58 }
59 public void register() {
60 refCount++;
61 if (refCount == 1) {
62 this.app.registerActivityLifecycleCallbacks(NfcActivityManager.this);
63 }
64 }
65 public void unregister() {
66 refCount--;
67 if (refCount == 0) {
68 this.app.unregisterActivityLifecycleCallbacks(NfcActivityManager.this);
69 } else if (refCount < 0) {
70 Log.e(TAG, "-ve refcount for " + app);
71 }
72 }
73 }
74
75 NfcApplicationState findAppState(Application app) {
76 for (NfcApplicationState appState : mApps) {
77 if (appState.app == app) {
78 return appState;
79 }
80 }
81 return null;
82 }
83
84 void registerApplication(Application app) {
85 NfcApplicationState appState = findAppState(app);
86 if (appState == null) {
87 appState = new NfcApplicationState(app);
88 mApps.add(appState);
89 }
90 appState.register();
91 }
92
93 void unregisterApplication(Application app) {
94 NfcApplicationState appState = findAppState(app);
95 if (appState == null) {
96 Log.e(TAG, "app was not registered " + app);
97 return;
98 }
99 appState.unregister();
100 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700101
102 /**
103 * NFC state associated with an {@link Activity}
104 */
105 class NfcActivityState {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700106 boolean resumed = false;
107 Activity activity;
108 NdefMessage ndefMessage = null; // static NDEF message
109 NfcAdapter.CreateNdefMessageCallback ndefMessageCallback = null;
110 NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
Nick Pelly1d7e9062012-04-03 17:46:00 -0700111 Uri uri = null;
112 String mimeType = null;
Nick Pelly8ce7a272012-03-21 15:14:09 -0700113 public NfcActivityState(Activity activity) {
114 if (activity.getWindow().isDestroyed()) {
115 throw new IllegalStateException("activity is already destroyed");
116 }
117 this.activity = activity;
118 registerApplication(activity.getApplication());
119 }
120 public void destroy() {
121 unregisterApplication(activity.getApplication());
122 resumed = false;
123 activity = null;
124 ndefMessage = null;
125 ndefMessageCallback = null;
126 onNdefPushCompleteCallback = null;
Nick Pelly1d7e9062012-04-03 17:46:00 -0700127 uri = null;
128 mimeType = null;
Nick Pelly8ce7a272012-03-21 15:14:09 -0700129 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700130 @Override
131 public String toString() {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700132 StringBuilder s = new StringBuilder("[").append(" ");
Nick Pellyc84c89a2011-08-22 22:27:11 -0700133 s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
Nick Pelly1d7e9062012-04-03 17:46:00 -0700134 s.append(onNdefPushCompleteCallback).append(" ").append(uri).append("]");
Nick Pellyc84c89a2011-08-22 22:27:11 -0700135 return s.toString();
136 }
137 }
138
Nick Pelly8ce7a272012-03-21 15:14:09 -0700139 /** find activity state from mActivities */
140 synchronized NfcActivityState findActivityState(Activity activity) {
141 for (NfcActivityState state : mActivities) {
142 if (state.activity == activity) {
143 return state;
144 }
145 }
146 return null;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700147 }
148
Nick Pelly8ce7a272012-03-21 15:14:09 -0700149 /** find or create activity state from mActivities */
150 synchronized NfcActivityState getActivityState(Activity activity) {
151 NfcActivityState state = findActivityState(activity);
152 if (state == null) {
153 state = new NfcActivityState(activity);
154 mActivities.add(state);
Nick Pellyc84c89a2011-08-22 22:27:11 -0700155 }
156 return state;
157 }
158
Nick Pelly8ce7a272012-03-21 15:14:09 -0700159 synchronized NfcActivityState findResumedActivityState() {
160 for (NfcActivityState state : mActivities) {
161 if (state.resumed) {
162 return state;
163 }
164 }
165 return null;
166 }
167
168 synchronized void destroyActivityState(Activity activity) {
169 NfcActivityState activityState = findActivityState(activity);
170 if (activityState != null) {
171 activityState.destroy();
172 mActivities.remove(activityState);
173 }
174 }
175
176 public NfcActivityManager(NfcAdapter adapter) {
177 mAdapter = adapter;
178 mActivities = new LinkedList<NfcActivityState>();
179 mApps = new ArrayList<NfcApplicationState>(1); // Android VM usually has 1 app
180 mDefaultEvent = new NfcEvent(mAdapter);
181 }
182
Nick Pelly1d7e9062012-04-03 17:46:00 -0700183 public void setNdefPushContentUri(Activity activity, String mimeType, Uri uri) {
184 boolean isResumed;
185 synchronized (NfcActivityManager.this) {
186 NfcActivityState state = getActivityState(activity);
187 state.uri = uri;
188 state.mimeType = mimeType;
189 isResumed = state.resumed;
190 }
191 if (isResumed) {
192 requestNfcServiceCallback(true);
193 }
194 }
195
Nick Pelly8ce7a272012-03-21 15:14:09 -0700196 public void setNdefPushMessage(Activity activity, NdefMessage message) {
197 boolean isResumed;
198 synchronized (NfcActivityManager.this) {
199 NfcActivityState state = getActivityState(activity);
200 state.ndefMessage = message;
201 isResumed = state.resumed;
202 }
203 if (isResumed) {
204 requestNfcServiceCallback(true);
205 }
206 }
207
208 public void setNdefPushMessageCallback(Activity activity,
209 NfcAdapter.CreateNdefMessageCallback callback) {
210 boolean isResumed;
211 synchronized (NfcActivityManager.this) {
212 NfcActivityState state = getActivityState(activity);
213 state.ndefMessageCallback = callback;
214 isResumed = state.resumed;
215 }
216 if (isResumed) {
217 requestNfcServiceCallback(true);
218 }
219 }
220
221 public void setOnNdefPushCompleteCallback(Activity activity,
222 NfcAdapter.OnNdefPushCompleteCallback callback) {
223 boolean isResumed;
224 synchronized (NfcActivityManager.this) {
225 NfcActivityState state = getActivityState(activity);
226 state.onNdefPushCompleteCallback = callback;
227 isResumed = state.resumed;
228 }
229 if (isResumed) {
230 requestNfcServiceCallback(true);
Nick Pellyc84c89a2011-08-22 22:27:11 -0700231 }
232 }
233
234 /**
Nick Pelly8ce7a272012-03-21 15:14:09 -0700235 * Request or unrequest NFC service callbacks for NDEF push.
236 * Makes IPC call - do not hold lock.
237 * TODO: Do not do IPC on every onPause/onResume
Nick Pellyc84c89a2011-08-22 22:27:11 -0700238 */
Nick Pelly8ce7a272012-03-21 15:14:09 -0700239 void requestNfcServiceCallback(boolean request) {
Nick Pellyc84c89a2011-08-22 22:27:11 -0700240 try {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700241 NfcAdapter.sService.setNdefPushCallback(request ? this : null);
Nick Pellyc84c89a2011-08-22 22:27:11 -0700242 } catch (RemoteException e) {
243 mAdapter.attemptDeadServiceRecovery(e);
244 }
245 }
246
Nick Pelly8ce7a272012-03-21 15:14:09 -0700247 /** Callback from NFC service, usually on binder thread */
Nick Pellyc84c89a2011-08-22 22:27:11 -0700248 @Override
249 public NdefMessage createMessage() {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700250 NfcAdapter.CreateNdefMessageCallback callback;
251 NdefMessage message;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700252 synchronized (NfcActivityManager.this) {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700253 NfcActivityState state = findResumedActivityState();
254 if (state == null) return null;
255
256 callback = state.ndefMessageCallback;
257 message = state.ndefMessage;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700258 }
259
Nick Pelly8ce7a272012-03-21 15:14:09 -0700260 // Make callback without lock
Nick Pellyc84c89a2011-08-22 22:27:11 -0700261 if (callback != null) {
262 return callback.createNdefMessage(mDefaultEvent);
Nick Pelly8ce7a272012-03-21 15:14:09 -0700263 } else {
264 return message;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700265 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700266 }
267
Nick Pelly8ce7a272012-03-21 15:14:09 -0700268 /** Callback from NFC service, usually on binder thread */
Nick Pellyc84c89a2011-08-22 22:27:11 -0700269 @Override
Nick Pelly1d7e9062012-04-03 17:46:00 -0700270 public Uri getUri() {
271 synchronized (NfcActivityManager.this) {
272 NfcActivityState state = findResumedActivityState();
273 if (state == null) return null;
274
275 return state.uri;
276 }
277 }
278 /** Callback from NFC service, usually on binder thread */
279 @Override
280 public String getMimeType() {
281 synchronized (NfcActivityManager.this) {
282 NfcActivityState state = findResumedActivityState();
283 if (state == null) return null;
284
285 return state.mimeType;
286 }
287 }
288 /** Callback from NFC service, usually on binder thread */
289 @Override
Nick Pellyc84c89a2011-08-22 22:27:11 -0700290 public void onNdefPushComplete() {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700291 NfcAdapter.OnNdefPushCompleteCallback callback;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700292 synchronized (NfcActivityManager.this) {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700293 NfcActivityState state = findResumedActivityState();
294 if (state == null) return;
295
296 callback = state.onNdefPushCompleteCallback;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700297 }
298
Nick Pelly8ce7a272012-03-21 15:14:09 -0700299 // Make callback without lock
Nick Pellyc84c89a2011-08-22 22:27:11 -0700300 if (callback != null) {
301 callback.onNdefPushComplete(mDefaultEvent);
302 }
303 }
Martijn Coenen3433a8a2011-09-01 19:18:02 -0700304
Nick Pelly8ce7a272012-03-21 15:14:09 -0700305 /** Callback from Activity life-cycle, on main thread */
306 @Override
307 public void onActivityCreated(Activity activity, Bundle savedInstanceState) { /* NO-OP */ }
308
309 /** Callback from Activity life-cycle, on main thread */
310 @Override
311 public void onActivityStarted(Activity activity) { /* NO-OP */ }
312
313 /** Callback from Activity life-cycle, on main thread */
314 @Override
315 public void onActivityResumed(Activity activity) {
316 synchronized (NfcActivityManager.this) {
317 NfcActivityState state = findActivityState(activity);
318 if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
319 if (state == null) return;
320 state.resumed = true;
321 }
322 requestNfcServiceCallback(true);
323 }
324
325 /** Callback from Activity life-cycle, on main thread */
326 @Override
327 public void onActivityPaused(Activity activity) {
328 synchronized (NfcActivityManager.this) {
329 NfcActivityState state = findActivityState(activity);
330 if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
331 if (state == null) return;
332 state.resumed = false;
333 }
334 requestNfcServiceCallback(false);
335 }
336
337 /** Callback from Activity life-cycle, on main thread */
338 @Override
339 public void onActivityStopped(Activity activity) { /* NO-OP */ }
340
341 /** Callback from Activity life-cycle, on main thread */
342 @Override
343 public void onActivitySaveInstanceState(Activity activity, Bundle outState) { /* NO-OP */ }
344
345 /** Callback from Activity life-cycle, on main thread */
346 @Override
347 public void onActivityDestroyed(Activity activity) {
348 synchronized (NfcActivityManager.this) {
349 NfcActivityState state = findActivityState(activity);
350 if (DBG) Log.d(TAG, "onDestroy() for " + activity + " " + state);
351 if (state != null) {
352 // release all associated references
353 destroyActivityState(activity);
354 }
355 }
356 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700357}