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

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

Powered by Google App Engine
This is Rietveld 408576698