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