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