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

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

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

Powered by Google App Engine
This is Rietveld 408576698