1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.chrome.browser.payments;
6 
7 import android.content.Intent;
8 import android.content.pm.ActivityInfo;
9 import android.content.pm.ApplicationInfo;
10 import android.content.pm.PackageInfo;
11 import android.content.pm.ResolveInfo;
12 import android.content.pm.ServiceInfo;
13 import android.content.pm.Signature;
14 import android.graphics.drawable.Drawable;
15 import android.os.Bundle;
16 
17 import androidx.annotation.Nullable;
18 
19 import org.chromium.components.payments.PackageManagerDelegate;
20 
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 
27 /** Simulates a package manager in memory. */
28 class MockPackageManagerDelegate extends PackageManagerDelegate {
29     private static final int PAYMENT_METHOD_NAMES_STRING_ARRAY_RESOURCE_ID = 1;
30     private static final int SUPPORTED_DELEGATIONS_STRING_ARRAY_RESOURCE_ID = 2;
31     private static final int RESOURCES_SIZE = 2;
32 
33     private final List<ResolveInfo> mActivities = new ArrayList<>();
34     private final Map<String, PackageInfo> mPackages = new HashMap<>();
35     private final Map<ResolveInfo, CharSequence> mLabels = new HashMap<>();
36     private final List<ResolveInfo> mServices = new ArrayList<>();
37     private final Map<ApplicationInfo, List<String[]>> mResources = new HashMap<>();
38 
39     private String mInvokedAppPackageName;
40 
41     /**
42      * Simulates an installed payment app with no supported delegations.
43      *
44      * @param label The user visible name of the app.
45      * @param packageName The identifying package name.
46      * @param defaultPaymentMethodName The name of the default payment method name for this app. If
47      *          null, then this app will not have metadata. If empty, then the default payment
48      * method name will not be set.
49      * @param signature The signature of the app. The SHA256 hash of this signature is called
50      *         "fingerprint" and should be present in the app's web app manifest. If null, then this
51      *         app will not have package info. If empty, then this app will not have any signatures.
52      */
installPaymentApp(CharSequence label, String packageName, String defaultPaymentMethodName, String signature)53     public void installPaymentApp(CharSequence label, String packageName,
54             String defaultPaymentMethodName, String signature) {
55         installPaymentApp(label, packageName, defaultPaymentMethodName,
56                 /*supportedDelegations=*/null, signature);
57     }
58 
59     /**
60      * Simulates an installed payment app.
61      *
62      * @param label The user visible name of the app.
63      * @param packageName The identifying package name.
64      * @param defaultPaymentMethodName The name of the default payment method name for this app. If
65      *         empty, then the default payment method name will not be set.
66      * @param supportedDelegations The delegations that the app can support. If both
67      *         supportedDelegations and defaultPaymentMethodName null, then this app will not have
68      *         metadata.
69      * @param signature The signature of the app. The SHA256 hash of this signature is called
70      *         "fingerprint" and should be present in the app's web app manifest. If null, then this
71      *         app will not have package info. If empty, then this app will not have any signatures.
72      */
installPaymentApp(CharSequence label, String packageName, String defaultPaymentMethodName, String[] supportedDelegations, String signature)73     public void installPaymentApp(CharSequence label, String packageName,
74             String defaultPaymentMethodName, String[] supportedDelegations, String signature) {
75         ResolveInfo paymentApp = new ResolveInfo();
76         paymentApp.activityInfo = new ActivityInfo();
77         paymentApp.activityInfo.packageName = packageName;
78         paymentApp.activityInfo.name = packageName + ".WebPaymentActivity";
79         paymentApp.activityInfo.applicationInfo = new ApplicationInfo();
80         if (defaultPaymentMethodName != null || supportedDelegations != null) {
81             Bundle metaData = new Bundle();
82             if (!defaultPaymentMethodName.isEmpty()) {
83                 metaData.putString(
84                         AndroidPaymentAppFinder.META_DATA_NAME_OF_DEFAULT_PAYMENT_METHOD_NAME,
85                         defaultPaymentMethodName);
86             }
87             if (supportedDelegations != null && supportedDelegations.length > 0) {
88                 metaData.putInt(AndroidPaymentAppFinder.META_DATA_NAME_OF_SUPPORTED_DELEGATIONS,
89                         SUPPORTED_DELEGATIONS_STRING_ARRAY_RESOURCE_ID);
90                 List<String[]> resources = Arrays.asList(new String[RESOURCES_SIZE][]);
91                 resources.set(
92                         SUPPORTED_DELEGATIONS_STRING_ARRAY_RESOURCE_ID - 1, supportedDelegations);
93                 mResources.put(paymentApp.activityInfo.applicationInfo, resources);
94             }
95             paymentApp.activityInfo.metaData = metaData;
96         }
97         mActivities.add(paymentApp);
98 
99         if (signature != null) {
100             PackageInfo packageInfo = new PackageInfo();
101             packageInfo.packageName = packageName;
102             packageInfo.versionCode = 10;
103             if (signature.isEmpty()) {
104                 packageInfo.signatures = new Signature[0];
105             } else {
106                 packageInfo.signatures = new Signature[1];
107                 packageInfo.signatures[0] = new Signature(signature);
108             }
109             mPackages.put(packageName, packageInfo);
110         }
111 
112         mLabels.put(paymentApp, label);
113     }
114 
115     /**
116      * Simulates an IS_READY_TO_PAY service in a payment app.
117      *
118      * @param packageName The identifying package name.
119      */
addIsReadyToPayService(String packageName)120     public void addIsReadyToPayService(String packageName) {
121         ResolveInfo service = new ResolveInfo();
122         service.serviceInfo = new ServiceInfo();
123         service.serviceInfo.packageName = packageName;
124         service.serviceInfo.name = packageName + ".IsReadyToPayService";
125         mServices.add(service);
126     }
127 
128     /**
129      * Simulates META_DATA_NAME_OF_PAYMENT_METHOD_NAMES metadata in a payment app.
130      *
131      * @param packageName The name of the simulated package that contains the metadata.
132      * @param metadata    The metadata to simulate.
133      */
setStringArrayMetaData(String packageName, String[] metadata)134     public void setStringArrayMetaData(String packageName, String[] metadata) {
135         for (int i = 0; i < mActivities.size(); i++) {
136             ResolveInfo paymentApp = mActivities.get(i);
137             if (paymentApp.activityInfo.packageName.equals(packageName)) {
138                 paymentApp.activityInfo.metaData.putInt(
139                         AndroidPaymentAppFinder.META_DATA_NAME_OF_PAYMENT_METHOD_NAMES,
140                         PAYMENT_METHOD_NAMES_STRING_ARRAY_RESOURCE_ID);
141                 List<String[]> resources;
142                 if (mResources.containsKey(paymentApp.activityInfo.applicationInfo)) {
143                     resources = mResources.get(paymentApp.activityInfo.applicationInfo);
144                     mResources.remove(paymentApp.activityInfo.applicationInfo);
145                 } else {
146                     resources = Arrays.asList(new String[RESOURCES_SIZE][]);
147                 }
148                 resources.set(PAYMENT_METHOD_NAMES_STRING_ARRAY_RESOURCE_ID - 1, metadata);
149                 mResources.put(paymentApp.activityInfo.applicationInfo, resources);
150                 return;
151             }
152         }
153         assert false : packageName + " package not found";
154     }
155 
156     /** Resets the package manager to the state of no installed apps. */
reset()157     public void reset() {
158         mActivities.clear();
159         mPackages.clear();
160         mLabels.clear();
161     }
162 
163     @Override
getActivitiesThatCanRespondToIntentWithMetaData(Intent intent)164     public List<ResolveInfo> getActivitiesThatCanRespondToIntentWithMetaData(Intent intent) {
165         return mActivities;
166     }
167 
168     @Override
getActivitiesThatCanRespondToIntent(Intent intent)169     public List<ResolveInfo> getActivitiesThatCanRespondToIntent(Intent intent) {
170         return getActivitiesThatCanRespondToIntentWithMetaData(intent);
171     }
172 
173     @Override
getServicesThatCanRespondToIntent(Intent intent)174     public List<ResolveInfo> getServicesThatCanRespondToIntent(Intent intent) {
175         return mServices;
176     }
177 
178     @Override
getPackageInfoWithSignatures(String packageName)179     public PackageInfo getPackageInfoWithSignatures(String packageName) {
180         return mPackages.get(packageName);
181     }
182 
183     @Override
getPackageInfoWithSignatures(int uid)184     public PackageInfo getPackageInfoWithSignatures(int uid) {
185         return mPackages.get(mInvokedAppPackageName);
186     }
187 
188     @Override
getAppLabel(ResolveInfo resolveInfo)189     public CharSequence getAppLabel(ResolveInfo resolveInfo) {
190         return mLabels.get(resolveInfo);
191     }
192 
193     @Override
getAppIcon(ResolveInfo resolveInfo)194     public Drawable getAppIcon(ResolveInfo resolveInfo) {
195         return null;
196     }
197 
198     @Override
199     @Nullable
getStringArrayResourceForApplication( ApplicationInfo applicationInfo, int resourceId)200     public String[] getStringArrayResourceForApplication(
201             ApplicationInfo applicationInfo, int resourceId) {
202         assert resourceId > 0 && resourceId <= RESOURCES_SIZE;
203         return mResources.get(applicationInfo).get(resourceId - 1);
204     }
205 
206     /**
207      * Sets the package name of the invoked payment app.
208      * @param packageName The package name of the invoked payment app.
209      */
setInvokedAppPackageName(String packageName)210     public void setInvokedAppPackageName(String packageName) {
211         assert mPackages.containsKey(packageName);
212         mInvokedAppPackageName = packageName;
213     }
214 }
215