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

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

Issue 2645813006: Download web payment manifests. (Closed)
Patch Set: Stricter DEPS Created 3 years, 9 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
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.Context;
8 import android.content.Intent; 7 import android.content.Intent;
9 import android.content.pm.PackageManager; 8 import android.content.pm.PackageManager;
10 import android.content.pm.ResolveInfo; 9 import android.content.pm.ResolveInfo;
11 import android.graphics.drawable.Drawable; 10 import android.graphics.drawable.Drawable;
12 import android.net.Uri; 11 import android.net.Uri;
13 import android.util.Pair; 12 import android.util.Pair;
14 13
15 import org.chromium.base.ContextUtils; 14 import org.chromium.base.ContextUtils;
16 import org.chromium.chrome.browser.ChromeActivity;
17 import org.chromium.chrome.browser.UrlConstants; 15 import org.chromium.chrome.browser.UrlConstants;
18 import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppCreatedC allback; 16 import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppCreatedC allback;
19 import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppFactoryA ddition; 17 import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppFactoryA ddition;
18 import org.chromium.chrome.browser.payments.PaymentManifestVerifier.ManifestVeri fyCallback;
20 import org.chromium.content_public.browser.WebContents; 19 import org.chromium.content_public.browser.WebContents;
21 20
21 import java.net.URI;
22 import java.net.URISyntaxException;
23 import java.util.ArrayList;
22 import java.util.HashMap; 24 import java.util.HashMap;
25 import java.util.HashSet;
23 import java.util.List; 26 import java.util.List;
24 import java.util.Map; 27 import java.util.Map;
25 import java.util.Set; 28 import java.util.Set;
26 29
27 /** Builds instances of payment apps based on installed third party Android paym ent apps. */ 30 /** Builds instances of payment apps based on installed third party Android paym ent apps. */
28 public class AndroidPaymentAppFactory implements PaymentAppFactoryAddition { 31 public class AndroidPaymentAppFactory implements PaymentAppFactoryAddition {
29 private static final String ACTION_IS_READY_TO_PAY = 32 @Override
30 "org.chromium.intent.action.IS_READY_TO_PAY"; 33 public void create(
31 34 WebContents webContents, Set<String> methods, PaymentAppCreatedCallb ack callback) {
32 /** The action name for the Pay Basic-card Intent. */ 35 new PaymentAppFinder(webContents, methods, callback).find();
33 private static final String ACTION_PAY_BASIC_CARD = "org.chromium.intent.act ion.PAY_BASIC_CARD"; 36 }
34 37
35 /** 38 /**
36 * The basic-card payment method name used by merchant and defined by W3C: 39 * Finds installed native Android payment apps and verifies their signatures according to the
37 * https://w3c.github.io/webpayments-methods-card/#method-id 40 * payment method manifests. The manifests are located based on the payment method name, which
41 * is a URI that starts with "https://". The "basic-card" payment method is an exception: it's a
42 * common payment method that does not have a manifest and can be used by an y payment app.
38 */ 43 */
39 private static final String BASIC_CARD_PAYMENT_METHOD = "basic-card"; 44 private static class PaymentAppFinder implements ManifestVerifyCallback {
40 45 /** The name of the intent for the service to check whether an app is re ady to pay. */
41 @Override 46 private static final String ACTION_IS_READY_TO_PAY =
42 public void create(WebContents webContents, Set<String> methods, 47 "org.chromium.intent.action.IS_READY_TO_PAY";
43 PaymentAppCreatedCallback callback) { 48
44 Context context = ChromeActivity.fromWebContents(webContents); 49 /** The name of the intent for the action of paying using "basic-card" m ethod. */
45 if (context == null) { 50 private static final String ACTION_PAY_BASIC_CARD =
46 callback.onAllPaymentAppsCreated(); 51 "org.chromium.intent.action.PAY_BASIC_CARD";
47 return; 52
48 } 53 /**
49 54 * The basic-card payment method name used by merchant and defined by W3 C:
50 Map<String, AndroidPaymentApp> installedApps = new HashMap<>(); 55 * https://w3c.github.io/webpayments-methods-card/#method-id
51 PackageManager pm = context.getPackageManager(); 56 */
52 Intent payIntent = new Intent(); 57 private static final String BASIC_CARD_PAYMENT_METHOD = "basic-card";
53 58
54 for (String methodName : methods) { 59 /** The maximum number of payment method manifests to download. */
55 if (methodName.startsWith(UrlConstants.HTTPS_URL_PREFIX)) { 60 private static final int MAX_NUMBER_OF_MANIFESTS = 10;
56 payIntent.setAction(AndroidPaymentApp.ACTION_PAY); 61
57 payIntent.setData(Uri.parse(methodName)); 62 private final WebContents mWebContents;
58 } else if (methodName.equals(BASIC_CARD_PAYMENT_METHOD)) { 63 private final PaymentAppCreatedCallback mCallback;
64
65 /** Whether "basic-card" supporting payment apps should be queried. */
66 private final boolean mQueryBasicCard;
67
68 /**
69 * A map of payment method names to the list of unverified (yet) Android apps that claim to
70 * handle these methods. Example payment method names in this data struc ture:
71 * "https://bobpay.com", "https://android.com/pay". Basic card is exclud ed.
72 */
73 private final Map<URI, Set<ResolveInfo>> mPendingApps;
74
75 /** A map of Android package name to the payment app. */
76 private final Map<String, AndroidPaymentApp> mResult;
77
78 private final Set<URI> mPaymentMethods;
79
80 private PaymentManifestParser mParser;
81
82 /**
83 * Builds a native Android payment app finder.
84 *
85 * @param webContents The web contents that invoked the web payments API .
86 * @param methods The list of payment methods requested by the merch ant. For example,
87 * "https://bobpay.com", "https://android.com/pay", " basic-card".
88 * @param callback The asynchronous callback to be invoked (on the UI thread) when all
89 * Android payment apps have been found.
90 */
91 public PaymentAppFinder(
92 WebContents webContents, Set<String> methods, PaymentAppCreatedC allback callback) {
93 mWebContents = webContents;
94 mCallback = callback;
95 mQueryBasicCard = methods.contains(BASIC_CARD_PAYMENT_METHOD);
96 mPendingApps = new HashMap<>();
97 mResult = new HashMap<>();
98
99 mPaymentMethods = new HashSet<>();
100 for (String method : methods) {
101 assert method != null;
gogerald1 2017/03/03 19:40:27 TextUtils.isEmpty(method) might be better
please use gerrit instead 2017/03/09 18:05:32 Done.
102 if (method.startsWith(UrlConstants.HTTPS_URL_PREFIX)) {
103 URI uri = null;
104 try {
105 // Don't use java.net.URL, because it performs a synchro nous DNS lookup in
106 // the constructor.
107 uri = new URI(method);
108 } catch (URISyntaxException e) {
109 continue;
110 }
111 if (uri.isAbsolute() && UrlConstants.HTTPS_SCHEME.equals(uri .getScheme())) {
gogerald1 2017/03/03 19:40:27 Is it possible that the scheme is not 'https' afte
please use gerrit instead 2017/03/09 18:05:32 Done.
112 mPaymentMethods.add(uri);
113 }
114 }
115 }
116 }
117
118 /** Looks for native Android payment apps. */
119 public void find() {
120 PackageManager pm = ContextUtils.getApplicationContext().getPackageM anager();
121 Intent payIntent = new Intent();
122 payIntent.setAction(AndroidPaymentApp.ACTION_PAY);
123
124 List<PaymentManifestVerifier> verifiers = new ArrayList<>();
125 for (URI methodName : mPaymentMethods) {
126 payIntent.setData(Uri.parse(methodName.toString()));
127 List<ResolveInfo> apps = pm.queryIntentActivities(payIntent, 0);
128 if (apps.isEmpty()) continue;
129
130 if (mParser == null) {
131 mParser = new PaymentManifestParser();
132 mParser.startUtilityProcess();
133 }
134
135 verifiers.add(
136 new PaymentManifestVerifier(mWebContents, methodName, ap ps, mParser, this));
137 mPendingApps.put(methodName, new HashSet<>(apps));
138 if (verifiers.size() == MAX_NUMBER_OF_MANIFESTS) break;
139 }
140
141 if (mQueryBasicCard) {
59 payIntent.setAction(ACTION_PAY_BASIC_CARD); 142 payIntent.setAction(ACTION_PAY_BASIC_CARD);
60 payIntent.setData(null); 143 payIntent.setData(null);
61 } else { 144 List<ResolveInfo> apps = pm.queryIntentActivities(payIntent, 0);
62 continue; 145 for (int i = 0; i < apps.size(); i++) {
63 } 146 onValidPaymentApp(BASIC_CARD_PAYMENT_METHOD, apps.get(i));
64
65 List<ResolveInfo> matches = pm.queryIntentActivities(payIntent, 0);
66 for (int i = 0; i < matches.size(); i++) {
67 ResolveInfo match = matches.get(i);
68 String packageName = match.activityInfo.packageName;
69 // Do not recommend disabled apps.
70 if (!PaymentPreferencesUtil.isAndroidPaymentAppEnabled(packageNa me)) continue;
71 AndroidPaymentApp installedApp = installedApps.get(packageName);
72 if (installedApp == null) {
73 CharSequence label = match.loadLabel(pm);
74 installedApp =
75 new AndroidPaymentApp(webContents, packageName, matc h.activityInfo.name,
76 label == null ? "" : label.toString(), match .loadIcon(pm));
77 callback.onPaymentAppCreated(installedApp);
78 installedApps.put(packageName, installedApp);
79 } 147 }
80 installedApp.addMethodName(methodName); 148 }
81 } 149
82 } 150 if (verifiers.isEmpty()) {
83 151 onSearchFinished();
84 List<ResolveInfo> matches = pm.queryIntentServices(new Intent(ACTION_IS_ READY_TO_PAY), 0); 152 return;
85 for (int i = 0; i < matches.size(); i++) { 153 }
86 ResolveInfo match = matches.get(i); 154
87 String packageName = match.serviceInfo.packageName; 155 for (int i = 0; i < verifiers.size(); i++) {
88 AndroidPaymentApp installedApp = installedApps.get(packageName); 156 verifiers.get(i).verify();
89 if (installedApp != null) installedApp.setIsReadyToPayAction(match.s erviceInfo.name); 157 }
90 } 158 }
91 159
92 callback.onAllPaymentAppsCreated(); 160 @Override
161 public void onValidPaymentApp(URI methodName, ResolveInfo resolveInfo) {
162 onValidPaymentApp(methodName.toString(), resolveInfo);
163 removePendingApp(methodName, resolveInfo);
164 }
165
166 /** Same as above, but also works for non-URI method names, e.g., "basic -card". */
167 private void onValidPaymentApp(String methodName, ResolveInfo resolveInf o) {
168 PackageManager pm = ContextUtils.getApplicationContext().getPackageM anager();
gogerald1 2017/03/03 19:40:27 move inside if (app == null)
please use gerrit instead 2017/03/09 18:05:32 No longer applicable after abstracting away Packag
169 String packageName = resolveInfo.activityInfo.packageName;
170 AndroidPaymentApp app = mResult.get(packageName);
171 if (app == null) {
172 CharSequence label = resolveInfo.loadLabel(pm);
173 app = new AndroidPaymentApp(mWebContents, packageName,
174 resolveInfo.activityInfo.name, label == null ? "" : labe l.toString(),
175 resolveInfo.loadIcon(pm));
176 mResult.put(packageName, app);
177 }
178 app.addMethodName(methodName);
179 }
180
181 @Override
182 public void onInvalidPaymentApp(URI methodName, ResolveInfo resolveInfo) {
183 removePendingApp(methodName, resolveInfo);
184 }
185
186 /** Removes the (method, app) pair from the list of pending information to be verified. */
187 private void removePendingApp(URI methodName, ResolveInfo resolveInfo) {
188 Set<ResolveInfo> pendingAppsForMethod = mPendingApps.get(methodName) ;
189 pendingAppsForMethod.remove(resolveInfo);
190 if (pendingAppsForMethod.isEmpty()) mPendingApps.remove(methodName);
191 if (mPendingApps.isEmpty()) onSearchFinished();
192 }
193
194 @Override
195 public void onInvalidManifest(URI methodName) {
196 mPendingApps.remove(methodName);
197 if (mPendingApps.isEmpty()) onSearchFinished();
198 }
199
200 /**
201 * Checks for IS_READY_TO_PAY service in each valid payment app and retu rns the valid apps
202 * to the caller. Called when finished verifying all payment methods and apps.
203 */
204 private void onSearchFinished() {
205 if (mParser != null) {
206 mParser.stopUtilityProcess();
207 mParser = null;
208 }
209
210 PackageManager pm = ContextUtils.getApplicationContext().getPackageM anager();
211 List<ResolveInfo> resolveInfos =
212 pm.queryIntentServices(new Intent(ACTION_IS_READY_TO_PAY), 0 );
213 for (int i = 0; i < resolveInfos.size(); i++) {
214 ResolveInfo resolveInfo = resolveInfos.get(i);
215 AndroidPaymentApp app = mResult.get(resolveInfo.serviceInfo.pack ageName);
216 if (app != null) app.setIsReadyToPayAction(resolveInfo.serviceIn fo.name);
217 }
218
219 for (Map.Entry<String, AndroidPaymentApp> entry : mResult.entrySet() ) {
220 mCallback.onPaymentAppCreated(entry.getValue());
221 }
222
223 mCallback.onAllPaymentAppsCreated();
224 }
93 } 225 }
94 226
95 /** 227 /**
96 * Checks whether there are Android payment apps on device. 228 * Checks whether there are Android payment apps on device.
97 * 229 *
98 * @return True if there are Android payment apps on device. 230 * @return True if there are Android payment apps on device.
99 */ 231 */
100 public static boolean hasAndroidPaymentApps() { 232 public static boolean hasAndroidPaymentApps() {
101 PackageManager pm = ContextUtils.getApplicationContext().getPackageManag er(); 233 PackageManager pm = ContextUtils.getApplicationContext().getPackageManag er();
102 // Note that all Android payment apps must support org.chromium.intent.a ction.PAY action 234 // Note that all Android payment apps must support org.chromium.intent.a ction.PAY action
(...skipping 18 matching lines...) Expand all
121 253
122 for (ResolveInfo match : matches) { 254 for (ResolveInfo match : matches) {
123 Pair<String, Drawable> appInfo = 255 Pair<String, Drawable> appInfo =
124 new Pair<>(match.loadLabel(pm).toString(), match.loadIcon(pm )); 256 new Pair<>(match.loadLabel(pm).toString(), match.loadIcon(pm ));
125 paymentAppsInfo.put(match.activityInfo.packageName, appInfo); 257 paymentAppsInfo.put(match.activityInfo.packageName, appInfo);
126 } 258 }
127 259
128 return paymentAppsInfo; 260 return paymentAppsInfo;
129 } 261 }
130 } 262 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698