blob: f80dae423f60a345edd1e1361a5b30845e9be8e0 [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 }
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;
Nick Pelly1d7e9062012-04-03 17:46:00 -0700131 uri = null;
132 mimeType = 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(" ");
Nick Pelly1d7e9062012-04-03 17:46:00 -0700138 s.append(onNdefPushCompleteCallback).append(" ").append(uri).append("]");
Nick Pellyc84c89a2011-08-22 22:27:11 -0700139 return s.toString();
140 }
141 }
142
Nick Pelly8ce7a272012-03-21 15:14:09 -0700143 /** find activity state from mActivities */
144 synchronized NfcActivityState findActivityState(Activity activity) {
145 for (NfcActivityState state : mActivities) {
146 if (state.activity == activity) {
147 return state;
148 }
149 }
150 return null;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700151 }
152
Nick Pelly8ce7a272012-03-21 15:14:09 -0700153 /** find or create activity state from mActivities */
154 synchronized NfcActivityState getActivityState(Activity activity) {
155 NfcActivityState state = findActivityState(activity);
156 if (state == null) {
157 state = new NfcActivityState(activity);
158 mActivities.add(state);
Nick Pellyc84c89a2011-08-22 22:27:11 -0700159 }
160 return state;
161 }
162
Nick Pelly8ce7a272012-03-21 15:14:09 -0700163 synchronized NfcActivityState findResumedActivityState() {
164 for (NfcActivityState state : mActivities) {
165 if (state.resumed) {
166 return state;
167 }
168 }
169 return null;
170 }
171
172 synchronized void destroyActivityState(Activity activity) {
173 NfcActivityState activityState = findActivityState(activity);
174 if (activityState != null) {
175 activityState.destroy();
176 mActivities.remove(activityState);
177 }
178 }
179
180 public NfcActivityManager(NfcAdapter adapter) {
181 mAdapter = adapter;
182 mActivities = new LinkedList<NfcActivityState>();
183 mApps = new ArrayList<NfcApplicationState>(1); // Android VM usually has 1 app
184 mDefaultEvent = new NfcEvent(mAdapter);
185 }
186
Nick Pelly1d7e9062012-04-03 17:46:00 -0700187 public void setNdefPushContentUri(Activity activity, String mimeType, Uri uri) {
188 boolean isResumed;
189 synchronized (NfcActivityManager.this) {
190 NfcActivityState state = getActivityState(activity);
191 state.uri = uri;
192 state.mimeType = mimeType;
193 isResumed = state.resumed;
194 }
195 if (isResumed) {
196 requestNfcServiceCallback(true);
197 }
198 }
199
Nick Pelly8ce7a272012-03-21 15:14:09 -0700200 public void setNdefPushMessage(Activity activity, NdefMessage message) {
201 boolean isResumed;
202 synchronized (NfcActivityManager.this) {
203 NfcActivityState state = getActivityState(activity);
204 state.ndefMessage = message;
205 isResumed = state.resumed;
206 }
207 if (isResumed) {
208 requestNfcServiceCallback(true);
209 }
210 }
211
212 public void setNdefPushMessageCallback(Activity activity,
213 NfcAdapter.CreateNdefMessageCallback callback) {
214 boolean isResumed;
215 synchronized (NfcActivityManager.this) {
216 NfcActivityState state = getActivityState(activity);
217 state.ndefMessageCallback = callback;
218 isResumed = state.resumed;
219 }
220 if (isResumed) {
221 requestNfcServiceCallback(true);
222 }
223 }
224
225 public void setOnNdefPushCompleteCallback(Activity activity,
226 NfcAdapter.OnNdefPushCompleteCallback callback) {
227 boolean isResumed;
228 synchronized (NfcActivityManager.this) {
229 NfcActivityState state = getActivityState(activity);
230 state.onNdefPushCompleteCallback = callback;
231 isResumed = state.resumed;
232 }
233 if (isResumed) {
234 requestNfcServiceCallback(true);
Nick Pellyc84c89a2011-08-22 22:27:11 -0700235 }
236 }
237
238 /**
Nick Pelly8ce7a272012-03-21 15:14:09 -0700239 * Request or unrequest NFC service callbacks for NDEF push.
240 * Makes IPC call - do not hold lock.
241 * TODO: Do not do IPC on every onPause/onResume
Nick Pellyc84c89a2011-08-22 22:27:11 -0700242 */
Nick Pelly8ce7a272012-03-21 15:14:09 -0700243 void requestNfcServiceCallback(boolean request) {
Nick Pellyc84c89a2011-08-22 22:27:11 -0700244 try {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700245 NfcAdapter.sService.setNdefPushCallback(request ? this : null);
Nick Pellyc84c89a2011-08-22 22:27:11 -0700246 } catch (RemoteException e) {
247 mAdapter.attemptDeadServiceRecovery(e);
248 }
249 }
250
Nick Pelly8ce7a272012-03-21 15:14:09 -0700251 /** Callback from NFC service, usually on binder thread */
Nick Pellyc84c89a2011-08-22 22:27:11 -0700252 @Override
253 public NdefMessage createMessage() {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700254 NfcAdapter.CreateNdefMessageCallback callback;
255 NdefMessage message;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700256 synchronized (NfcActivityManager.this) {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700257 NfcActivityState state = findResumedActivityState();
258 if (state == null) return null;
259
260 callback = state.ndefMessageCallback;
261 message = state.ndefMessage;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700262 }
263
Nick Pelly8ce7a272012-03-21 15:14:09 -0700264 // Make callback without lock
Nick Pellyc84c89a2011-08-22 22:27:11 -0700265 if (callback != null) {
266 return callback.createNdefMessage(mDefaultEvent);
Nick Pelly8ce7a272012-03-21 15:14:09 -0700267 } else {
268 return message;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700269 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700270 }
271
Nick Pelly8ce7a272012-03-21 15:14:09 -0700272 /** Callback from NFC service, usually on binder thread */
Nick Pellyc84c89a2011-08-22 22:27:11 -0700273 @Override
Nick Pelly1d7e9062012-04-03 17:46:00 -0700274 public Uri getUri() {
275 synchronized (NfcActivityManager.this) {
276 NfcActivityState state = findResumedActivityState();
277 if (state == null) return null;
278
279 return state.uri;
280 }
281 }
282 /** Callback from NFC service, usually on binder thread */
283 @Override
284 public String getMimeType() {
285 synchronized (NfcActivityManager.this) {
286 NfcActivityState state = findResumedActivityState();
287 if (state == null) return null;
288
289 return state.mimeType;
290 }
291 }
292 /** Callback from NFC service, usually on binder thread */
293 @Override
Nick Pellyc84c89a2011-08-22 22:27:11 -0700294 public void onNdefPushComplete() {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700295 NfcAdapter.OnNdefPushCompleteCallback callback;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700296 synchronized (NfcActivityManager.this) {
Nick Pelly8ce7a272012-03-21 15:14:09 -0700297 NfcActivityState state = findResumedActivityState();
298 if (state == null) return;
299
300 callback = state.onNdefPushCompleteCallback;
Nick Pellyc84c89a2011-08-22 22:27:11 -0700301 }
302
Nick Pelly8ce7a272012-03-21 15:14:09 -0700303 // Make callback without lock
Nick Pellyc84c89a2011-08-22 22:27:11 -0700304 if (callback != null) {
305 callback.onNdefPushComplete(mDefaultEvent);
306 }
307 }
Martijn Coenen3433a8a2011-09-01 19:18:02 -0700308
Nick Pelly8ce7a272012-03-21 15:14:09 -0700309 /** Callback from Activity life-cycle, on main thread */
310 @Override
311 public void onActivityCreated(Activity activity, Bundle savedInstanceState) { /* NO-OP */ }
312
313 /** Callback from Activity life-cycle, on main thread */
314 @Override
315 public void onActivityStarted(Activity activity) { /* NO-OP */ }
316
317 /** Callback from Activity life-cycle, on main thread */
318 @Override
319 public void onActivityResumed(Activity activity) {
320 synchronized (NfcActivityManager.this) {
321 NfcActivityState state = findActivityState(activity);
322 if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
323 if (state == null) return;
324 state.resumed = true;
325 }
326 requestNfcServiceCallback(true);
327 }
328
329 /** Callback from Activity life-cycle, on main thread */
330 @Override
331 public void onActivityPaused(Activity activity) {
332 synchronized (NfcActivityManager.this) {
333 NfcActivityState state = findActivityState(activity);
334 if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
335 if (state == null) return;
336 state.resumed = false;
337 }
338 requestNfcServiceCallback(false);
339 }
340
341 /** Callback from Activity life-cycle, on main thread */
342 @Override
343 public void onActivityStopped(Activity activity) { /* NO-OP */ }
344
345 /** Callback from Activity life-cycle, on main thread */
346 @Override
347 public void onActivitySaveInstanceState(Activity activity, Bundle outState) { /* NO-OP */ }
348
349 /** Callback from Activity life-cycle, on main thread */
350 @Override
351 public void onActivityDestroyed(Activity activity) {
352 synchronized (NfcActivityManager.this) {
353 NfcActivityState state = findActivityState(activity);
354 if (DBG) Log.d(TAG, "onDestroy() for " + activity + " " + state);
355 if (state != null) {
356 // release all associated references
357 destroyActivityState(activity);
358 }
359 }
360 }
Nick Pellyc84c89a2011-08-22 22:27:11 -0700361}