blob: 7c3123f449cfddcdf4abf45602db2e700d3f3a16 [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;
Martijn Coenen20e8dd92012-04-12 16:37:18 -0700111 NfcAdapter.CreateBeamUrisCallback uriCallback = null;
112 Uri[] uris = 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 }
Martijn Coenen20fe5372012-04-05 10:50:05 -0700117 // Check if activity is resumed right now, as we will not
118 // immediately get a callback for that.
119 resumed = activity.isResumed();
120
Nick Pelly8ce7a272012-03-21 15:14:09 -0700121 this.activity = activity;
122 registerApplication(activity.getApplication());
123 }
124 public void destroy() {
125 unregisterApplication(activity.getApplication());
126 resumed = false;
127 activity = null;
128 ndefMessage = null;
129 ndefMessageCallback = null;
130 onNdefPushCompleteCallback = null;
Martijn Coenen20e8dd92012-04-12 16:37:18 -0700131 uriCallback = null;
132 uris = null;
Nick Pelly8ce7a272012-03-21 15:14:09 -0700133 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700134 @Override
135 public String toString() {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700136 StringBuilder s = new StringBuilder("[").append(" ");
Nick Pellyc84c89a2011-08-22 22:27:11 -0700137 s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
Martijn Coenen20e8dd92012-04-12 16:37:18 -0700138 s.append(uriCallback).append(" ");
139 if (uris != null) {
140 for (Uri uri : uris) {
141 s.append(onNdefPushCompleteCallback).append(" ").append(uri).append("]");
142 }
143 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700144 return s.toString();
145 }
146 }
147
Nick Pelly8ce7a272012-03-21 15:14:09 -0700148 /** find activity state from mActivities */
149 synchronized NfcActivityState findActivityState(Activity activity) {
150 for (NfcActivityState state : mActivities) {
151 if (state.activity == activity) {
152 return state;
153 }
154 }
155 return null;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700156 }
157
Nick Pelly8ce7a272012-03-21 15:14:09 -0700158 /** find or create activity state from mActivities */
159 synchronized NfcActivityState getActivityState(Activity activity) {
160 NfcActivityState state = findActivityState(activity);
161 if (state == null) {
162 state = new NfcActivityState(activity);
163 mActivities.add(state);
Nick Pellyc84c89a2011-08-22 22:27:11 -0700164 }
165 return state;
166 }
167
Nick Pelly8ce7a272012-03-21 15:14:09 -0700168 synchronized NfcActivityState findResumedActivityState() {
169 for (NfcActivityState state : mActivities) {
170 if (state.resumed) {
171 return state;
172 }
173 }
174 return null;
175 }
176
177 synchronized void destroyActivityState(Activity activity) {
178 NfcActivityState activityState = findActivityState(activity);
179 if (activityState != null) {
180 activityState.destroy();
181 mActivities.remove(activityState);
182 }
183 }
184
185 public NfcActivityManager(NfcAdapter adapter) {
186 mAdapter = adapter;
187 mActivities = new LinkedList<NfcActivityState>();
188 mApps = new ArrayList<NfcApplicationState>(1); // Android VM usually has 1 app
189 mDefaultEvent = new NfcEvent(mAdapter);
190 }
191
Martijn Coenen20e8dd92012-04-12 16:37:18 -0700192 public void setNdefPushContentUri(Activity activity, Uri[] uris) {
Nick Pelly1d7e9062012-04-03 17:46:00 -0700193 boolean isResumed;
194 synchronized (NfcActivityManager.this) {
195 NfcActivityState state = getActivityState(activity);
Martijn Coenen20e8dd92012-04-12 16:37:18 -0700196 state.uris = uris;
197 isResumed = state.resumed;
198 }
199 if (isResumed) {
Martijn Coenen1360c552013-01-07 16:34:20 -0800200 requestNfcServiceCallback();
Martijn Coenen20e8dd92012-04-12 16:37:18 -0700201 }
202 }
203
204
205 public void setNdefPushContentUriCallback(Activity activity,
206 NfcAdapter.CreateBeamUrisCallback callback) {
207 boolean isResumed;
208 synchronized (NfcActivityManager.this) {
209 NfcActivityState state = getActivityState(activity);
210 state.uriCallback = callback;
Nick Pelly1d7e9062012-04-03 17:46:00 -0700211 isResumed = state.resumed;
212 }
213 if (isResumed) {
Martijn Coenen1360c552013-01-07 16:34:20 -0800214 requestNfcServiceCallback();
Nick Pelly1d7e9062012-04-03 17:46:00 -0700215 }
216 }
217
Nick Pelly8ce7a272012-03-21 15:14:09 -0700218 public void setNdefPushMessage(Activity activity, NdefMessage message) {
219 boolean isResumed;
220 synchronized (NfcActivityManager.this) {
221 NfcActivityState state = getActivityState(activity);
222 state.ndefMessage = message;
223 isResumed = state.resumed;
224 }
225 if (isResumed) {
Martijn Coenen1360c552013-01-07 16:34:20 -0800226 requestNfcServiceCallback();
Nick Pelly8ce7a272012-03-21 15:14:09 -0700227 }
228 }
229
230 public void setNdefPushMessageCallback(Activity activity,
231 NfcAdapter.CreateNdefMessageCallback callback) {
232 boolean isResumed;
233 synchronized (NfcActivityManager.this) {
234 NfcActivityState state = getActivityState(activity);
235 state.ndefMessageCallback = callback;
236 isResumed = state.resumed;
237 }
238 if (isResumed) {
Martijn Coenen1360c552013-01-07 16:34:20 -0800239 requestNfcServiceCallback();
Nick Pelly8ce7a272012-03-21 15:14:09 -0700240 }
241 }
242
243 public void setOnNdefPushCompleteCallback(Activity activity,
244 NfcAdapter.OnNdefPushCompleteCallback callback) {
245 boolean isResumed;
246 synchronized (NfcActivityManager.this) {
247 NfcActivityState state = getActivityState(activity);
248 state.onNdefPushCompleteCallback = callback;
249 isResumed = state.resumed;
250 }
251 if (isResumed) {
Martijn Coenen1360c552013-01-07 16:34:20 -0800252 requestNfcServiceCallback();
Nick Pellyc84c89a2011-08-22 22:27:11 -0700253 }
254 }
255
256 /**
Nick Pelly8ce7a272012-03-21 15:14:09 -0700257 * Request or unrequest NFC service callbacks for NDEF push.
258 * Makes IPC call - do not hold lock.
Nick Pellyc84c89a2011-08-22 22:27:11 -0700259 */
Martijn Coenen1360c552013-01-07 16:34:20 -0800260 void requestNfcServiceCallback() {
Nick Pellyc84c89a2011-08-22 22:27:11 -0700261 try {
Martijn Coenen1360c552013-01-07 16:34:20 -0800262 NfcAdapter.sService.setNdefPushCallback(this);
Nick Pellyc84c89a2011-08-22 22:27:11 -0700263 } catch (RemoteException e) {
264 mAdapter.attemptDeadServiceRecovery(e);
265 }
266 }
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
270 public NdefMessage createMessage() {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700271 NfcAdapter.CreateNdefMessageCallback callback;
272 NdefMessage message;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700273 synchronized (NfcActivityManager.this) {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700274 NfcActivityState state = findResumedActivityState();
275 if (state == null) return null;
276
277 callback = state.ndefMessageCallback;
278 message = state.ndefMessage;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700279 }
280
Nick Pelly8ce7a272012-03-21 15:14:09 -0700281 // Make callback without lock
Nick Pellyc84c89a2011-08-22 22:27:11 -0700282 if (callback != null) {
283 return callback.createNdefMessage(mDefaultEvent);
Nick Pelly8ce7a272012-03-21 15:14:09 -0700284 } else {
285 return message;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700286 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700287 }
288
Nick Pelly8ce7a272012-03-21 15:14:09 -0700289 /** Callback from NFC service, usually on binder thread */
Nick Pellyc84c89a2011-08-22 22:27:11 -0700290 @Override
Martijn Coenen20e8dd92012-04-12 16:37:18 -0700291 public Uri[] getUris() {
292 Uri[] uris;
293 NfcAdapter.CreateBeamUrisCallback callback;
Nick Pelly1d7e9062012-04-03 17:46:00 -0700294 synchronized (NfcActivityManager.this) {
295 NfcActivityState state = findResumedActivityState();
296 if (state == null) return null;
Martijn Coenen20e8dd92012-04-12 16:37:18 -0700297 uris = state.uris;
298 callback = state.uriCallback;
299 }
300 if (callback != null) {
Martijn Coenen2c103112012-05-15 10:32:15 -0700301 uris = callback.createBeamUris(mDefaultEvent);
302 if (uris != null) {
303 for (Uri uri : uris) {
304 if (uri == null) {
305 Log.e(TAG, "Uri not allowed to be null.");
306 return null;
307 }
308 String scheme = uri.getScheme();
309 if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
310 !scheme.equalsIgnoreCase("content"))) {
311 Log.e(TAG, "Uri needs to have " +
312 "either scheme file or scheme content");
313 return null;
314 }
315 }
316 }
317 return uris;
Martijn Coenen20e8dd92012-04-12 16:37:18 -0700318 } else {
319 return uris;
Nick Pelly1d7e9062012-04-03 17:46:00 -0700320 }
321 }
Nick Pelly1d7e9062012-04-03 17:46:00 -0700322
Nick Pelly1d7e9062012-04-03 17:46:00 -0700323 /** Callback from NFC service, usually on binder thread */
324 @Override
Nick Pellyc84c89a2011-08-22 22:27:11 -0700325 public void onNdefPushComplete() {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700326 NfcAdapter.OnNdefPushCompleteCallback callback;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700327 synchronized (NfcActivityManager.this) {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700328 NfcActivityState state = findResumedActivityState();
329 if (state == null) return;
330
331 callback = state.onNdefPushCompleteCallback;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700332 }
333
Nick Pelly8ce7a272012-03-21 15:14:09 -0700334 // Make callback without lock
Nick Pellyc84c89a2011-08-22 22:27:11 -0700335 if (callback != null) {
336 callback.onNdefPushComplete(mDefaultEvent);
337 }
338 }
Martijn Coenen3433a8a2011-09-01 19:18:02 -0700339
Nick Pelly8ce7a272012-03-21 15:14:09 -0700340 /** Callback from Activity life-cycle, on main thread */
341 @Override
342 public void onActivityCreated(Activity activity, Bundle savedInstanceState) { /* NO-OP */ }
343
344 /** Callback from Activity life-cycle, on main thread */
345 @Override
346 public void onActivityStarted(Activity activity) { /* NO-OP */ }
347
348 /** Callback from Activity life-cycle, on main thread */
349 @Override
350 public void onActivityResumed(Activity activity) {
351 synchronized (NfcActivityManager.this) {
352 NfcActivityState state = findActivityState(activity);
353 if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
354 if (state == null) return;
355 state.resumed = true;
356 }
Martijn Coenen1360c552013-01-07 16:34:20 -0800357 requestNfcServiceCallback();
Nick Pelly8ce7a272012-03-21 15:14:09 -0700358 }
359
360 /** Callback from Activity life-cycle, on main thread */
361 @Override
362 public void onActivityPaused(Activity activity) {
363 synchronized (NfcActivityManager.this) {
364 NfcActivityState state = findActivityState(activity);
365 if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
366 if (state == null) return;
367 state.resumed = false;
368 }
Nick Pelly8ce7a272012-03-21 15:14:09 -0700369 }
370
371 /** Callback from Activity life-cycle, on main thread */
372 @Override
373 public void onActivityStopped(Activity activity) { /* NO-OP */ }
374
375 /** Callback from Activity life-cycle, on main thread */
376 @Override
377 public void onActivitySaveInstanceState(Activity activity, Bundle outState) { /* NO-OP */ }
378
379 /** Callback from Activity life-cycle, on main thread */
380 @Override
381 public void onActivityDestroyed(Activity activity) {
382 synchronized (NfcActivityManager.this) {
383 NfcActivityState state = findActivityState(activity);
384 if (DBG) Log.d(TAG, "onDestroy() for " + activity + " " + state);
385 if (state != null) {
386 // release all associated references
387 destroyActivityState(activity);
388 }
389 }
390 }
Martijn Coenen20e8dd92012-04-12 16:37:18 -0700391
Nick Pellyc84c89a2011-08-22 22:27:11 -0700392}