• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 package com.android.nfc.cardemulation;
17 
18 import android.app.ActivityManager;
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.nfc.cardemulation.NfcFServiceInfo;
22 import android.nfc.cardemulation.Utils;
23 import android.os.Handler;
24 import android.os.Looper;
25 import android.os.UserHandle;
26 import android.sysprop.NfcProperties;
27 import android.util.Log;
28 import android.util.proto.ProtoOutputStream;
29 
30 import androidx.annotation.VisibleForTesting;
31 
32 import com.android.nfc.ForegroundUtils;
33 
34 import java.io.FileDescriptor;
35 import java.io.PrintWriter;
36 
37 public class EnabledNfcFServices implements com.android.nfc.ForegroundUtils.Callback {
38     static final String TAG = "EnabledNfcFCardEmulationServices";
39     static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
40 
41     final Context mContext;
42     final RegisteredNfcFServicesCache mNfcFServiceCache;
43     final RegisteredT3tIdentifiersCache mT3tIdentifiersCache;
44     final Callback mCallback;
45     final ForegroundUtils mForegroundUtils;
46     final Handler mHandler = new Handler(Looper.getMainLooper());
47 
48     final Object mLock = new Object();
49     // Variables below synchronized on mLock
50     ComponentName mForegroundComponent = null; // The computed enabled foreground component
51     ComponentName mForegroundRequested = null; // The component requested to be enabled by fg app
52     int mForegroundUid = -1; // The UID of the fg app, or -1 if fg app didn't request
53 
54     boolean mComputeFgRequested = false;
55     boolean mActivated = false;
56 
57     public interface Callback {
58         /**
59          * Notify when enabled foreground NfcF service is changed.
60          */
onEnabledForegroundNfcFServiceChanged(int userId, ComponentName service)61         void onEnabledForegroundNfcFServiceChanged(int userId, ComponentName service);
62     }
63 
EnabledNfcFServices(Context context, RegisteredNfcFServicesCache nfcFServiceCache, RegisteredT3tIdentifiersCache t3tIdentifiersCache, Callback callback)64     public EnabledNfcFServices(Context context,
65             RegisteredNfcFServicesCache nfcFServiceCache,
66             RegisteredT3tIdentifiersCache t3tIdentifiersCache, Callback callback) {
67         if (DBG) Log.d(TAG, "EnabledNfcFServices");
68         mContext = context;
69         mForegroundUtils = ForegroundUtils.getInstance(
70                 context.getSystemService(ActivityManager.class));
71         mNfcFServiceCache = nfcFServiceCache;
72         mT3tIdentifiersCache = t3tIdentifiersCache;
73         mCallback = callback;
74     }
75 
computeEnabledForegroundService()76     void computeEnabledForegroundService() {
77         if (DBG) Log.d(TAG, "computeEnabledForegroundService");
78         ComponentName foregroundRequested = null;
79         boolean changed = false;
80         synchronized (mLock) {
81             if (mActivated) {
82                 Log.d(TAG, "computeEnabledForegroundService: configuration will be postponed "
83                         + "until deactivation");
84                 mComputeFgRequested = true;
85                 return;
86             }
87             mComputeFgRequested = false;
88             foregroundRequested = mForegroundRequested;
89             if (mForegroundRequested != null &&
90                     (mForegroundComponent == null ||
91                     !mForegroundRequested.equals(mForegroundComponent))) {
92                 mForegroundComponent = mForegroundRequested;
93                 changed = true;
94             } else if (mForegroundRequested == null && mForegroundComponent != null) {
95                 mForegroundComponent = mForegroundRequested;
96                 changed = true;
97             }
98         }
99         // Notify if anything changed
100         if (changed) {
101             int userId = UserHandle.getUserHandleForUid(mForegroundUid).getIdentifier();
102             mCallback.onEnabledForegroundNfcFServiceChanged(userId, foregroundRequested);
103         }
104     }
105 
onServicesUpdated()106     public void onServicesUpdated() {
107         if (DBG) Log.d(TAG, "onServicesUpdated");
108         // If enabled foreground service is set, remove it
109         boolean changed = false;
110         synchronized (mLock) {
111             if (mForegroundComponent != null) {
112                 Log.d(TAG, "onServicesUpdated: Removing foreground enabled service because of"
113                         + " service update");
114                 mForegroundRequested = null;
115                 mForegroundUid = -1;
116                 changed = true;
117             }
118         }
119         if (changed) {
120             computeEnabledForegroundService();
121         }
122     }
123 
registerEnabledForegroundService(ComponentName service, int callingUid)124     public boolean registerEnabledForegroundService(ComponentName service, int callingUid) {
125         if (DBG) Log.d(TAG, "registerEnabledForegroundService");
126         boolean success = false;
127         synchronized (mLock) {
128             int userId = UserHandle.getUserHandleForUid(callingUid).getIdentifier();
129             NfcFServiceInfo serviceInfo = mNfcFServiceCache.getService(
130                     userId, service);
131             if (serviceInfo == null) {
132                 return false;
133             } else {
134                 if (serviceInfo.getSystemCode().equalsIgnoreCase("NULL") ||
135                         serviceInfo.getNfcid2().equalsIgnoreCase("NULL") ||
136                         serviceInfo.getT3tPmm().equalsIgnoreCase("NULL")) {
137                     return false;
138                 }
139             }
140             if (service.equals(mForegroundRequested) && mForegroundUid == callingUid) {
141                 Log.e(TAG, "registerEnabledForegroundService: The service is already requested "
142                         + "to the foreground service");
143                 return true;
144             }
145             if (mForegroundUtils.registerUidToBackgroundCallback(this, callingUid)) {
146                 mForegroundRequested = service;
147                 mForegroundUid = callingUid;
148                 success = true;
149             } else {
150                 Log.e(TAG, "registerEnabledForegroundService: Calling UID is not in "
151                         + "the foreground, ignorning!");
152             }
153         }
154         if (success) {
155             computeEnabledForegroundService();
156         }
157         return success;
158     }
159 
unregisterForegroundService(int uid)160     boolean unregisterForegroundService(int uid) {
161         if (DBG) Log.d(TAG, "unregisterForegroundService");
162         boolean success = false;
163         synchronized (mLock) {
164             if (mForegroundUid == uid) {
165                 mForegroundRequested = null;
166                 mForegroundUid = -1;
167                 success = true;
168             } // else, other UID in foreground
169         }
170         if (success) {
171             computeEnabledForegroundService();
172         }
173         return success;
174     }
175 
unregisteredEnabledForegroundService(int callingUid)176     public boolean unregisteredEnabledForegroundService(int callingUid) {
177         if (DBG) Log.d(TAG, "unregisterEnabledForegroundService");
178         // Verify the calling UID is in the foreground
179         if (mForegroundUtils.isInForeground(callingUid)) {
180             return unregisterForegroundService(callingUid);
181         } else {
182             Log.e(TAG, "unregisterEnabledForegroundService: Calling UID is not in "
183                     + "the foreground, ignorning!");
184             return false;
185         }
186     }
187 
188     @Override
onUidToBackground(int uid)189     public void onUidToBackground(int uid) {
190         if (DBG) Log.d(TAG, "onUidToBackground");
191         unregisterForegroundService(uid);
192     }
193 
onHostEmulationActivated()194     public void onHostEmulationActivated() {
195         if (DBG) Log.d(TAG, "onHostEmulationActivated");
196         synchronized (mLock) {
197             mActivated = true;
198         }
199     }
200 
onHostEmulationDeactivated()201     public void onHostEmulationDeactivated() {
202         if (DBG) Log.d(TAG, "onHostEmulationDeactivated");
203         boolean needComputeFg = false;
204         synchronized (mLock) {
205             mActivated = false;
206             if (mComputeFgRequested) {
207                 needComputeFg = true;
208             }
209         }
210         if (needComputeFg) {
211             Log.d(TAG, "onHostEmulationDeactivated: do postponed configuration");
212             computeEnabledForegroundService();
213         }
214     }
215 
onNfcDisabled()216     public void onNfcDisabled() {
217         synchronized (mLock) {
218             mForegroundComponent = null;
219             mForegroundRequested = null;
220             mActivated = false;
221             mComputeFgRequested = false;
222             mForegroundUid = -1;
223         }
224     }
225 
onUserSwitched(int userId)226     public void onUserSwitched(int userId) {
227         synchronized (mLock) {
228             mForegroundComponent = null;
229             mForegroundRequested = null;
230             mActivated = false;
231             mComputeFgRequested = false;
232             mForegroundUid = -1;
233         }
234     }
235 
dump(FileDescriptor fd, PrintWriter pw, String[] args)236     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
237     }
238 
239     /**
240      * Dump debugging information as a EnabledNfcFServicesProto
241      *
242      * Note:
243      * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
244      * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
245      * {@link ProtoOutputStream#end(long)} after.
246      * Never reuse a proto field number. When removing a field, mark it as reserved.
247      */
dumpDebug(ProtoOutputStream proto)248     public void dumpDebug(ProtoOutputStream proto) {
249         synchronized (mLock) {
250             if (mForegroundComponent != null) {
251                 Utils.dumpDebugComponentName(
252                         mForegroundComponent, proto, EnabledNfcFServicesProto.FOREGROUND_COMPONENT);
253             }
254             if (mForegroundRequested != null) {
255                 Utils.dumpDebugComponentName(
256                         mForegroundRequested, proto, EnabledNfcFServicesProto.FOREGROUND_REQUESTED);
257             }
258             proto.write(EnabledNfcFServicesProto.ACTIVATED, mActivated);
259             proto.write(EnabledNfcFServicesProto.COMPUTE_FG_REQUESTED, mComputeFgRequested);
260             proto.write(EnabledNfcFServicesProto.FOREGROUND_UID, mForegroundUid);
261         }
262     }
263 
264     @VisibleForTesting
isActivated()265     public boolean isActivated() {
266         return mActivated;
267     }
268 
269     @VisibleForTesting
isNfcDisabled()270     public boolean isNfcDisabled() {
271         return !mActivated && mForegroundUid == -1;
272     }
273 
274     @VisibleForTesting
isUserSwitched()275     public boolean isUserSwitched() {
276         return !mActivated && mForegroundUid == -1 && !mComputeFgRequested;
277     }
278 }
279