Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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.chrome.browser.webapps; | 5 package org.chromium.chrome.browser.webapps; |
| 6 | 6 |
| 7 import android.app.Activity; | 7 import android.app.Activity; |
| 8 import android.content.Intent; | 8 import android.content.Intent; |
| 9 import android.net.Uri; | 9 import android.net.Uri; |
| 10 import android.os.Build; | 10 import android.os.Build; |
| 11 import android.os.Bundle; | 11 import android.os.Bundle; |
| 12 import android.util.Base64; | 12 import android.util.Base64; |
| 13 | 13 |
| 14 import org.chromium.base.ApiCompatibilityUtils; | 14 import org.chromium.base.ApiCompatibilityUtils; |
| 15 import org.chromium.base.ApplicationStatus; | 15 import org.chromium.base.ApplicationStatus; |
| 16 import org.chromium.base.CommandLine; | |
| 16 import org.chromium.base.ContextUtils; | 17 import org.chromium.base.ContextUtils; |
| 17 import org.chromium.base.Log; | 18 import org.chromium.base.Log; |
| 19 import org.chromium.chrome.browser.ChromeSwitches; | |
| 18 import org.chromium.chrome.browser.IntentHandler; | 20 import org.chromium.chrome.browser.IntentHandler; |
| 19 import org.chromium.chrome.browser.ShortcutHelper; | 21 import org.chromium.chrome.browser.ShortcutHelper; |
| 20 import org.chromium.chrome.browser.document.ChromeLauncherActivity; | 22 import org.chromium.chrome.browser.document.ChromeLauncherActivity; |
| 21 import org.chromium.chrome.browser.metrics.LaunchMetrics; | 23 import org.chromium.chrome.browser.metrics.LaunchMetrics; |
| 22 import org.chromium.chrome.browser.tab.Tab; | 24 import org.chromium.chrome.browser.tab.Tab; |
| 23 import org.chromium.chrome.browser.util.IntentUtils; | 25 import org.chromium.chrome.browser.util.IntentUtils; |
| 26 import org.chromium.webapk.lib.client.WebApkValidator; | |
| 24 | 27 |
| 25 import java.lang.ref.WeakReference; | 28 import java.lang.ref.WeakReference; |
| 26 | 29 |
| 27 /** | 30 /** |
| 28 * Launches web apps. This was separated from the ChromeLauncherActivity becaus e the | 31 * Launches web apps. This was separated from the ChromeLauncherActivity becaus e the |
| 29 * ChromeLauncherActivity is not allowed to be excluded from Android's Recents: crbug.com/517426. | 32 * ChromeLauncherActivity is not allowed to be excluded from Android's Recents: crbug.com/517426. |
| 30 */ | 33 */ |
| 31 public class WebappLauncherActivity extends Activity { | 34 public class WebappLauncherActivity extends Activity { |
| 32 /** | 35 /** |
| 33 * Action fired when an Intent is trying to launch a WebappActivity. | 36 * Action fired when an Intent is trying to launch a WebappActivity. |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 44 if (webappInfo == null) { | 47 if (webappInfo == null) { |
| 45 super.onCreate(null); | 48 super.onCreate(null); |
| 46 ApiCompatibilityUtils.finishAndRemoveTask(this); | 49 ApiCompatibilityUtils.finishAndRemoveTask(this); |
| 47 return; | 50 return; |
| 48 } | 51 } |
| 49 | 52 |
| 50 super.onCreate(savedInstanceState); | 53 super.onCreate(savedInstanceState); |
| 51 Intent intent = getIntent(); | 54 Intent intent = getIntent(); |
| 52 String webappId = webappInfo.id(); | 55 String webappId = webappInfo.id(); |
| 53 String webappUrl = webappInfo.uri().toString(); | 56 String webappUrl = webappInfo.uri().toString(); |
| 57 String webappPackageName = webappInfo.packageName(); | |
| 54 int webappSource = webappInfo.source(); | 58 int webappSource = webappInfo.source(); |
| 55 | 59 |
| 56 if (webappId != null && webappUrl != null) { | 60 if (webappId != null && webappUrl != null) { |
| 57 String webappMacString = IntentUtils.safeGetStringExtra( | |
| 58 intent, ShortcutHelper.EXTRA_MAC); | |
| 59 byte[] webappMac = | |
| 60 webappMacString == null ? null : Base64.decode(webappMacStri ng, Base64.DEFAULT); | |
| 61 | |
| 62 Intent launchIntent = null; | 61 Intent launchIntent = null; |
| 63 | 62 |
| 64 // Permit the launch to a standalone web app frame if the intent was sent by Chrome, or | 63 // Permit the launch to a standalone web app frame if: |
| 65 // if the MAC is present and valid for the URL to be opened. | 64 // - the request was for a WebAPK that is valid |
| 66 boolean isTrusted = IntentHandler.wasIntentSenderChrome(intent, | 65 // - the MAC is present and valid for the homescreen shortcut to be opened. |
| 67 ContextUtils.getApplicationContext()); | 66 // - the intent was sent by Chrome |
| 68 boolean isUrlValid = (webappMac != null | 67 boolean isWebApk = CommandLine.getInstance().hasSwitch(ChromeSwitche s.ENABLE_WEBAPK) |
| 69 && WebappAuthenticator.isUrlValid(this, webappUrl, webappMac )); | 68 && isValidWebApk(webappPackageName, webappUrl); |
| 70 | 69 if (isWebApk |
| 71 if (isTrusted || isUrlValid) { | 70 || isValidFullscreenPwa(webappUrl, IntentUtils.safeGetString Extra( |
| 71 intent, ShortcutHelper.EXTRA_MAC)) | |
| 72 || wasIntentFromChrome(intent)) { | |
|
pkotwicz
2016/05/18 23:00:17
The security review will take a lot of time (and i
Xi Han
2016/05/19 18:31:50
Good idea, since the security check isn't the focu
| |
| 72 LaunchMetrics.recordHomeScreenLaunchIntoStandaloneActivity(webap pUrl, webappSource); | 73 LaunchMetrics.recordHomeScreenLaunchIntoStandaloneActivity(webap pUrl, webappSource); |
| 73 | 74 |
| 74 String activityName = WebappActivity.class.getName(); | 75 String activityName = WebappActivity.class.getName(); |
| 75 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { | 76 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { |
| 76 // Specifically assign the app to a particular WebappActivit y instance. | 77 // Specifically assign the app to a particular WebappActivit y instance. |
| 77 int activityIndex = ActivityAssigner.instance(this).assign(w ebappId); | 78 int activityIndex = ActivityAssigner.instance(this).assign(w ebappId); |
| 78 activityName += String.valueOf(activityIndex); | 79 activityName += String.valueOf(activityIndex); |
| 79 } | 80 } |
| 80 | 81 |
| 81 // Create an intent to launch the Webapp in an unmapped WebappAc tivity. | 82 // Create an intent to launch the Webapp in an unmapped WebappAc tivity. |
| 82 launchIntent = new Intent(); | 83 launchIntent = new Intent(); |
| 83 launchIntent.setClassName(this, activityName); | 84 launchIntent.setClassName(this, activityName); |
| 84 webappInfo.setWebappIntentExtras(launchIntent); | 85 webappInfo.setWebappIntentExtras(launchIntent); |
| 85 | 86 |
| 86 // On L+, firing intents with the exact same data should relaunc h a particular | 87 // On L+, firing intents with the exact same data should relaunc h a particular |
| 87 // Activity. | 88 // Activity. |
| 88 launchIntent.setAction(Intent.ACTION_VIEW); | 89 launchIntent.setAction(Intent.ACTION_VIEW); |
| 89 launchIntent.setData(Uri.parse(WebappActivity.WEBAPP_SCHEME + ": //" + webappId)); | 90 launchIntent.setData(Uri.parse(WebappActivity.WEBAPP_SCHEME + ": //" + webappId)); |
| 90 } else { | 91 } else { |
| 91 Log.e(TAG, "Shortcut (%s) opened in Chrome.", webappUrl); | 92 Log.e(TAG, "Shortcut (%s) opened in Chrome.", webappUrl); |
| 92 | 93 |
| 93 // The shortcut data doesn't match the current encoding. Change the intent action | 94 // The shortcut data doesn't match the current encoding. Change the intent action |
| 94 // launch the URL with a VIEW Intent in the regular browser. | 95 // launch the URL with a VIEW Intent in the regular browser. |
| 95 launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webappUr l)); | 96 launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webappUr l)); |
| 96 launchIntent.setClassName(getPackageName(), ChromeLauncherActivi ty.class.getName()); | 97 launchIntent.setClassName(getPackageName(), ChromeLauncherActivi ty.class.getName()); |
| 97 launchIntent.putExtra(ShortcutHelper.REUSE_URL_MATCHING_TAB_ELSE _NEW_TAB, true); | |
| 98 launchIntent.putExtra(ShortcutHelper.EXTRA_SOURCE, webappSource) ; | |
| 99 } | 98 } |
| 100 | 99 if (!isWebApk) { |
| 101 launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 100 launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
| 102 | ApiCompatibilityUtils.getActivityNewDocumentFlag()); | 101 | ApiCompatibilityUtils.getActivityNewDocumentFlag()); |
| 102 } | |
| 103 startActivity(launchIntent); | 103 startActivity(launchIntent); |
| 104 } | 104 } |
| 105 | 105 |
| 106 ApiCompatibilityUtils.finishAndRemoveTask(this); | 106 ApiCompatibilityUtils.finishAndRemoveTask(this); |
| 107 } | 107 } |
| 108 | 108 |
| 109 /** | 109 /** |
| 110 * Checks whether the package being targeted is a valid WebAPK and whether t he url supplied | |
| 111 * can be fulfilled by that WebAPK. | |
| 112 * @param webapkPackage The package name of the requested WebAPK | |
| 113 * @param url The url to navigate to | |
| 114 * @return true iff all validation criteria are met | |
| 115 */ | |
| 116 private boolean isValidWebApk(String webapkPackage, String url) { | |
| 117 boolean isValidWebApk = WebApkValidator.isValidWebApk(this, webapkPackag e); | |
| 118 if (!isValidWebApk) { | |
| 119 Log.d(TAG, "isValidWebApk(pkg=\"" + webapkPackage + "\",url=\"" + ur l + "\")=false"); | |
| 120 return false; | |
| 121 } | |
| 122 | |
| 123 boolean result = webapkPackage != null | |
| 124 && webapkPackage.equals(WebApkValidator.queryWebApkPackage(this, url)); | |
| 125 Log.d(TAG, "isValidWebApk(pkg=\"" + webapkPackage + "\",url=\"" + url + "\")=" + result); | |
| 126 return result; | |
| 127 } | |
| 128 | |
| 129 /** | |
| 130 * Returns whether the MAC is present and valid for the fullscreen PWA to be opened. | |
| 131 * @param url the url to launch | |
| 132 * @param mac the MAC code provided which must match the requested url | |
| 133 * @return whether the supplied MAC is valid for the supplied url | |
| 134 */ | |
| 135 private boolean isValidFullscreenPwa(String url, String mac) { | |
| 136 return mac != null | |
| 137 && WebappAuthenticator.isUrlValid(this, url, Base64.decode(mac, Base64.DEFAULT)); | |
| 138 } | |
| 139 | |
| 140 private boolean wasIntentFromChrome(Intent intent) { | |
| 141 return IntentHandler.wasIntentSenderChrome(intent, | |
| 142 ContextUtils.getApplicationContext()); | |
| 143 } | |
| 144 | |
| 145 /** | |
| 110 * Brings a live WebappActivity back to the foreground if one exists for the given tab ID. | 146 * Brings a live WebappActivity back to the foreground if one exists for the given tab ID. |
| 111 * @param tabId ID of the Tab to bring back to the foreground. | 147 * @param tabId ID of the Tab to bring back to the foreground. |
| 112 * @return True if a live WebappActivity was found, false otherwise. | 148 * @return True if a live WebappActivity was found, false otherwise. |
| 113 */ | 149 */ |
| 114 public static boolean bringWebappToFront(int tabId) { | 150 public static boolean bringWebappToFront(int tabId) { |
| 115 if (tabId == Tab.INVALID_TAB_ID) return false; | 151 if (tabId == Tab.INVALID_TAB_ID) return false; |
| 116 | 152 |
| 117 for (WeakReference<Activity> activityRef : ApplicationStatus.getRunningA ctivities()) { | 153 for (WeakReference<Activity> activityRef : ApplicationStatus.getRunningA ctivities()) { |
| 118 Activity activity = activityRef.get(); | 154 Activity activity = activityRef.get(); |
| 119 if (activity == null || !(activity instanceof WebappActivity)) conti nue; | 155 if (activity == null || !(activity instanceof WebappActivity)) conti nue; |
| 120 | 156 |
| 121 WebappActivity webappActivity = (WebappActivity) activity; | 157 WebappActivity webappActivity = (WebappActivity) activity; |
| 122 if (webappActivity.getActivityTab() != null | 158 if (webappActivity.getActivityTab() != null |
| 123 && webappActivity.getActivityTab().getId() == tabId) { | 159 && webappActivity.getActivityTab().getId() == tabId) { |
| 124 Tab tab = webappActivity.getActivityTab(); | 160 Tab tab = webappActivity.getActivityTab(); |
| 125 tab.getTabWebContentsDelegateAndroid().activateContents(); | 161 tab.getTabWebContentsDelegateAndroid().activateContents(); |
| 126 return true; | 162 return true; |
| 127 } | 163 } |
| 128 } | 164 } |
| 129 | 165 |
| 130 return false; | 166 return false; |
| 131 } | 167 } |
| 132 } | 168 } |
| OLD | NEW |