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