1/*
2 * Copyright (C) 2014 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 */
16package com.android.nfc.cardemulation;
17
18import java.io.FileDescriptor;
19import java.io.PrintWriter;
20import java.util.List;
21
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.nfc.INfcCardEmulation;
26import android.nfc.cardemulation.AidGroup;
27import android.nfc.cardemulation.ApduServiceInfo;
28import android.nfc.cardemulation.CardEmulation;
29import android.os.Binder;
30import android.os.RemoteException;
31import android.os.UserHandle;
32import android.provider.Settings;
33import android.util.Log;
34
35import com.android.nfc.NfcPermissions;
36import com.android.nfc.cardemulation.RegisteredServicesCache;
37
38/**
39 * CardEmulationManager is the central entity
40 * responsible for delegating to individual components
41 * implementing card emulation:
42 * - RegisteredServicesCache keeping track of HCE and SE services on the device
43 * - RegisteredAidCache keeping track of AIDs registered by those services and manages
44 *   the routing table in the NFCC.
45 * - HostEmulationManager handles incoming APDUs for the host and forwards to HCE
46 *   services as necessary.
47 */
48public class CardEmulationManager implements RegisteredServicesCache.Callback,
49        PreferredServices.Callback {
50    static final String TAG = "CardEmulationManager";
51    static final boolean DBG = false;
52
53    final RegisteredAidCache mAidCache;
54    final RegisteredServicesCache mServiceCache;
55    final HostEmulationManager mHostEmulationManager;
56    final PreferredServices mPreferredServices;
57    final Context mContext;
58    final CardEmulationInterface mCardEmulationInterface;
59
60    public CardEmulationManager(Context context) {
61        mContext = context;
62        mCardEmulationInterface = new CardEmulationInterface();
63        mAidCache = new RegisteredAidCache(context);
64        mHostEmulationManager = new HostEmulationManager(context, mAidCache);
65        mServiceCache = new RegisteredServicesCache(context, this);
66        mPreferredServices = new PreferredServices(context, mServiceCache, mAidCache, this);
67
68        mServiceCache.initialize();
69    }
70
71    public INfcCardEmulation getNfcCardEmulationInterface() {
72        return mCardEmulationInterface;
73    }
74
75    public void onHostCardEmulationActivated() {
76        mHostEmulationManager.onHostEmulationActivated();
77        mPreferredServices.onHostEmulationActivated();
78    }
79
80    public void onHostCardEmulationData(byte[] data) {
81        mHostEmulationManager.onHostEmulationData(data);
82    }
83
84    public void onHostCardEmulationDeactivated() {
85        mHostEmulationManager.onHostEmulationDeactivated();
86        mPreferredServices.onHostEmulationDeactivated();
87    }
88
89    public void onOffHostAidSelected() {
90        mHostEmulationManager.onOffHostAidSelected();
91    }
92
93    public void onUserSwitched(int userId) {
94        mServiceCache.invalidateCache(userId);
95        mPreferredServices.onUserSwitched(userId);
96    }
97
98    public void onNfcEnabled() {
99        mAidCache.onNfcEnabled();
100    }
101
102    public void onNfcDisabled() {
103        mAidCache.onNfcDisabled();
104    }
105
106    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
107        mServiceCache.dump(fd, pw, args);
108        mPreferredServices.dump(fd, pw, args);
109        mAidCache.dump(fd, pw, args);
110        mHostEmulationManager.dump(fd, pw, args);
111    }
112
113    @Override
114    public void onServicesUpdated(int userId, List<ApduServiceInfo> services) {
115        // Verify defaults are still sane
116        verifyDefaults(userId, services);
117        // Update the AID cache
118        mAidCache.onServicesUpdated(userId, services);
119        // Update the preferred services list
120        mPreferredServices.onServicesUpdated();
121    }
122
123    void verifyDefaults(int userId, List<ApduServiceInfo> services) {
124        ComponentName defaultPaymentService =
125                getDefaultServiceForCategory(userId, CardEmulation.CATEGORY_PAYMENT, false);
126        if (DBG) Log.d(TAG, "Current default: " + defaultPaymentService);
127        if (defaultPaymentService != null) {
128            // Validate the default is still installed and handling payment
129            ApduServiceInfo serviceInfo = mServiceCache.getService(userId, defaultPaymentService);
130            if (serviceInfo == null || !serviceInfo.hasCategory(CardEmulation.CATEGORY_PAYMENT)) {
131                if (serviceInfo == null) {
132                    Log.e(TAG, "Default payment service unexpectedly removed.");
133                } else if (!serviceInfo.hasCategory(CardEmulation.CATEGORY_PAYMENT)) {
134                    if (DBG) Log.d(TAG, "Default payment service had payment category removed");
135                }
136                int numPaymentServices = 0;
137                ComponentName lastFoundPaymentService = null;
138                for (ApduServiceInfo service : services) {
139                    if (service.hasCategory(CardEmulation.CATEGORY_PAYMENT))  {
140                        numPaymentServices++;
141                        lastFoundPaymentService = service.getComponent();
142                    }
143                }
144                if (DBG) Log.d(TAG, "Number of payment services is " +
145                        Integer.toString(numPaymentServices));
146                if (numPaymentServices == 0) {
147                    if (DBG) Log.d(TAG, "Default removed, no services left.");
148                    // No payment services left, unset default and don't ask the user
149                    setDefaultServiceForCategoryChecked(userId, null, CardEmulation.CATEGORY_PAYMENT);
150                } else if (numPaymentServices == 1) {
151                    // Only one left, automatically make it the default
152                    if (DBG) Log.d(TAG, "Default removed, making remaining service default.");
153                    setDefaultServiceForCategoryChecked(userId, lastFoundPaymentService,
154                            CardEmulation.CATEGORY_PAYMENT);
155                } else if (numPaymentServices > 1) {
156                    // More than one left, unset default and ask the user if he wants
157                    // to set a new one
158                    if (DBG) Log.d(TAG, "Default removed, asking user to pick.");
159                    setDefaultServiceForCategoryChecked(userId, null,
160                            CardEmulation.CATEGORY_PAYMENT);
161                    Intent intent = new Intent(mContext, DefaultRemovedActivity.class);
162                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
163                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
164                }
165            } else {
166                // Default still exists and handles the category, nothing do
167                if (DBG) Log.d(TAG, "Default payment service still ok.");
168            }
169        } else {
170            // A payment service may have been removed, leaving only one;
171            // in that case, automatically set that app as default.
172            int numPaymentServices = 0;
173            ComponentName lastFoundPaymentService = null;
174            for (ApduServiceInfo service : services) {
175                if (service.hasCategory(CardEmulation.CATEGORY_PAYMENT))  {
176                    numPaymentServices++;
177                    lastFoundPaymentService = service.getComponent();
178                }
179            }
180            if (numPaymentServices > 1) {
181                // More than one service left, leave default unset
182                if (DBG) Log.d(TAG, "No default set, more than one service left.");
183            } else if (numPaymentServices == 1) {
184                // Make single found payment service the default
185                if (DBG) Log.d(TAG, "No default set, making single service default.");
186                setDefaultServiceForCategoryChecked(userId, lastFoundPaymentService,
187                        CardEmulation.CATEGORY_PAYMENT);
188            } else {
189                // No payment services left, leave default at null
190                if (DBG) Log.d(TAG, "No default set, last payment service removed.");
191            }
192        }
193    }
194
195    ComponentName getDefaultServiceForCategory(int userId, String category,
196             boolean validateInstalled) {
197        if (!CardEmulation.CATEGORY_PAYMENT.equals(category)) {
198            Log.e(TAG, "Not allowing defaults for category " + category);
199            return null;
200        }
201        // Load current payment default from settings
202        String name = Settings.Secure.getStringForUser(
203                mContext.getContentResolver(), Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
204                userId);
205        if (name != null) {
206            ComponentName service = ComponentName.unflattenFromString(name);
207            if (!validateInstalled || service == null) {
208                return service;
209            } else {
210                return mServiceCache.hasService(userId, service) ? service : null;
211             }
212        } else {
213            return null;
214        }
215    }
216
217    boolean setDefaultServiceForCategoryChecked(int userId, ComponentName service,
218            String category) {
219        if (!CardEmulation.CATEGORY_PAYMENT.equals(category)) {
220            Log.e(TAG, "Not allowing defaults for category " + category);
221            return false;
222        }
223        // TODO Not really nice to be writing to Settings.Secure here...
224        // ideally we overlay our local changes over whatever is in
225        // Settings.Secure
226        if (service == null || mServiceCache.hasService(userId, service)) {
227            Settings.Secure.putStringForUser(mContext.getContentResolver(),
228                    Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
229                    service != null ? service.flattenToString() : null, userId);
230        } else {
231            Log.e(TAG, "Could not find default service to make default: " + service);
232        }
233        return true;
234    }
235
236    boolean isServiceRegistered(int userId, ComponentName service) {
237        boolean serviceFound = mServiceCache.hasService(userId, service);
238        if (!serviceFound) {
239            // If we don't know about this service yet, it may have just been enabled
240            // using PackageManager.setComponentEnabledSetting(). The PackageManager
241            // broadcasts are delayed by 10 seconds in that scenario, which causes
242            // calls to our APIs referencing that service to fail.
243            // Hence, update the cache in case we don't know about the service.
244            if (DBG) Log.d(TAG, "Didn't find passed in service, invalidating cache.");
245            mServiceCache.invalidateCache(userId);
246        }
247        return mServiceCache.hasService(userId, service);
248    }
249
250    /**
251     * Returns whether a service in this package is preferred,
252     * either because it's the default payment app or it's running
253     * in the foreground.
254     */
255    public boolean packageHasPreferredService(String packageName) {
256        return mPreferredServices.packageHasPreferredService(packageName);
257    }
258
259    /**
260     * This class implements the application-facing APIs
261     * and are called from binder. All calls must be
262     * permission-checked.
263     *
264     */
265    final class CardEmulationInterface extends INfcCardEmulation.Stub {
266        @Override
267        public boolean isDefaultServiceForCategory(int userId, ComponentName service,
268                String category) {
269            NfcPermissions.enforceUserPermissions(mContext);
270            NfcPermissions.validateUserId(userId);
271            if (!isServiceRegistered(userId, service)) {
272                return false;
273            }
274            ComponentName defaultService =
275                    getDefaultServiceForCategory(userId, category, true);
276            return (defaultService != null && defaultService.equals(service));
277        }
278
279        @Override
280        public boolean isDefaultServiceForAid(int userId,
281                ComponentName service, String aid) throws RemoteException {
282            NfcPermissions.validateUserId(userId);
283            NfcPermissions.enforceUserPermissions(mContext);
284            if (!isServiceRegistered(userId, service)) {
285                return false;
286            }
287            return mAidCache.isDefaultServiceForAid(userId, service, aid);
288        }
289
290        @Override
291        public boolean setDefaultServiceForCategory(int userId,
292                ComponentName service, String category) throws RemoteException {
293            NfcPermissions.validateUserId(userId);
294            NfcPermissions.enforceAdminPermissions(mContext);
295            if (!isServiceRegistered(userId, service)) {
296                return false;
297            }
298            return setDefaultServiceForCategoryChecked(userId, service, category);
299        }
300
301        @Override
302        public boolean setDefaultForNextTap(int userId, ComponentName service)
303                throws RemoteException {
304            NfcPermissions.validateUserId(userId);
305            NfcPermissions.enforceAdminPermissions(mContext);
306            if (!isServiceRegistered(userId, service)) {
307                return false;
308            }
309            return mPreferredServices.setDefaultForNextTap(service);
310        }
311
312        @Override
313        public boolean registerAidGroupForService(int userId,
314                ComponentName service, AidGroup aidGroup) throws RemoteException {
315            NfcPermissions.validateUserId(userId);
316            NfcPermissions.enforceUserPermissions(mContext);
317            if (!isServiceRegistered(userId, service)) {
318                return false;
319            }
320            return mServiceCache.registerAidGroupForService(userId, Binder.getCallingUid(), service,
321                    aidGroup);
322        }
323
324        @Override
325        public AidGroup getAidGroupForService(int userId,
326                ComponentName service, String category) throws RemoteException {
327            NfcPermissions.validateUserId(userId);
328            NfcPermissions.enforceUserPermissions(mContext);
329            if (!isServiceRegistered(userId, service)) {
330                return null;
331            }
332            return mServiceCache.getAidGroupForService(userId, Binder.getCallingUid(), service,
333                    category);
334        }
335
336        @Override
337        public boolean removeAidGroupForService(int userId,
338                ComponentName service, String category) throws RemoteException {
339            NfcPermissions.validateUserId(userId);
340            NfcPermissions.enforceUserPermissions(mContext);
341            if (!isServiceRegistered(userId, service)) {
342                return false;
343            }
344            return mServiceCache.removeAidGroupForService(userId, Binder.getCallingUid(), service,
345                    category);
346        }
347
348        @Override
349        public List<ApduServiceInfo> getServices(int userId, String category)
350                throws RemoteException {
351            NfcPermissions.validateUserId(userId);
352            NfcPermissions.enforceAdminPermissions(mContext);
353            return mServiceCache.getServicesForCategory(userId, category);
354        }
355
356        @Override
357        public boolean setPreferredService(ComponentName service)
358                throws RemoteException {
359            NfcPermissions.enforceUserPermissions(mContext);
360            if (!isServiceRegistered(UserHandle.getCallingUserId(), service)) {
361                Log.e(TAG, "setPreferredService: unknown component.");
362                return false;
363            }
364            return mPreferredServices.registerPreferredForegroundService(service,
365                    Binder.getCallingUid());
366        }
367
368        @Override
369        public boolean unsetPreferredService() throws RemoteException {
370            NfcPermissions.enforceUserPermissions(mContext);
371            return mPreferredServices.unregisteredPreferredForegroundService(
372                    Binder.getCallingUid());
373
374        }
375
376        @Override
377        public boolean supportsAidPrefixRegistration() throws RemoteException {
378            return mAidCache.supportsAidPrefixRegistration();
379        }
380    }
381
382    @Override
383    public void onPreferredPaymentServiceChanged(ComponentName service) {
384        mAidCache.onPreferredPaymentServiceChanged(service);
385        mHostEmulationManager.onPreferredPaymentServiceChanged(service);
386    }
387
388    @Override
389    public void onPreferredForegroundServiceChanged(ComponentName service) {
390        mAidCache.onPreferredForegroundServiceChanged(service);
391        mHostEmulationManager.onPreferredForegroundServiceChanged(service);
392    };
393}
394