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