• 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 
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                     "resolveNfcid2: Resolved to: "
111                             + (resolveInfo == null ? "null" : resolveInfo.toString()));
112             return resolveInfo;
113         }
114     }
115 
generateUserNfcFServiceInfoLocked(int userId, List<NfcFServiceInfo> services)116     void generateUserNfcFServiceInfoLocked(int userId, List<NfcFServiceInfo> services) {
117         mUserNfcFServiceInfo.put(userId, services);
118     }
119 
getProfileParentId(int userId)120     private int getProfileParentId(int userId) {
121         UserManager um = mContext.createContextAsUser(
122                 UserHandle.of(userId), /*flags=*/0)
123                 .getSystemService(UserManager.class);
124         UserHandle uh = um.getProfileParent(UserHandle.of(userId));
125         return uh == null ? userId : uh.getIdentifier();
126     }
127 
generateForegroundT3tIdentifiersCacheLocked()128     void generateForegroundT3tIdentifiersCacheLocked() {
129         if (DBG) Log.d(TAG, "generateForegroundT3tIdentifiersCacheLocked");
130         mForegroundT3tIdentifiersCache.clear();
131         if (mEnabledForegroundService != null) {
132             for (NfcFServiceInfo service :
133                     mUserNfcFServiceInfo.get(mEnabledForegroundServiceUserId)) {
134                 if (mEnabledForegroundService.equals(service.getComponent())) {
135                     if (!service.getSystemCode().equalsIgnoreCase("NULL") &&
136                             !service.getNfcid2().equalsIgnoreCase("NULL")) {
137                         mForegroundT3tIdentifiersCache.put(service.getNfcid2(), service);
138                     }
139                     break;
140                 }
141             }
142         }
143 
144         if (DBG)  {
145             Log.d(TAG,
146                     "generateForegroundT3tIdentifiersCacheLocked: "
147                             + "mForegroundT3tIdentifiersCache: size="
148                             + mForegroundT3tIdentifiersCache.size());
149             for (Map.Entry<String, NfcFServiceInfo> entry : mForegroundT3tIdentifiersCache
150                     .entrySet()) {
151                 Log.d(TAG, "generateForegroundT3tIdentifiersCacheLocked:    " + entry.getKey() + "/"
152                         + entry.getValue().getComponent().toString());
153             }
154         }
155 
156         updateRoutingLocked(false);
157     }
158 
updateRoutingLocked(boolean force)159     void updateRoutingLocked(boolean force) {
160         if (DBG) Log.d(TAG, "updateRoutingLocked");
161         if (!mNfcEnabled) {
162             Log.d(TAG, "updateRoutingLocked: Not updating routing table because NFC is off");
163             return;
164         }
165 
166         List<T3tIdentifier> t3tIdentifiers = new ArrayList<T3tIdentifier>();
167 
168         // Sending an empty table will de-register all entries
169         if (force) {
170             mRoutingManager.configureRouting(t3tIdentifiers);
171         }
172         Iterator<Map.Entry<String, NfcFServiceInfo>> it;
173         // Register foreground service
174         it = mForegroundT3tIdentifiersCache.entrySet().iterator();
175         while (it.hasNext()) {
176             Map.Entry<String, NfcFServiceInfo> entry =
177                     (Map.Entry<String, NfcFServiceInfo>) it.next();
178             t3tIdentifiers.add(new T3tIdentifier(
179                     entry.getValue().getSystemCode(), entry.getValue().getNfcid2(), entry.getValue().getT3tPmm()));
180         }
181         mRoutingManager.configureRouting(t3tIdentifiers);
182     }
183 
onTriggerRoutingTableUpdate()184     public void onTriggerRoutingTableUpdate() {
185         synchronized(mLock) {
186             updateRoutingLocked(true);
187         }
188     }
189 
onServicesUpdated(int userId, List<NfcFServiceInfo> services)190     public void onServicesUpdated(int userId, List<NfcFServiceInfo> services) {
191         if (DBG) Log.d(TAG, "onServicesUpdated");
192         synchronized (mLock) {
193             mUserNfcFServiceInfo.put(userId, services);
194         }
195     }
196 
197     /**
198      * Enabled Foreground NfcF service changed
199      */
onEnabledForegroundNfcFServiceChanged(int userId, ComponentName component)200     public void onEnabledForegroundNfcFServiceChanged(int userId, ComponentName component) {
201         if (DBG) Log.d(TAG, "onEnabledForegroundNfcFServiceChanged");
202         synchronized (mLock) {
203             if (component != null) {
204                 if (mEnabledForegroundService != null
205                         && mEnabledForegroundServiceUserId == userId) {
206                     return;
207                 }
208                 mEnabledForegroundService = component;
209                 mEnabledForegroundServiceUserId = userId;
210             } else {
211                 if (mEnabledForegroundService == null) {
212                     return;
213                 }
214                 mEnabledForegroundService = null;
215                 mEnabledForegroundServiceUserId = -1;
216             }
217             generateForegroundT3tIdentifiersCacheLocked();
218         }
219     }
220 
onNfcEnabled()221     public void onNfcEnabled() {
222         synchronized (mLock) {
223             mNfcEnabled = true;
224         }
225     }
226 
onNfcDisabled()227     public void onNfcDisabled() {
228         synchronized (mLock) {
229             mNfcEnabled = false;
230             mForegroundT3tIdentifiersCache.clear();
231             mEnabledForegroundService = null;
232             mEnabledForegroundServiceUserId = -1;
233         }
234         mRoutingManager.onNfccRoutingTableCleared();
235     }
236 
onUserSwitched()237     public void onUserSwitched() {
238         synchronized (mLock) {
239             mForegroundT3tIdentifiersCache.clear();
240             updateRoutingLocked(false);
241             mEnabledForegroundService = null;
242             mEnabledForegroundServiceUserId = -1;
243         }
244     }
245 
dump(FileDescriptor fd, PrintWriter pw, String[] args)246     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
247         pw.println("T3T Identifier cache entries: ");
248         ParcelFileDescriptor pFd;
249         try {
250             pFd = ParcelFileDescriptor.dup(fd);
251             for (Map.Entry<String, NfcFServiceInfo> entry
252                     : mForegroundT3tIdentifiersCache.entrySet()) {
253                 pw.println("    NFCID2: " + entry.getKey());
254                 pw.println("    NfcFServiceInfo: ");
255                 entry.getValue().dump(pFd, pw, args);
256             }
257             pw.println("");
258             mRoutingManager.dump(fd, pw, args);
259             pw.println("");
260             pFd.close();
261         } catch (IOException e) {
262             pw.println("Failed to dump T3T idenitifier cache entries: " + e);
263         }
264     }
265 
266     /**
267      * Dump debugging information as a RegisteredT3tIdentifiersCacheProto
268      *
269      * Note:
270      * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
271      * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
272      * {@link ProtoOutputStream#end(long)} after.
273      * Never reuse a proto field number. When removing a field, mark it as reserved.
274      */
dumpDebug(ProtoOutputStream proto)275     void dumpDebug(ProtoOutputStream proto) {
276         for (NfcFServiceInfo serviceInfo : mForegroundT3tIdentifiersCache.values()) {
277             long token = proto.start(
278                     RegisteredT3tIdentifiersCacheProto.T3T_IDENTIFIER_CACHE_ENTRIES);
279             serviceInfo.dumpDebug(proto);
280             proto.end(token);
281         }
282         long token = proto.start(RegisteredT3tIdentifiersCacheProto.ROUTING_MANAGER);
283         mRoutingManager.dumpDebug(proto);
284         proto.end(token);
285     }
286 }
287