Bug 1533385 - Make GeckoView read configuration options from filesystem. r=droeh

The configuration file format is YAML and looks like:
```
prefs:
  foo.bar.boolean: true
  foo.bar.string: "string"
  foo.bar.int: 500
env:
  MOZ_LOG: nsHttp:5
args: [--marionette]
```
By default, if the consuming App is debuggable, GeckoView will read
configuration from `/data/local/tmp/$PACKAGE-geckoview-config.yaml` at
startup.

For consumers (including browsers) that want to allow the underlying
GeckoView to be remote controlled in some way, the
`GeckoRuntimeSettings.Builder.configFilePath()` method allows to avoid
the default behaviour depending on the `android:debuggable` flag.  For
example, release versions of Firefox for Android will want to allow
this configuration when appropriate App-level settings are toggled.

The additional configuration is appended after any existing configuration
methods, e.g., after anything specified using Intent argument extras
or existing `GeckoRuntimeSettings.Builder` methods.

Differential Revision: https://phabricator.services.mozilla.com/D25885

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Nick Alexander 2019-04-03 23:51:17 +00:00
Родитель 817c9ec8c9
Коммит b03aa4ed38
7 изменённых файлов: 163 добавлений и 3 удалений

Просмотреть файл

@ -170,6 +170,9 @@
-dontwarn java.lang.management.**
-dontwarn javax.management.**
# Don't warn when classes referenced by, but not used at runtime by, SnakeYAML are missing.
-dontwarn java.beans.**
-include "adjust-keeps.cfg"
-include "leakcanary-keeps.cfg"

Просмотреть файл

