PreferredServices.java revision da772582c17e3f5ffe36e4cab3e1ede3cba32060
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; 64da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final Callback mCallback; 65da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final ForegroundUtils mForegroundUtils = ForegroundUtils.getInstance(); 66da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final Handler mHandler = new Handler(Looper.getMainLooper()); 67da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 68da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final class PaymentDefaults { 69da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean preferForeground; // The current selection mode for this category 70da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName settingsDefault; // The component preferred in settings (eg Tap&Pay) 71da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName currentPreferred; // The computed preferred component 72da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 73da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 74da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final Object mLock = new Object(); 75da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Variables below synchronized on mLock 76da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen PaymentDefaults mPaymentDefaults = new PaymentDefaults(); 77da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 78da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName mForegroundRequested; // The component preferred by fg app 79da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen int mForegroundUid; // The UID of the fg app, or -1 if fg app didn't request 80da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 81da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName mNextTapDefault; // The component preferred by active disambig dialog 82da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean mClearNextTapDefault = false; // Set when the next tap default must be cleared 83da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 84da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName mForegroundCurrent; // The currently computed foreground component 85da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 86da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public interface Callback { 87da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen void onPreferredPaymentServiceChanged(ComponentName service); 88da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen void onPreferredForegroundServiceChanged(ComponentName service); 89da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 90da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 91da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public PreferredServices(Context context, RegisteredServicesCache serviceCache, 92da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Callback callback) { 93da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mContext = context; 94da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mServiceCache = serviceCache; 95da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mCallback = callback; 96da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mSettingsObserver = new SettingsObserver(mHandler); 97da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mContext.getContentResolver().registerContentObserver( 98da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen paymentDefaultUri, 99da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen true, mSettingsObserver, UserHandle.USER_ALL); 100da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 101da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mContext.getContentResolver().registerContentObserver( 102da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen paymentForegroundUri, 103da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen true, mSettingsObserver, UserHandle.USER_ALL); 104da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 105da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Load current settings defaults for payments 106da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen loadDefaultsFromSettings(ActivityManager.getCurrentUser()); 107da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 108da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 109da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private final class SettingsObserver extends ContentObserver { 110da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public SettingsObserver(Handler handler) { 111da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen super(handler); 112da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 113da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 114da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen @Override 115da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onChange(boolean selfChange, Uri uri) { 116da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen super.onChange(selfChange, uri); 117da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Do it just for the current user. If it was in fact 118da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // a change made for another user, we'll sync it down 119da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // on user switch. 120da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen int currentUser = ActivityManager.getCurrentUser(); 121da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen loadDefaultsFromSettings(currentUser); 122da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 123da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen }; 124da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 125da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen void loadDefaultsFromSettings(int userId) { 126da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean paymentDefaultChanged = false; 127da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean paymentPreferForegroundChanged = false; 128da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Load current payment default from settings 129da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen String name = Settings.Secure.getStringForUser( 130da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mContext.getContentResolver(), Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, 131da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen userId); 132da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName newDefault = name != null ? ComponentName.unflattenFromString(name) : null; 133da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean preferForeground = false; 134da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen try { 135da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen preferForeground = Settings.Secure.getInt(mContext.getContentResolver(), 136da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0; 137da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } catch (SettingNotFoundException e) { 138da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 139da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 140da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen paymentPreferForegroundChanged = (preferForeground != mPaymentDefaults.preferForeground); 141da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mPaymentDefaults.preferForeground = preferForeground; 142da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 143da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mPaymentDefaults.settingsDefault = newDefault; 144da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (newDefault != null && !newDefault.equals(mPaymentDefaults.currentPreferred)) { 145da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen paymentDefaultChanged = true; 146da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mPaymentDefaults.currentPreferred = newDefault; 147da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } else if (newDefault == null && mPaymentDefaults.currentPreferred != null) { 148da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen paymentDefaultChanged = true; 149da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mPaymentDefaults.currentPreferred = newDefault; 150da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } else { 151da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Same default as before 152da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 153da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 154da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Notify if anything changed 155da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (paymentDefaultChanged) { 156da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mCallback.onPreferredPaymentServiceChanged(newDefault); 157da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 158da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (paymentPreferForegroundChanged) { 159da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen computePreferredForegroundService(); 160da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 161da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 162da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 163da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen void computePreferredForegroundService() { 164da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ComponentName preferredService = null; 165da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean changed = false; 166da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 167da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Prio 1: next tap default 168da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen preferredService = mNextTapDefault; 169da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (preferredService == null) { 170da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Prio 2: foreground requested by app 171da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen preferredService = mForegroundRequested; 172da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 173da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (preferredService != null && !preferredService.equals(mForegroundCurrent)) { 174da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mForegroundCurrent = preferredService; 175da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen changed = true; 176da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } else if (preferredService == null && mForegroundCurrent != null){ 177da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mForegroundCurrent = preferredService; 178da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen changed = true; 179da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 180da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 181da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Notify if anything changed 182da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (changed) { 183da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mCallback.onPreferredForegroundServiceChanged(preferredService); 184da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 185da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 186da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 187da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public boolean setDefaultForNextTap(ComponentName service) { 188da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // This is a trusted API, so update without checking 189da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 190da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mNextTapDefault = service; 191da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 192da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen computePreferredForegroundService(); 193da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return true; 194da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 195da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 196da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public boolean registerPreferredForegroundService(ComponentName service, int callingUid) { 197da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean success = false; 198da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ApduServiceInfo serviceInfo = mServiceCache.getService(ActivityManager.getCurrentUser(), 199da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen service); 200da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 201da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Do some sanity checking 202da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (!mPaymentDefaults.preferForeground) { 203da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Foreground apps are not allowed to override payment default 204da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Check if this app registers payment AIDs, in which case we'll fail anyway 205da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (serviceInfo.hasCategory(CardEmulation.CATEGORY_PAYMENT)) { 206da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Log.d(TAG, "User doesn't allow payment services to be overridden."); 207da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return false; 208da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 209da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // If no payment AIDs, get AIDs of category other, and see if there's any 210da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // conflict with payment AIDs of current default payment app. That means 211da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // the current default payment app said this was a payment AID, and the 212da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // foreground app says it was not. In this case we'll still prefer the payment 213da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // app, since that is the one that the user has explicitly selected (and said 214da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // it's not allowed to be overridden). 215da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen final ArrayList<String> otherAids = serviceInfo.getAids(); 216da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ApduServiceInfo paymentServiceInfo = mServiceCache.getService( 217da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ActivityManager.getCurrentUser(), mPaymentDefaults.currentPreferred); 218da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (paymentServiceInfo != null && otherAids != null && otherAids.size() > 0) { 219da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen for (String aid : otherAids) { 220da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (CardEmulation.CATEGORY_PAYMENT.equals( 221da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen paymentServiceInfo.getCategoryForAid(aid))) { 222da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Log.e(TAG, "AID " + aid + " is registered by the default payment app, " + 223da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen "and the user has not allowed payments to be overridden."); 224da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return false; 225da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 226da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 227da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } else { 228da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Could not find payment service or fg app doesn't register other AIDs; 229da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // okay to proceed. 230da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 231da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 232da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (mForegroundUtils.registerUidToBackgroundCallback(this, callingUid)) { 233da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mForegroundRequested = service; 234da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mForegroundUid = callingUid; 235da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen success = true; 236da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } else { 237da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Log.e(TAG, "Calling UID is not in the foreground, ignorning!"); 238da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen success = false; 239da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 240da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 241da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (success) { 242da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen computePreferredForegroundService(); 243da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 244da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return success; 245da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 246da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 247da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean unregisterForegroundService(int uid) { 248da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean success = false; 249da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 250da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (mForegroundUid == uid) { 251da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mForegroundRequested = null; 252da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mForegroundUid = -1; 253da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen success = true; 254da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } // else, other UID in foreground 255da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 256da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return success; 257da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 258da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 259da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public boolean unregisteredPreferredForegroundService(int callingUid) { 260da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Verify the calling UID is in the foreground 261da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (mForegroundUtils.isInForeground(callingUid)) { 262da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return unregisterForegroundService(callingUid); 263da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } else { 264da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Log.e(TAG, "Calling UID is not in the foreground, ignorning!"); 265da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return false; 266da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 267da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 268da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 269da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen @Override 270da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onUidToBackground(int uid) { 271da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen unregisterForegroundService(uid); 272da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 273da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 274da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onHostEmulationActivated() { 275da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 276da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mClearNextTapDefault = (mNextTapDefault != null); 277da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 278da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 279da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 280da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onHostEmulationDeactivated() { 281da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // If we had any next tap defaults set, clear them out 282da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean changed = false; 283da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 284da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (mClearNextTapDefault) { 285da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // The reason we need to check this boolean is because the next tap 286da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // default may have been set while the user held the phone 287da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // on the reader; when the user then removes his phone from 288da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // the reader (causing the "onHostEmulationDeactivated" event), 289da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // the next tap default would immediately be cleared 290da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // again. Instead, clear out defaults only if a next tap default 291da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // had already been set at time of activation, which is captured 292da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // by mClearNextTapDefault. 293da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (mNextTapDefault != null) { 294da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mNextTapDefault = null; 295da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen changed = true; 296da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 297da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mClearNextTapDefault = false; 298da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 299da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 300da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (changed) { 301da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen computePreferredForegroundService(); 302da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 303da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 304da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 305da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onUserSwitched(int userId) { 306da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen loadDefaultsFromSettings(userId); 307da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 308da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 309da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 310da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println("Preferred services (in order of importance): "); 311da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(" *** Current preferred foreground service: " + mForegroundCurrent); 312da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(" *** Current preferred payment service: " + mPaymentDefaults.currentPreferred); 313da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(" Next tap default: " + mNextTapDefault); 314da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(" Default for foreground app (UID: " + mForegroundUid + 315da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen "): " + mForegroundRequested); 316da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(" Default in payment settings: " + mPaymentDefaults.settingsDefault); 317da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(" Payment settings allows override: " + mPaymentDefaults.preferForeground); 318da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pw.println(""); 319da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 320da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen} 321