• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package com.android.nfc.cardemulation;
17 
18 import android.app.ActivityManager;
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.database.ContentObserver;
22 import android.net.Uri;
23 import android.nfc.cardemulation.ApduServiceInfo;
24 import android.nfc.cardemulation.CardEmulation;
25 import android.nfc.cardemulation.Utils;
26 import android.os.Handler;
27 import android.os.Looper;
28 import android.os.UserHandle;
29 import android.os.UserManager;
30 import android.provider.Settings;
31 import android.provider.Settings.SettingNotFoundException;
32 import android.sysprop.NfcProperties;
33 import android.util.Log;
34 import android.util.proto.ProtoOutputStream;
35 
36 import com.android.nfc.ForegroundUtils;
37 
38 import java.io.FileDescriptor;
39 import java.io.PrintWriter;
40 import java.util.List;
41 
42 /**
43  * This class keeps track of what HCE/SE-based services are
44  * preferred by the user. It currently has 3 inputs:
45  * 1) The default set in tap&pay menu for payment category
46  * 2) An app in the foreground asking for a specific
47  *    service for a specific category
48  * 3) If we had to disambiguate a previous tap (because no
49  *    preferred service was there), we need to temporarily
50  *    store the user's choice for the next tap.
51  *
52  * This class keeps track of all 3 inputs, and computes a new
53  * preferred services as needed. It then passes this service
54  * (if it changed) through a callback, which allows other components
55  * to adapt as necessary (ie the AID cache can update its AID
56  * mappings and the routing table).
57  */
58 public class PreferredServices implements com.android.nfc.ForegroundUtils.Callback {
59     static final String TAG = "PreferredCardEmulationServices";
60     static final boolean DBG = NfcProperties.debug_enabled().orElse(false);
61     static final Uri paymentDefaultUri = Settings.Secure.getUriFor(
62             Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
63     static final Uri paymentForegroundUri = Settings.Secure.getUriFor(
64             Settings.Secure.NFC_PAYMENT_FOREGROUND);
65 
66     final SettingsObserver mSettingsObserver;
67     final Context mContext;
68     final RegisteredServicesCache mServiceCache;
69     final RegisteredAidCache mAidCache;
70     final Callback mCallback;
71     final ForegroundUtils mForegroundUtils;
72     final Handler mHandler = new Handler(Looper.getMainLooper());
73 
74     final class PaymentDefaults {
75         boolean preferForeground; // The current selection mode for this category
76         ComponentName settingsDefault; // The component preferred in settings (eg Tap&Pay)
77         ComponentName currentPreferred; // The computed preferred component
78         UserHandle mUserHandle;
79     }
80 
81     final Object mLock = new Object();
82     // Variables below synchronized on mLock
83     PaymentDefaults mPaymentDefaults = new PaymentDefaults();
84 
85     ComponentName mForegroundRequested; // The component preferred by fg app
86     int mForegroundUid; // The UID of the fg app, or -1 if fg app didn't request
87 
88     ComponentName mNextTapDefault; // The component preferred by active disambig dialog
89     int mNextTapDefaultUserId;
90     boolean mClearNextTapDefault = false; // Set when the next tap default must be cleared
91 
92     ComponentName mForegroundCurrent; // The currently computed foreground component
93     int mForegroundCurrentUid; // The UID of the currently computed foreground component
94 
95     public interface Callback {
96         /**
97          * Notify when preferred payment service is changed
98          */
onPreferredPaymentServiceChanged(int userId, ComponentName service)99         void onPreferredPaymentServiceChanged(int userId, ComponentName service);
100         /**
101          * Notify when preferred foreground service is changed
102          */
onPreferredForegroundServiceChanged(int userId, ComponentName service)103         void onPreferredForegroundServiceChanged(int userId, ComponentName service);
104     }
105 
PreferredServices(Context context, RegisteredServicesCache serviceCache, RegisteredAidCache aidCache, Callback callback)106     public PreferredServices(Context context, RegisteredServicesCache serviceCache,
107             RegisteredAidCache aidCache, Callback callback) {
108         mContext = context;
109         mForegroundUtils = ForegroundUtils.getInstance(
110                 context.getSystemService(ActivityManager.class));
111         mServiceCache = serviceCache;
112         mAidCache = aidCache;
113         mCallback = callback;
114         mSettingsObserver = new SettingsObserver(mHandler);
115         mContext.getContentResolver().registerContentObserverAsUser(
116                 paymentDefaultUri,
117                 true, mSettingsObserver, UserHandle.ALL);
118 
119         mContext.getContentResolver().registerContentObserverAsUser(
120                 paymentForegroundUri,
121                 true, mSettingsObserver, UserHandle.ALL);
122 
123         // Load current settings defaults for payments
124         loadDefaultsFromSettings(ActivityManager.getCurrentUser(), false);
125     }
126 
127     private final class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler)128         public SettingsObserver(Handler handler) {
129             super(handler);
130         }
131 
132         @Override
onChange(boolean selfChange, Uri uri)133         public void onChange(boolean selfChange, Uri uri) {
134             super.onChange(selfChange, uri);
135             // Do it just for the current user. If it was in fact
136             // a change made for another user, we'll sync it down
137             // on user switch.
138             int currentUser = ActivityManager.getCurrentUser();
139             loadDefaultsFromSettings(currentUser, false);
140         }
141     };
142 
loadDefaultsFromSettings(int userId, boolean force)143     void loadDefaultsFromSettings(int userId, boolean force) {
144         boolean paymentDefaultChanged = false;
145         boolean paymentPreferForegroundChanged = false;
146         // Load current payment default from settings
147         UserHandle currentUser = UserHandle.of(ActivityManager.getCurrentUser());
148         UserManager um = mContext.createContextAsUser(currentUser, /*flags=*/0)
149                 .getSystemService(UserManager.class);
150         List<UserHandle> userHandles = um.getEnabledProfiles();
151 
152         String name = null;
153         String newDefaultName = null;
154         UserHandle newUser = null;
155         // search for default payment setting within enabled profiles
156         for (UserHandle uh : userHandles) {
157             name = Settings.Secure.getString(
158                     mContext.createContextAsUser(uh, 0).getContentResolver(),
159                     Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
160             if (name != null) {
161                 newUser = uh;
162                 newDefaultName = name;
163             }
164             if (uh.getIdentifier() == userId) {
165                 currentUser = uh;
166             }
167         }
168         if (currentUser == null) {
169             Log.e(TAG, "NULL/ Error fetching currentUser info");
170             return;
171         }
172         // no default payment setting in all profles
173         if (newUser == null) {
174             newUser = currentUser;
175         }
176         ComponentName newDefault = newDefaultName != null
177                 ? ComponentName.unflattenFromString(newDefaultName) : null;
178         boolean preferForeground = false;
179         try {
180             // get the setting from the main user instead of from the user profiles.
181             preferForeground = Settings.Secure.getInt(mContext
182                     .createContextAsUser(currentUser, 0).getContentResolver(),
183                     Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0;
184         } catch (SettingNotFoundException e) {
185         }
186         synchronized (mLock) {
187             paymentPreferForegroundChanged = (preferForeground != mPaymentDefaults.preferForeground);
188             mPaymentDefaults.preferForeground = preferForeground;
189 
190             mPaymentDefaults.settingsDefault = newDefault;
191             if (newDefault != null && (!newDefault.equals(mPaymentDefaults.currentPreferred)
192                     || mPaymentDefaults.mUserHandle.getIdentifier() != newUser.getIdentifier())) {
193                 paymentDefaultChanged = true;
194                 mPaymentDefaults.currentPreferred = newDefault;
195                 mPaymentDefaults.mUserHandle = newUser;
196             } else if (newDefault == null && mPaymentDefaults.currentPreferred != null) {
197                 paymentDefaultChanged = true;
198                 mPaymentDefaults.currentPreferred = newDefault;
199                 mPaymentDefaults.mUserHandle = newUser;
200             } else {
201                 // Same default as before
202             }
203         }
204         // Notify if anything changed
205         if (paymentDefaultChanged || force) {
206             mCallback.onPreferredPaymentServiceChanged(newUser.getIdentifier(), newDefault);
207         }
208         if (paymentPreferForegroundChanged || force) {
209             computePreferredForegroundService();
210         }
211     }
212 
computePreferredForegroundService()213     void computePreferredForegroundService() {
214         ComponentName preferredService = null;
215         int preferredServiceUserId;
216         boolean changed = false;
217         synchronized (mLock) {
218             // Prio 1: next tap default
219             preferredService = mNextTapDefault;
220             preferredServiceUserId = mNextTapDefaultUserId;
221             if (preferredService == null) {
222                 // Prio 2: foreground requested by app
223                 preferredService = mForegroundRequested;
224                 preferredServiceUserId =
225                         UserHandle.getUserHandleForUid(mForegroundUid).getIdentifier();
226             }
227             if (preferredService != null && (!preferredService.equals(mForegroundCurrent)
228                       || preferredServiceUserId
229                       != UserHandle.getUserHandleForUid(mForegroundCurrentUid).getIdentifier())) {
230                 mForegroundCurrent = preferredService;
231                 mForegroundCurrentUid = mForegroundUid;
232                 changed = true;
233             } else if (preferredService == null && mForegroundCurrent != null){
234                 mForegroundCurrent = preferredService;
235                 mForegroundCurrentUid = mForegroundUid;
236                 changed = true;
237             }
238         }
239         // Notify if anything changed
240         if (changed) {
241             mCallback.onPreferredForegroundServiceChanged(preferredServiceUserId, preferredService);
242         }
243     }
244 
245     /**
246      *  Set default service for next tap
247      */
setDefaultForNextTap(int userId, ComponentName service)248     public boolean setDefaultForNextTap(int userId, ComponentName service) {
249         // This is a trusted API, so update without checking
250         synchronized (mLock) {
251             mNextTapDefault = service;
252             mNextTapDefaultUserId = userId;
253         }
254         computePreferredForegroundService();
255         return true;
256     }
257 
onServicesUpdated()258     public void onServicesUpdated() {
259         // If this service is the current foreground service, verify
260         // there are no conflicts
261         boolean changed = false;
262         synchronized (mLock) {
263             // Check if the current foreground service is still allowed to override;
264             // it could have registered new AIDs that make it conflict with user
265             // preferences.
266             if (mForegroundCurrent != null) {
267                 if (!isForegroundAllowedLocked(mForegroundCurrent, mForegroundCurrentUid))  {
268                     Log.d(TAG, "Removing foreground preferred service.");
269                     mForegroundRequested = null;
270                     mForegroundUid = -1;
271                     mForegroundCurrentUid = -1;
272                     changed = true;
273                 }
274             } else {
275                 // Don't care about this service
276             }
277         }
278         if (changed) {
279             computePreferredForegroundService();
280         }
281     }
282 
283     // Verifies whether a service is allowed to register as preferred
isForegroundAllowedLocked(ComponentName service, int callingUid)284     boolean isForegroundAllowedLocked(ComponentName service, int callingUid) {
285         if (service.equals(mPaymentDefaults.currentPreferred)) {
286             // If the requester is already the payment default, allow it to request foreground
287             // override as well (it could use this to make sure it handles AIDs of category OTHER)
288             return true;
289         }
290         ApduServiceInfo serviceInfo = mServiceCache.getService(
291                 UserHandle.getUserHandleForUid(callingUid).getIdentifier(), service);
292         if (serviceInfo == null) {
293             Log.d(TAG, "Requested foreground service unexpectedly removed");
294             return false;
295         }
296         // Do some quick checking
297         if (!mPaymentDefaults.preferForeground) {
298             // Foreground apps are not allowed to override payment default
299             // Check if this app registers payment AIDs, in which case we'll fail anyway
300             if (serviceInfo.hasCategory(CardEmulation.CATEGORY_PAYMENT)) {
301                 Log.d(TAG, "User doesn't allow payment services to be overridden.");
302                 return false;
303             }
304             // If no payment AIDs, get AIDs of category other, and see if there's any
305             // conflict with payment AIDs of current default payment app. That means
306             // the current default payment app said this was a payment AID, and the
307             // foreground app says it was not. In this case we'll still prefer the payment
308             // app, since that is the one that the user has explicitly selected (and said
309             // it's not allowed to be overridden).
310             final List<String> otherAids = serviceInfo.getAids();
311             ApduServiceInfo paymentServiceInfo = mServiceCache.getService(
312                     mPaymentDefaults.mUserHandle.getIdentifier(),
313                     mPaymentDefaults.currentPreferred);
314             if (paymentServiceInfo != null && otherAids != null && otherAids.size() > 0) {
315                 for (String aid : otherAids) {
316                     RegisteredAidCache.AidResolveInfo resolveInfo = mAidCache.resolveAid(aid);
317                     if (CardEmulation.CATEGORY_PAYMENT.equals(resolveInfo.category) &&
318                             paymentServiceInfo.equals(resolveInfo.defaultService)) {
319                         if (DBG) Log.d(TAG, "AID " + aid + " is handled by the default payment app,"
320                                 + " and the user has not allowed payments to be overridden.");
321                         return false;
322                     }
323                 }
324                 return true;
325             } else {
326                 // Could not find payment service or fg app doesn't register other AIDs;
327                 // okay to proceed.
328                 return true;
329             }
330         } else {
331             // Payment allows override, so allow anything.
332             return true;
333         }
334     }
335 
registerPreferredForegroundService(ComponentName service, int callingUid)336     public boolean registerPreferredForegroundService(ComponentName service, int callingUid) {
337         boolean success = false;
338         synchronized (mLock) {
339             if (isForegroundAllowedLocked(service, callingUid)) {
340                 if (mForegroundUtils.registerUidToBackgroundCallback(this, callingUid)) {
341                     mForegroundRequested = service;
342                     mForegroundUid = callingUid;
343                     success = true;
344                 } else {
345                     Log.e(TAG, "Calling UID is not in the foreground, ignorning!");
346                     success = false;
347                 }
348             } else {
349                 Log.e(TAG, "Requested foreground service conflicts or was removed.");
350             }
351         }
352         if (success) {
353             computePreferredForegroundService();
354         }
355         return success;
356     }
357 
unregisterForegroundService(int uid)358     boolean unregisterForegroundService(int uid) {
359         boolean success = false;
360         synchronized (mLock) {
361             if (mForegroundUid == uid) {
362                 mForegroundRequested = null;
363                 mForegroundUid = -1;
364                 success = true;
365             } // else, other UID in foreground
366         }
367         if (success) {
368             computePreferredForegroundService();
369         }
370         return success;
371     }
372 
unregisteredPreferredForegroundService(int callingUid)373     public boolean unregisteredPreferredForegroundService(int callingUid) {
374         // Verify the calling UID is in the foreground
375         if (mForegroundUtils.isInForeground(callingUid)) {
376             return unregisterForegroundService(callingUid);
377         } else {
378             Log.e(TAG, "Calling UID is not in the foreground, ignorning!");
379             return false;
380         }
381     }
382 
383     @Override
onUidToBackground(int uid)384     public void onUidToBackground(int uid) {
385         unregisterForegroundService(uid);
386     }
387 
onHostEmulationActivated()388     public void onHostEmulationActivated() {
389         synchronized (mLock) {
390             mClearNextTapDefault = (mNextTapDefault != null);
391         }
392     }
393 
onHostEmulationDeactivated()394     public void onHostEmulationDeactivated() {
395         // If we had any next tap defaults set, clear them out
396         boolean changed = false;
397         synchronized (mLock) {
398             if (mClearNextTapDefault) {
399                 // The reason we need to check this boolean is because the next tap
400                 // default may have been set while the user held the phone
401                 // on the reader; when the user then removes his phone from
402                 // the reader (causing the "onHostEmulationDeactivated" event),
403                 // the next tap default would immediately be cleared
404                 // again. Instead, clear out defaults only if a next tap default
405                 // had already been set at time of activation, which is captured
406                 // by mClearNextTapDefault.
407                 if (mNextTapDefault != null) {
408                     mNextTapDefault = null;
409                     changed = true;
410                 }
411                 mClearNextTapDefault = false;
412             }
413         }
414         if (changed) {
415             computePreferredForegroundService();
416         }
417     }
418 
onUserSwitched(int userId)419     public void onUserSwitched(int userId) {
420         loadDefaultsFromSettings(userId, true);
421     }
422 
packageHasPreferredService(String packageName)423     public boolean packageHasPreferredService(String packageName) {
424         if (packageName == null) return false;
425         synchronized (mLock) {
426             if (mPaymentDefaults.currentPreferred != null
427                     && packageName.equals(mPaymentDefaults.currentPreferred.getPackageName())) {
428                 return true;
429             }
430             return (mForegroundCurrent != null
431                 && packageName.equals(mForegroundCurrent.getPackageName()));
432         }
433     }
434 
dump(FileDescriptor fd, PrintWriter pw, String[] args)435     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
436         synchronized (mLock) {
437             pw.println("Preferred services (in order of importance): ");
438             pw.println("    *** Current preferred foreground service: " + mForegroundCurrent
439                     + " (UID:" + mForegroundCurrentUid + ")");
440             pw.println("    *** Current preferred payment service: "
441                     + mPaymentDefaults.currentPreferred + "("
442                     + getUserName(mPaymentDefaults.mUserHandle) + ")");
443             pw.println("        Next tap default: " + mNextTapDefault
444                     + " (" + getUserName(UserHandle.of(mNextTapDefaultUserId)) + ")");
445             pw.println("        Default for foreground app (UID: " + mForegroundUid
446                     + "): " + mForegroundRequested);
447             pw.println("        Default in payment settings: " + mPaymentDefaults.settingsDefault
448                     + "(" + getUserName(mPaymentDefaults.mUserHandle) + ")");
449             pw.println("        Payment settings allows override: "
450                     + mPaymentDefaults.preferForeground);
451             pw.println("");
452         }
453     }
454 
getUserName(UserHandle uh)455     private String getUserName(UserHandle uh) {
456         if (uh == null) {
457             return null;
458         }
459         UserManager um = mContext.createContextAsUser(
460                 uh, /*flags=*/0).getSystemService(UserManager.class);
461         if (um == null) {
462             return null;
463         }
464         return um.getUserName();
465     }
466 
467     /**
468      * Dump debugging information as a PreferredServicesProto
469      *
470      * Note:
471      * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto
472      * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and
473      * {@link ProtoOutputStream#end(long)} after.
474      * Never reuse a proto field number. When removing a field, mark it as reserved.
475      */
dumpDebug(ProtoOutputStream proto)476     void dumpDebug(ProtoOutputStream proto) {
477         synchronized (mLock) {
478             if (mForegroundCurrent != null) {
479                 Utils.dumpDebugComponentName(
480                         mForegroundCurrent, proto, PreferredServicesProto.FOREGROUND_CURRENT);
481             }
482             if (mPaymentDefaults.currentPreferred != null) {
483                 mPaymentDefaults.currentPreferred.dumpDebug(proto,
484                         PreferredServicesProto.FOREGROUND_CURRENT);
485             }
486             if (mNextTapDefault != null) {
487                 mNextTapDefault.dumpDebug(proto, PreferredServicesProto.NEXT_TAP_DEFAULT);
488             }
489             proto.write(PreferredServicesProto.FOREGROUND_UID, mForegroundUid);
490             if (mForegroundRequested != null) {
491                 Utils.dumpDebugComponentName(
492                         mForegroundRequested, proto, PreferredServicesProto.FOREGROUND_REQUESTED);
493             }
494             if (mPaymentDefaults.settingsDefault != null) {
495                 mPaymentDefaults.settingsDefault.dumpDebug(proto,
496                         PreferredServicesProto.SETTINGS_DEFAULT);
497             }
498             proto.write(PreferredServicesProto.PREFER_FOREGROUND,
499                     mPaymentDefaults.preferForeground);
500         }
501     }
502 }
503