1 /*
2  * Copyright (C) 2023 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.permissioncontroller.role.ui.behavior.v35;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.pm.ApplicationInfo;
22 import android.content.pm.PackageManager;
23 import android.content.pm.ResolveInfo;
24 import android.graphics.drawable.Drawable;
25 import android.nfc.cardemulation.ApduServiceInfo;
26 import android.nfc.cardemulation.CardEmulation;
27 import android.nfc.cardemulation.HostApduService;
28 import android.nfc.cardemulation.OffHostApduService;
29 import android.os.Build;
30 import android.os.UserHandle;
31 import android.text.TextUtils;
32 import android.util.Log;
33 
34 import androidx.annotation.NonNull;
35 import androidx.annotation.Nullable;
36 import androidx.annotation.RequiresApi;
37 import androidx.core.util.Pair;
38 import androidx.preference.Preference;
39 
40 import com.android.permissioncontroller.role.ui.TwoTargetPreference;
41 import com.android.permissioncontroller.role.ui.behavior.RoleUiBehavior;
42 import com.android.role.controller.model.Role;
43 import com.android.role.controller.util.UserUtils;
44 
45 import org.xmlpull.v1.XmlPullParserException;
46 
47 import java.io.IOException;
48 import java.util.ArrayList;
49 import java.util.List;
50 
51 /***
52  * Class for UI behavior of Wallet role
53  */
54 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
55 public class WalletRoleUiBehavior implements RoleUiBehavior {
56 
57     private static final String LOG_TAG = WalletRoleUiBehavior.class.getSimpleName();
58 
59     @Override
preparePreferenceAsUser(@onNull Role role, @NonNull TwoTargetPreference preference, @NonNull List<ApplicationInfo> applicationInfos, @NonNull UserHandle user, @NonNull Context context)60     public void preparePreferenceAsUser(@NonNull Role role, @NonNull TwoTargetPreference preference,
61             @NonNull List<ApplicationInfo> applicationInfos, @NonNull UserHandle user,
62             @NonNull Context context) {
63         Context userContext = UserUtils.getUserContext(context, user);
64         if (!applicationInfos.isEmpty()) {
65             preparePreferenceInternal(preference.asPreference(), applicationInfos.get(0),
66                     false, user, userContext);
67         }
68     }
69 
70     @Override
prepareApplicationPreferenceAsUser(@onNull Role role, @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, @NonNull Context context)71     public void prepareApplicationPreferenceAsUser(@NonNull Role role,
72             @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
73             @NonNull UserHandle user, @NonNull Context context) {
74         Context userContext = UserUtils.getUserContext(context, user);
75         preparePreferenceInternal(preference, applicationInfo, true, user, userContext);
76     }
77 
preparePreferenceInternal(@onNull Preference preference, @NonNull ApplicationInfo applicationInfo, boolean setTitle, @NonNull UserHandle user, @NonNull Context context)78     private void preparePreferenceInternal(@NonNull Preference preference,
79             @NonNull ApplicationInfo applicationInfo, boolean setTitle, @NonNull UserHandle user,
80             @NonNull Context context) {
81         if (isSystemApplication(applicationInfo)) {
82             List<ApduServiceInfo> serviceInfos = getNfcServicesForPackage(
83                     applicationInfo.packageName, user, context);
84 
85             Pair<Drawable, CharSequence> bannerAndLabel =
86                     getNonPaymentServiceBannerAndLabelIfExists(serviceInfos, user, context);
87             if (bannerAndLabel != null) {
88                 preference.setIcon(bannerAndLabel.first);
89                 if (setTitle) {
90                     preference.setTitle(bannerAndLabel.second);
91                 } else {
92                     preference.setSummary(bannerAndLabel.second);
93                 }
94             }
95         }
96     }
97 
98     @NonNull
getNfcServicesForPackage(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)99     private static List<ApduServiceInfo> getNfcServicesForPackage(@NonNull String packageName,
100             @NonNull UserHandle user, @NonNull Context context) {
101         PackageManager packageManager = context.getPackageManager();
102         Intent hostApduIntent = new Intent(HostApduService.SERVICE_INTERFACE);
103         Intent offHostApduIntent = new Intent(OffHostApduService.SERVICE_INTERFACE);
104         hostApduIntent.setPackage(packageName);
105         offHostApduIntent.setPackage(packageName);
106         List<ResolveInfo> hostApduServices = packageManager.queryIntentServicesAsUser(
107                 hostApduIntent,
108                 PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA
109                         | PackageManager.MATCH_DISABLED_COMPONENTS), user);
110         List<ResolveInfo> offHostApduServices = packageManager.queryIntentServicesAsUser(
111                 offHostApduIntent,
112                 PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA
113                         | PackageManager.MATCH_DISABLED_COMPONENTS), user);
114         List<ApduServiceInfo> nfcServices = new ArrayList<>();
115         int apduServiceInfoSize = hostApduServices.size();
116         for (int i = 0; i < apduServiceInfoSize; i++) {
117             ResolveInfo resolveInfo = hostApduServices.get(i);
118             ApduServiceInfo apduServiceInfo;
119             try {
120                 apduServiceInfo = new ApduServiceInfo(packageManager, resolveInfo, true);
121             } catch (XmlPullParserException | IOException e) {
122                 Log.e(LOG_TAG, "Error creating the apduserviceinfo.", e);
123                 continue;
124             }
125             nfcServices.add(apduServiceInfo);
126         }
127         int offHostApduServiceInfoSize = offHostApduServices.size();
128         for (int i = 0; i < offHostApduServiceInfoSize; i++) {
129             ResolveInfo resolveInfo = offHostApduServices.get(i);
130             ApduServiceInfo apduServiceInfo;
131             try {
132                 apduServiceInfo = new ApduServiceInfo(packageManager, resolveInfo, false);
133             } catch (XmlPullParserException | IOException e) {
134                 Log.e(LOG_TAG, "Error creating the apduserviceinfo.", e);
135                 continue;
136             }
137             nfcServices.add(apduServiceInfo);
138         }
139         return nfcServices;
140     }
141 
142     @Nullable
getNonPaymentServiceBannerAndLabelIfExists( @onNull List<ApduServiceInfo> apduServiceInfos, @NonNull UserHandle user, @NonNull Context context)143     private Pair<Drawable, CharSequence> getNonPaymentServiceBannerAndLabelIfExists(
144             @NonNull List<ApduServiceInfo> apduServiceInfos, @NonNull UserHandle user,
145             @NonNull Context context) {
146         Context userContext = UserUtils.getUserContext(context, user);
147         PackageManager userPackageManager = userContext.getPackageManager();
148         Pair<Drawable, CharSequence> bannerAndLabel;
149         int apduServiceInfoSize = apduServiceInfos.size();
150         for (int i = 0; i < apduServiceInfoSize; i++) {
151             ApduServiceInfo serviceInfo = apduServiceInfos.get(i);
152             if (serviceInfo.getAids().isEmpty()) {
153                 bannerAndLabel = loadBannerAndLabel(serviceInfo, userPackageManager);
154                 if (bannerAndLabel != null) {
155                     return bannerAndLabel;
156                 }
157             } else {
158                 List<String> aids = serviceInfo.getAids();
159                 int aidsSize = aids.size();
160                 for (int j = 0; j < aidsSize; j++) {
161                     String aid = aids.get(j);
162                     String category = serviceInfo.getCategoryForAid(aid);
163                     if (!CardEmulation.CATEGORY_PAYMENT.equals(category)) {
164                         bannerAndLabel = loadBannerAndLabel(serviceInfo, userPackageManager);
165                         if (bannerAndLabel != null) {
166                             return bannerAndLabel;
167                         }
168                     }
169                 }
170             }
171         }
172         return null;
173     }
174 
175     @Nullable
loadBannerAndLabel(@onNull ApduServiceInfo info, @NonNull PackageManager userPackageManager)176     private Pair<Drawable, CharSequence> loadBannerAndLabel(@NonNull ApduServiceInfo info,
177             @NonNull PackageManager userPackageManager) {
178         Drawable drawable = info.loadBanner(userPackageManager);
179         CharSequence label = info.loadLabel(userPackageManager);
180         if (drawable != null && !TextUtils.isEmpty(label)) {
181             return new Pair<>(drawable, label);
182         } else {
183             return null;
184         }
185     }
186 
isSystemApplication(@onNull ApplicationInfo applicationInfo)187     private static boolean isSystemApplication(@NonNull ApplicationInfo applicationInfo) {
188         return (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
189     }
190 }
191