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