| 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());
|
| + }
|
| +}
|
|
|