@ -211,6 +211,7 @@ package org.mozilla.geckoview {
method @android.support.annotation.NonNull public java.lang.String[] getArguments();
method public boolean getAutomaticFontSizeAdjustment();
method public int getAutoplayDefault();
method @android.support.annotation.Nullable public java.lang.String getConfigFilePath();
method public boolean getConsoleOutputEnabled();
method @android.support.annotation.NonNull public org.mozilla.geckoview.ContentBlocking.Settings getContentBlocking();
method @android.support.annotation.Nullable public java.lang.Class<?> getCrashHandler();
@ -252,6 +253,7 @@ package org.mozilla.geckoview {
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder arguments(@android.support.annotation.NonNull java.lang.String[]);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder automaticFontSizeAdjustment(boolean);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder autoplayDefault(int);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder configFilePath(@android.support.annotation.Nullable java.lang.String);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder consoleOutput(boolean);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder contentBlocking(@android.support.annotation.NonNull org.mozilla.geckoview.ContentBlocking.Settings);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder crashHandler(java.lang.Class<?>);

Просмотреть файл

@ -216,6 +216,7 @@ tasks.withType(Javadoc) {
dependencies {
implementation "com.android.support:support-v4:$support_library_version"
implementation "com.android.support:palette-v7:$support_library_version"
implementation "org.yaml:snakeyaml:1.24"
testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
testImplementation 'junit:junit:4.12'

Просмотреть файл

@ -0,0 +1,90 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.util;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import org.mozilla.gecko.GeckoThread;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DebugConfig {
private static final String LOGTAG = "GeckoDebugConfig";
protected Map<String, Object> prefs;
protected Map<String, String> env;
protected List<String> args;
public static @NonNull DebugConfig fromFile(final @NonNull File configFile) throws FileNotFoundException {
final Constructor constructor = new Constructor(DebugConfig.class);
final TypeDescription description = new TypeDescription(DebugConfig.class);
description.putMapPropertyType("prefs", String.class, Object.class);
description.putMapPropertyType("env", String.class, String.class);
description.putListPropertyType("args", String.class);
final Yaml yaml = new Yaml(constructor);
yaml.addTypeDescription(description);
final FileInputStream fileInputStream = new FileInputStream(configFile);
try {
return yaml.load(fileInputStream);
} finally {
IOUtils.safeStreamClose(fileInputStream);
}
}
public void mergeIntoInitInfo(final @NonNull GeckoThread.InitInfo info) {
if (env != null) {
Log.d(LOGTAG, "Adding environment variables from debug config: " + env);
if (info.extras == null) {
info.extras = new Bundle();
}
int c = 0;
while (info.extras.getString("env" + c) != null) {
c += 1;
}
for (final Map.Entry<String, String> entry : env.entrySet()) {
info.extras.putString("env" + c, entry.getKey() + "=" + entry.getValue());
c += 1;
}
}
if (args != null) {
Log.d(LOGTAG, "Adding arguments from debug config: " + args);
final ArrayList<String> combinedArgs = new ArrayList<>();
combinedArgs.addAll(Arrays.asList(info.args));
combinedArgs.addAll(args);
info.args = combinedArgs.toArray(new String[combinedArgs.size()]);
}
if (prefs != null) {
Log.d(LOGTAG, "Adding prefs from debug config: " + prefs);
final Map<String, Object> combinedPrefs = new HashMap<>();
combinedPrefs.putAll(info.prefs);
combinedPrefs.putAll(prefs);
info.prefs = Collections.unmodifiableMap(prefs);
}
}
}

Просмотреть файл

@ -8,14 +8,15 @@ package org.mozilla.geckoview;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.content.Context;
import android.os.Process;
import android.support.annotation.AnyThread;
import android.support.annotation.NonNull;
@ -25,21 +26,26 @@ import android.util.Log;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoSystemStateListener;
import org.mozilla.gecko.GeckoScreenOrientation;
import org.mozilla.gecko.GeckoSystemStateListener;
import org.mozilla.gecko.GeckoThread;
import org.mozilla.gecko.PrefsHelper;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.DebugConfig;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ThreadUtils;
import org.yaml.snakeyaml.error.YAMLException;
import java.io.File;
import java.io.FileNotFoundException;
public final class GeckoRuntime implements Parcelable {
private static final String LOGTAG = "GeckoRuntime";
private static final boolean DEBUG = false;
private static final String CONFIG_FILE_PATH_TEMPLATE = "/data/local/tmp/%s-geckoview-config.yaml";
/**
* Intent action sent to the crash handler when a crash is encountered.
* @see GeckoRuntimeSettings.Builder#crashHandler(Class)
@ -215,6 +221,28 @@ public final class GeckoRuntime implements Parcelable {
info.flags = flags;
info.prefs = settings.getPrefsMap();
String configFilePath = settings.getConfigFilePath();
if (configFilePath == null) {
// Default to /data/local/tmp/$PACKAGE-geckoview-config.yaml if android:debuggable="true"
// and to not read configuration from a file if android:debuggable="false".
final ApplicationInfo applicationInfo = context.getApplicationInfo();
final boolean isPackageDebuggable = (applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
if (isPackageDebuggable) {
configFilePath = String.format(CONFIG_FILE_PATH_TEMPLATE, applicationInfo.packageName);
}
}
if (configFilePath != null && !configFilePath.isEmpty()) {
try {
final DebugConfig debugConfig = DebugConfig.fromFile(new File(configFilePath));
Log.i(LOGTAG, "Adding debug configuration from: " + configFilePath);
debugConfig.mergeIntoInitInfo(info);
} catch (YAMLException e) {
Log.w(LOGTAG, "Failed to add debug configuration from: " + configFilePath, e);
} catch (FileNotFoundException e) {
}
}
if (!GeckoThread.init(info)) {
Log.w(LOGTAG, "init failed (could not initiate GeckoThread)");
return false;

Просмотреть файл

@ -77,6 +77,19 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
return this;
}
/**
* Path to configuration file from which GeckoView will read configuration options such as
* Gecko process arguments, environment variables, and preferences.
*
* @param configFilePath Configuration file path to read from, or <code>null</code> to use
* default location <code>/data/local/tmp/$PACKAGE-geckoview-config.yaml</code>.
* @return This Builder instance.
*/
public @NonNull Builder configFilePath(final @Nullable String configFilePath) {
getSettings().mConfigFilePath = configFilePath;
return this;
}
/**
* Set whether JavaScript support should be enabled.
*
@ -330,6 +343,7 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
/* package */ boolean mUseContentProcess;
/* package */ String[] mArgs;
/* package */ Bundle mExtras;
/* package */ String mConfigFilePath;
/* package */ ContentBlocking.Settings mContentBlocking;
@ -412,6 +426,7 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
mScreenHeightOverride = settings.mScreenHeightOverride;
mCrashHandler = settings.mCrashHandler;
mRequestedLocales = settings.mRequestedLocales;
mConfigFilePath = settings.mConfigFilePath;
}
/* package */ void commit() {
@ -446,6 +461,18 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
return mExtras;
}
/**
* Path to configuration file from which GeckoView will read configuration options such as
* Gecko process arguments, environment variables, and preferences.
*
* @return Path to configuration file from which GeckoView will read configuration options,
* or <code>null</code> for default location
* <code>/data/local/tmp/$PACKAGE-geckoview-config.yaml</code>.
*/
public @Nullable String getConfigFilePath() {
return mConfigFilePath;
}
/**
* Get whether JavaScript support is enabled.
*
@ -822,6 +849,7 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
out.writeInt(mScreenHeightOverride);
out.writeString(mCrashHandler != null ? mCrashHandler.getName() : null);
out.writeStringArray(mRequestedLocales);
out.writeString(mConfigFilePath);
}
// AIDL code may call readFromParcel even though it's not part of Parcelable.
@ -851,6 +879,7 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
}
mRequestedLocales = source.createStringArray();
mConfigFilePath = source.readString();
}
public static final Parcelable.Creator<GeckoRuntimeSettings> CREATOR

Просмотреть файл

@ -42,6 +42,13 @@ exclude: true
[68.9]: ../GeckoRuntimeSettings.html#setPreferredColorScheme-int-
- Added [`GeckoRuntimeSettings.Builder#configFilePath`][68.10] to set
a path to a configuration file from which GeckoView will read
configuration options such as Gecko process arguments, environment
variables, and preferences.
[68.10]: ../GeckoRuntimeSettings.Builder.html#configFilePath-java.lang.String-
## v67
- Added [`setAutomaticFontSizeAdjustment`][67.2] to
[`GeckoRuntimeSettings`][67.3] for automatically adjusting font size settings
@ -248,4 +255,4 @@ exclude: true
[65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
[65.25]: ../GeckoResult.html
[api-version]: 053d9b4164690ff13996be9e7288dd183e2a1db4
[api-version]: affe9cc5dc22f0700867a1ac7f9b55a033a4b88c