OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser.payments; | 5 package org.chromium.chrome.browser.payments; |
6 | 6 |
7 import android.content.Intent; | 7 import android.content.Intent; |
8 import android.content.pm.ActivityInfo; | 8 import android.content.pm.ActivityInfo; |
9 import android.content.pm.ResolveInfo; | 9 import android.content.pm.ResolveInfo; |
10 import android.content.res.Resources; | 10 import android.content.res.Resources; |
(...skipping 15 matching lines...) Expand all Loading... |
26 import java.util.HashSet; | 26 import java.util.HashSet; |
27 import java.util.List; | 27 import java.util.List; |
28 import java.util.Locale; | 28 import java.util.Locale; |
29 import java.util.Map; | 29 import java.util.Map; |
30 import java.util.Set; | 30 import java.util.Set; |
31 | 31 |
32 import javax.annotation.Nullable; | 32 import javax.annotation.Nullable; |
33 | 33 |
34 /** | 34 /** |
35 * Finds installed native Android payment apps and verifies their signatures acc
ording to the | 35 * Finds installed native Android payment apps and verifies their signatures acc
ording to the |
36 * payment method manifests. The manifests are located based on the payment meth
od name, which | 36 * payment method manifests. The manifests are located based on the payment meth
od name, which is a |
37 * is a URI that starts with "https://". The "basic-card" payment method is an e
xception: it's a | 37 * URI that starts with "https://". The W3C-published non-URI payment method nam
es are exceptions: |
38 * common payment method that does not have a manifest and can be used by any pa
yment app. | 38 * these are common payment method names that do not have a manifest and can be
used by any payment |
| 39 * app. |
39 */ | 40 */ |
40 public class AndroidPaymentAppFinder implements ManifestVerifyCallback { | 41 public class AndroidPaymentAppFinder implements ManifestVerifyCallback { |
41 private static final String TAG = "cr_PaymentAppFinder"; | 42 private static final String TAG = "cr_PaymentAppFinder"; |
42 | 43 |
| 44 /** The maximum number of payment method manifests to download. */ |
| 45 private static final int MAX_NUMBER_OF_MANIFESTS = 10; |
| 46 |
43 /** The name of the intent for the service to check whether an app is ready
to pay. */ | 47 /** The name of the intent for the service to check whether an app is ready
to pay. */ |
44 /* package */ static final String ACTION_IS_READY_TO_PAY = | 48 /* package */ static final String ACTION_IS_READY_TO_PAY = |
45 "org.chromium.intent.action.IS_READY_TO_PAY"; | 49 "org.chromium.intent.action.IS_READY_TO_PAY"; |
46 | 50 |
47 /** | 51 /** |
48 * The basic-card payment method name used by merchant and defined by W3C: | 52 * The basic-card payment method name used by merchant and defined by W3C: |
49 * https://w3c.github.io/webpayments-methods-card/#method-id | 53 * https://w3c.github.io/webpayments-methods-card/#method-id |
50 */ | 54 */ |
51 /* package */ static final String BASIC_CARD_PAYMENT_METHOD = "basic-card"; | 55 /* package */ static final String BASIC_CARD_PAYMENT_METHOD = "basic-card"; |
52 | 56 |
53 /** | 57 /** |
54 * Meta data name of an app's supported payment method names. | 58 * Meta data name of an app's supported payment method names. |
55 */ | 59 */ |
56 static final String META_DATA_NAME_OF_PAYMENT_METHOD_NAMES = | 60 /* package */ static final String META_DATA_NAME_OF_PAYMENT_METHOD_NAMES = |
57 "org.chromium.payment_method_names"; | 61 "org.chromium.payment_method_names"; |
58 | 62 |
59 /** | 63 /** |
60 * Meta data name of an app's supported default payment method name. | 64 * Meta data name of an app's supported default payment method name. |
61 */ | 65 */ |
62 static final String META_DATA_NAME_OF_DEFAULT_PAYMENT_METHOD_NAME = | 66 /* package */ static final String META_DATA_NAME_OF_DEFAULT_PAYMENT_METHOD_N
AME = |
63 "org.chromium.default_payment_method_name"; | 67 "org.chromium.default_payment_method_name"; |
64 | 68 |
65 /** The maximum number of payment method manifests to download. */ | |
66 private static final int MAX_NUMBER_OF_MANIFESTS = 10; | |
67 | |
68 private final WebContents mWebContents; | 69 private final WebContents mWebContents; |
69 private final boolean mQueryBasicCard; | 70 private final Set<String> mNonUriPaymentMethods; |
70 private final Set<URI> mPaymentMethods; | 71 private final Set<URI> mUriPaymentMethods; |
71 private final PaymentManifestDownloader mDownloader; | 72 private final PaymentManifestDownloader mDownloader; |
72 private final PaymentManifestParser mParser; | 73 private final PaymentManifestParser mParser; |
73 private final PackageManagerDelegate mPackageManagerDelegate; | 74 private final PackageManagerDelegate mPackageManagerDelegate; |
74 private final PaymentAppCreatedCallback mCallback; | 75 private final PaymentAppCreatedCallback mCallback; |
75 private final boolean mIsIncognito; | 76 private final boolean mIsIncognito; |
76 | 77 |
77 /** | 78 /** |
78 * A map of payment method names to the list of (yet) unverified Android app
s that claim to | 79 * A map of payment method names to the list of (yet) unverified Android app
s that claim to |
79 * handle these methods. Example payment method names in this data structure
: | 80 * handle these methods. Example payment method names in this data structure
: |
80 * "https://bobpay.com", "https://android.com/pay". Basic card is excluded. | 81 * "https://bobpay.com", "https://android.com/pay". Items in the supportedNo
nUriPaymentMethods |
| 82 * are excluded. |
81 */ | 83 */ |
82 private final Map<URI, Set<ResolveInfo>> mPendingApps; | 84 private final Map<URI, Set<ResolveInfo>> mPendingApps; |
83 | 85 |
84 /** A map of Android package name to the payment app. */ | 86 /** A map of Android package name to the payment app. */ |
85 private final Map<String, AndroidPaymentApp> mResult; | 87 private final Map<String, AndroidPaymentApp> mResult; |
86 | 88 |
87 /** | 89 /** |
88 * Finds native Android payment apps. | 90 * Finds native Android payment apps. |
89 * | 91 * |
90 * @param webContents The web contents that invoked the web payme
nts API. | 92 * @param webContents The web contents that invoked the web payme
nts API. |
(...skipping 11 matching lines...) Expand all Loading... |
102 PackageManagerDelegate packageManagerDelegate, PaymentAppCreatedCall
back callback) { | 104 PackageManagerDelegate packageManagerDelegate, PaymentAppCreatedCall
back callback) { |
103 new AndroidPaymentAppFinder( | 105 new AndroidPaymentAppFinder( |
104 webContents, methods, downloader, parser, packageManagerDelegate
, callback) | 106 webContents, methods, downloader, parser, packageManagerDelegate
, callback) |
105 .findAndroidPaymentApps(); | 107 .findAndroidPaymentApps(); |
106 } | 108 } |
107 | 109 |
108 private AndroidPaymentAppFinder(WebContents webContents, Set<String> methods
, | 110 private AndroidPaymentAppFinder(WebContents webContents, Set<String> methods
, |
109 PaymentManifestDownloader downloader, PaymentManifestParser parser, | 111 PaymentManifestDownloader downloader, PaymentManifestParser parser, |
110 PackageManagerDelegate packageManagerDelegate, PaymentAppCreatedCall
back callback) { | 112 PackageManagerDelegate packageManagerDelegate, PaymentAppCreatedCall
back callback) { |
111 mWebContents = webContents; | 113 mWebContents = webContents; |
112 mQueryBasicCard = methods.contains(BASIC_CARD_PAYMENT_METHOD); | 114 |
113 mPaymentMethods = new HashSet<>(); | 115 // For non-URI payment method names, only names published by W3C should
be supported. |
| 116 Set<String> supportedNonUriPaymentMethods = new HashSet<>(); |
| 117 supportedNonUriPaymentMethods.add(BASIC_CARD_PAYMENT_METHOD); |
| 118 |
| 119 mNonUriPaymentMethods = new HashSet<>(); |
| 120 mUriPaymentMethods = new HashSet<>(); |
114 for (String method : methods) { | 121 for (String method : methods) { |
115 assert !TextUtils.isEmpty(method); | 122 assert !TextUtils.isEmpty(method); |
| 123 if (supportedNonUriPaymentMethods.contains(method)) { |
| 124 mNonUriPaymentMethods.add(method); |
| 125 } else if (method.startsWith(UrlConstants.HTTPS_URL_PREFIX)) { |
| 126 URI uri; |
| 127 try { |
| 128 // Don't use java.net.URL, because it performs a synchronous
DNS lookup in |
| 129 // the constructor. |
| 130 uri = new URI(method); |
| 131 } catch (URISyntaxException e) { |
| 132 continue; |
| 133 } |
116 | 134 |
117 if (!method.startsWith(UrlConstants.HTTPS_URL_PREFIX)) continue; | 135 if (uri.isAbsolute()) { |
118 | 136 assert UrlConstants.HTTPS_SCHEME.equals(uri.getScheme()); |
119 URI uri; | 137 mUriPaymentMethods.add(uri); |
120 try { | 138 } |
121 // Don't use java.net.URL, because it performs a synchronous DNS
lookup in | |
122 // the constructor. | |
123 uri = new URI(method); | |
124 } catch (URISyntaxException e) { | |
125 continue; | |
126 } | |
127 | |
128 if (uri.isAbsolute()) { | |
129 assert UrlConstants.HTTPS_SCHEME.equals(uri.getScheme()); | |
130 mPaymentMethods.add(uri); | |
131 } | 139 } |
132 } | 140 } |
133 | 141 |
134 mDownloader = downloader; | 142 mDownloader = downloader; |
135 mParser = parser; | 143 mParser = parser; |
136 mPackageManagerDelegate = packageManagerDelegate; | 144 mPackageManagerDelegate = packageManagerDelegate; |
137 mCallback = callback; | 145 mCallback = callback; |
138 mPendingApps = new HashMap<>(); | 146 mPendingApps = new HashMap<>(); |
139 mResult = new HashMap<>(); | 147 mResult = new HashMap<>(); |
140 ChromeActivity activity = ChromeActivity.fromWebContents(mWebContents); | 148 ChromeActivity activity = ChromeActivity.fromWebContents(mWebContents); |
141 mIsIncognito = activity != null && activity.getCurrentTabModel() != null | 149 mIsIncognito = activity != null && activity.getCurrentTabModel() != null |
142 && activity.getCurrentTabModel().isIncognito(); | 150 && activity.getCurrentTabModel().isIncognito(); |
143 } | 151 } |
144 | 152 |
145 private void findAndroidPaymentApps() { | 153 private void findAndroidPaymentApps() { |
146 Intent payIntent = new Intent(AndroidPaymentApp.ACTION_PAY); | 154 Intent payIntent = new Intent(AndroidPaymentApp.ACTION_PAY); |
147 List<ResolveInfo> apps = | 155 List<ResolveInfo> apps = |
148 mPackageManagerDelegate.getActivitiesThatCanRespondToIntentWithM
etaData(payIntent); | 156 mPackageManagerDelegate.getActivitiesThatCanRespondToIntentWithM
etaData(payIntent); |
149 if (apps.isEmpty()) { | 157 if (apps.isEmpty()) { |
150 onSearchFinished(); | 158 onSearchFinished(); |
151 return; | 159 return; |
152 } | 160 } |
153 | 161 |
154 List<String[]> appSupportedMethods = new ArrayList<String[]>(); | 162 List<Set<String>> appSupportedMethods = new ArrayList<>(); |
155 for (int i = 0; i < apps.size(); i++) { | 163 for (int i = 0; i < apps.size(); i++) { |
156 appSupportedMethods.add(getPaymentMethodNames(apps.get(i).activityIn
fo)); | 164 appSupportedMethods.add(getPaymentMethodNames(apps.get(i).activityIn
fo)); |
157 } | 165 } |
158 | 166 |
159 List<String> appSupportedDefaultMethods = new ArrayList<String>(); | |
160 for (int i = 0; i < apps.size(); i++) { | |
161 appSupportedDefaultMethods.add(getDefaultPaymentMethodName(apps.get(
i).activityInfo)); | |
162 } | |
163 | |
164 List<PaymentManifestVerifier> verifiers = new ArrayList<>(); | 167 List<PaymentManifestVerifier> verifiers = new ArrayList<>(); |
165 for (URI methodName : mPaymentMethods) { | 168 for (URI uriMethodName : mUriPaymentMethods) { |
166 List<ResolveInfo> supportedApps = filterAppsByMethodName( | 169 List<ResolveInfo> supportedApps = |
167 apps, appSupportedMethods, appSupportedDefaultMethods, metho
dName.toString()); | 170 filterAppsByMethodName(apps, appSupportedMethods, uriMethodN
ame.toString()); |
168 if (supportedApps.isEmpty()) continue; | 171 if (supportedApps.isEmpty()) continue; |
169 | 172 |
170 // Start the parser utility process as soon as possible, once we kno
w that a | 173 // Start the parser utility process as soon as possible, once we kno
w that a |
171 // manifest file needs to be parsed. The startup can take up to 2 se
conds. | 174 // manifest file needs to be parsed. The startup can take up to 2 se
conds. |
172 if (!mParser.isUtilityProcessRunning()) mParser.startUtilityProcess(
); | 175 if (!mParser.isUtilityProcessRunning()) mParser.startUtilityProcess(
); |
173 | 176 |
174 verifiers.add(new PaymentManifestVerifier(methodName, supportedApps,
mDownloader, | 177 verifiers.add(new PaymentManifestVerifier(uriMethodName, supportedAp
ps, mDownloader, |
175 mParser, mPackageManagerDelegate, this /* callback */)); | 178 mParser, mPackageManagerDelegate, this /* callback */)); |
176 mPendingApps.put(methodName, new HashSet<>(supportedApps)); | 179 mPendingApps.put(uriMethodName, new HashSet<>(supportedApps)); |
177 | 180 |
178 if (verifiers.size() == MAX_NUMBER_OF_MANIFESTS) { | 181 if (verifiers.size() == MAX_NUMBER_OF_MANIFESTS) { |
179 Log.d(TAG, "Reached maximum number of allowed payment app manife
sts."); | 182 Log.d(TAG, "Reached maximum number of allowed payment app manife
sts."); |
180 break; | 183 break; |
181 } | 184 } |
182 } | 185 } |
183 | 186 |
184 if (mQueryBasicCard) { | 187 for (String nonUriMethodName : mNonUriPaymentMethods) { |
185 List<ResolveInfo> supportedApps = filterAppsByMethodName(apps, appSu
pportedMethods, | 188 List<ResolveInfo> supportedApps = |
186 appSupportedDefaultMethods, BASIC_CARD_PAYMENT_METHOD); | 189 filterAppsByMethodName(apps, appSupportedMethods, nonUriMeth
odName); |
187 for (int i = 0; i < supportedApps.size(); i++) { | 190 for (int i = 0; i < supportedApps.size(); i++) { |
188 // Chrome does not verify app manifests for "basic-card" support
. | 191 // Chrome does not verify app manifests for non-URI payment meth
od support. |
189 onValidPaymentApp(BASIC_CARD_PAYMENT_METHOD, supportedApps.get(i
)); | 192 onValidPaymentApp(nonUriMethodName, supportedApps.get(i)); |
190 } | 193 } |
191 } | 194 } |
192 | 195 |
193 if (verifiers.isEmpty()) { | 196 if (verifiers.isEmpty()) { |
194 onSearchFinished(); | 197 onSearchFinished(); |
195 return; | 198 return; |
196 } | 199 } |
197 | 200 |
198 for (int i = 0; i < verifiers.size(); i++) { | 201 for (int i = 0; i < verifiers.size(); i++) { |
199 verifiers.get(i).verify(); | 202 verifiers.get(i).verify(); |
200 } | 203 } |
201 } | 204 } |
202 | 205 |
203 @Nullable | 206 @Nullable |
204 private String[] getPaymentMethodNames(ActivityInfo activityInfo) { | 207 private Set<String> getPaymentMethodNames(ActivityInfo activityInfo) { |
205 if (activityInfo.metaData == null) return null; | 208 Set<String> result = new HashSet<>(); |
| 209 if (activityInfo.metaData == null) return result; |
| 210 |
| 211 String defaultMethodName = |
| 212 activityInfo.metaData.getString(META_DATA_NAME_OF_DEFAULT_PAYMEN
T_METHOD_NAME); |
| 213 if (!TextUtils.isEmpty(defaultMethodName)) result.add(defaultMethodName)
; |
206 | 214 |
207 int resId = activityInfo.metaData.getInt(META_DATA_NAME_OF_PAYMENT_METHO
D_NAMES); | 215 int resId = activityInfo.metaData.getInt(META_DATA_NAME_OF_PAYMENT_METHO
D_NAMES); |
208 if (resId == 0) return null; | 216 if (resId == 0) return result; |
209 | 217 |
210 Resources resources = | 218 Resources resources = |
211 mPackageManagerDelegate.getResourcesForApplication(activityInfo.
applicationInfo); | 219 mPackageManagerDelegate.getResourcesForApplication(activityInfo.
applicationInfo); |
212 if (resources == null) return null; | 220 if (resources == null) return result; |
213 return resources.getStringArray(resId); | 221 |
| 222 String[] methodNames = resources.getStringArray(resId); |
| 223 if (methodNames == null) return result; |
| 224 |
| 225 for (int i = 0; i < methodNames.length; i++) { |
| 226 result.add(methodNames[i]); |
| 227 } |
| 228 |
| 229 return result; |
214 } | 230 } |
215 | 231 |
216 @Nullable | 232 private static List<ResolveInfo> filterAppsByMethodName( |
217 private String getDefaultPaymentMethodName(ActivityInfo activityInfo) { | 233 List<ResolveInfo> apps, List<Set<String>> methodNames, String target
MethodName) { |
218 if (activityInfo.metaData == null) return null; | 234 assert apps.size() == methodNames.size(); |
219 | 235 |
220 return activityInfo.metaData.getString(META_DATA_NAME_OF_DEFAULT_PAYMENT
_METHOD_NAME); | 236 // Note that apps and methodNames must have the same size. The informat
ion at the same |
221 } | 237 // index must correspond to the same app. |
222 | 238 List<ResolveInfo> supportedApps = new ArrayList<>(); |
223 private static List<ResolveInfo> filterAppsByMethodName(List<ResolveInfo> ap
ps, | |
224 List<String[]> appsMethods, List<String> appsDefaultMethods, String
targetMethodName) { | |
225 assert apps.size() == appsMethods.size(); | |
226 assert apps.size() == appsDefaultMethods.size(); | |
227 | |
228 // Note that apps, appsMethods and appsDefaultMethods must have the same
size. And the | |
229 // information at the same index must correspond to the same app. | |
230 List<ResolveInfo> supportedApps = new ArrayList<ResolveInfo>(); | |
231 for (int i = 0; i < apps.size(); i++) { | 239 for (int i = 0; i < apps.size(); i++) { |
232 if (targetMethodName.equals(appsDefaultMethods.get(i))) { | 240 if (methodNames.get(i).contains(targetMethodName)) { |
233 supportedApps.add(apps.get(i)); | 241 supportedApps.add(apps.get(i)); |
234 continue; | 242 continue; |
235 } | 243 } |
236 | |
237 String[] methods = appsMethods.get(i); | |
238 if (methods == null) continue; | |
239 for (int j = 0; j < methods.length; j++) { | |
240 if (targetMethodName.equals(methods[j])) { | |
241 supportedApps.add(apps.get(i)); | |
242 break; | |
243 } | |
244 } | |
245 } | 244 } |
246 return supportedApps; | 245 return supportedApps; |
247 } | 246 } |
248 | 247 |
249 @Override | 248 @Override |
250 public void onValidPaymentApp(URI methodName, ResolveInfo resolveInfo) { | 249 public void onValidPaymentApp(URI methodName, ResolveInfo resolveInfo) { |
251 onValidPaymentApp(methodName.toString(), resolveInfo); | 250 onValidPaymentApp(methodName.toString(), resolveInfo); |
252 removePendingApp(methodName, resolveInfo); | 251 removePendingApp(methodName, resolveInfo); |
253 } | 252 } |
254 | 253 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
311 } | 310 } |
312 } | 311 } |
313 | 312 |
314 for (Map.Entry<String, AndroidPaymentApp> entry : mResult.entrySet()) { | 313 for (Map.Entry<String, AndroidPaymentApp> entry : mResult.entrySet()) { |
315 mCallback.onPaymentAppCreated(entry.getValue()); | 314 mCallback.onPaymentAppCreated(entry.getValue()); |
316 } | 315 } |
317 | 316 |
318 mCallback.onAllPaymentAppsCreated(); | 317 mCallback.onAllPaymentAppsCreated(); |
319 } | 318 } |
320 } | 319 } |
OLD | NEW |