1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.nfc;
18 
19 import android.app.ActivityManager;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.graphics.drawable.Drawable;
24 import android.nfc.NfcAdapter;
25 import android.nfc.cardemulation.ApduServiceInfo;
26 import android.nfc.cardemulation.CardEmulation;
27 import android.os.Handler;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.os.UserHandle;
31 import android.os.UserManager;
32 import android.provider.Settings;
33 import android.provider.Settings.SettingNotFoundException;
34 
35 import com.android.internal.content.PackageMonitor;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 public class PaymentBackend {
41     public static final String TAG = "Settings.PaymentBackend";
42 
43     public interface Callback {
onPaymentAppsChanged()44         void onPaymentAppsChanged();
45     }
46 
47     public static class PaymentAppInfo {
48         public CharSequence label;
49         CharSequence description;
50         boolean isDefault;
51         public ComponentName componentName;
52         public ComponentName settingsComponent;
53         public UserHandle userHandle;
54         public Drawable icon;
55     }
56 
57     /**
58      * ComponentName of the payment application and the userId that it belongs to.
59      */
60     public static class PaymentInfo {
61         public ComponentName componentName;
62         public int userId;
63     }
64 
65     private final Context mContext;
66     private final NfcAdapter mAdapter;
67     private final CardEmulation mCardEmuManager;
68     private final PackageMonitor mSettingsPackageMonitor = new SettingsPackageMonitor();
69     // Fields below only modified on UI thread
70     private ArrayList<PaymentAppInfo> mAppInfos;
71     private PaymentAppInfo mDefaultAppInfo;
72     private ArrayList<Callback> mCallbacks = new ArrayList<>();
73 
PaymentBackend(Context context)74     public PaymentBackend(Context context) {
75         mContext = context;
76 
77         mAdapter = NfcAdapter.getDefaultAdapter(context);
78         mCardEmuManager = CardEmulation.getInstance(mAdapter);
79         refresh();
80     }
81 
onPause()82     public void onPause() {
83         mSettingsPackageMonitor.unregister();
84     }
85 
onResume()86     public void onResume() {
87         mSettingsPackageMonitor.register(mContext, mContext.getMainLooper(), false);
88         refresh();
89     }
90 
refresh()91     public void refresh() {
92         PackageManager pm = mContext.getPackageManager();
93         ArrayList<PaymentAppInfo> appInfosAllProfiles = new ArrayList<PaymentAppInfo>();
94 
95         UserManager um = mContext.createContextAsUser(
96                 UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0)
97                 .getSystemService(UserManager.class);
98         List<UserHandle> userHandles = um.getEnabledProfiles();
99 
100         PaymentInfo defaultAppName = getDefaultPaymentApp();
101         PaymentAppInfo foundDefaultApp = null;
102         for (UserHandle uh : userHandles) {
103             List<ApduServiceInfo> serviceInfosByProfile =
104                     mCardEmuManager.getServices(CardEmulation.CATEGORY_PAYMENT, uh.getIdentifier());
105             if (serviceInfosByProfile == null) continue;
106 
107             ArrayList<PaymentAppInfo> appInfos = new ArrayList<PaymentAppInfo>();
108 
109             for (ApduServiceInfo service : serviceInfosByProfile) {
110                 PaymentAppInfo appInfo = new PaymentAppInfo();
111                 appInfo.userHandle = uh;
112                 appInfo.label = service.loadLabel(pm);
113                 if (appInfo.label == null) {
114                     appInfo.label = service.loadAppLabel(pm);
115                 }
116                 if (defaultAppName == null) {
117                     appInfo.isDefault = false;
118                 } else {
119                     appInfo.isDefault =
120                             service.getComponent().equals(defaultAppName.componentName)
121                             && defaultAppName.userId == uh.getIdentifier();
122                 }
123                 if (appInfo.isDefault) {
124                     foundDefaultApp = appInfo;
125                 }
126                 appInfo.componentName = service.getComponent();
127                 String settingsActivity = service.getSettingsActivityName();
128                 if (settingsActivity != null) {
129                     appInfo.settingsComponent = new ComponentName(
130                             appInfo.componentName.getPackageName(),
131                             settingsActivity);
132                 } else {
133                     appInfo.settingsComponent = null;
134                 }
135                 appInfo.description = service.getDescription();
136                 Drawable icon = (service.loadBanner(pm) != null)
137                         ? service.loadBanner(pm) : service.loadIcon(pm);
138                 appInfo.icon = pm.getUserBadgedIcon(icon, appInfo.userHandle);
139 
140                 appInfos.add(appInfo);
141             }
142             appInfosAllProfiles.addAll(appInfos);
143         }
144         mAppInfos = appInfosAllProfiles;
145         mDefaultAppInfo = foundDefaultApp;
146         makeCallbacks();
147     }
148 
registerCallback(Callback callback)149     public void registerCallback(Callback callback) {
150         mCallbacks.add(callback);
151     }
152 
unregisterCallback(Callback callback)153     public void unregisterCallback(Callback callback) {
154         mCallbacks.remove(callback);
155     }
156 
getPaymentAppInfos()157     public List<PaymentAppInfo> getPaymentAppInfos() {
158         return mAppInfos;
159     }
160 
getDefaultApp()161     public PaymentAppInfo getDefaultApp() {
162         return mDefaultAppInfo;
163     }
164 
makeCallbacks()165     void makeCallbacks() {
166         for (Callback callback : mCallbacks) {
167             callback.onPaymentAppsChanged();
168         }
169     }
170 
isForegroundMode()171     boolean isForegroundMode() {
172         try {
173             return Settings.Secure.getIntForUser(mContext.getContentResolver(),
174                     Settings.Secure.NFC_PAYMENT_FOREGROUND, UserHandle.myUserId()) != 0;
175         } catch (SettingNotFoundException e) {
176             return false;
177         }
178     }
179 
setForegroundMode(boolean foreground)180     void setForegroundMode(boolean foreground) {
181         UserManager um = mContext.createContextAsUser(
182                 UserHandle.of(UserHandle.myUserId()), /*flags=*/0)
183                 .getSystemService(UserManager.class);
184         List<UserHandle> userHandles = um.getEnabledProfiles();
185         for (UserHandle uh : userHandles) {
186             Settings.Secure.putIntForUser(mContext.getContentResolver(),
187                     Settings.Secure.NFC_PAYMENT_FOREGROUND, foreground ? 1 : 0, uh.getIdentifier());
188         }
189     }
190 
getDefaultPaymentApp()191     PaymentInfo getDefaultPaymentApp() {
192         UserManager um = mContext.createContextAsUser(
193                 UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0)
194                 .getSystemService(UserManager.class);
195         List<UserHandle> userHandles = um.getEnabledProfiles();
196         for (UserHandle uh : userHandles) {
197             ComponentName defaultApp = getDefaultPaymentApp(uh.getIdentifier());
198             if (defaultApp != null) {
199                 PaymentInfo appInfo = new PaymentInfo();
200                 appInfo.userId = uh.getIdentifier();
201                 appInfo.componentName = defaultApp;
202                 return appInfo;
203             }
204         }
205         return null;
206     }
207 
getDefaultPaymentApp(int userId)208     ComponentName getDefaultPaymentApp(int userId) {
209         String componentString = Settings.Secure.getStringForUser(mContext.getContentResolver(),
210                 Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, userId);
211         if (componentString != null) {
212             return ComponentName.unflattenFromString(componentString);
213         } else {
214             return null;
215         }
216     }
217 
setDefaultPaymentApp(ComponentName app)218     public void setDefaultPaymentApp(ComponentName app) {
219         setDefaultPaymentApp(app, UserHandle.myUserId());
220     }
221 
222     /**
223      *  Set Nfc default payment application
224      */
setDefaultPaymentApp(ComponentName app, int userId)225     public void setDefaultPaymentApp(ComponentName app, int userId) {
226         UserManager um = mContext.createContextAsUser(
227                 UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0)
228                 .getSystemService(UserManager.class);
229         List<UserHandle> userHandles = um.getEnabledProfiles();
230 
231         for (UserHandle uh : userHandles) {
232             if (uh.getIdentifier() == userId) {
233                 Settings.Secure.putStringForUser(mContext.getContentResolver(),
234                         Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
235                         app != null ? app.flattenToString() : null, uh.getIdentifier());
236             } else {
237                 Settings.Secure.putStringForUser(mContext.getContentResolver(),
238                         Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
239                         null, uh.getIdentifier());
240             }
241         }
242         refresh();
243     }
244 
245     private class SettingsPackageMonitor extends PackageMonitor {
246         private Handler mHandler;
247 
248         @Override
register(Context context, Looper thread, UserHandle user, boolean externalStorage)249         public void register(Context context, Looper thread, UserHandle user,
250                 boolean externalStorage) {
251             if (mHandler == null) {
252                 mHandler = new Handler(thread) {
253                     @Override
254                     public void dispatchMessage(Message msg) {
255                         refresh();
256                     }
257                 };
258             }
259             super.register(context, thread, user, externalStorage);
260         }
261 
262         @Override
onPackageAdded(String packageName, int uid)263         public void onPackageAdded(String packageName, int uid) {
264             mHandler.obtainMessage().sendToTarget();
265         }
266 
267         @Override
onPackageAppeared(String packageName, int reason)268         public void onPackageAppeared(String packageName, int reason) {
269             mHandler.obtainMessage().sendToTarget();
270         }
271 
272         @Override
onPackageDisappeared(String packageName, int reason)273         public void onPackageDisappeared(String packageName, int reason) {
274             mHandler.obtainMessage().sendToTarget();
275         }
276 
277         @Override
onPackageRemoved(String packageName, int uid)278         public void onPackageRemoved(String packageName, int uid) {
279             mHandler.obtainMessage().sendToTarget();
280         }
281     }
282 }
283