| 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 |