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

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

Issue 2762343003: Revert of Add Android implementation of navigator.getInstalledRelatedApps. (Closed)
Patch Set: 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.content.browser.installedapp;
6
7 import android.content.Context;
8 import android.content.pm.ApplicationInfo;
9 import android.content.pm.PackageManager;
10 import android.content.pm.PackageManager.NameNotFoundException;
11 import android.content.res.Resources;
12
13 import org.json.JSONArray;
14 import org.json.JSONException;
15 import org.json.JSONObject;
16
17 import org.chromium.base.Log;
18 import org.chromium.base.VisibleForTesting;
19 import org.chromium.installedapp.mojom.InstalledAppProvider;
20 import org.chromium.installedapp.mojom.RelatedApplication;
21 import org.chromium.mojo.system.MojoException;
22
23 import java.net.URI;
24 import java.net.URISyntaxException;
25 import java.util.ArrayList;
26
27 /**
28 * Android implementation of the InstalledAppProvider service defined in
29 * installed_app_provider.mojom
30 */
31 public class InstalledAppProviderImpl implements InstalledAppProvider {
32 @VisibleForTesting
33 public static final String ASSET_STATEMENTS_KEY = "asset_statements";
34 private static final String ASSET_STATEMENT_FIELD_TARGET = "target";
35 private static final String ASSET_STATEMENT_FIELD_NAMESPACE = "namespace";
36 private static final String ASSET_STATEMENT_FIELD_SITE = "site";
37 @VisibleForTesting
38 public static final String ASSET_STATEMENT_NAMESPACE_WEB = "web";
39 @VisibleForTesting
40 public static final String RELATED_APP_PLATFORM_ANDROID = "play";
41
42 private static final String TAG = "InstalledAppProvider";
43
44 private final FrameUrlDelegate mFrameUrlDelegate;
45 private final Context mContext;
46
47 /**
48 * Small interface for dynamically getting the URL of the current frame.
49 *
50 * Abstract to allow for testing.
51 */
52 public static interface FrameUrlDelegate {
53 /**
54 * Gets the URL of the current frame. Can return null (if the frame has disappeared).
55 */
56 public URI getUrl();
57 }
58
59 public InstalledAppProviderImpl(FrameUrlDelegate frameUrlDelegate, Context c ontext) {
60 mFrameUrlDelegate = frameUrlDelegate;
61 mContext = context;
62 }
63
64 @Override
65 public void filterInstalledApps(
66 RelatedApplication[] relatedApps, FilterInstalledAppsResponse callba ck) {
67 URI frameUrl = mFrameUrlDelegate.getUrl();
68 ArrayList<RelatedApplication> installedApps = new ArrayList<RelatedAppli cation>();
69 PackageManager pm = mContext.getPackageManager();
70 for (RelatedApplication app : relatedApps) {
71 // If the package is of type "play", it is installed, and the origin is associated with
72 // package, add the package to the list of valid packages.
73 // NOTE: For security, it must not be possible to distinguish (from the response)
74 // between the app not being installed and the origin not being asso ciated with the app
75 // (otherwise, arbitrary websites would be able to test whether un-a ssociated apps are
76 // installed on the user's device).
77 if (app.platform.equals(RELATED_APP_PLATFORM_ANDROID) && app.id != n ull
78 && isAppInstalledAndAssociatedWithOrigin(app.id, frameUrl, p m)) {
79 installedApps.add(app);
80 }
81 }
82 RelatedApplication[] installedAppsArray = new RelatedApplication[install edApps.size()];
83 installedApps.toArray(installedAppsArray);
84 callback.call(installedAppsArray);
85 }
86
87 @Override
88 public void close() {}
89
90 @Override
91 public void onConnectionError(MojoException e) {}
92
93 /**
94 * Determines whether a particular app is installed and matches the origin.
95 *
96 * @param packageName Name of the Android package to check if installed. Ret urns false if the
97 * app is not installed.
98 * @param frameUrl Returns false if the Android package does not declare ass ociation with the
99 * origin of this URL. Can be null.
100 */
101 private static boolean isAppInstalledAndAssociatedWithOrigin(
102 String packageName, URI frameUrl, PackageManager pm) {
103 if (frameUrl == null) return false;
104
105 // Early-exit if the Android app is not installed.
106 JSONArray statements;
107 try {
108 statements = getAssetStatements(packageName, pm);
109 } catch (NameNotFoundException e) {
110 return false;
111 }
112
113 // The installed Android app has provided us with a list of asset statem ents. If any one of
114 // those statements is a web asset that matches the given origin, return true.
115 for (int i = 0; i < statements.length(); i++) {
116 JSONObject statement;
117 try {
118 statement = statements.getJSONObject(i);
119 } catch (JSONException e) {
120 // If an element is not an object, just ignore it.
121 continue;
122 }
123
124 URI site = getSiteForWebAsset(statement);
125
126 // The URI is considered equivalent if the scheme, host, and port ma tch, according
127 // to the DigitalAssetLinks v1 spec.
128 if (site != null && statementTargetMatches(frameUrl, site)) {
129 return true;
130 }
131 }
132
133 // No asset matched the origin.
134 return false;
135 }
136
137 /**
138 * Gets the asset statements from an Android app's manifest.
139 *
140 * This retrieves the list of statements from the Android app's "asset_state ments" manifest
141 * resource, as specified in Digital Asset Links v1.
142 *
143 * @param packageName Name of the Android package to get statements from.
144 * @return The list of asset statements, parsed from JSON.
145 * @throws NameNotFoundException if the application is not installed.
146 */
147 private static JSONArray getAssetStatements(String packageName, PackageManag er pm)
148 throws NameNotFoundException {
149 // Get the <meta-data> from this app's manifest.
150 // Throws NameNotFoundException if the application is not installed.
151 ApplicationInfo appInfo = pm.getApplicationInfo(packageName, PackageMana ger.GET_META_DATA);
152 int identifier = appInfo.metaData.getInt(ASSET_STATEMENTS_KEY);
153 if (identifier == 0) {
154 return new JSONArray();
155 }
156
157 // Throws NameNotFoundException in the rare case that the application wa s uninstalled since
158 // getting |appInfo| (or resources could not be loaded for some other re ason).
159 Resources resources = pm.getResourcesForApplication(appInfo);
160
161 String statements;
162 try {
163 statements = resources.getString(identifier);
164 } catch (Resources.NotFoundException e) {
165 // This should never happen, but it could if there was a broken APK, so handle it
166 // gracefully without crashing.
167 Log.w(TAG,
168 "Android package " + packageName + " missing asset statement s resource (0x"
169 + Integer.toHexString(identifier) + ").");
170 return new JSONArray();
171 }
172
173 try {
174 return new JSONArray(statements);
175 } catch (JSONException e) {
176 // If the JSON is invalid or not an array, assume it is empty.
177 Log.w(TAG,
178 "Android package " + packageName
179 + " has JSON syntax error in asset statements resour ce (0x"
180 + Integer.toHexString(identifier) + ").");
181 return new JSONArray();
182 }
183 }
184
185 /**
186 * Gets the "site" URI from an Android asset statement.
187 *
188 * @return The site, or null if the asset string was invalid or not related to a web site. This
189 * could be because: the JSON string was invalid, there was no "targ et" field, this was
190 * not a web asset, there was no "site" field, or the "site" field w as invalid.
191 */
192 private static URI getSiteForWebAsset(JSONObject statement) {
193 JSONObject target;
194 try {
195 // Ignore the "relation" field and allow an asset with any relation to this origin.
196 // TODO(mgiuca): [Spec issue] Should we require a specific relation string, rather
197 // than any or no relation?
198 target = statement.getJSONObject(ASSET_STATEMENT_FIELD_TARGET);
199 } catch (JSONException e) {
200 return null;
201 }
202
203 // If it is not a web asset, skip it.
204 if (!isAssetWeb(target)) {
205 return null;
206 }
207
208 try {
209 return new URI(target.getString(ASSET_STATEMENT_FIELD_SITE));
210 } catch (JSONException | URISyntaxException e) {
211 return null;
212 }
213 }
214
215 /**
216 * Determines whether an Android asset statement is for a website.
217 *
218 * @param target The "target" field of the asset statement.
219 */
220 private static boolean isAssetWeb(JSONObject target) {
221 String namespace;
222 try {
223 namespace = target.getString(ASSET_STATEMENT_FIELD_NAMESPACE);
224 } catch (JSONException e) {
225 return false;
226 }
227
228 return namespace.equals(ASSET_STATEMENT_NAMESPACE_WEB);
229 }
230
231 private static boolean statementTargetMatches(URI frameUrl, URI assetUrl) {
232 if (assetUrl.getScheme() == null || assetUrl.getAuthority() == null) {
233 return false;
234 }
235
236 return assetUrl.getScheme().equals(frameUrl.getScheme())
237 && assetUrl.getAuthority().equals(frameUrl.getAuthority());
238 }
239 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698