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 
17 package com.android.nfc.cardemulation;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.nfc.cardemulation.NfcFServiceInfo;
22 import android.os.ParcelFileDescriptor;
23 import android.os.UserHandle;
24 import android.os.UserManager;
25 import android.sysprop.NfcProperties;
26 import android.util.Log;
27 import android.util.proto.ProtoOutputStream;
28 
29 import androidx.annotation.VisibleForTesting;
30 
31 import java.io.FileDescriptor;
32 import java.io.IOException;
33 import java.io.PrintWriter;
34 import java.util.ArrayList;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39 
40 public class RegisteredT3tIdentifiersCache {
41     static final String TAG = "RegisteredT3tIdentifiersCache";
42 
43     static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
44 
45     // All NFC-F services that have registered
46     final Map<Integer, List<NfcFServiceInfo>> mUserNfcFServiceInfo =
47             new HashMap<Integer, List<NfcFServiceInfo>>();
48 
49     final HashMap<String, NfcFServiceInfo> mForegroundT3tIdentifiersCache =
50             new HashMap<String, NfcFServiceInfo>();
51 
52     ComponentName mEnabledForegroundService;
53     int mEnabledForegroundServiceUserId = -1;
54 
55     final class T3tIdentifier {
56         public final String systemCode;
57         public final String nfcid2;
58         public final String t3tPmm;
59 
T3tIdentifier(String systemCode, String nfcid2, String t3tPmm)60         T3tIdentifier(String systemCode, String nfcid2, String t3tPmm) {
61             this.systemCode = systemCode;
62             this.nfcid2 = nfcid2;
63             this.t3tPmm = t3tPmm;
64         }
65 
66         @Override
equals(Object o)67         public boolean equals(Object o) {
68             if (this == o) return true;
69             if (o == null || getClass() != o.getClass()) return false;
70 
71             T3tIdentifier that = (T3tIdentifier) o;
72             if (!systemCode.equalsIgnoreCase(that.systemCode)) return false;
73             if (!nfcid2.equalsIgnoreCase(that.nfcid2)) return false;
74 
75             return true;
76         }
77 
78         @Override
hashCode()79         public int hashCode() {
80             int result = systemCode.hashCode();
81             result = 31 * result + nfcid2.hashCode();
82             return result;
83         }
84     }
85 
86     final Context mContext;
87     final SystemCodeRoutingManager mRoutingManager;
88 
89     final Object mLock = new Object();
90 
91     boolean mNfcEnabled = false;
92 
RegisteredT3tIdentifiersCache(Context context)93     public RegisteredT3tIdentifiersCache(Context context) {
94         this(context, new SystemCodeRoutingManager());
95     }
96 
97     @VisibleForTesting
RegisteredT3tIdentifiersCache(Context context, SystemCodeRoutingManager routingManager)98     RegisteredT3tIdentifiersCache(Context context, SystemCodeRoutingManager routingManager) {
99         Log.d(TAG, "RegisteredT3tIdentifiersCache");
100         mContext = context;
101         mRoutingManager = routingManager;
102     }
103 
resolveNfcid2(String nfcid2)104     public NfcFServiceInfo resolveNfcid2(String nfcid2) {
105         synchronized (mLock) {
106             if (DBG) Log.d(TAG, "resolveNfcid2: resolving NFCID " + nfcid2);
107             NfcFServiceInfo resolveInfo;
108             resolveInfo = mForegroundT3tIdentifiersCache.get(nfcid2);
109             Log.d(TAG,
110                     "Resolved to: " + (resolveInfo == null ? "null" : resolveInfo.toString()));
111             return resolveInfo;
112         }
113     }
114 
generateUserNfcFServiceInfoLocked(int userId, List<NfcFServiceInfo> services)115     void generateUserNfcFServiceInfoLocked(int userId, List<NfcFServiceInfo> services) {
116         mUserNfcFServiceInfo.put(userId, services);
117     }
118 
getProfileParentId(int userId)119     private int getProfileParentId(int userId) {
120         UserManager um = mContext.createContextAsUser(
121                 UserHandle.of(userId), /*flags=*/0)
122                 .getSystemService(UserManager.class);
123         UserHandle uh = um.getProfileParent(UserHandle.of(userId));
124         return uh == null ? userId : uh.getIdentifier();
125     }
126 
generateForegroundT3tIdentifiersCacheLocked()127     void generateForegroundT3tIdentifiersCacheLocked() {
128         if (DBG) Log.d(TAG, "generateForegroundT3tIdentifiersCacheLocked");
129         mForegroundT3tIdentifiersCache.clear();
130         if (mEnabledForegroundService != null) {
131             for (NfcFServiceInfo service :
132                     mUserNfcFServiceInfo.get(mEnabledForegroundServiceUserId)) {
133                 if (mEnabledForegroundService.equals(service.getComponent())) {
134                     if (!service.getSystemCode().equalsIgnoreCase("NULL") &&
135                             !service.getNfcid2().equalsIgnoreCase("NULL")) {
136                         mForegroundT3tIdentifiersCache.put(service.getNfcid2(), service);
137                     }
138                     break;
139                 }
140             }
141         }
142 
143         if (DBG) {
144             Log.d(TAG, "mForegroundT3tIdentifiersCache: size=" +
145                     mForegroundT3tIdentifiersCache.size());
146             for (Map.Entry<String, NfcFServiceInfo> entry :
147                     mForegroundT3tIdentifiersCache.entrySet()) {
148                 Log.d(TAG, "    " + entry.getKey() +
149                         "/" + entry.getValue().getComponent().toString());
150             }
151         }
152 
153         updateRoutingLocked(false);
154     }
155 
updateRoutingLocked(boolean force)156     void updateRoutingLocked(boolean force) {
157         if (DBG) Log.d(TAG, "updateRoutingLocked");
158         if (!mNfcEnabled) {
159             Log.d(TAG, "Not updating routing table because NFC is off.");
160             return;
161         }
162 
163         List<T3tIdentifier> t3tIdentifiers = new ArrayList<T3tIdentifier>();
164 
165         // Sending an empty table will de-register all entries
166         if (force) {
167             mRoutingManager.configureRouting(t3tIdentifiers);
168         }
169         Iterator<Map.Entry<String, NfcFServiceInfo>> it;
170         // Register foreground service
171         it = mForegroundT3tIdentifiersCache.entrySet().iterator();
172         while (it.hasNext()) {
173             Map.Entry<String, NfcFServiceInfo> entry =
174                     (Map.Entry<String, NfcFServiceInfo>) it.next();
175             t3tIdentifiers.add(new T3tIdentifier(
176                     entry.getValue().getSystemCode(), entry.getValue().getNfcid2(), entry.getValue().getT3tPmm()));
177         }
178         mRoutingManager.configureRouting(t3tIdentifiers);
179     }
180 
onSecureNfcToggled()181     public void onSecureNfcToggled() {
182         synchronized(mLock) {
183             updateRoutingLocked(true);
184       }
185     }
186 
onServicesUpdated(int userId, List<NfcFServiceInfo> services)187     public void onServicesUpdated(int userId, List<NfcFServiceInfo> services) {
188         if (DBG) Log.d(TAG, "onServicesUpdated");
189         synchronized (mLock) {
190             mUserNfcFServiceInfo.put(userId, services);
191         }
192     }
193 
194     /**
195      * Enabled Foreground NfcF service changed
196      */
onEnabledForegroundNfcFServiceChanged(int userId, ComponentName component)197     public void onEnabledForegroundNfcFServiceChanged(int userId, ComponentName component) {
198         if (DBG) Log.d(TAG, "Enabled foreground service changed.");
199         synchronized (mLock) {
200             if (component != null) {
201                 if (mEnabledForegroundService != null
202                         && mEnabledForegroundServiceUserId == userId) {
203                     return;
204                 }
205                 mEnabledForegroundService = component;
206                 mEnabledForegroundServiceUserId = userId;
207             } else {
208                 if (mEnabledForegroundService == null) {
209                     return;
210                 }
211                 mEnabledForegroundService = null;
212                 mEnabledForegroundServiceUserId = -1;
213             }
214             generateForegroundT3tIdentifiersCacheLocked();
215         }
216     }
217 
onNfcEnabled()218     public void onNfcEnabled() {
219         synchronized (mLock) {
220             mNfcEnabled = true;
221         }
222     }
223 
onNfcDisabled()224     public void onNfcDisabled() {
225         synchronized (mLock) {
226             mNfcEnabled = false;
227             mForegroundT3tIdentifiersCache.clear();
228             mEnabledForegroundService = null;
229             mEnabledForegroundServiceUserId = -1;
230         }
231         mRoutingManager.onNfccRoutingTableCleared();
232     }
233 
onUserSwitched()234     public void onUserSwitched() {
235         synchronized (mLock) {
236             mForegroundT3tIdentifiersCache.clear();
237             updateRoutingLocked(false);
238             mEnabledForegroundService = null;
239             mEnabledForegroundServiceUserId = -1;
240         }
241     }
242 
dump(FileDescriptor fd, PrintWriter pw, String[] args)243     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
244         pw.println("T3T Identifier cache entries: ");
245         ParcelFileDescriptor pFd;
246         try {
247             pFd = ParcelFileDescriptor.dup(fd);
248             for (Map.Entry<String, NfcFServiceInfo> entry
249                     : mForegroundT3tIdentifiersCache.entrySet()) {
250                 pw.println("    NFCID2: " + entry.getKey());
251                 pw.println("    NfcFServiceInfo: ");
252                 entry.getValue().dump(pFd, pw, args);
253             }
254             pw.println("");
255             mRoutingManager.dump(fd, pw, args);
256             pw.println("");
257             pFd.close();
258         } catch (IOException e) {
259             pw.println("Failed to dump T3T idenitifier cache entries: " + e);
260         }
261     }
262 
263     /**
264      * Dump debugging information as a RegisteredT3tIdentifiersCacheProto
265      *
266      * Note:
267      * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
268      * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
269      * {@link ProtoOutputStream#end(long)} after.
270      * Never reuse a proto field number. When removing a field, mark it as reserved.
271      */
dumpDebug(ProtoOutputStream proto)272     void dumpDebug(ProtoOutputStream proto) {
273         for (NfcFServiceInfo serviceInfo : mForegroundT3tIdentifiersCache.values()) {
274             long token = proto.start(
275                     RegisteredT3tIdentifiersCacheProto.T3T_IDENTIFIER_CACHE_ENTRIES);
276             serviceInfo.dumpDebug(proto);
277             proto.end(token);
278         }
279         long token = proto.start(RegisteredT3tIdentifiersCacheProto.ROUTING_MANAGER);
280         mRoutingManager.dumpDebug(proto);
281         proto.end(token);
282     }
283 }
284