Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,8 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/Flutte
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/ApplicationInfoLoader.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterApplicationInfo.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/ResourceExtractor.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorView.java
Expand Down
6 changes: 4 additions & 2 deletions common/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,10 @@ struct Settings {
bool enable_dart_profiling = false;
bool disable_dart_asserts = false;

// Used to signal the embedder whether HTTP connections are disabled.
bool disable_http = false;
// Whether embedder only allows secure connections.
bool may_insecurely_connect_to_all_domains = true;
// JSON-formatted domain network policy.
std::string domain_network_policy;

// Used as the script URI in debug messages. Does not affect how the Dart code
// is executed.
Expand Down
30 changes: 19 additions & 11 deletions lib/io/dart_io.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,27 @@ using tonic::ToDart;

namespace flutter {

void DartIO::InitForIsolate(bool disable_http) {
Dart_Handle result = Dart_SetNativeResolver(
Dart_LookupLibrary(ToDart("dart:io")), dart::bin::LookupIONative,
dart::bin::LookupIONativeSymbol);
void DartIO::InitForIsolate(bool may_insecurely_connect_to_all_domains,
std::string domain_network_policy) {
Dart_Handle io_lib = Dart_LookupLibrary(ToDart("dart:io"));
Dart_Handle result = Dart_SetNativeResolver(io_lib, dart::bin::LookupIONative,
dart::bin::LookupIONativeSymbol);
FML_CHECK(!LogIfError(result));

// The SDK expects this field to represent "allow http" so we switch the
// value.
Dart_Handle allow_http_value = disable_http ? Dart_False() : Dart_True();
Dart_Handle set_field_result =
Dart_SetField(Dart_LookupLibrary(ToDart("dart:_http")),
ToDart("_embedderAllowsHttp"), allow_http_value);
FML_CHECK(!LogIfError(set_field_result));
Dart_Handle embedder_config_type =
Dart_GetType(io_lib, ToDart("_EmbedderConfig"), 0, nullptr);
FML_CHECK(!LogIfError(embedder_config_type));

Dart_Handle allow_insecure_connections_result = Dart_SetField(
embedder_config_type, ToDart("_mayInsecurelyConnectToAllDomains"),
ToDart(may_insecurely_connect_to_all_domains));
FML_CHECK(!LogIfError(allow_insecure_connections_result));

Dart_Handle dart_args[1];
dart_args[0] = ToDart(domain_network_policy);
Dart_Handle set_domain_network_policy_result = Dart_Invoke(
embedder_config_type, ToDart("_setDomainPolicies"), 1, dart_args);
FML_CHECK(!LogIfError(set_domain_network_policy_result));
}

} // namespace flutter
4 changes: 3 additions & 1 deletion lib/io/dart_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
#define FLUTTER_LIB_IO_DART_IO_H_

#include <cstdint>
#include <string>

#include "flutter/fml/macros.h"

namespace flutter {

class DartIO {
public:
static void InitForIsolate(bool disable_http);
static void InitForIsolate(bool may_insecurely_connect_to_all_domains,
std::string domain_network_policy);

private:
FML_DISALLOW_IMPLICIT_CONSTRUCTORS(DartIO);
Expand Down
7 changes: 5 additions & 2 deletions runtime/dart_isolate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,9 @@ DartIsolate::DartIsolate(const Settings& settings,
settings.unhandled_exception_callback,
DartVMRef::GetIsolateNameServer(),
is_root_isolate),
disable_http_(settings.disable_http) {
may_insecurely_connect_to_all_domains_(
settings.may_insecurely_connect_to_all_domains),
domain_network_policy_(settings.domain_network_policy) {
phase_ = Phase::Uninitialized;
}

Expand Down Expand Up @@ -263,7 +265,8 @@ bool DartIsolate::LoadLibraries() {

tonic::DartState::Scope scope(this);

DartIO::InitForIsolate(disable_http_);
DartIO::InitForIsolate(may_insecurely_connect_to_all_domains_,
domain_network_policy_);

DartUI::InitForIsolate();

Expand Down
3 changes: 2 additions & 1 deletion runtime/dart_isolate.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,8 @@ class DartIsolate : public UIDartState {
std::vector<std::shared_ptr<const fml::Mapping>> kernel_buffers_;
std::vector<std::unique_ptr<AutoFireClosure>> shutdown_callbacks_;
fml::RefPtr<fml::TaskRunner> message_handling_task_runner_;
const bool disable_http_;
const bool may_insecurely_connect_to_all_domains_;
std::string domain_network_policy_;

DartIsolate(const Settings& settings,
TaskRunners task_runners,
Expand Down
7 changes: 5 additions & 2 deletions shell/common/switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,11 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
}
}

settings.disable_http =
command_line.HasOption(FlagForSwitch(Switch::DisableHttp));
settings.may_insecurely_connect_to_all_domains = !command_line.HasOption(
FlagForSwitch(Switch::DisallowInsecureConnections));

command_line.GetOptionValue(FlagForSwitch(Switch::DomainNetworkPolicy),
&settings.domain_network_policy);

// Disable need for authentication codes for VM service communication, if
// specified.
Expand Down
15 changes: 9 additions & 6 deletions shell/common/switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,15 @@ DEF_SWITCH(DisableDartAsserts,
"disabled. This flag may be specified if the user wishes to run "
"with assertions disabled in the debug product mode (i.e. with JIT "
"or DBC).")
DEF_SWITCH(DisableHttp,
"disable-http",
"Dart VM has a master switch that can be set to disable insecure "
"HTTP and WebSocket protocols. Localhost or loopback addresses are "
"exempted. This flag can be specified if the embedder wants this "
"for a particular platform.")
DEF_SWITCH(DisallowInsecureConnections,
"disallow-insecure-connections",
"By default, dart:io allows all socket connections. If this switch "
"is set, all insecure connections are rejected.")
DEF_SWITCH(DomainNetworkPolicy,
"domain-network-policy",
"JSON encoded network policy per domain. This overrides the "
"DisallowInsecureConnections switch. Embedder can specify whether "
"to allow or disallow insecure connections at a domain level.")
DEF_SWITCH(
ForceMultithreading,
"force-multithreading",
Expand Down
3 changes: 3 additions & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ android_java_sources = [
"io/flutter/embedding/engine/dart/DartExecutor.java",
"io/flutter/embedding/engine/dart/DartMessenger.java",
"io/flutter/embedding/engine/dart/PlatformMessageHandler.java",
"io/flutter/embedding/engine/loader/ApplicationInfoLoader.java",
"io/flutter/embedding/engine/loader/FlutterApplicationInfo.java",
"io/flutter/embedding/engine/loader/FlutterLoader.java",
"io/flutter/embedding/engine/loader/ResourceExtractor.java",
"io/flutter/embedding/engine/mutatorsstack/FlutterMutatorView.java",
Expand Down Expand Up @@ -430,6 +432,7 @@ action("robolectric_tests") {
"test/io/flutter/embedding/engine/PluginComponentTest.java",
"test/io/flutter/embedding/engine/RenderingComponentTest.java",
"test/io/flutter/embedding/engine/dart/DartExecutorTest.java",
"test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java",
"test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java",
"test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java",
"test/io/flutter/embedding/engine/systemchannels/RestorationChannelTest.java",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.embedding.engine.loader;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.security.NetworkSecurityPolicy;
import androidx.annotation.NonNull;
import java.io.IOException;
import org.json.JSONArray;
import org.xmlpull.v1.XmlPullParserException;

/** Loads application information given a Context. */
final class ApplicationInfoLoader {
// XML Attribute keys supported in AndroidManifest.xml
static final String PUBLIC_AOT_SHARED_LIBRARY_NAME =
FlutterLoader.class.getName() + '.' + FlutterLoader.AOT_SHARED_LIBRARY_NAME;
static final String PUBLIC_VM_SNAPSHOT_DATA_KEY =
FlutterLoader.class.getName() + '.' + FlutterLoader.VM_SNAPSHOT_DATA_KEY;
static final String PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY =
FlutterLoader.class.getName() + '.' + FlutterLoader.ISOLATE_SNAPSHOT_DATA_KEY;
static final String PUBLIC_FLUTTER_ASSETS_DIR_KEY =
FlutterLoader.class.getName() + '.' + FlutterLoader.FLUTTER_ASSETS_DIR_KEY;
static final String NETWORK_POLICY_METADATA_KEY = "io.flutter.network-policy";

@NonNull
private static ApplicationInfo getApplicationInfo(@NonNull Context applicationContext) {
try {
return applicationContext
.getPackageManager()
.getApplicationInfo(applicationContext.getPackageName(), PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
}

private static String getString(Bundle metadata, String key) {
if (metadata == null) {
return null;
}
return metadata.getString(key, null);
}

private static String getNetworkPolicy(ApplicationInfo appInfo, Context context) {
// We cannot use reflection to look at networkSecurityConfigRes because
// Android throws an error when we try to access fields marked as @hide.
// Instead we rely on metadata.
Bundle metadata = appInfo.metaData;
if (metadata == null) {
return null;
}

int networkSecurityConfigRes = metadata.getInt(NETWORK_POLICY_METADATA_KEY, 0);
if (networkSecurityConfigRes <= 0) {
return null;
}

JSONArray output = new JSONArray();
try {
XmlResourceParser xrp = context.getResources().getXml(networkSecurityConfigRes);
xrp.next();
int eventType = xrp.getEventType();
while (eventType != XmlResourceParser.END_DOCUMENT) {
if (eventType == XmlResourceParser.START_TAG) {
if (xrp.getName().equals("domain-config")) {
parseDomainConfig(xrp, output, false);
}
}
eventType = xrp.next();
}
} catch (IOException | XmlPullParserException e) {
return null;
}
return output.toString();
}

private static boolean getUseEmbeddedView(ApplicationInfo appInfo) {
Bundle bundle = appInfo.metaData;
return bundle != null && bundle.getBoolean("io.flutter.embedded_views_preview");
}

private static void parseDomainConfig(
XmlResourceParser xrp, JSONArray output, boolean inheritedCleartextPermitted)
throws IOException, XmlPullParserException {
boolean cleartextTrafficPermitted =
xrp.getAttributeBooleanValue(
null, "cleartextTrafficPermitted", inheritedCleartextPermitted);
while (true) {
int eventType = xrp.next();
if (eventType == XmlResourceParser.START_TAG) {
if (xrp.getName().equals("domain")) {
// There can be multiple domains.
parseDomain(xrp, output, cleartextTrafficPermitted);
} else if (xrp.getName().equals("domain-config")) {
parseDomainConfig(xrp, output, cleartextTrafficPermitted);
} else {
skipTag(xrp);
}
} else if (eventType == XmlResourceParser.END_TAG) {
break;
}
}
}

private static void skipTag(XmlResourceParser xrp) throws IOException, XmlPullParserException {
String name = xrp.getName();
int eventType = xrp.getEventType();
while (eventType != XmlResourceParser.END_TAG || xrp.getName() != name) {
eventType = xrp.next();
}
}

private static void parseDomain(
XmlResourceParser xrp, JSONArray output, boolean cleartextPermitted)
throws IOException, XmlPullParserException {
boolean includeSubDomains = xrp.getAttributeBooleanValue(null, "includeSubdomains", false);
xrp.next();
if (xrp.getEventType() != XmlResourceParser.TEXT) {
throw new IllegalStateException("Expected text");
}
String domain = xrp.getText().trim();
JSONArray outputArray = new JSONArray();
outputArray.put(domain);
outputArray.put(includeSubDomains);
outputArray.put(cleartextPermitted);
output.put(outputArray);
xrp.next();
if (xrp.getEventType() != XmlResourceParser.END_TAG) {
throw new IllegalStateException("Expected end of domain tag");
}
}

/**
* Initialize our Flutter config values by obtaining them from the manifest XML file, falling back
* to default values.
*/
@NonNull
public static FlutterApplicationInfo load(@NonNull Context applicationContext) {
ApplicationInfo appInfo = getApplicationInfo(applicationContext);
// Prior to API 23, cleartext traffic is allowed.
boolean clearTextPermitted = true;
if (android.os.Build.VERSION.SDK_INT >= 23) {
clearTextPermitted = NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted();
}

return new FlutterApplicationInfo(
getString(appInfo.metaData, PUBLIC_AOT_SHARED_LIBRARY_NAME),
getString(appInfo.metaData, PUBLIC_VM_SNAPSHOT_DATA_KEY),
getString(appInfo.metaData, PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY),
getString(appInfo.metaData, PUBLIC_FLUTTER_ASSETS_DIR_KEY),
getNetworkPolicy(appInfo, applicationContext),
appInfo.nativeLibraryDir,
clearTextPermitted,
getUseEmbeddedView(appInfo));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.embedding.engine.loader;

/** Encapsulates all the information that Flutter needs from application manifest. */
public final class FlutterApplicationInfo {
private static final String DEFAULT_AOT_SHARED_LIBRARY_NAME = "libapp.so";
private static final String DEFAULT_VM_SNAPSHOT_DATA = "vm_snapshot_data";
private static final String DEFAULT_ISOLATE_SNAPSHOT_DATA = "isolate_snapshot_data";
private static final String DEFAULT_FLUTTER_ASSETS_DIR = "flutter_assets";

final String aotSharedLibraryName;
final String vmSnapshotData;
final String isolateSnapshotData;
final String flutterAssetsDir;
final String domainNetworkPolicy;
final String nativeLibraryDir;
final boolean clearTextPermitted;
// TODO(cyanlaz): Remove this when dynamic thread merging is done.
// https://github.com/flutter/flutter/issues/59930
final boolean useEmbeddedView;

public FlutterApplicationInfo(
String aotSharedLibraryName,
String vmSnapshotData,
String isolateSnapshotData,
String flutterAssetsDir,
String domainNetworkPolicy,
String nativeLibraryDir,
boolean clearTextPermitted,
boolean useEmbeddedView) {
this.aotSharedLibraryName =
aotSharedLibraryName == null ? DEFAULT_AOT_SHARED_LIBRARY_NAME : aotSharedLibraryName;
this.vmSnapshotData = vmSnapshotData == null ? DEFAULT_VM_SNAPSHOT_DATA : vmSnapshotData;
this.isolateSnapshotData =
isolateSnapshotData == null ? DEFAULT_ISOLATE_SNAPSHOT_DATA : isolateSnapshotData;
this.flutterAssetsDir =
flutterAssetsDir == null ? DEFAULT_FLUTTER_ASSETS_DIR : flutterAssetsDir;
this.nativeLibraryDir = nativeLibraryDir;
this.domainNetworkPolicy = domainNetworkPolicy == null ? "" : domainNetworkPolicy;
this.clearTextPermitted = clearTextPermitted;
this.useEmbeddedView = useEmbeddedView;
}
}
Loading