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