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

Unified Diff: content/public/android/java/src/org/chromium/content/browser/installedapp/InstalledAppProviderImpl.java

Issue 1756793004: Chrome-side patch for IsAppInstalled. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@splitpatch2
Patch Set: Created 4 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 side-by-side diff with in-line comments
Download patch
Index: content/public/android/java/src/org/chromium/content/browser/installedapp/InstalledAppProviderImpl.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/installedapp/InstalledAppProviderImpl.java b/content/public/android/java/src/org/chromium/content/browser/installedapp/InstalledAppProviderImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..61dc47eab07799cd8cb540fee01f6e57601264d5
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/installedapp/InstalledAppProviderImpl.java
@@ -0,0 +1,146 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser.installedapp;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojom.content.InstalledAppProvider;
+import org.chromium.mojom.content.RelatedApplication;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Android implementation of the InstalledAppProvider service defined in
+ * content/common/installedapp/installed_app_provider.mojom.
+ */
+public class InstalledAppProviderImpl implements InstalledAppProvider {
+ private Context mContext;
+ private static final String ASSET_DESCRIPTOR_FIELD_TARGET = "target";
+ private static final String ASSET_DESCRIPTOR_WEB = "web";
+ private static final String ASSET_DESCRIPTOR_FIELD_SITE = "site";
+ private static final String ASSET_DESCRIPTOR_FIELD_NAMESPACE = "namespace";
+ private static final String ASSOCIATED_ASSETS_KEY = "associated_assets";
+ private static final String RELATED_APP_ANDROID_PLATFORM = "play";
+
+ public InstalledAppProviderImpl(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void filterInstalledApps(
+ RelatedApplication[] apps, String origin, FilterInstalledAppsResponse callback) {
+ callback.call(filterAndroidInstalledApps(apps, origin));
+ }
+
+ public RelatedApplication[] filterAndroidInstalledApps(
+ RelatedApplication[] apps, String origin) {
+ ArrayList<RelatedApplication> installedApps = new ArrayList<RelatedApplication>();
+ PackageManager pm = mContext.getPackageManager();
+ for (RelatedApplication app : apps) {
+ // If the package is of type "play" and origin is associated with package,
+ // add the package to the list of valid packages.
+ if (app.platform.equals(RELATED_APP_ANDROID_PLATFORM)
+ && isOriginAssociatedWithPackage(app.id, origin, pm)) {
+ installedApps.add(app);
+ }
+ }
+ RelatedApplication installedAppsArray[] = new RelatedApplication[installedApps.size()];
+ installedAppsArray = installedApps.toArray(installedAppsArray);
+ return installedAppsArray;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public void onConnectionError(MojoException e) {}
+
+ private static List<String> getAssociations(String packageName, PackageManager pm) {
+ Resources resources;
+
+ try {
+ resources = pm.getResourcesForApplication(packageName);
+ } catch (NameNotFoundException e) {
+ return Collections.<String>emptyList();
+ }
+
+ if (resources == null) {
+ return Collections.<String>emptyList();
+ }
+
+ int identifier = resources.getIdentifier(ASSOCIATED_ASSETS_KEY, "array", packageName);
+ if (identifier == 0) {
+ return Collections.<String>emptyList();
+ }
+ return Arrays.asList(resources.getStringArray(identifier));
+ }
+
+ private static boolean isOriginAssociatedWithPackage(
+ String packageName, String origin, PackageManager pm) {
+ List<String> associations = getAssociations(packageName, pm);
+ URL originURL;
+ try {
+ originURL = new URL(origin);
+ } catch (MalformedURLException e) {
+ return false;
+ }
+
+ for (String statementString : associations) {
+ try {
+ JSONObject statement = new JSONObject(statementString);
+ String assetDescriptorJson = statement.getString(ASSET_DESCRIPTOR_FIELD_TARGET);
+ JSONObject assetJson = new JSONObject(assetDescriptorJson);
+
+ // If it is not a web appet, skip it.
+ if (!isAssetIsWeb(assetJson)) {
+ continue;
+ }
+
+ URL assetURL;
+ try {
+ assetURL = new URL(assetJson.getString(ASSET_DESCRIPTOR_FIELD_SITE));
+ } catch (MalformedURLException e) {
+ continue;
+ }
+
+ // The URL is considered equivalent if the scheme, host, and port match, according
+ // to the DigitalAssetLinks v1 spec.
+ if (statementTargetMatches(originURL, assetURL)) {
+ return true;
+ }
+ } catch (JSONException e) {
+ continue;
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isAssetIsWeb(JSONObject asset) {
+ try {
+ return asset.getString(ASSET_DESCRIPTOR_FIELD_NAMESPACE).equals(ASSET_DESCRIPTOR_WEB);
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+
+ private static boolean statementTargetMatches(URL originURL, URL assetURL) {
+ return (assetURL.getProtocol().equals(originURL.getProtocol())
+ && assetURL.getHost().equals(originURL.getHost())
+ && assetURL.getPort() == originURL.getPort());
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698