1 /*
2  * Copyright 2021 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.annotation.NonNull;
20 import android.nfc.NfcAdapter.ControllerAlwaysOnListener;
21 import android.os.Binder;
22 import android.os.RemoteException;
23 import android.util.Log;
24 
25 import java.util.HashMap;
26 import java.util.Map;
27 import java.util.concurrent.Executor;
28 
29 /**
30  * @hide
31  */
32 public class NfcControllerAlwaysOnListener extends INfcControllerAlwaysOnListener.Stub {
33     private static final String TAG = NfcControllerAlwaysOnListener.class.getSimpleName();
34 
35     private final INfcAdapter mAdapter;
36 
37     private final Map<ControllerAlwaysOnListener, Executor> mListenerMap = new HashMap<>();
38 
39     private boolean mCurrentState = false;
40     private boolean mIsRegistered = false;
41 
NfcControllerAlwaysOnListener(@onNull INfcAdapter adapter)42     public NfcControllerAlwaysOnListener(@NonNull INfcAdapter adapter) {
43         mAdapter = adapter;
44     }
45 
46     /**
47      * Register a {@link ControllerAlwaysOnListener} with this
48      * {@link NfcControllerAlwaysOnListener}
49      *
50      * @param executor an {@link Executor} to execute given listener
51      * @param listener user implementation of the {@link ControllerAlwaysOnListener}
52      */
register(@onNull Executor executor, @NonNull ControllerAlwaysOnListener listener)53     public void register(@NonNull Executor executor,
54             @NonNull ControllerAlwaysOnListener listener) {
55         try {
56             if (!mAdapter.isControllerAlwaysOnSupported()) {
57                 return;
58             }
59         } catch (RemoteException e) {
60             Log.w(TAG, "Failed to register");
61             return;
62         }
63         synchronized (this) {
64             if (mListenerMap.containsKey(listener)) {
65                 return;
66             }
67 
68             mListenerMap.put(listener, executor);
69             if (!mIsRegistered) {
70                 try {
71                     mAdapter.registerControllerAlwaysOnListener(this);
72                     mIsRegistered = true;
73                 } catch (RemoteException e) {
74                     Log.w(TAG, "Failed to register");
75                 }
76             }
77         }
78     }
79 
80     /**
81      * Unregister the specified {@link ControllerAlwaysOnListener}
82      *
83      * @param listener user implementation of the {@link ControllerAlwaysOnListener}
84      */
unregister(@onNull ControllerAlwaysOnListener listener)85     public void unregister(@NonNull ControllerAlwaysOnListener listener) {
86         try {
87             if (!mAdapter.isControllerAlwaysOnSupported()) {
88                 return;
89             }
90         } catch (RemoteException e) {
91             Log.w(TAG, "Failed to unregister");
92             return;
93         }
94         synchronized (this) {
95             if (!mListenerMap.containsKey(listener)) {
96                 return;
97             }
98 
99             mListenerMap.remove(listener);
100 
101             if (mListenerMap.isEmpty() && mIsRegistered) {
102                 try {
103                     mAdapter.unregisterControllerAlwaysOnListener(this);
104                 } catch (RemoteException e) {
105                     Log.w(TAG, "Failed to unregister");
106                 }
107                 mIsRegistered = false;
108             }
109         }
110     }
111 
sendCurrentState(@onNull ControllerAlwaysOnListener listener)112     private void sendCurrentState(@NonNull ControllerAlwaysOnListener listener) {
113         synchronized (this) {
114             Executor executor = mListenerMap.get(listener);
115 
116             final long identity = Binder.clearCallingIdentity();
117             try {
118                 executor.execute(() -> listener.onControllerAlwaysOnChanged(
119                         mCurrentState));
120             } finally {
121                 Binder.restoreCallingIdentity(identity);
122             }
123         }
124     }
125 
126     @Override
onControllerAlwaysOnChanged(boolean isEnabled)127     public void onControllerAlwaysOnChanged(boolean isEnabled) {
128         synchronized (this) {
129             mCurrentState = isEnabled;
130             for (ControllerAlwaysOnListener cb : mListenerMap.keySet()) {
131                 sendCurrentState(cb);
132             }
133         }
134     }
135 }
136 
137