1da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen/* 2da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * Copyright (C) 2014 The Android Open Source Project 3da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * 4da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * Licensed under the Apache License, Version 2.0 (the "License"); 5da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * you may not use this file except in compliance with the License. 6da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * You may obtain a copy of the License at 7da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * 8da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * http://www.apache.org/licenses/LICENSE-2.0 9da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * 10da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * Unless required by applicable law or agreed to in writing, software 11da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * distributed under the License is distributed on an "AS IS" BASIS, 12da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * See the License for the specific language governing permissions and 14da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * limitations under the License. 15da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen */ 16da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenpackage com.android.nfc.cardemulation; 17da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 18da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport java.io.FileDescriptor; 19da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport java.io.PrintWriter; 20da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport java.util.ArrayList; 21da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 22da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport com.android.nfc.ForegroundUtils; 23da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 24da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.app.ActivityManager; 25da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.content.ComponentName; 26da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.content.Context; 27da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.database.ContentObserver; 28da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.net.Uri; 29da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.nfc.cardemulation.ApduServiceInfo; 30da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.nfc.cardemulation.CardEmulation; 31da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.os.Handler; 32da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.os.Looper; 33da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.os.UserHandle; 34da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.provider.Settings; 35da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.provider.Settings.SettingNotFoundException; 36da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.util.Log; 37da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 38da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen/** 39da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * This class keeps track of what HCE/SE-based services are 40da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * preferred by the user. It currently has 3 inputs: 41da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * 1) The default set in tap&pay menu for payment category 42da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * 2) An app in the foreground asking for a specific 43da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * service for a specific category 44da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * 3) If we had to disambiguate a previous tap (because no 45da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * preferred service was there), we need to temporarily 46da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * store the user's choice for the next tap. 47da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * 48da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * This class keeps track of all 3 inputs, and computes a new 49da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * preferred services as needed. It then passes this service 50da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * (if it changed) through a callback, which allows other components 51da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * to adapt as necessary (ie the AID cache can update its AID 52da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * mappings and the routing table). 53da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen */ 54da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenpublic class PreferredServices implements com.android.nfc.ForegroundUtils.Callback { 55da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen static final String TAG = "PreferredCardEmulationServices"; 56da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen static final Uri paymentDefaultUri = Settings.Secure.getUriFor( 57da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT); 58da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen static final Uri paymentForegroundUri = Settings.Secure.getUriFor( 59da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Settings.Secure.NFC_PAYMENT_FOREGROUND); 60da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 61da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final SettingsObserver mSettingsObserver; 62da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final Context mContext; 63da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final RegisteredServicesCache mServiceCache; 640ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen final RegisteredAidCache mAidCache; 65da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final Callback mCallback; 66da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final ForegroundUtils mForegroundUtils = ForegroundUtils.getInstance(); 67da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final Handler mHandler = new Handler(Looper.getMainLooper()); 68da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 69da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final class PaymentDefaults { 70da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean preferForeground; // The current selection mode for this category 71da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName settingsDefault; // The component preferred in settings (eg Tap&Pay) 72da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName currentPreferred; // The computed preferred component 73da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 74da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 75da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final Object mLock = new Object(); 76da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Variables below synchronized on mLock 77da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen PaymentDefaults mPaymentDefaults = new PaymentDefaults(); 78da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 79da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName mForegroundRequested; // The component preferred by fg app 80da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen int mForegroundUid; // The UID of the fg app, or -1 if fg app didn't request 81da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 82da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName mNextTapDefault; // The component preferred by active disambig dialog 83da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean mClearNextTapDefault = false; // Set when the next tap default must be cleared 84da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 85da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName mForegroundCurrent; // The currently computed foreground component 86da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 87da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public interface Callback { 88da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen void onPreferredPaymentServiceChanged(ComponentName service); 89da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen void onPreferredForegroundServiceChanged(ComponentName service); 90da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 91da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 92da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public PreferredServices(Context context, RegisteredServicesCache serviceCache, 930ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen RegisteredAidCache aidCache, Callback callback) { 94da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mContext = context; 95da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mServiceCache = serviceCache; 960ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen mAidCache = aidCache; 97da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mCallback = callback; 98da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mSettingsObserver = new SettingsObserver(mHandler); 99da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mContext.getContentResolver().registerContentObserver( 100da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen paymentDefaultUri, 101da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen true, mSettingsObserver, UserHandle.USER_ALL); 102da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 103da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mContext.getContentResolver().registerContentObserver( 104da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen paymentForegroundUri, 105da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen true, mSettingsObserver, UserHandle.USER_ALL); 106da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 107da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Load current settings defaults for payments 108da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen loadDefaultsFromSettings(ActivityManager.getCurrentUser()); 109da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 110da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 111da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private final class SettingsObserver extends ContentObserver { 112da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public SettingsObserver(Handler handler) { 113da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen super(handler); 114da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 115da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 116da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen @Override 117da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onChange(boolean selfChange, Uri uri) { 118da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen super.onChange(selfChange, uri); 119da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Do it just for the current user. If it was in fact 120da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // a change made for another user, we'll sync it down 121da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // on user switch. 122da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen int currentUser = ActivityManager.getCurrentUser(); 123da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen loadDefaultsFromSettings(currentUser); 124da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 125da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen }; 126da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 127da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen void loadDefaultsFromSettings(int userId) { 128da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean paymentDefaultChanged = false; 129da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean paymentPreferForegroundChanged = false; 130da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Load current payment default from settings 131da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen String name = Settings.Secure.getStringForUser( 132da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mContext.getContentResolver(), Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, 133da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen userId); 134da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName newDefault = name != null ? ComponentName.unflattenFromString(name) : null; 135da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean preferForeground = false; 136da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen try { 137da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen preferForeground = Settings.Secure.getInt(mContext.getContentResolver(), 138da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0; 139da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } catch (SettingNotFoundException e) { 140da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 141da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 142da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen paymentPreferForegroundChanged = (preferForeground != mPaymentDefaults.preferForeground); 143da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mPaymentDefaults.preferForeground = preferForeground; 144da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 145da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mPaymentDefaults.settingsDefault = newDefault; 146da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (newDefault != null && !newDefault.equals(mPaymentDefaults.currentPreferred)) { 147da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen paymentDefaultChanged = true; 148da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mPaymentDefaults.currentPreferred = newDefault; 149da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } else if (newDefault == null && mPaymentDefaults.currentPreferred != null) { 150da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen paymentDefaultChanged = true; 151da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mPaymentDefaults.currentPreferred = newDefault; 152da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } else { 153da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Same default as before 154da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 155da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 156da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Notify if anything changed 157da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (paymentDefaultChanged) { 158da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mCallback.onPreferredPaymentServiceChanged(newDefault); 159da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 160da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (paymentPreferForegroundChanged) { 161da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen computePreferredForegroundService(); 162da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 163da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 164da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 165da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen void computePreferredForegroundService() { 166da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName preferredService = null; 167da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean changed = false; 168da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 169da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Prio 1: next tap default 170da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen preferredService = mNextTapDefault; 171da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (preferredService == null) { 172da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Prio 2: foreground requested by app 173da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen preferredService = mForegroundRequested; 174da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 175da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (preferredService != null && !preferredService.equals(mForegroundCurrent)) { 176da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mForegroundCurrent = preferredService; 177da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen changed = true; 178da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } else if (preferredService == null && mForegroundCurrent != null){ 179da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mForegroundCurrent = preferredService; 180da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen changed = true; 181da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 182da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 183da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Notify if anything changed 184da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (changed) { 185da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mCallback.onPreferredForegroundServiceChanged(preferredService); 186da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 187da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 188da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 189da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public boolean setDefaultForNextTap(ComponentName service) { 190da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // This is a trusted API, so update without checking 191da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 192da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mNextTapDefault = service; 193da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 194da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen computePreferredForegroundService(); 195da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return true; 196da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 197da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 198ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen public void onServicesUpdated() { 199ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // If this service is the current foreground service, verify 200ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // there are no conflicts 201ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen boolean changed = false; 202da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 203ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // Check if the current foreground service is still allowed to override; 204ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // it could have registered new AIDs that make it conflict with user 205ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // preferences. 206ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen if (mForegroundCurrent != null) { 207ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen if (!isForegroundAllowedLocked(mForegroundCurrent)) { 208ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen Log.d(TAG, "Removing foreground preferred service because of conflict."); 209ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen mForegroundRequested = null; 210ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen mForegroundUid = -1; 211ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen changed = true; 212da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 213ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen } else { 214ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // Don't care about this service 215ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen } 216ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen } 217ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen if (changed) { 218ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen computePreferredForegroundService(); 219ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen } 220ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen } 221ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen 222ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // Verifies whether a service is allowed to register as preferred 223ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen boolean isForegroundAllowedLocked(ComponentName service) { 2240ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen if (service.equals(mPaymentDefaults.currentPreferred)) { 2250ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen // If the requester is already the payment default, allow it to request foreground 2260ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen // override as well (it could use this to make sure it handles AIDs of category OTHER) 2270ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen return true; 2280ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen } 229ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen ApduServiceInfo serviceInfo = mServiceCache.getService(ActivityManager.getCurrentUser(), 230ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen service); 231ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // Do some sanity checking 232ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen if (!mPaymentDefaults.preferForeground) { 233ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // Foreground apps are not allowed to override payment default 234ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // Check if this app registers payment AIDs, in which case we'll fail anyway 235ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen if (serviceInfo.hasCategory(CardEmulation.CATEGORY_PAYMENT)) { 236ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen Log.d(TAG, "User doesn't allow payment services to be overridden."); 237ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen return false; 238ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen } 239ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // If no payment AIDs, get AIDs of category other, and see if there's any 240ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // conflict with payment AIDs of current default payment app. That means 241ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // the current default payment app said this was a payment AID, and the 242ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // foreground app says it was not. In this case we'll still prefer the payment 243ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // app, since that is the one that the user has explicitly selected (and said 244ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // it's not allowed to be overridden). 245ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen final ArrayList<String> otherAids = serviceInfo.getAids(); 246ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen ApduServiceInfo paymentServiceInfo = mServiceCache.getService( 247ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen ActivityManager.getCurrentUser(), mPaymentDefaults.currentPreferred); 248ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen if (paymentServiceInfo != null && otherAids != null && otherAids.size() > 0) { 249ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen for (String aid : otherAids) { 2500ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen RegisteredAidCache.AidResolveInfo resolveInfo = mAidCache.resolveAid(aid); 2510ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen if (CardEmulation.CATEGORY_PAYMENT.equals(resolveInfo.category) && 2520ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen paymentServiceInfo.equals(resolveInfo.defaultService)) { 2530ad654d57bf17d73f2a7de5c44c9d69b7b450962Martijn Coenen Log.d(TAG, "AID " + aid + " is handled by the default payment app, " + 254ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen "and the user has not allowed payments to be overridden."); 255ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen return false; 256da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 257da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 25831208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen return true; 259da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } else { 260ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // Could not find payment service or fg app doesn't register other AIDs; 261ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen // okay to proceed. 26231208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen return true; 263ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen } 26431208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } else { 26531208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen // Payment allows override, so allow anything. 26631208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen return true; 267ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen } 268ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen } 269ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen 270ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen public boolean registerPreferredForegroundService(ComponentName service, int callingUid) { 271ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen boolean success = false; 272ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen synchronized (mLock) { 273ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen if (isForegroundAllowedLocked(service)) { 274ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen if (mForegroundUtils.registerUidToBackgroundCallback(this, callingUid)) { 275ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen mForegroundRequested = service; 276ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen mForegroundUid = callingUid; 277ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen success = true; 278ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen } else { 279ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen Log.e(TAG, "Calling UID is not in the foreground, ignorning!"); 280ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen success = false; 281ea8bbf3e6a41b4ace768f0b2c74bdcf21e2aee64Martijn Coenen } 28231208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen } else { 28331208d3ee36f583fd998c89508a3e93bb550cb29Martijn Coenen Log.e(TAG, "Requested foreground service conflicts with default payment app."); 284da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 285da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 286da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (success) { 287da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen computePreferredForegroundService(); 288da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 289da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return success; 290da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 291da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 292da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean unregisterForegroundService(int uid) { 293da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean success = false; 294da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 295da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (mForegroundUid == uid) { 296da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mForegroundRequested = null; 297da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mForegroundUid = -1; 298da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen success = true; 299da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } // else, other UID in foreground 300da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 3012f07e3e221cb157918c198e4cd58d8242e897b68Martijn Coenen if (success) { 3022f07e3e221cb157918c198e4cd58d8242e897b68Martijn Coenen computePreferredForegroundService(); 3032f07e3e221cb157918c198e4cd58d8242e897b68Martijn Coenen } 304da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return success; 305da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 306da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 307da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public boolean unregisteredPreferredForegroundService(int callingUid) { 308da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Verify the calling UID is in the foreground 309da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (mForegroundUtils.isInForeground(callingUid)) { 310da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return unregisterForegroundService(callingUid); 311da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } else { 312da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Log.e(TAG, "Calling UID is not in the foreground, ignorning!"); 313da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return false; 314da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 315da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 316da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 317da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen @Override 318da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onUidToBackground(int uid) { 319da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen unregisterForegroundService(uid); 320da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 321da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 322da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onHostEmulationActivated() { 323da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 324da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mClearNextTapDefault = (mNextTapDefault != null); 325da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 326da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 327da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 328da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onHostEmulationDeactivated() { 329da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // If we had any next tap defaults set, clear them out 330da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean changed = false; 331da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 332da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (mClearNextTapDefault) { 333da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // The reason we need to check this boolean is because the next tap 334da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // default may have been set while the user held the phone 335da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // on the reader; when the user then removes his phone from 336da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // the reader (causing the "onHostEmulationDeactivated" event), 337da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // the next tap default would immediately be cleared 338da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // again. Instead, clear out defaults only if a next tap default 339da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // had already been set at time of activation, which is captured 340da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // by mClearNextTapDefault. 341da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (mNextTapDefault != null) { 342da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mNextTapDefault = null; 343da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen changed = true; 344da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 345da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mClearNextTapDefault = false; 346da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 347da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 348da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (changed) { 349da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen computePreferredForegroundService(); 350da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 351da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 352da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 353da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onUserSwitched(int userId) { 354da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen loadDefaultsFromSettings(userId); 355da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 356da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 357da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 358da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println("Preferred services (in order of importance): "); 359da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(" *** Current preferred foreground service: " + mForegroundCurrent); 360da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(" *** Current preferred payment service: " + mPaymentDefaults.currentPreferred); 361da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(" Next tap default: " + mNextTapDefault); 362da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(" Default for foreground app (UID: " + mForegroundUid + 363da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen "): " + mForegroundRequested); 364da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(" Default in payment settings: " + mPaymentDefaults.settingsDefault); 365da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(" Payment settings allows override: " + mPaymentDefaults.preferForeground); 366da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(""); 367da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 368da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen} 369