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