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

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

Issue 2645813006: Download web payment manifests. (Closed)
Patch Set: Rebase 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
(Empty)
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
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.payments;
6
7 import android.content.Intent;
8 import android.content.pm.ResolveInfo;
9 import android.net.Uri;
10 import android.text.TextUtils;
11
12 import org.chromium.base.Log;
13 import org.chromium.chrome.browser.UrlConstants;
14 import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppCreatedC allback;
15 import org.chromium.chrome.browser.payments.PaymentManifestVerifier.ManifestVeri fyCallback;
16 import org.chromium.components.payments.PaymentManifestDownloader;
17 import org.chromium.components.payments.PaymentManifestParser;
18 import org.chromium.content_public.browser.WebContents;
19
20 import java.net.URI;
21 import java.net.URISyntaxException;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28
29 /**
30 * Finds installed native Android payment apps and verifies their signatures acc ording to the
31 * payment method manifests. The manifests are located based on the payment meth od name, which
32 * is a URI that starts with "https://". The "basic-card" payment method is an e xception: it's a
33 * common payment method that does not have a manifest and can be used by any pa yment app.
34 */
35 public class AndroidPaymentAppFinder implements ManifestVerifyCallback {
36 private static final String TAG = "cr_PaymentAppFinder";
37
38 /** The name of the intent for the service to check whether an app is ready to pay. */
39 private static final String ACTION_IS_READY_TO_PAY =
40 "org.chromium.intent.action.IS_READY_TO_PAY";
41
42 /** The name of the intent for the action of paying using "basic-card" metho d. */
43 private static final String ACTION_PAY_BASIC_CARD = "org.chromium.intent.act ion.PAY_BASIC_CARD";
44
45 /**
46 * The basic-card payment method name used by merchant and defined by W3C:
47 * https://w3c.github.io/webpayments-methods-card/#method-id
48 */
49 private static final String BASIC_CARD_PAYMENT_METHOD = "basic-card";
50
51 /** The maximum number of payment method manifests to download. */
52 private static final int MAX_NUMBER_OF_MANIFESTS = 10;
53
54 private final WebContents mWebContents;
55 private final boolean mQueryBasicCard;
56 private final Set<URI> mPaymentMethods;
57 private final PaymentManifestDownloader mDownloader;
58 private final PaymentManifestParser mParser;
59 private final PackageManagerDelegate mPackageManagerDelegate;
60 private final PaymentAppCreatedCallback mCallback;
61
62 /**
63 * A map of payment method names to the list of (yet) unverified Android app s that claim to
64 * handle these methods. Example payment method names in this data structure :
65 * "https://bobpay.com", "https://android.com/pay". Basic card is excluded.
66 */
67 private final Map<URI, Set<ResolveInfo>> mPendingApps;
68
69 /** A map of Android package name to the payment app. */
70 private final Map<String, AndroidPaymentApp> mResult;
71
72 /**
73 * Whether payment apps are required to have an intent filter with a single PAY action and no
74 * additional data, i.e., whether payments apps are required to show up in " Autofill and
75 * Payments" settings.
76 */
77 private final boolean mRequireShowInSettings;
78
79 /**
80 * The intent filter for a single PAY action and no additional data. Used to filter out payment
81 * apps that don't show up in "Autofill and Payments" settings.
82 */
83 private final Intent mSettingsLookup;
84
85 /**
86 * Finds native Android payment apps.
87 *
88 * @param webContents The web contents that invoked the web payme nts API.
89 * @param methods The list of payment methods requested by th e merchant. For
90 * example, "https://bobpay.com", "https://and roid.com/pay",
91 * "basic-card".
92 * @param requireShowInSettings Whether payment apps are required to show u p in "autofill and
93 * Payments" settings.
94 * @param downloader The manifest downloader.
95 * @param parser The manifest parser.
96 * @param packageManagerDelegate The package information retriever.
97 * @param callback The asynchronous callback to be invoked (on the UI thread) when
98 * all Android payment apps have been found.
99 */
100 public static void find(WebContents webContents, Set<String> methods,
101 boolean requireShowInSettings, PaymentManifestDownloader downloader,
102 PaymentManifestParser parser, PackageManagerDelegate packageManagerD elegate,
103 PaymentAppCreatedCallback callback) {
104 new AndroidPaymentAppFinder(webContents, methods, requireShowInSettings, downloader, parser,
105 packageManagerDelegate, callback)
106 .findAndroidPaymentApps();
107 }
108
109 private AndroidPaymentAppFinder(WebContents webContents, Set<String> methods ,
110 boolean requireShowInSettings, PaymentManifestDownloader downloader,
111 PaymentManifestParser parser, PackageManagerDelegate packageManagerD elegate,
112 PaymentAppCreatedCallback callback) {
113 mWebContents = webContents;
114 mQueryBasicCard = methods.contains(BASIC_CARD_PAYMENT_METHOD);
115 mPaymentMethods = new HashSet<>();
116 for (String method : methods) {
117 assert !TextUtils.isEmpty(method);
118
119 if (!method.startsWith(UrlConstants.HTTPS_URL_PREFIX)) continue;
120
121 URI uri;
122 try {
123 // Don't use java.net.URL, because it performs a synchronous DNS lookup in
124 // the constructor.
125 uri = new URI(method);
126 } catch (URISyntaxException e) {
127 continue;
128 }
129
130 if (uri.isAbsolute()) {
131 assert UrlConstants.HTTPS_SCHEME.equals(uri.getScheme());
132 mPaymentMethods.add(uri);
133 }
134 }
135
136 mDownloader = downloader;
137 mParser = parser;
138 mPackageManagerDelegate = packageManagerDelegate;
139 mCallback = callback;
140 mPendingApps = new HashMap<>();
141 mResult = new HashMap<>();
142 mRequireShowInSettings = requireShowInSettings;
143 mSettingsLookup = new Intent(AndroidPaymentApp.ACTION_PAY);
144 }
145
146 private void findAndroidPaymentApps() {
147 List<PaymentManifestVerifier> verifiers = new ArrayList<>();
148 if (!mPaymentMethods.isEmpty()) {
149 Intent payIntent = new Intent(AndroidPaymentApp.ACTION_PAY);
150 for (URI methodName : mPaymentMethods) {
151 payIntent.setData(Uri.parse(methodName.toString()));
152 List<ResolveInfo> apps =
153 mPackageManagerDelegate.getActivitiesThatCanRespondToInt ent(payIntent);
154 if (apps.isEmpty()) continue;
155
156 // Start the parser utility process as soon as possible, once we know that a
157 // manifest file needs to be parsed. The startup can take up to 2 seconds.
158 if (!mParser.isUtilityProcessRunning()) mParser.startUtilityProc ess();
159
160 verifiers.add(new PaymentManifestVerifier(methodName, apps, mDow nloader, mParser,
161 mPackageManagerDelegate, this /* callback */));
162 mPendingApps.put(methodName, new HashSet<>(apps));
163 if (verifiers.size() == MAX_NUMBER_OF_MANIFESTS) {
164 Log.d(TAG, "Reached maximum number of allowed payment app ma nifests.");
165 break;
166 }
167 }
168 }
169
170 if (mQueryBasicCard) {
171 Intent basicCardPayIntent = new Intent(ACTION_PAY_BASIC_CARD);
172 List<ResolveInfo> apps =
173 mPackageManagerDelegate.getActivitiesThatCanRespondToIntent( basicCardPayIntent);
174 for (int i = 0; i < apps.size(); i++) {
175 // Chrome does not verify app manifests for "basic-card" support .
176 onValidPaymentApp(BASIC_CARD_PAYMENT_METHOD, apps.get(i));
177 }
178 }
179
180 if (verifiers.isEmpty()) {
181 onSearchFinished();
182 return;
183 }
184
185 for (int i = 0; i < verifiers.size(); i++) {
186 verifiers.get(i).verify();
187 }
188 }
189
190 @Override
191 public void onValidPaymentApp(URI methodName, ResolveInfo resolveInfo) {
192 onValidPaymentApp(methodName.toString(), resolveInfo);
193 removePendingApp(methodName, resolveInfo);
194 }
195
196 /** Same as above, but also works for non-URI method names, e.g., "basic-car d". */
197 private void onValidPaymentApp(String methodName, ResolveInfo resolveInfo) {
198 String packageName = resolveInfo.activityInfo.packageName;
199 AndroidPaymentApp app = mResult.get(packageName);
200 if (app == null) {
201 if (mRequireShowInSettings) {
202 mSettingsLookup.setPackage(packageName);
203 if (mPackageManagerDelegate.resolveActivity(mSettingsLookup) == null) return;
204 }
205 CharSequence label = mPackageManagerDelegate.getAppLabel(resolveInfo );
206 if (TextUtils.isEmpty(label)) return;
Ted C 2017/03/13 21:13:34 I'd add logging here too that we skipped the packa
please use gerrit instead 2017/03/13 22:19:54 Done.
207 app = new AndroidPaymentApp(mWebContents, packageName, resolveInfo.a ctivityInfo.name,
208 label.toString(), mPackageManagerDelegate.getAppIcon(resolve Info));
209 mResult.put(packageName, app);
210 }
211 app.addMethodName(methodName);
212 }
213
214 @Override
215 public void onInvalidPaymentApp(URI methodName, ResolveInfo resolveInfo) {
216 removePendingApp(methodName, resolveInfo);
217 }
218
219 /** Removes the (method, app) pair from the list of pending information to b e verified. */
220 private void removePendingApp(URI methodName, ResolveInfo resolveInfo) {
221 Set<ResolveInfo> pendingAppsForMethod = mPendingApps.get(methodName);
222 pendingAppsForMethod.remove(resolveInfo);
223 if (pendingAppsForMethod.isEmpty()) mPendingApps.remove(methodName);
224 if (mPendingApps.isEmpty()) onSearchFinished();
225 }
226
227 @Override
228 public void onInvalidManifest(URI methodName) {
229 mPendingApps.remove(methodName);
230 if (mPendingApps.isEmpty()) onSearchFinished();
231 }
232
233 /**
234 * Checks for IS_READY_TO_PAY service in each valid payment app and returns the valid apps
235 * to the caller. Called when finished verifying all payment methods and app s.
236 */
237 private void onSearchFinished() {
238 assert mPendingApps.isEmpty();
239
240 if (mParser.isUtilityProcessRunning()) mParser.stopUtilityProcess();
241
242 List<ResolveInfo> resolveInfos = mPackageManagerDelegate.getServicesThat CanRespondToIntent(
243 new Intent(ACTION_IS_READY_TO_PAY));
244 for (int i = 0; i < resolveInfos.size(); i++) {
245 ResolveInfo resolveInfo = resolveInfos.get(i);
246 AndroidPaymentApp app = mResult.get(resolveInfo.serviceInfo.packageN ame);
247 if (app != null) app.setIsReadyToPayAction(resolveInfo.serviceInfo.n ame);
248 }
249
250 for (Map.Entry<String, AndroidPaymentApp> entry : mResult.entrySet()) {
251 mCallback.onPaymentAppCreated(entry.getValue());
252 }
253
254 mCallback.onAllPaymentAppsCreated();
255 }
256 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698