Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 } |
| OLD | NEW |