• 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.compat.annotation.UnsupportedAppUsage;
22 import android.nfc.NfcAdapter.ReaderCallback;
23 import android.os.Binder;
24 import android.os.Bundle;
25 import android.os.RemoteException;
26 import android.util.Log;
27 
28 import java.util.ArrayList;
29 import java.util.LinkedList;
30 import java.util.List;
31 
32 /**
33  * Manages NFC API's that are coupled to the life-cycle of an Activity.
34  *
35  * <p>Uses {@link Application#registerActivityLifecycleCallbacks} to hook
36  * into activity life-cycle events such as onPause() and onResume().
37  *
38  * @hide
39  */
40 public final class NfcActivityManager extends IAppCallback.Stub
41         implements Application.ActivityLifecycleCallbacks {
42     static final String TAG = NfcAdapter.TAG;
43     static final Boolean DBG = false;
44 
45     @UnsupportedAppUsage
46     final NfcAdapter mAdapter;
47 
48     // All objects in the lists are protected by this
49     final List<NfcApplicationState> mApps;  // Application(s) that have NFC state. Usually one
50     final List<NfcActivityState> mActivities;  // Activities that have NFC state
51 
52     /**
53      * @hide
54      */
findAppState(Application app)55     public NfcApplicationState findAppState(Application app) {
56         for (NfcApplicationState appState : mApps) {
57             if (appState.app == app) {
58                 return appState;
59             }
60         }
61         return null;
62     }
63 
64     /**
65      * @hide
66      */
registerApplication(Application app)67     public void registerApplication(Application app) {
68         NfcApplicationState appState = findAppState(app);
69         if (appState == null) {
70             appState = new NfcApplicationState(app, this);
71             mApps.add(appState);
72         }
73         appState.register();
74     }
75 
76     /**
77      * @hide
78      */
unregisterApplication(Application app)79     public void unregisterApplication(Application app) {
80         NfcApplicationState appState = findAppState(app);
81         if (appState == null) {
82             Log.e(TAG, "unregisterApplication: app was not registered " + app);
83             return;
84         }
85         appState.unregister();
86     }
87 
88     /** find activity state from mActivities
89      *
90      * @hide
91      */
findActivityState(Activity activity)92     public synchronized NfcActivityState findActivityState(Activity activity) {
93         for (NfcActivityState state : mActivities) {
94             if (state.activity == activity) {
95                 return state;
96             }
97         }
98         return null;
99     }
100 
101     /** find or create activity state from mActivities
102      *
103      * @hide
104      */
getActivityState(Activity activity)105     public synchronized NfcActivityState getActivityState(Activity activity) {
106         NfcActivityState state = findActivityState(activity);
107         if (state == null) {
108             state = new NfcActivityState(activity, this);
109             mActivities.add(state);
110         }
111         return state;
112     }
113 
114     /**
115     * @hide
116     */
findResumedActivityState()117     public synchronized NfcActivityState findResumedActivityState() {
118         for (NfcActivityState state : mActivities) {
119             if (state.resumed) {
120                 return state;
121             }
122         }
123         return null;
124     }
125 
126     /**
127      * @hide
128      */
destroyActivityState(Activity activity)129     public synchronized void destroyActivityState(Activity activity) {
130         NfcActivityState activityState = findActivityState(activity);
131         if (activityState != null) {
132             activityState.destroy();
133             mActivities.remove(activityState);
134         }
135     }
136 
NfcActivityManager(NfcAdapter adapter)137     public NfcActivityManager(NfcAdapter adapter) {
138         mAdapter = adapter;
139         mActivities = new LinkedList<NfcActivityState>();
140         mApps = new ArrayList<NfcApplicationState>(1);  // Android VM usually has 1 app
141     }
142 
enableReaderMode(Activity activity, ReaderCallback callback, int flags, Bundle extras)143     public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
144             Bundle extras) {
145         boolean isResumed;
146         Binder token;
147         int pollTech, listenTech;
148         synchronized (NfcActivityManager.this) {
149             NfcActivityState state = getActivityState(activity);
150             state.readerCallback = callback;
151             state.readerModeFlags = flags;
152             state.readerModeExtras = extras;
153             pollTech = state.mPollTech;
154             listenTech = state.mListenTech;
155             token = state.token;
156             isResumed = state.resumed;
157         }
158         if (isResumed) {
159             if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH
160                     || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) {
161                 throw new IllegalStateException(
162                     "Cannot be used when alternative DiscoveryTechnology is set");
163             } else {
164                 setReaderMode(token, flags, extras);
165             }
166         }
167     }
168 
disableReaderMode(Activity activity)169     public void disableReaderMode(Activity activity) {
170         boolean isResumed;
171         Binder token;
172         synchronized (NfcActivityManager.this) {
173             NfcActivityState state = getActivityState(activity);
174             state.readerCallback = null;
175             state.readerModeFlags = 0;
176             state.readerModeExtras = null;
177             token = state.token;
178             isResumed = state.resumed;
179         }
180         if (isResumed) {
181             setReaderMode(token, 0, null);
182         }
183 
184     }
185 
setReaderMode(Binder token, int flags, Bundle extras)186     public void setReaderMode(Binder token, int flags, Bundle extras) {
187         if (DBG) Log.d(TAG, "setReaderModee");
188         NfcAdapter.callService(() -> NfcAdapter.sService.setReaderMode(
189                 token, this, flags, extras, mAdapter.getContext().getPackageName()));
190     }
191 
192     /**
193      * Request or unrequest NFC service callbacks.
194      * Makes IPC call - do not hold lock.
195      */
requestNfcServiceCallback()196     void requestNfcServiceCallback() {
197         NfcAdapter.callService(() -> NfcAdapter.sService.setAppCallback(this));
198     }
199 
verifyNfcPermission()200     void verifyNfcPermission() {
201         NfcAdapter.callService(() -> NfcAdapter.sService.verifyNfcPermission());
202     }
203 
204     @Override
onTagDiscovered(Tag tag)205     public void onTagDiscovered(Tag tag) throws RemoteException {
206         NfcAdapter.ReaderCallback callback;
207         synchronized (NfcActivityManager.this) {
208             NfcActivityState state = findResumedActivityState();
209             if (state == null) return;
210 
211             callback = state.readerCallback;
212         }
213 
214         // Make callback without lock
215         if (callback != null) {
216             callback.onTagDiscovered(tag);
217         }
218 
219     }
220     /** Callback from Activity life-cycle, on main thread */
221     @Override
onActivityCreated(Activity activity, Bundle savedInstanceState)222     public void onActivityCreated(Activity activity, Bundle savedInstanceState) { /* NO-OP */ }
223 
224     /** Callback from Activity life-cycle, on main thread */
225     @Override
onActivityStarted(Activity activity)226     public void onActivityStarted(Activity activity) { /* NO-OP */ }
227 
228     /** Callback from Activity life-cycle, on main thread */
229     @Override
onActivityResumed(Activity activity)230     public void onActivityResumed(Activity activity) {
231         int readerModeFlags = 0;
232         Bundle readerModeExtras = null;
233         Binder token;
234         int pollTech;
235         int listenTech;
236 
237         synchronized (NfcActivityManager.this) {
238             NfcActivityState state = findActivityState(activity);
239             if (DBG) Log.d(TAG, "onActivityResumed: " + activity + " " + state);
240             if (state == null) return;
241             state.resumed = true;
242             token = state.token;
243             readerModeFlags = state.readerModeFlags;
244             readerModeExtras = state.readerModeExtras;
245 
246             pollTech = state.mPollTech;
247             listenTech = state.mListenTech;
248         }
249         if (readerModeFlags != 0) {
250             setReaderMode(token, readerModeFlags, readerModeExtras);
251         } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH
252                 || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) {
253             changeDiscoveryTech(token, pollTech, listenTech);
254         }
255         requestNfcServiceCallback();
256     }
257 
258     /** Callback from Activity life-cycle, on main thread */
259     @Override
onActivityPaused(Activity activity)260     public void onActivityPaused(Activity activity) {
261         boolean readerModeFlagsSet;
262         Binder token;
263         int pollTech;
264         int listenTech;
265 
266         synchronized (NfcActivityManager.this) {
267             NfcActivityState state = findActivityState(activity);
268             if (DBG) Log.d(TAG, "onActivityPaused: " + activity + " " + state);
269             if (state == null) return;
270             state.resumed = false;
271             token = state.token;
272             readerModeFlagsSet = state.readerModeFlags != 0;
273 
274             pollTech = state.mPollTech;
275             listenTech = state.mListenTech;
276         }
277         if (readerModeFlagsSet) {
278             // Restore default p2p modes
279             setReaderMode(token, 0, null);
280         } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH
281                 || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) {
282             changeDiscoveryTech(token,
283                     NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH);
284         }
285     }
286 
287     /** Callback from Activity life-cycle, on main thread */
288     @Override
onActivityStopped(Activity activity)289     public void onActivityStopped(Activity activity) { /* NO-OP */ }
290 
291     /** Callback from Activity life-cycle, on main thread */
292     @Override
onActivitySaveInstanceState(Activity activity, Bundle outState)293     public void onActivitySaveInstanceState(Activity activity, Bundle outState) { /* NO-OP */ }
294 
295     /** Callback from Activity life-cycle, on main thread */
296     @Override
onActivityDestroyed(Activity activity)297     public void onActivityDestroyed(Activity activity) {
298         synchronized (NfcActivityManager.this) {
299             NfcActivityState state = findActivityState(activity);
300             if (DBG) Log.d(TAG, "onActivityDestroyed: " + activity + " " + state);
301             if (state != null) {
302                 // release all associated references
303                 destroyActivityState(activity);
304             }
305         }
306     }
307 
308     /** setDiscoveryTechnology() implementation */
setDiscoveryTech(Activity activity, int pollTech, int listenTech)309     public void setDiscoveryTech(Activity activity, int pollTech, int listenTech) {
310         boolean isResumed;
311         Binder token;
312         boolean readerModeFlagsSet;
313         synchronized (NfcActivityManager.this) {
314             NfcActivityState state = getActivityState(activity);
315             readerModeFlagsSet = state.readerModeFlags != 0;
316             state.mListenTech = listenTech;
317             state.mPollTech = pollTech;
318             token = state.token;
319             isResumed = state.resumed;
320         }
321         if (!readerModeFlagsSet && isResumed) {
322             changeDiscoveryTech(token, pollTech, listenTech);
323         } else if (readerModeFlagsSet) {
324             throw new IllegalStateException("Cannot be used when the Reader Mode is enabled");
325         }
326     }
327 
328     /** resetDiscoveryTechnology() implementation */
resetDiscoveryTech(Activity activity)329     public void resetDiscoveryTech(Activity activity) {
330         boolean isResumed;
331         Binder token;
332         boolean readerModeFlagsSet;
333         synchronized (NfcActivityManager.this) {
334             NfcActivityState state = getActivityState(activity);
335             state.mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
336             state.mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
337             token = state.token;
338             isResumed = state.resumed;
339         }
340         if (isResumed) {
341             changeDiscoveryTech(token, NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH);
342         }
343 
344     }
345 
changeDiscoveryTech(Binder token, int pollTech, int listenTech)346     private void changeDiscoveryTech(Binder token, int pollTech, int listenTech) {
347         NfcAdapter.callService(
348                 () -> NfcAdapter.sService.updateDiscoveryTechnology(
349                         token, pollTech, listenTech, mAdapter.getContext().getPackageName()));
350     }
351 
352 }
353