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.content.browser.installedapp; | 5 package org.chromium.content.browser.installedapp; |
6 | 6 |
7 import android.content.Context; | 7 import android.content.Context; |
8 import android.content.pm.ApplicationInfo; | 8 import android.content.pm.ApplicationInfo; |
9 import android.content.pm.PackageManager; | 9 import android.content.pm.PackageManager; |
10 import android.content.pm.PackageManager.NameNotFoundException; | 10 import android.content.pm.PackageManager.NameNotFoundException; |
11 import android.content.res.Resources; | 11 import android.content.res.Resources; |
12 import android.os.AsyncTask; | 12 import android.os.AsyncTask; |
| 13 import android.util.Pair; |
13 | 14 |
14 import org.json.JSONArray; | 15 import org.json.JSONArray; |
15 import org.json.JSONException; | 16 import org.json.JSONException; |
16 import org.json.JSONObject; | 17 import org.json.JSONObject; |
17 | 18 |
18 import org.chromium.base.Log; | 19 import org.chromium.base.Log; |
| 20 import org.chromium.base.ThreadUtils; |
19 import org.chromium.base.VisibleForTesting; | 21 import org.chromium.base.VisibleForTesting; |
20 import org.chromium.installedapp.mojom.InstalledAppProvider; | 22 import org.chromium.installedapp.mojom.InstalledAppProvider; |
21 import org.chromium.installedapp.mojom.RelatedApplication; | 23 import org.chromium.installedapp.mojom.RelatedApplication; |
22 import org.chromium.mojo.system.MojoException; | 24 import org.chromium.mojo.system.MojoException; |
23 | 25 |
24 import java.net.URI; | 26 import java.net.URI; |
25 import java.net.URISyntaxException; | 27 import java.net.URISyntaxException; |
26 import java.util.ArrayList; | 28 import java.util.ArrayList; |
27 | 29 |
28 /** | 30 /** |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
72 final RelatedApplication[] relatedApps, final FilterInstalledAppsRes
ponse callback) { | 74 final RelatedApplication[] relatedApps, final FilterInstalledAppsRes
ponse callback) { |
73 if (mFrameUrlDelegate.isIncognito()) { | 75 if (mFrameUrlDelegate.isIncognito()) { |
74 callback.call(new RelatedApplication[0]); | 76 callback.call(new RelatedApplication[0]); |
75 return; | 77 return; |
76 } | 78 } |
77 | 79 |
78 final URI frameUrl = mFrameUrlDelegate.getUrl(); | 80 final URI frameUrl = mFrameUrlDelegate.getUrl(); |
79 | 81 |
80 // Use an AsyncTask to execute the installed/related checks on a backgro
und thread (so as | 82 // Use an AsyncTask to execute the installed/related checks on a backgro
und thread (so as |
81 // not to block the UI thread). | 83 // not to block the UI thread). |
82 new AsyncTask<Void, Void, RelatedApplication[]>() { | 84 new AsyncTask<Void, Void, Pair<RelatedApplication[], Integer>>() { |
83 @Override | 85 @Override |
84 protected RelatedApplication[] doInBackground(Void... unused) { | 86 protected Pair<RelatedApplication[], Integer> doInBackground(Void...
unused) { |
85 return filterInstalledAppsOnBackgroundThread(relatedApps, frameU
rl); | 87 return filterInstalledAppsOnBackgroundThread(relatedApps, frameU
rl); |
86 } | 88 } |
87 | 89 |
88 @Override | 90 @Override |
89 protected void onPostExecute(RelatedApplication[] installedApps) { | 91 protected void onPostExecute(Pair<RelatedApplication[], Integer> res
ult) { |
90 callback.call(installedApps); | 92 final RelatedApplication[] installedApps = result.first; |
| 93 int delayMillis = result.second; |
| 94 // Before calling the callback, delay for the amount of time tha
t has been |
| 95 // calculated in |delayMillis|. |
| 96 delayThenRun(new Runnable() { |
| 97 @Override |
| 98 public void run() { |
| 99 callback.call(installedApps); |
| 100 } |
| 101 }, delayMillis); |
91 } | 102 } |
92 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); | 103 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
93 } | 104 } |
94 | 105 |
95 @Override | 106 @Override |
96 public void close() {} | 107 public void close() {} |
97 | 108 |
98 @Override | 109 @Override |
99 public void onConnectionError(MojoException e) {} | 110 public void onConnectionError(MojoException e) {} |
100 | 111 |
101 /** | 112 /** |
102 * Filters a list of apps, returning those that are both installed and match
the origin. | 113 * Filters a list of apps, returning those that are both installed and match
the origin. |
103 * | 114 * |
104 * This method is expected to be called on a background thread (not the main
UI thread). | 115 * This method is expected to be called on a background thread (not the main
UI thread). |
105 * | 116 * |
106 * @param relatedApps A list of applications to be filtered. | 117 * @param relatedApps A list of applications to be filtered. |
107 * @param frameUrl The URL of the frame this operation was called from. | 118 * @param frameUrl The URL of the frame this operation was called from. |
108 * @return A subsequence of applications that meet the criteria. | 119 * @return Pair of: A subsequence of applications that meet the criteria, an
d, the total amount |
| 120 * of time in ms that should be delayed before returning to the user
, to mask the |
| 121 * installed state of the requested apps. |
109 */ | 122 */ |
110 private RelatedApplication[] filterInstalledAppsOnBackgroundThread( | 123 private Pair<RelatedApplication[], Integer> filterInstalledAppsOnBackgroundT
hread( |
111 RelatedApplication[] relatedApps, URI frameUrl) { | 124 RelatedApplication[] relatedApps, URI frameUrl) { |
112 ArrayList<RelatedApplication> installedApps = new ArrayList<RelatedAppli
cation>(); | 125 ArrayList<RelatedApplication> installedApps = new ArrayList<RelatedAppli
cation>(); |
| 126 int delayMillis = 0; |
113 PackageManager pm = mContext.getPackageManager(); | 127 PackageManager pm = mContext.getPackageManager(); |
114 for (RelatedApplication app : relatedApps) { | 128 for (RelatedApplication app : relatedApps) { |
115 // If the package is of type "play", it is installed, and the origin
is associated with | 129 // If the package is of type "play", it is installed, and the origin
is associated with |
116 // package, add the package to the list of valid packages. | 130 // package, add the package to the list of valid packages. |
117 // NOTE: For security, it must not be possible to distinguish (from
the response) | 131 // NOTE: For security, it must not be possible to distinguish (from
the response) |
118 // between the app not being installed and the origin not being asso
ciated with the app | 132 // between the app not being installed and the origin not being asso
ciated with the app |
119 // (otherwise, arbitrary websites would be able to test whether un-a
ssociated apps are | 133 // (otherwise, arbitrary websites would be able to test whether un-a
ssociated apps are |
120 // installed on the user's device). | 134 // installed on the user's device). |
121 if (app.platform.equals(RELATED_APP_PLATFORM_ANDROID) && app.id != n
ull | 135 if (app.platform.equals(RELATED_APP_PLATFORM_ANDROID) && app.id != n
ull) { |
122 && isAppInstalledAndAssociatedWithOrigin(app.id, frameUrl, p
m)) { | 136 delayMillis += calculateDelayForPackageMs(app.id); |
123 installedApps.add(app); | 137 if (isAppInstalledAndAssociatedWithOrigin(app.id, frameUrl, pm))
{ |
| 138 installedApps.add(app); |
| 139 } |
124 } | 140 } |
125 } | 141 } |
126 | 142 |
127 RelatedApplication[] installedAppsArray = new RelatedApplication[install
edApps.size()]; | 143 RelatedApplication[] installedAppsArray = new RelatedApplication[install
edApps.size()]; |
128 installedApps.toArray(installedAppsArray); | 144 installedApps.toArray(installedAppsArray); |
129 return installedAppsArray; | 145 return Pair.create(installedAppsArray, delayMillis); |
130 } | 146 } |
131 | 147 |
132 /** | 148 /** |
| 149 * Determines how long to artifically delay for, for a particular package na
me. |
| 150 */ |
| 151 private int calculateDelayForPackageMs(String packageName) { |
| 152 // Important timing-attack prevention measure: delay by a pseudo-random
amount of time, to |
| 153 // add significant noise to the time taken to check whether this app is
installed and |
| 154 // related. Otherwise, it would be possible to tell whether a non-relate
d app is installed, |
| 155 // based on the time this operation takes. |
| 156 // |
| 157 // Generate a 16-bit hash based on a unique device ID + the package name
. |
| 158 short hash = PackageHash.hashForPackage(packageName); |
| 159 |
| 160 // The time delay is the low 10 bits of the hash in 100ths of a ms (betw
een 0 and 10ms). |
| 161 int delayHundredthsOfMs = hash & 0x3ff; |
| 162 return delayHundredthsOfMs / 100; |
| 163 } |
| 164 |
| 165 /** |
133 * Determines whether a particular app is installed and matches the origin. | 166 * Determines whether a particular app is installed and matches the origin. |
134 * | 167 * |
135 * @param packageName Name of the Android package to check if installed. Ret
urns false if the | 168 * @param packageName Name of the Android package to check if installed. Ret
urns false if the |
136 * app is not installed. | 169 * app is not installed. |
137 * @param frameUrl Returns false if the Android package does not declare ass
ociation with the | 170 * @param frameUrl Returns false if the Android package does not declare ass
ociation with the |
138 * origin of this URL. Can be null. | 171 * origin of this URL. Can be null. |
139 */ | 172 */ |
140 private static boolean isAppInstalledAndAssociatedWithOrigin( | 173 private boolean isAppInstalledAndAssociatedWithOrigin( |
141 String packageName, URI frameUrl, PackageManager pm) { | 174 String packageName, URI frameUrl, PackageManager pm) { |
142 if (frameUrl == null) return false; | 175 if (frameUrl == null) return false; |
143 | 176 |
144 // Early-exit if the Android app is not installed. | 177 // Early-exit if the Android app is not installed. |
145 JSONArray statements; | 178 JSONArray statements; |
146 try { | 179 try { |
147 statements = getAssetStatements(packageName, pm); | 180 statements = getAssetStatements(packageName, pm); |
148 } catch (NameNotFoundException e) { | 181 } catch (NameNotFoundException e) { |
149 return false; | 182 return false; |
150 } | 183 } |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 } | 301 } |
269 | 302 |
270 private static boolean statementTargetMatches(URI frameUrl, URI assetUrl) { | 303 private static boolean statementTargetMatches(URI frameUrl, URI assetUrl) { |
271 if (assetUrl.getScheme() == null || assetUrl.getAuthority() == null) { | 304 if (assetUrl.getScheme() == null || assetUrl.getAuthority() == null) { |
272 return false; | 305 return false; |
273 } | 306 } |
274 | 307 |
275 return assetUrl.getScheme().equals(frameUrl.getScheme()) | 308 return assetUrl.getScheme().equals(frameUrl.getScheme()) |
276 && assetUrl.getAuthority().equals(frameUrl.getAuthority()); | 309 && assetUrl.getAuthority().equals(frameUrl.getAuthority()); |
277 } | 310 } |
| 311 |
| 312 /** |
| 313 * Runs a Runnable task after a given delay. |
| 314 * |
| 315 * Protected and non-static for testing. |
| 316 * |
| 317 * @param r The Runnable that will be executed. |
| 318 * @param delayMillis The delay (in ms) until the Runnable will be executed. |
| 319 * @return True if the Runnable was successfully placed into the message que
ue. |
| 320 */ |
| 321 protected void delayThenRun(Runnable r, long delayMillis) { |
| 322 ThreadUtils.postOnUiThreadDelayed(r, delayMillis); |
| 323 } |
278 } | 324 } |
OLD | NEW |