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