Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java

Issue 2838403002: Enable supporting arbitrary short string payment method names. (Closed)
Patch Set: Update comments Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698