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