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