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 #include <syslog.h>
10 
11 #include <sstream>
12 #include <string>
13 
14 #include "flutter/common/constants.h"
15 #include "flutter/common/task_runners.h"
16 #include "flutter/fml/mapping.h"
17 #include "flutter/fml/message_loop.h"
18 #include "flutter/fml/platform/darwin/scoped_nsobject.h"
19 #include "flutter/runtime/dart_vm.h"
20 #include "flutter/shell/common/shell.h"
21 #include "flutter/shell/common/switches.h"
22 #import "flutter/shell/platform/darwin/common/command_line.h"
23 #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
24 
25 extern "C" {
26 #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
27 // Used for debugging dart:* sources.
28 extern const uint8_t kPlatformStrongDill[];
29 extern const intptr_t kPlatformStrongDillSize;
30 #endif
31 }
32 
33 static const char* kApplicationKernelSnapshotFileName = "kernel_blob.bin";
34 
36  auto command_line = flutter::CommandLineFromNSProcessInfo();
37 
38  // Precedence:
39  // 1. Settings from the specified NSBundle.
40  // 2. Settings passed explicitly via command-line arguments.
41  // 3. Settings from the NSBundle with the default bundle ID.
42  // 4. Settings from the main NSBundle and default values.
43 
44  NSBundle* mainBundle = [NSBundle mainBundle];
45  NSBundle* engineBundle = [NSBundle bundleForClass:[FlutterViewController class]];
46 
47  bool hasExplicitBundle = bundle != nil;
48  if (bundle == nil) {
49  bundle = [NSBundle bundleWithIdentifier:[FlutterDartProject defaultBundleIdentifier]];
50  }
51  if (bundle == nil) {
52  bundle = mainBundle;
53  }
54 
55  auto settings = flutter::SettingsFromCommandLine(command_line);
56 
57  settings.task_observer_add = [](intptr_t key, fml::closure callback) {
59  };
60 
61  settings.task_observer_remove = [](intptr_t key) {
63  };
64 
65  settings.log_message_callback = [](const std::string& tag, const std::string& message) {
66  // TODO(cbracken): replace this with os_log-based approach.
67  // https://github.com/flutter/flutter/issues/44030
68  std::stringstream stream;
69  if (tag.size() > 0) {
70  stream << tag << ": ";
71  }
72  stream << message;
73  std::string log = stream.str();
74  syslog(LOG_ALERT, "%.*s", (int)log.size(), log.c_str());
75  };
76 
77  // The command line arguments may not always be complete. If they aren't, attempt to fill in
78  // defaults.
79 
80  // Flutter ships the ICU data file in the bundle of the engine. Look for it there.
81  if (settings.icu_data_path.size() == 0) {
82  NSString* icuDataPath = [engineBundle pathForResource:@"icudtl" ofType:@"dat"];
83  if (icuDataPath.length > 0) {
84  settings.icu_data_path = icuDataPath.UTF8String;
85  }
86  }
87 
89  if (hasExplicitBundle) {
90  NSString* executablePath = bundle.executablePath;
91  if ([[NSFileManager defaultManager] fileExistsAtPath:executablePath]) {
92  settings.application_library_path.push_back(executablePath.UTF8String);
93  }
94  }
95 
96  // No application bundle specified. Try a known location from the main bundle's Info.plist.
97  if (settings.application_library_path.size() == 0) {
98  NSString* libraryName = [mainBundle objectForInfoDictionaryKey:@"FLTLibraryPath"];
99  NSString* libraryPath = [mainBundle pathForResource:libraryName ofType:@""];
100  if (libraryPath.length > 0) {
101  NSString* executablePath = [NSBundle bundleWithPath:libraryPath].executablePath;
102  if (executablePath.length > 0) {
103  settings.application_library_path.push_back(executablePath.UTF8String);
104  }
105  }
106  }
107 
108  // In case the application bundle is still not specified, look for the App.framework in the
109  // Frameworks directory.
110  if (settings.application_library_path.size() == 0) {
111  NSString* applicationFrameworkPath = [mainBundle pathForResource:@"Frameworks/App.framework"
112  ofType:@""];
113  if (applicationFrameworkPath.length > 0) {
114  NSString* executablePath =
115  [NSBundle bundleWithPath:applicationFrameworkPath].executablePath;
116  if (executablePath.length > 0) {
117  settings.application_library_path.push_back(executablePath.UTF8String);
118  }
119  }
120  }
121  }
122 
123  // Checks to see if the flutter assets directory is already present.
124  if (settings.assets_path.size() == 0) {
125  NSString* assetsName = [FlutterDartProject flutterAssetsName:bundle];
126  NSString* assetsPath = [bundle pathForResource:assetsName ofType:@""];
127 
128  if (assetsPath.length == 0) {
129  assetsPath = [mainBundle pathForResource:assetsName ofType:@""];
130  }
131 
132  if (assetsPath.length == 0) {
133  NSLog(@"Failed to find assets path for \"%@\"", assetsName);
134  } else {
135  settings.assets_path = assetsPath.UTF8String;
136 
137  // Check if there is an application kernel snapshot in the assets directory we could
138  // potentially use. Looking for the snapshot makes sense only if we have a VM that can use
139  // it.
141  NSURL* applicationKernelSnapshotURL =
142  [NSURL URLWithString:@(kApplicationKernelSnapshotFileName)
143  relativeToURL:[NSURL fileURLWithPath:assetsPath]];
144  if ([[NSFileManager defaultManager] fileExistsAtPath:applicationKernelSnapshotURL.path]) {
145  settings.application_kernel_asset = applicationKernelSnapshotURL.path.UTF8String;
146  } else {
147  NSLog(@"Failed to find snapshot: %@", applicationKernelSnapshotURL.path);
148  }
149  }
150  }
151  }
152 
153  // Domain network configuration
154  // Disabled in https://github.com/flutter/flutter/issues/72723.
155  // Re-enable in https://github.com/flutter/flutter/issues/54448.
156  settings.may_insecurely_connect_to_all_domains = true;
157  settings.domain_network_policy = "";
158 
159  // SkParagraph text layout library
160  NSNumber* enableSkParagraph = [mainBundle objectForInfoDictionaryKey:@"FLTEnableSkParagraph"];
161  settings.enable_skparagraph = (enableSkParagraph != nil) ? enableSkParagraph.boolValue : false;
162 
163 #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
164  // There are no ownership concerns here as all mappings are owned by the
165  // embedder and not the engine.
166  auto make_mapping_callback = [](const uint8_t* mapping, size_t size) {
167  return [mapping, size]() { return std::make_unique<fml::NonOwnedMapping>(mapping, size); };
168  };
169 
170  settings.dart_library_sources_kernel =
171  make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize);
172 #endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
173 
174  // If we even support setting this e.g. from the command line or the plist,
175  // we should let the user override it.
176  // Otherwise, we want to set this to a value that will avoid having the OS
177  // kill us. On most iOS devices, that happens somewhere near half
178  // the available memory.
179  // The VM expects this value to be in megabytes.
180  if (settings.old_gen_heap_size <= 0) {
181  settings.old_gen_heap_size = std::round([NSProcessInfo processInfo].physicalMemory * .48 /
183  }
184  return settings;
185 }
186 
187 @implementation FlutterDartProject {
188  flutter::Settings _settings;
189 }
190 
191 #pragma mark - Override base class designated initializers
192 
193 - (instancetype)init {
194  return [self initWithPrecompiledDartBundle:nil];
195 }
196 
197 #pragma mark - Designated initializers
198 
199 - (instancetype)initWithPrecompiledDartBundle:(nullable NSBundle*)bundle {
200  self = [super init];
201 
202  if (self) {
203  _settings = FLTDefaultSettingsForBundle(bundle);
204  }
205 
206  return self;
207 }
208 
209 - (instancetype)initWithSettings:(const flutter::Settings&)settings {
210  self = [self initWithPrecompiledDartBundle:nil];
211 
212  if (self) {
213  _settings = settings;
214  }
215 
216  return self;
217 }
218 
219 #pragma mark - PlatformData accessors
220 
222  flutter::PlatformData PlatformData;
223  PlatformData.lifecycle_state = std::string("AppLifecycleState.detached");
224  return PlatformData;
225 }
226 
227 #pragma mark - Settings accessors
228 
229 - (const flutter::Settings&)settings {
230  return _settings;
231 }
232 
234  return [self runConfigurationForEntrypoint:nil];
235 }
236 
237 - (flutter::RunConfiguration)runConfigurationForEntrypoint:(nullable NSString*)entrypointOrNil {
238  return [self runConfigurationForEntrypoint:entrypointOrNil libraryOrNil:nil];
239 }
240 
241 - (flutter::RunConfiguration)runConfigurationForEntrypoint:(nullable NSString*)entrypointOrNil
242  libraryOrNil:(nullable NSString*)dartLibraryOrNil {
243  auto config = flutter::RunConfiguration::InferFromSettings(_settings);
244  if (dartLibraryOrNil && entrypointOrNil) {
245  config.SetEntrypointAndLibrary(std::string([entrypointOrNil UTF8String]),
246  std::string([dartLibraryOrNil UTF8String]));
247 
248  } else if (entrypointOrNil) {
249  config.SetEntrypoint(std::string([entrypointOrNil UTF8String]));
250  }
251  return config;
252 }
253 
254 #pragma mark - Assets-related utilities
255 
256 + (NSString*)flutterAssetsName:(NSBundle*)bundle {
257  if (bundle == nil) {
258  bundle = [NSBundle bundleWithIdentifier:[FlutterDartProject defaultBundleIdentifier]];
259  }
260  if (bundle == nil) {
261  bundle = [NSBundle mainBundle];
262  }
263  NSString* flutterAssetsName = [bundle objectForInfoDictionaryKey:@"FLTAssetsPath"];
264  if (flutterAssetsName == nil) {
265  flutterAssetsName = @"Frameworks/App.framework/flutter_assets";
266  }
267  return flutterAssetsName;
268 }
269 
270 + (NSString*)domainNetworkPolicy:(NSDictionary*)appTransportSecurity {
271  // https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsexceptiondomains
272  NSDictionary* exceptionDomains = [appTransportSecurity objectForKey:@"NSExceptionDomains"];
273  if (exceptionDomains == nil) {
274  return @"";
275  }
276  NSMutableArray* networkConfigArray = [[NSMutableArray alloc] init];
277  for (NSString* domain in exceptionDomains) {
278  NSDictionary* domainConfiguration = [exceptionDomains objectForKey:domain];
279  // Default value is false.
280  bool includesSubDomains =
281  [[domainConfiguration objectForKey:@"NSIncludesSubdomains"] boolValue];
282  bool allowsCleartextCommunication =
283  [[domainConfiguration objectForKey:@"NSExceptionAllowsInsecureHTTPLoads"] boolValue];
284  [networkConfigArray addObject:@[
285  domain, includesSubDomains ? @YES : @NO, allowsCleartextCommunication ? @YES : @NO
286  ]];
287  }
288  NSData* jsonData = [NSJSONSerialization dataWithJSONObject:networkConfigArray
289  options:0
290  error:NULL];
291  return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
292 }
293 
294 + (bool)allowsArbitraryLoads:(NSDictionary*)appTransportSecurity {
295  return [[appTransportSecurity objectForKey:@"NSAllowsArbitraryLoads"] boolValue];
296 }
297 
298 + (NSString*)lookupKeyForAsset:(NSString*)asset {
299  return [self lookupKeyForAsset:asset fromBundle:nil];
300 }
301 
302 + (NSString*)lookupKeyForAsset:(NSString*)asset fromBundle:(nullable NSBundle*)bundle {
303  NSString* flutterAssetsName = [FlutterDartProject flutterAssetsName:bundle];
304  return [NSString stringWithFormat:@"%@/%@", flutterAssetsName, asset];
305 }
306 
307 + (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
308  return [self lookupKeyForAsset:asset fromPackage:package fromBundle:nil];
309 }
310 
311 + (NSString*)lookupKeyForAsset:(NSString*)asset
312  fromPackage:(NSString*)package
313  fromBundle:(nullable NSBundle*)bundle {
314  return [self lookupKeyForAsset:[NSString stringWithFormat:@"packages/%@/%@", package, asset]
315  fromBundle:bundle];
316 }
317 
319  return @"io.flutter.flutter.app";
320 }
321 
322 #pragma mark - Settings utilities
323 
324 - (void)setPersistentIsolateData:(NSData*)data {
325  if (data == nil) {
326  return;
327  }
328 
329  NSData* persistent_isolate_data = [data copy];
330  fml::NonOwnedMapping::ReleaseProc data_release_proc = [persistent_isolate_data](auto, auto) {
331  [persistent_isolate_data release];
332  };
333  _settings.persistent_isolate_data = std::make_shared<fml::NonOwnedMapping>(
334  static_cast<const uint8_t*>(persistent_isolate_data.bytes), // bytes
335  persistent_isolate_data.length, // byte length
336  data_release_proc // release proc
337  );
338 }
339 
340 #pragma mark - PlatformData utilities
341 
342 @end
flutter::RunConfiguration runConfigurationForEntrypoint:libraryOrNil:(nullable NSString *entrypointOrNil, [libraryOrNil] nullable NSString *dartLibraryOrNil)
constexpr double kMegaByteSizeInBytes
Definition: constants.h:6
void AddTaskObserver(intptr_t key, const fml::closure &callback)
Definition: message_loop.cc:64
std::function< void(const uint8_t *data, size_t size)> ReleaseProc
Definition: mapping.h:107
flutter::Settings FLTDefaultSettingsForBundle(NSBundle *bundle)
NSString * defaultBundleIdentifier()
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
Definition: message_loop.cc:19
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
Settings SettingsFromCommandLine(const fml::CommandLine &command_line)
Definition: switches.cc:228
const uint8_t kPlatformStrongDill[]
NSString * flutterAssetsName:(NSBundle *bundle)
FlKeyEvent FlKeyResponderAsyncCallback callback
static const char * kApplicationKernelSnapshotFileName
fml::CommandLine CommandLineFromNSProcessInfo()
Definition: command_line.mm:11
instancetype initWithPrecompiledDartBundle:(nullable NSBundle *NS_DESIGNATED_INITIALIZER)
NSString * lookupKeyForAsset:fromPackage:fromBundle:(NSString *asset, [fromPackage] NSString *package, [fromBundle] nullable NSBundle *bundle)
std::function< void()> closure
Definition: closure.h:14
static RunConfiguration InferFromSettings(const Settings &settings, fml::RefPtr< fml::TaskRunner > io_worker=nullptr)
Attempts to infer a run configuration from the settings object. This tries to create a run configurat...
const intptr_t kPlatformStrongDillSize
Specifies all the configuration required by the runtime library to launch the root isolate...
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:202
const flutter::Settings & settings()
NSString * lookupKeyForAsset:fromBundle:(NSString *asset, [fromBundle] nullable NSBundle *bundle)
flutter::RunConfiguration runConfiguration()
flutter::RunConfiguration runConfigurationForEntrypoint:(nullable NSString *entrypointOrNil)
void RemoveTaskObserver(intptr_t key)
Definition: message_loop.cc:68
const flutter::PlatformData defaultPlatformData()
std::string lifecycle_state
Definition: platform_data.h:40