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