Flutter Engine
The Flutter Engine
FlutterDartProject.mm
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#define FML_USED_ON_EMBEDDER
6
7#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
8
9#import <Metal/Metal.h>
10#import <UIKit/UIKit.h>
11
12#include <syslog.h>
13
14#include "flutter/common/constants.h"
15#include "flutter/shell/common/switches.h"
16#import "flutter/shell/platform/darwin/common/command_line.h"
17
19
20extern "C" {
21#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
22// Used for debugging dart:* sources.
23extern const uint8_t kPlatformStrongDill[];
24extern const intptr_t kPlatformStrongDillSize;
25#endif
26}
27
28static const char* kApplicationKernelSnapshotFileName = "kernel_blob.bin";
29
31 static BOOL result = NO;
32 static dispatch_once_t once_token = 0;
33 dispatch_once(&once_token, ^{
34 id<MTLDevice> device = MTLCreateSystemDefaultDevice();
35 if (@available(iOS 13.0, *)) {
36 // MTLGPUFamilyApple2 = A9/A10
37 result = [device supportsFamily:MTLGPUFamilyApple2];
38 } else {
39 // A9/A10 on iOS 10+
40 result = [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2];
41 }
42 });
43 return result;
44}
45
46flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle, NSProcessInfo* processInfoOrNil) {
47 auto command_line = flutter::CommandLineFromNSProcessInfo(processInfoOrNil);
48
49 // Precedence:
50 // 1. Settings from the specified NSBundle (except for enable-impeller).
51 // 2. Settings passed explicitly via command-line arguments.
52 // 3. Settings from the NSBundle with the default bundle ID.
53 // 4. Settings from the main NSBundle and default values.
54
55 NSBundle* mainBundle = FLTGetApplicationBundle();
56 NSBundle* engineBundle = [NSBundle bundleForClass:[FlutterDartProject class]];
57
58 bool hasExplicitBundle = bundle != nil;
59 if (bundle == nil) {
60 bundle = FLTFrameworkBundleWithIdentifier([FlutterDartProject defaultBundleIdentifier]);
61 }
62
63 auto settings = flutter::SettingsFromCommandLine(command_line);
64
65 settings.task_observer_add = [](intptr_t key, const fml::closure& callback) {
67 };
68
69 settings.task_observer_remove = [](intptr_t key) {
71 };
72
73 settings.log_message_callback = [](const std::string& tag, const std::string& message) {
74 // TODO(cbracken): replace this with os_log-based approach.
75 // https://github.com/flutter/flutter/issues/44030
76 std::stringstream stream;
77 if (!tag.empty()) {
78 stream << tag << ": ";
79 }
80 stream << message;
81 std::string log = stream.str();
82 syslog(LOG_ALERT, "%.*s", (int)log.size(), log.c_str());
83 };
84
85 settings.enable_platform_isolates = true;
86
87 // The command line arguments may not always be complete. If they aren't, attempt to fill in
88 // defaults.
89
90 // Flutter ships the ICU data file in the bundle of the engine. Look for it there.
91 if (settings.icu_data_path.empty()) {
92 NSString* icuDataPath = [engineBundle pathForResource:@"icudtl" ofType:@"dat"];
93 if (icuDataPath.length > 0) {
94 settings.icu_data_path = icuDataPath.UTF8String;
95 }
96 }
97
99 if (hasExplicitBundle) {
100 NSString* executablePath = bundle.executablePath;
101 if ([[NSFileManager defaultManager] fileExistsAtPath:executablePath]) {
102 settings.application_library_path.push_back(executablePath.UTF8String);
103 }
104 }
105
106 // No application bundle specified. Try a known location from the main bundle's Info.plist.
107 if (settings.application_library_path.empty()) {
108 NSString* libraryName = [mainBundle objectForInfoDictionaryKey:@"FLTLibraryPath"];
109 NSString* libraryPath = [mainBundle pathForResource:libraryName ofType:@""];
110 if (libraryPath.length > 0) {
111 NSString* executablePath = [NSBundle bundleWithPath:libraryPath].executablePath;
112 if (executablePath.length > 0) {
113 settings.application_library_path.push_back(executablePath.UTF8String);
114 }
115 }
116 }
117
118 // In case the application bundle is still not specified, look for the App.framework in the
119 // Frameworks directory.
120 if (settings.application_library_path.empty()) {
121 NSString* applicationFrameworkPath = [mainBundle pathForResource:@"Frameworks/App.framework"
122 ofType:@""];
123 if (applicationFrameworkPath.length > 0) {
124 NSString* executablePath =
125 [NSBundle bundleWithPath:applicationFrameworkPath].executablePath;
126 if (executablePath.length > 0) {
127 settings.application_library_path.push_back(executablePath.UTF8String);
128 }
129 }
130 }
131 }
132
133 // Checks to see if the flutter assets directory is already present.
134 if (settings.assets_path.empty()) {
135 NSString* assetsPath = FLTAssetsPathFromBundle(bundle);
136
137 if (assetsPath.length == 0) {
138 NSLog(@"Failed to find assets path for \"%@\"", bundle);
139 } else {
140 settings.assets_path = assetsPath.UTF8String;
141
142 // Check if there is an application kernel snapshot in the assets directory we could
143 // potentially use. Looking for the snapshot makes sense only if we have a VM that can use
144 // it.
146 NSURL* applicationKernelSnapshotURL =
147 [NSURL URLWithString:@(kApplicationKernelSnapshotFileName)
148 relativeToURL:[NSURL fileURLWithPath:assetsPath]];
149 NSError* error;
150 if ([applicationKernelSnapshotURL checkResourceIsReachableAndReturnError:&error]) {
151 settings.application_kernel_asset = applicationKernelSnapshotURL.path.UTF8String;
152 } else {
153 NSLog(@"Failed to find snapshot at %@: %@", applicationKernelSnapshotURL.path, error);
154 }
155 }
156 }
157 }
158
159 // Domain network configuration
160 // Disabled in https://github.com/flutter/flutter/issues/72723.
161 // Re-enable in https://github.com/flutter/flutter/issues/54448.
162 settings.may_insecurely_connect_to_all_domains = true;
163 settings.domain_network_policy = "";
164
165 // Whether to enable wide gamut colors.
166#if TARGET_OS_SIMULATOR
167 // As of Xcode 14.1, the wide gamut surface pixel formats are not supported by
168 // the simulator.
169 settings.enable_wide_gamut = false;
170 // Removes unused function warning.
172#else
173 NSNumber* nsEnableWideGamut = [mainBundle objectForInfoDictionaryKey:@"FLTEnableWideGamut"];
174 BOOL enableWideGamut =
175 (nsEnableWideGamut ? nsEnableWideGamut.boolValue : YES) && DoesHardwareSupportWideGamut();
176 settings.enable_wide_gamut = enableWideGamut;
177#endif
178
179 // TODO(dnfield): We should reverse the order for all these settings so that command line options
180 // are preferred to plist settings. https://github.com/flutter/flutter/issues/124049
181 // Whether to enable Impeller. If the command line explicitly
182 // specified an option for this, ignore what's in the plist.
183 if (!command_line.HasOption("enable-impeller")) {
184 // Next, look in the app bundle.
185 NSNumber* enableImpeller = [bundle objectForInfoDictionaryKey:@"FLTEnableImpeller"];
186 if (enableImpeller == nil) {
187 // If it isn't in the app bundle, look in the main bundle.
188 enableImpeller = [mainBundle objectForInfoDictionaryKey:@"FLTEnableImpeller"];
189 }
190 // Change the default only if the option is present.
191 if (enableImpeller != nil) {
192 settings.enable_impeller = enableImpeller.boolValue;
193 }
194 }
195
196 settings.warn_on_impeller_opt_out = true;
197
198 NSNumber* enableTraceSystrace = [mainBundle objectForInfoDictionaryKey:@"FLTTraceSystrace"];
199 // Change the default only if the option is present.
200 if (enableTraceSystrace != nil) {
201 settings.trace_systrace = enableTraceSystrace.boolValue;
202 }
203
204 NSNumber* enableDartAsserts = [mainBundle objectForInfoDictionaryKey:@"FLTEnableDartAsserts"];
205 if (enableDartAsserts != nil) {
206 settings.dart_flags.push_back("--enable-asserts");
207 }
208
209 NSNumber* enableDartProfiling = [mainBundle objectForInfoDictionaryKey:@"FLTEnableDartProfiling"];
210 // Change the default only if the option is present.
211 if (enableDartProfiling != nil) {
212 settings.enable_dart_profiling = enableDartProfiling.boolValue;
213 }
214
215 // Leak Dart VM settings, set whether leave or clean up the VM after the last shell shuts down.
216 NSNumber* leakDartVM = [mainBundle objectForInfoDictionaryKey:@"FLTLeakDartVM"];
217 // It will change the default leak_vm value in settings only if the key exists.
218 if (leakDartVM != nil) {
219 settings.leak_vm = leakDartVM.boolValue;
220 }
221
222#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
223 // There are no ownership concerns here as all mappings are owned by the
224 // embedder and not the engine.
225 auto make_mapping_callback = [](const uint8_t* mapping, size_t size) {
226 return [mapping, size]() { return std::make_unique<fml::NonOwnedMapping>(mapping, size); };
227 };
228
229 settings.dart_library_sources_kernel =
230 make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize);
231#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
232
233 // If we even support setting this e.g. from the command line or the plist,
234 // we should let the user override it.
235 // Otherwise, we want to set this to a value that will avoid having the OS
236 // kill us. On most iOS devices, that happens somewhere near half
237 // the available memory.
238 // The VM expects this value to be in megabytes.
239 if (settings.old_gen_heap_size <= 0) {
240 settings.old_gen_heap_size = std::round([NSProcessInfo processInfo].physicalMemory * .48 /
242 }
243
244 // This is the formula Android uses.
245 // https://android.googlesource.com/platform/frameworks/base/+/39ae5bac216757bc201490f4c7b8c0f63006c6cd/libs/hwui/renderthread/CacheManager.cpp#45
246 CGFloat scale = [UIScreen mainScreen].scale;
247 CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width * scale;
248 CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height * scale;
249 settings.resource_cache_max_bytes_threshold = screenWidth * screenHeight * 12 * 4;
250
251 // Whether to enable ios embedder api.
252 NSNumber* enable_embedder_api =
253 [mainBundle objectForInfoDictionaryKey:@"FLTEnableIOSEmbedderAPI"];
254 // Change the default only if the option is present.
255 if (enable_embedder_api) {
256 settings.enable_embedder_api = enable_embedder_api.boolValue;
257 }
258
259 return settings;
260}
261
262@implementation FlutterDartProject {
263 flutter::Settings _settings;
264}
265
266// This property is marked unavailable on iOS in the common header.
267// That doesn't seem to be enough to prevent this property from being synthesized.
268// Mark dynamic to avoid warnings.
269@dynamic dartEntrypointArguments;
270
271#pragma mark - Override base class designated initializers
272
273- (instancetype)init {
274 return [self initWithPrecompiledDartBundle:nil];
275}
276
277#pragma mark - Designated initializers
278
279- (instancetype)initWithPrecompiledDartBundle:(nullable NSBundle*)bundle {
280 self = [super init];
281
282 if (self) {
283 _settings = FLTDefaultSettingsForBundle(bundle);
284 }
285
286 return self;
287}
288
289- (instancetype)initWithSettings:(const flutter::Settings&)settings {
290 self = [self initWithPrecompiledDartBundle:nil];
291
292 if (self) {
293 _settings = settings;
294 }
295
296 return self;
297}
298
299#pragma mark - PlatformData accessors
300
301- (const flutter::PlatformData)defaultPlatformData {
302 flutter::PlatformData PlatformData;
303 PlatformData.lifecycle_state = std::string("AppLifecycleState.detached");
304 return PlatformData;
305}
306
307#pragma mark - Settings accessors
308
309- (const flutter::Settings&)settings {
310 return _settings;
311}
312
313- (flutter::RunConfiguration)runConfiguration {
314 return [self runConfigurationForEntrypoint:nil];
315}
316
317- (flutter::RunConfiguration)runConfigurationForEntrypoint:(nullable NSString*)entrypointOrNil {
318 return [self runConfigurationForEntrypoint:entrypointOrNil libraryOrNil:nil];
319}
320
321- (flutter::RunConfiguration)runConfigurationForEntrypoint:(nullable NSString*)entrypointOrNil
322 libraryOrNil:(nullable NSString*)dartLibraryOrNil {
323 return [self runConfigurationForEntrypoint:entrypointOrNil
324 libraryOrNil:dartLibraryOrNil
325 entrypointArgs:nil];
326}
327
328- (flutter::RunConfiguration)runConfigurationForEntrypoint:(nullable NSString*)entrypointOrNil
329 libraryOrNil:(nullable NSString*)dartLibraryOrNil
330 entrypointArgs:
331 (nullable NSArray<NSString*>*)entrypointArgs {
332 auto config = flutter::RunConfiguration::InferFromSettings(_settings);
333 if (dartLibraryOrNil && entrypointOrNil) {
334 config.SetEntrypointAndLibrary(std::string([entrypointOrNil UTF8String]),
335 std::string([dartLibraryOrNil UTF8String]));
336
337 } else if (entrypointOrNil) {
338 config.SetEntrypoint(std::string([entrypointOrNil UTF8String]));
339 }
340
341 if (entrypointArgs.count) {
342 std::vector<std::string> cppEntrypointArgs;
343 for (NSString* arg in entrypointArgs) {
344 cppEntrypointArgs.push_back(std::string([arg UTF8String]));
345 }
346 config.SetEntrypointArgs(std::move(cppEntrypointArgs));
347 }
348
349 return config;
350}
351
352#pragma mark - Assets-related utilities
353
354+ (NSString*)flutterAssetsName:(NSBundle*)bundle {
355 if (bundle == nil) {
356 bundle = FLTFrameworkBundleWithIdentifier([FlutterDartProject defaultBundleIdentifier]);
357 }
358 return FLTAssetPath(bundle);
359}
360
361+ (NSString*)domainNetworkPolicy:(NSDictionary*)appTransportSecurity {
362 // https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsexceptiondomains
363 NSDictionary* exceptionDomains = appTransportSecurity[@"NSExceptionDomains"];
364 if (exceptionDomains == nil) {
365 return @"";
366 }
367 NSMutableArray* networkConfigArray = [[NSMutableArray alloc] init];
368 for (NSString* domain in exceptionDomains) {
369 NSDictionary* domainConfiguration = exceptionDomains[domain];
370 // Default value is false.
371 bool includesSubDomains = [domainConfiguration[@"NSIncludesSubdomains"] boolValue];
372 bool allowsCleartextCommunication =
373 [domainConfiguration[@"NSExceptionAllowsInsecureHTTPLoads"] boolValue];
374 [networkConfigArray addObject:@[
375 domain, includesSubDomains ? @YES : @NO, allowsCleartextCommunication ? @YES : @NO
376 ]];
377 }
378 NSData* jsonData = [NSJSONSerialization dataWithJSONObject:networkConfigArray
379 options:0
380 error:NULL];
381 return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
382}
383
384+ (bool)allowsArbitraryLoads:(NSDictionary*)appTransportSecurity {
385 return [appTransportSecurity[@"NSAllowsArbitraryLoads"] boolValue];
386}
387
388+ (NSString*)lookupKeyForAsset:(NSString*)asset {
389 return [self lookupKeyForAsset:asset fromBundle:nil];
390}
391
392+ (NSString*)lookupKeyForAsset:(NSString*)asset fromBundle:(nullable NSBundle*)bundle {
393 NSString* flutterAssetsName = [FlutterDartProject flutterAssetsName:bundle];
394 return [NSString stringWithFormat:@"%@/%@", flutterAssetsName, asset];
395}
396
397+ (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
398 return [self lookupKeyForAsset:asset fromPackage:package fromBundle:nil];
399}
400
401+ (NSString*)lookupKeyForAsset:(NSString*)asset
402 fromPackage:(NSString*)package
403 fromBundle:(nullable NSBundle*)bundle {
404 return [self lookupKeyForAsset:[NSString stringWithFormat:@"packages/%@/%@", package, asset]
405 fromBundle:bundle];
406}
407
408+ (NSString*)defaultBundleIdentifier {
409 return @"io.flutter.flutter.app";
410}
411
412- (BOOL)isWideGamutEnabled {
413 return _settings.enable_wide_gamut;
414}
415
416- (BOOL)isImpellerEnabled {
417 return _settings.enable_impeller;
418}
419
420@end
NSBundle * FLTFrameworkBundleWithIdentifier(NSString *flutterFrameworkBundleID)
NSString * FLTAssetsPathFromBundle(NSBundle *bundle)
NSBundle * FLTGetApplicationBundle()
NSString * FLTAssetPath(NSBundle *bundle)
static void round(SkPoint *p)
static bool IsRunningPrecompiledCode()
Checks if VM instances in the process can run precompiled code. This call can be made at any time and...
Definition: dart_vm.cc:205
static RunConfiguration InferFromSettings(const Settings &settings, const fml::RefPtr< fml::TaskRunner > &io_worker=nullptr, IsolateLaunchType launch_type=IsolateLaunchType::kNewGroup)
Attempts to infer a run configuration from the settings object. This tries to create a run configurat...
void RemoveTaskObserver(intptr_t key)
Definition: message_loop.cc:68
void AddTaskObserver(intptr_t key, const fml::closure &callback)
Definition: message_loop.cc:64
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
Definition: message_loop.cc:19
VkDevice device
Definition: main.cc:53
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
NSString * flutterAssetsName:(NSBundle *bundle)
static const char * kApplicationKernelSnapshotFileName
const intptr_t kPlatformStrongDillSize
FLUTTER_ASSERT_ARC const uint8_t kPlatformStrongDill[]
flutter::Settings FLTDefaultSettingsForBundle(NSBundle *bundle, NSProcessInfo *processInfoOrNil)
static BOOL DoesHardwareSupportWideGamut()
Win32Message message
static bool init()
Settings SettingsFromCommandLine(const fml::CommandLine &command_line)
Definition: switches.cc:228
fml::CommandLine CommandLineFromNSProcessInfo(NSProcessInfo *processInfoOrNil=nil)
Definition: command_line.mm:11
constexpr double kMegaByteSizeInBytes
Definition: constants.h:9
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network 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 old gen heap size
Definition: switches.h:259
std::function< void()> closure
Definition: closure.h:14
const Scalar scale
std::string lifecycle_state
Definition: platform_data.h:44
int BOOL
Definition: windows_types.h:37