Commit d466f07d authored by Nate Fischer's avatar Nate Fischer Committed by Commit Bot

AW: handle developer mode with a dummy component

This separates developer mode state from the mechanism for plumbing
developer mode information. These were previously tied together (we
examined the enabled state of the ContentProvider which provided flags
over IPC), but we observed an issue on Android Q related to disabling
and re-enabling a ContentProvider in the same process.

Instead, DeveloperModeContentProvider is always enabled and exported,
and we examine the state of DeveloperModeState, which is a dummy
component we never launch. While this creates slightly more complex
code, this significantly simplifies the runtime behavior, because we no
longer need to worry about functional code falling into a
non-recoverable disabled state.

We use an <activity-alias> as the dummy component because it requires
the least amount of boilerplate, and there's little harm if the user
somehow successfully launches this component.

This also includes temporary workaround code: because the previous
versions explicitly disabled the ContentProvider, and PackageManager
persists this across upgrades, we need to explicitly reset the component
state. To make potential future migrations easier, this updates the
disableDeveloperMode() to set the component to DEFAULT rather than
DISABLED.

Bug: 1041943
Test: Manual - try enabling and disabling developer mode on Android Q
Test: Repro the bug with buggy APK, upgrade to this APK, UI works
Test: Disable dev mode with buggy APK, upgrade to this APK, UI works
Change-Id: I410f36fa590026ddc53837f076e6b5912fa075f4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2005973Reviewed-by: default avatarRichard Coles <torne@chromium.org>
Commit-Queue: Nate Fischer <ntfschr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#734546}
parent 1cae9396
...@@ -10,7 +10,6 @@ import android.content.pm.PackageManager; ...@@ -10,7 +10,6 @@ import android.content.pm.PackageManager;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import org.chromium.android_webview.common.services.ServiceNames;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import java.util.HashMap; import java.util.HashMap;
...@@ -26,6 +25,8 @@ public final class DeveloperModeUtils { ...@@ -26,6 +25,8 @@ public final class DeveloperModeUtils {
// Do not instantiate this class. // Do not instantiate this class.
private DeveloperModeUtils() {} private DeveloperModeUtils() {}
public static final String DEVELOPER_MODE_STATE_COMPONENT =
"org.chromium.android_webview.devui.DeveloperModeState";
public static final String URI_AUTHORITY_SUFFIX = ".DeveloperModeContentProvider"; public static final String URI_AUTHORITY_SUFFIX = ".DeveloperModeContentProvider";
public static final String FLAG_OVERRIDE_URI_PATH = "/flag-overrides"; public static final String FLAG_OVERRIDE_URI_PATH = "/flag-overrides";
public static final String FLAG_OVERRIDE_NAME_COLUMN = "flagName"; public static final String FLAG_OVERRIDE_NAME_COLUMN = "flagName";
...@@ -44,10 +45,10 @@ public final class DeveloperModeUtils { ...@@ -44,10 +45,10 @@ public final class DeveloperModeUtils {
*/ */
public static boolean isDeveloperModeEnabled(String webViewPackageName) { public static boolean isDeveloperModeEnabled(String webViewPackageName) {
final Context context = ContextUtils.getApplicationContext(); final Context context = ContextUtils.getApplicationContext();
ComponentName developerModeContentProvider = ComponentName developerModeComponent =
new ComponentName(webViewPackageName, ServiceNames.DEVELOPER_MODE_CONTENT_PROVIDER); new ComponentName(webViewPackageName, DEVELOPER_MODE_STATE_COMPONENT);
int enabledState = context.getPackageManager().getComponentEnabledSetting( int enabledState =
developerModeContentProvider); context.getPackageManager().getComponentEnabledSetting(developerModeComponent);
return enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED; return enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
} }
......
...@@ -44,6 +44,11 @@ ...@@ -44,6 +44,11 @@
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Don't actually try to launch with this alias: it only exists so we can query its enabled state. -->
<activity-alias android:name="org.chromium.android_webview.devui.DeveloperModeState"
android:targetActivity="org.chromium.android_webview.devui.MainActivity"
android:enabled="false"
android:process=":webview_apk" /> {# Explicit process required for monochrome compatibility. #}
<activity android:name="org.chromium.android_webview.devui.CrashesListActivity" <activity android:name="org.chromium.android_webview.devui.CrashesListActivity"
android:label="WebView Crashes" android:label="WebView Crashes"
android:theme="@style/Theme.DevUi.DayNight" android:theme="@style/Theme.DevUi.DayNight"
...@@ -75,7 +80,6 @@ ...@@ -75,7 +80,6 @@
<!-- Disabled by default, enabled at runtime by Developer UI. --> <!-- Disabled by default, enabled at runtime by Developer UI. -->
<provider android:name="org.chromium.android_webview.services.DeveloperModeContentProvider" <provider android:name="org.chromium.android_webview.services.DeveloperModeContentProvider"
android:exported="true" android:exported="true"
android:enabled="false"
android:authorities="{{ manifest_package }}.DeveloperModeContentProvider" android:authorities="{{ manifest_package }}.DeveloperModeContentProvider"
android:process=":webview_service" /> {# Explicit process required for monochrome compatibility. #} android:process=":webview_service" /> {# Explicit process required for monochrome compatibility. #}
{% if donor_package is not defined %} {% if donor_package is not defined %}
......
...@@ -64,6 +64,18 @@ public class WebViewApkApplication extends Application { ...@@ -64,6 +64,18 @@ public class WebViewApkApplication extends Application {
if (isWebViewProcess()) { if (isWebViewProcess()) {
PathUtils.setPrivateDataDirectorySuffix("webview", "WebView"); PathUtils.setPrivateDataDirectorySuffix("webview", "WebView");
CommandLineUtil.initCommandLine(); CommandLineUtil.initCommandLine();
// Temporary code: old clients would toggle DeveloperModeContentProvider's state, and
// may have disabled it if the user had activated and disabled developer mode.
// PackageManager persists this state across package upgrades and reboots, so we need to
// explicitly reset the component state to safely handle these clients. Only do this if
// we're in a WebView process, because only WebView's Context can change this state.
// TODO(ntfschr): remove this in M83, when all clients are likely to have hit this code.
Context ctx = ContextUtils.getApplicationContext();
ComponentName developerModeContentProvider = new ComponentName(
ctx, "org.chromium.android_webview.services.DeveloperModeContentProvider");
ctx.getPackageManager().setComponentEnabledSetting(developerModeContentProvider,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, PackageManager.DONT_KILL_APP);
} }
} }
......
...@@ -64,10 +64,10 @@ public final class DeveloperModeContentProvider extends ContentProvider { ...@@ -64,10 +64,10 @@ public final class DeveloperModeContentProvider extends ContentProvider {
} }
private void disableDeveloperMode() { private void disableDeveloperMode() {
ComponentName developerModeContentProvider = ComponentName developerModeState =
new ComponentName(getContext(), DeveloperModeContentProvider.class.getName()); new ComponentName(getContext(), DeveloperModeUtils.DEVELOPER_MODE_STATE_COMPONENT);
getContext().getPackageManager().setComponentEnabledSetting(developerModeContentProvider, getContext().getPackageManager().setComponentEnabledSetting(developerModeState,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, PackageManager.DONT_KILL_APP);
// Stop the service explicitly, in case it's running. NOOP if the service is not running. // Stop the service explicitly, in case it's running. NOOP if the service is not running.
getContext().stopService(new Intent(getContext(), DeveloperUiService.class)); getContext().stopService(new Intent(getContext(), DeveloperUiService.class));
......
...@@ -17,6 +17,7 @@ import android.os.Build; ...@@ -17,6 +17,7 @@ import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.os.Process; import android.os.Process;
import org.chromium.android_webview.common.DeveloperModeUtils;
import org.chromium.android_webview.common.services.IDeveloperUiService; import org.chromium.android_webview.common.services.IDeveloperUiService;
import java.util.HashMap; import java.util.HashMap;
...@@ -130,9 +131,9 @@ public final class DeveloperUiService extends Service { ...@@ -130,9 +131,9 @@ public final class DeveloperUiService extends Service {
startService(new Intent(this, DeveloperUiService.class)); startService(new Intent(this, DeveloperUiService.class));
markAsForegroundService(); markAsForegroundService();
ComponentName developerModeContentProvider = ComponentName developerModeState =
new ComponentName(this, DeveloperModeContentProvider.class.getName()); new ComponentName(this, DeveloperModeUtils.DEVELOPER_MODE_STATE_COMPONENT);
getPackageManager().setComponentEnabledSetting(developerModeContentProvider, getPackageManager().setComponentEnabledSetting(developerModeState,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
mDeveloperModeEnabled = true; mDeveloperModeEnabled = true;
...@@ -144,10 +145,10 @@ public final class DeveloperUiService extends Service { ...@@ -144,10 +145,10 @@ public final class DeveloperUiService extends Service {
if (!mDeveloperModeEnabled) return; if (!mDeveloperModeEnabled) return;
mDeveloperModeEnabled = false; mDeveloperModeEnabled = false;
ComponentName developerModeContentProvider = ComponentName developerModeState =
new ComponentName(this, DeveloperModeContentProvider.class.getName()); new ComponentName(this, DeveloperModeUtils.DEVELOPER_MODE_STATE_COMPONENT);
getPackageManager().setComponentEnabledSetting(developerModeContentProvider, getPackageManager().setComponentEnabledSetting(developerModeState,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, PackageManager.DONT_KILL_APP);
// Finally, stop the service explicitly. Do this last to make sure we do the other // Finally, stop the service explicitly. Do this last to make sure we do the other
// necessary cleanup. // necessary cleanup.
......
...@@ -1048,6 +1048,11 @@ ...@@ -1048,6 +1048,11 @@
<data android:scheme="file"/> <data android:scheme="file"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity-alias
android:enabled="false"
android:name="org.chromium.android_webview.devui.DeveloperModeState"
android:process=":webview_apk"
android:targetActivity="org.chromium.android_webview.devui.MainActivity"/>
<activity-alias <activity-alias
android:exported="false" android:exported="false"
android:name="org.chromium.chrome.browser.webapps.SecureWebAppLauncher" android:name="org.chromium.chrome.browser.webapps.SecureWebAppLauncher"
...@@ -1307,7 +1312,6 @@ ...@@ -1307,7 +1312,6 @@
</provider> </provider>
<provider <provider
android:authorities="org.chromium.chrome.DeveloperModeContentProvider" android:authorities="org.chromium.chrome.DeveloperModeContentProvider"
android:enabled="false"
android:exported="true" android:exported="true"
android:name="org.chromium.android_webview.services.DeveloperModeContentProvider" android:name="org.chromium.android_webview.services.DeveloperModeContentProvider"
android:process=":webview_service"/> android:process=":webview_service"/>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment