• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.app.Application;
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.os.RemoteException;
24 import android.util.Log;
25 
26 import java.util.ArrayList;
27 import java.util.LinkedList;
28 import java.util.List;
29 
30 /**
31  * Manages NFC API's that are coupled to the life-cycle of an Activity.
32  *
33  * <p>Uses {@link Application#registerActivityLifecycleCallbacks} to hook
34  * into activity life-cycle events such as onPause() and onResume().
35  *
36  * @hide
37  */
38 public final class NfcActivityManager extends INdefPushCallback.Stub
39         implements Application.ActivityLifecycleCallbacks {
40     static final String TAG = NfcAdapter.TAG;
41     static final Boolean DBG = false;
42 
43     final NfcAdapter mAdapter;
44     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;
NfcApplicationState(Application app)56         public NfcApplicationState(Application app) {
57             this.app = app;
58         }
register()59         public void register() {
60             refCount++;
61             if (refCount == 1) {
62                 this.app.registerActivityLifecycleCallbacks(NfcActivityManager.this);
63             }
64         }
unregister()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 
findAppState(Application app)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 
registerApplication(Application app)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 
unregisterApplication(Application app)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     }
101 
102     /**
103      * NFC state associated with an {@link Activity}
104      */
105     class NfcActivityState {
106         boolean resumed = false;
107         Activity activity;
108         NdefMessage ndefMessage = null;  // static NDEF message
109         NfcAdapter.CreateNdefMessageCallback ndefMessageCallback = null;
110         NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
111         NfcAdapter.CreateBeamUrisCallback uriCallback = null;
112         Uri[] uris = null;
NfcActivityState(Activity activity)113         public NfcActivityState(Activity activity) {
114             if (activity.getWindow().isDestroyed()) {
115                 throw new IllegalStateException("activity is already destroyed");
116             }
117             // Check if activity is resumed right now, as we will not
118             // immediately get a callback for that.
119             resumed = activity.isResumed();
120 
121             this.activity = activity;
122             registerApplication(activity.getApplication());
123         }
destroy()124         public void destroy() {
125             unregisterApplication(activity.getApplication());
126             resumed = false;
127             activity = null;
128             ndefMessage = null;
129             ndefMessageCallback = null;
130             onNdefPushCompleteCallback = null;
131             uriCallback = null;
132             uris = null;
133         }
134         @Override
toString()135         public String toString() {
136             StringBuilder s = new StringBuilder("[").append(" ");
137             s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
138             s.append(uriCallback).append(" ");
139             if (uris != null) {
140                 for (Uri uri : uris) {
141                     s.append(onNdefPushCompleteCallback).append(" ").append(uri).append("]");
142                 }
143             }
144             return s.toString();
145         }
146     }
147 
148     /** find activity state from mActivities */
findActivityState(Activity activity)149     synchronized NfcActivityState findActivityState(Activity activity) {
150         for (NfcActivityState state : mActivities) {
151             if (state.activity == activity) {
152                 return state;
153             }
154         }
155         return null;
156     }
157 
158     /** find or create activity state from mActivities */
getActivityState(Activity activity)159     synchronized NfcActivityState getActivityState(Activity activity) {
160         NfcActivityState state = findActivityState(activity);
161         if (state == null) {
162             state = new NfcActivityState(activity);
163             mActivities.add(state);
164         }
165         return state;
166     }
167 
findResumedActivityState()168     synchronized NfcActivityState findResumedActivityState() {
169         for (NfcActivityState state : mActivities) {
170             if (state.resumed) {
171                 return state;
172             }
173         }
174         return null;
175     }
176 
destroyActivityState(Activity activity)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 
NfcActivityManager(NfcAdapter adapter)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 
setNdefPushContentUri(Activity activity, Uri[] uris)192     public void setNdefPushContentUri(Activity activity, Uri[] uris) {
193         boolean isResumed;
194         synchronized (NfcActivityManager.this) {
195             NfcActivityState state = getActivityState(activity);
196             state.uris = uris;
197             isResumed = state.resumed;
198         }
199         if (isResumed) {
200             requestNfcServiceCallback(true);
201         }
202     }
203 
204 
setNdefPushContentUriCallback(Activity activity, NfcAdapter.CreateBeamUrisCallback callback)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;
211             isResumed = state.resumed;
212         }
213         if (isResumed) {
214             requestNfcServiceCallback(true);
215         }
216     }
217 
setNdefPushMessage(Activity activity, NdefMessage message)218     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) {
226             requestNfcServiceCallback(true);
227         }
228     }
229 
setNdefPushMessageCallback(Activity activity, NfcAdapter.CreateNdefMessageCallback callback)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) {
239             requestNfcServiceCallback(true);
240         }
241     }
242 
setOnNdefPushCompleteCallback(Activity activity, NfcAdapter.OnNdefPushCompleteCallback callback)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) {
252             requestNfcServiceCallback(true);
253         }
254     }
255 
256     /**
257      * Request or unrequest NFC service callbacks for NDEF push.
258      * Makes IPC call - do not hold lock.
259      * TODO: Do not do IPC on every onPause/onResume
260      */
requestNfcServiceCallback(boolean request)261     void requestNfcServiceCallback(boolean request) {
262         try {
263             NfcAdapter.sService.setNdefPushCallback(request ? this : null);
264         } catch (RemoteException e) {
265             mAdapter.attemptDeadServiceRecovery(e);
266         }
267     }
268 
269     /** Callback from NFC service, usually on binder thread */
270     @Override
createMessage()271     public NdefMessage createMessage() {
272         NfcAdapter.CreateNdefMessageCallback callback;
273         NdefMessage message;
274         synchronized (NfcActivityManager.this) {
275             NfcActivityState state = findResumedActivityState();
276             if (state == null) return null;
277 
278             callback = state.ndefMessageCallback;
279             message = state.ndefMessage;
280         }
281 
282         // Make callback without lock
283         if (callback != null) {
284             return callback.createNdefMessage(mDefaultEvent);
285         } else {
286             return message;
287         }
288     }
289 
290     /** Callback from NFC service, usually on binder thread */
291     @Override
getUris()292     public Uri[] getUris() {
293         Uri[] uris;
294         NfcAdapter.CreateBeamUrisCallback callback;
295         synchronized (NfcActivityManager.this) {
296             NfcActivityState state = findResumedActivityState();
297             if (state == null) return null;
298             uris = state.uris;
299             callback = state.uriCallback;
300         }
301         if (callback != null) {
302             uris = callback.createBeamUris(mDefaultEvent);
303             if (uris != null) {
304                 for (Uri uri : uris) {
305                     if (uri == null) {
306                         Log.e(TAG, "Uri not allowed to be null.");
307                         return null;
308                     }
309                     String scheme = uri.getScheme();
310                     if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
311                             !scheme.equalsIgnoreCase("content"))) {
312                         Log.e(TAG, "Uri needs to have " +
313                                 "either scheme file or scheme content");
314                         return null;
315                     }
316                 }
317             }
318             return uris;
319         } else {
320             return uris;
321         }
322     }
323 
324     /** Callback from NFC service, usually on binder thread */
325     @Override
onNdefPushComplete()326     public void onNdefPushComplete() {
327         NfcAdapter.OnNdefPushCompleteCallback callback;
328         synchronized (NfcActivityManager.this) {
329             NfcActivityState state = findResumedActivityState();
330             if (state == null) return;
331 
332             callback = state.onNdefPushCompleteCallback;
333         }
334 
335         // Make callback without lock
336         if (callback != null) {
337             callback.onNdefPushComplete(mDefaultEvent);
338         }
339     }
340 
341     /** Callback from Activity life-cycle, on main thread */
342     @Override
onActivityCreated(Activity activity, Bundle savedInstanceState)343     public void onActivityCreated(Activity activity, Bundle savedInstanceState) { /* NO-OP */ }
344 
345     /** Callback from Activity life-cycle, on main thread */
346     @Override
onActivityStarted(Activity activity)347     public void onActivityStarted(Activity activity) { /* NO-OP */ }
348 
349     /** Callback from Activity life-cycle, on main thread */
350     @Override
onActivityResumed(Activity activity)351     public void onActivityResumed(Activity activity) {
352         synchronized (NfcActivityManager.this) {
353             NfcActivityState state = findActivityState(activity);
354             if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
355             if (state == null) return;
356             state.resumed = true;
357         }
358         requestNfcServiceCallback(true);
359     }
360 
361     /** Callback from Activity life-cycle, on main thread */
362     @Override
onActivityPaused(Activity activity)363     public void onActivityPaused(Activity activity) {
364         synchronized (NfcActivityManager.this) {
365             NfcActivityState state = findActivityState(activity);
366             if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
367             if (state == null) return;
368             state.resumed = false;
369         }
370         requestNfcServiceCallback(false);
371     }
372 
373     /** Callback from Activity life-cycle, on main thread */
374     @Override
onActivityStopped(Activity activity)375     public void onActivityStopped(Activity activity) { /* NO-OP */ }
376 
377     /** Callback from Activity life-cycle, on main thread */
378     @Override
onActivitySaveInstanceState(Activity activity, Bundle outState)379     public void onActivitySaveInstanceState(Activity activity, Bundle outState) { /* NO-OP */ }
380 
381     /** Callback from Activity life-cycle, on main thread */
382     @Override
onActivityDestroyed(Activity activity)383     public void onActivityDestroyed(Activity activity) {
384         synchronized (NfcActivityManager.this) {
385             NfcActivityState state = findActivityState(activity);
386             if (DBG) Log.d(TAG, "onDestroy() for " + activity + " " + state);
387             if (state != null) {
388                 // release all associated references
389                 destroyActivityState(activity);
390             }
391         }
392     }
393 
394 }
395