Version 2.13.0-49.0.dev

Merge commit '9f68048cae8ec3344e845917f3f42dbc8340e8a3' into 'dev'
diff --git a/pkg/vm/bin/kernel_service.dart b/pkg/vm/bin/kernel_service.dart
index 7e0dc6a..0fcaf18 100644
--- a/pkg/vm/bin/kernel_service.dart
+++ b/pkg/vm/bin/kernel_service.dart
@@ -153,7 +153,7 @@
 }
 
 abstract class Compiler {
-  final int isolateId;
+  final int isolateGroupId;
   final FileSystem fileSystem;
   final Uri platformKernelPath;
   final bool enableAsserts;
@@ -172,7 +172,7 @@
 
   CompilerOptions options;
 
-  Compiler(this.isolateId, this.fileSystem, this.platformKernelPath,
+  Compiler(this.isolateGroupId, this.fileSystem, this.platformKernelPath,
       {this.enableAsserts: false,
       this.nullSafety: kNullSafetyOptionUnspecified,
       this.experimentalFlags: null,
@@ -214,7 +214,7 @@
 
       if (errors.isEmpty) {
         // Record dependencies only if compilation was error free.
-        _recordDependencies(isolateId, component, options.packagesFileUri);
+        _recordDependencies(isolateGroupId, component, options.packagesFileUri);
       }
 
       return compilerResult;
@@ -289,14 +289,14 @@
   IncrementalCompiler generator;
 
   IncrementalCompilerWrapper(
-      int isolateId, FileSystem fileSystem, Uri platformKernelPath,
+      int isolateGroupId, FileSystem fileSystem, Uri platformKernelPath,
       {bool enableAsserts: false,
       int nullSafety: kNullSafetyOptionUnspecified,
       List<String> experimentalFlags: null,
       String packageConfig: null,
       String invocationModes: '',
       String verbosityLevel: Verbosity.defaultValue})
-      : super(isolateId, fileSystem, platformKernelPath,
+      : super(isolateGroupId, fileSystem, platformKernelPath,
             enableAsserts: enableAsserts,
             nullSafety: nullSafety,
             experimentalFlags: experimentalFlags,
@@ -308,7 +308,7 @@
 
   factory IncrementalCompilerWrapper.forExpressionCompilationOnly(
       Component component,
-      int isolateId,
+      int isolateGroupId,
       FileSystem fileSystem,
       Uri platformKernelPath,
       {bool enableAsserts: false,
@@ -316,7 +316,7 @@
       String packageConfig: null,
       String invocationModes: ''}) {
     IncrementalCompilerWrapper result = IncrementalCompilerWrapper(
-        isolateId, fileSystem, platformKernelPath,
+        isolateGroupId, fileSystem, platformKernelPath,
         enableAsserts: enableAsserts,
         experimentalFlags: experimentalFlags,
         packageConfig: packageConfig,
@@ -342,9 +342,9 @@
   void accept() => generator.accept();
   void invalidate(Uri uri) => generator.invalidate(uri);
 
-  Future<IncrementalCompilerWrapper> clone(int isolateId) async {
+  Future<IncrementalCompilerWrapper> clone(int isolateGroupId) async {
     IncrementalCompilerWrapper clone = IncrementalCompilerWrapper(
-        isolateId, fileSystem, platformKernelPath,
+        isolateGroupId, fileSystem, platformKernelPath,
         enableAsserts: enableAsserts,
         nullSafety: nullSafety,
         experimentalFlags: experimentalFlags,
@@ -359,7 +359,7 @@
     // clone should be used for.
     MemoryFileSystem memoryFileSystem = (fileSystem as HybridFileSystem).memory;
 
-    String filename = 'full-component-$isolateId.dill';
+    String filename = 'full-component-$isolateGroupId.dill';
     Sink sink = FileSink(memoryFileSystem.entityForUri(Uri.file(filename)));
     new BinaryPrinter(sink).writeComponentFile(fullComponent);
     await sink.close();
@@ -374,7 +374,7 @@
   final bool requireMain;
 
   SingleShotCompilerWrapper(
-      int isolateId, FileSystem fileSystem, Uri platformKernelPath,
+      int isolateGroupId, FileSystem fileSystem, Uri platformKernelPath,
       {this.requireMain: false,
       bool enableAsserts: false,
       int nullSafety: kNullSafetyOptionUnspecified,
@@ -382,7 +382,7 @@
       String packageConfig: null,
       String invocationModes: '',
       String verbosityLevel: Verbosity.defaultValue})
-      : super(isolateId, fileSystem, platformKernelPath,
+      : super(isolateGroupId, fileSystem, platformKernelPath,
             enableAsserts: enableAsserts,
             nullSafety: nullSafety,
             experimentalFlags: experimentalFlags,
@@ -405,18 +405,15 @@
   }
 }
 
-// TODO(33428): This state is leaked on isolate shutdown.
-final Map<int, IncrementalCompilerWrapper> isolateCompilers =
-    new Map<int, IncrementalCompilerWrapper>();
-final Map<int, List<Uri>> isolateDependencies = new Map<int, List<Uri>>();
-final Map<int, _ExpressionCompilationFromDillSettings> isolateLoadNotifies =
-    new Map<int, _ExpressionCompilationFromDillSettings>();
+final Map<int, IncrementalCompilerWrapper> isolateCompilers = {};
+final Map<int, List<Uri>> isolateDependencies = {};
+final Map<int, _ExpressionCompilationFromDillSettings> isolateLoadNotifies = {};
 
-IncrementalCompilerWrapper lookupIncrementalCompiler(int isolateId) {
-  return isolateCompilers[isolateId];
+IncrementalCompilerWrapper lookupIncrementalCompiler(int isolateGroupId) {
+  return isolateCompilers[isolateGroupId];
 }
 
-Future<Compiler> lookupOrBuildNewIncrementalCompiler(int isolateId,
+Future<Compiler> lookupOrBuildNewIncrementalCompiler(int isolateGroupId,
     List sourceFiles, Uri platformKernelPath, List<int> platformKernel,
     {bool enableAsserts: false,
     int nullSafety: kNullSafetyOptionUnspecified,
@@ -426,7 +423,8 @@
     String multirootScheme,
     String invocationModes: '',
     String verbosityLevel: Verbosity.defaultValue}) async {
-  IncrementalCompilerWrapper compiler = lookupIncrementalCompiler(isolateId);
+  IncrementalCompilerWrapper compiler =
+      lookupIncrementalCompiler(isolateGroupId);
   if (compiler != null) {
     updateSources(compiler, sourceFiles);
     invalidateSources(compiler, sourceFiles);
@@ -439,7 +437,7 @@
         sourceFiles[1] == null) {
       // Just use first compiler that should represent main isolate as a source for cloning.
       var source = isolateCompilers.entries.first;
-      compiler = await source.value.clone(isolateId);
+      compiler = await source.value.clone(isolateGroupId);
     } else {
       FileSystem fileSystem = _buildFileSystem(
           sourceFiles, platformKernel, multirootFilepaths, multirootScheme);
@@ -449,7 +447,7 @@
       // isolate needs to receive a message indicating that particular
       // isolate was shut down. Message should be handled here in this script.
       compiler = new IncrementalCompilerWrapper(
-          isolateId, fileSystem, platformKernelPath,
+          isolateGroupId, fileSystem, platformKernelPath,
           enableAsserts: enableAsserts,
           nullSafety: nullSafety,
           experimentalFlags: experimentalFlags,
@@ -457,7 +455,7 @@
           invocationModes: invocationModes,
           verbosityLevel: verbosityLevel);
     }
-    isolateCompilers[isolateId] = compiler;
+    isolateCompilers[isolateGroupId] = compiler;
   }
   return compiler;
 }
@@ -494,7 +492,7 @@
 // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc.
 Future _processExpressionCompilationRequest(request) async {
   final SendPort port = request[1];
-  final int isolateId = request[2];
+  final int isolateGroupId = request[2];
   final dynamic dart_platform_kernel = request[3];
   final String expression = request[4];
   final List<String> definitions = request[5].cast<String>();
@@ -508,15 +506,15 @@
   final List<String> experimentalFlags =
       request[13] != null ? request[13].cast<String>() : null;
 
-  IncrementalCompilerWrapper compiler = isolateCompilers[isolateId];
+  IncrementalCompilerWrapper compiler = isolateCompilers[isolateGroupId];
 
   _ExpressionCompilationFromDillSettings isolateLoadDillData =
-      isolateLoadNotifies[isolateId];
+      isolateLoadNotifies[isolateGroupId];
   if (isolateLoadDillData != null) {
     // Check if we can reuse the compiler.
     if (isolateLoadDillData.blobLoadCount != blobLoadCount ||
         isolateLoadDillData.prevDillCount != dillData.length) {
-      compiler = isolateCompilers[isolateId] = null;
+      compiler = isolateCompilers[isolateGroupId] = null;
     }
   }
 
@@ -525,7 +523,7 @@
       if (verbose) {
         print("DFE: Initializing compiler from ${dillData.length} dill files");
       }
-      isolateLoadNotifies[isolateId] =
+      isolateLoadNotifies[isolateGroupId] =
           new _ExpressionCompilationFromDillSettings(
               blobLoadCount, dillData.length);
 
@@ -585,11 +583,11 @@
       // isolate was shut down. Message should be handled here in this script.
       try {
         compiler = new IncrementalCompilerWrapper.forExpressionCompilationOnly(
-            component, isolateId, fileSystem, null,
+            component, isolateGroupId, fileSystem, null,
             enableAsserts: enableAsserts,
             experimentalFlags: experimentalFlags,
             packageConfig: dotPackagesFile);
-        isolateCompilers[isolateId] = compiler;
+        isolateCompilers[isolateGroupId] = compiler;
         await compiler.compile(
             component.mainMethod?.enclosingLibrary?.importUri ??
                 component.libraries.last.importUri);
@@ -640,8 +638,8 @@
 }
 
 void _recordDependencies(
-    int isolateId, Component component, Uri packageConfig) {
-  final dependencies = isolateDependencies[isolateId] ??= <Uri>[];
+    int isolateGroupId, Component component, Uri packageConfig) {
+  final dependencies = isolateDependencies[isolateGroupId] ??= <Uri>[];
 
   if (component != null) {
     for (var lib in component.libraries) {
@@ -673,8 +671,9 @@
   return utf8.encode(uris.map(_escapeDependency).join(" "));
 }
 
-Future _processListDependenciesRequest(SendPort port, int isolateId) async {
-  final List<Uri> dependencies = isolateDependencies[isolateId] ?? <Uri>[];
+Future _processListDependenciesRequest(
+    SendPort port, int isolateGroupId) async {
+  final List<Uri> dependencies = isolateDependencies[isolateGroupId] ?? <Uri>[];
 
   CompilationResult result;
   try {
@@ -687,10 +686,10 @@
 }
 
 Future _processIsolateShutdownNotification(request) async {
-  final int isolateId = request[1];
-  isolateCompilers.remove(isolateId);
-  isolateDependencies.remove(isolateId);
-  isolateLoadNotifies.remove(isolateId);
+  final int isolateGroupId = request[1];
+  isolateCompilers.remove(isolateGroupId);
+  isolateDependencies.remove(isolateGroupId);
+  isolateLoadNotifies.remove(isolateGroupId);
 }
 
 Future _processLoadRequest(request) async {
@@ -733,10 +732,10 @@
   }
 
   final SendPort port = request[1];
-  final int isolateId = request[7];
+  final int isolateGroupId = request[7];
 
   if (tag == kListDependenciesTag) {
-    await _processListDependenciesRequest(port, isolateId);
+    await _processListDependenciesRequest(port, isolateGroupId);
     return;
   }
 
@@ -777,7 +776,7 @@
   if (tag == kUpdateSourcesTag) {
     assert(incremental,
         "Incremental compiler required for use of 'kUpdateSourcesTag'");
-    compiler = lookupIncrementalCompiler(isolateId);
+    compiler = lookupIncrementalCompiler(isolateGroupId);
     if (compiler == null) {
       port.send(new CompilationResult.errors(
               ["No incremental compiler available for this isolate."], null)
@@ -790,7 +789,7 @@
   } else if (tag == kAcceptTag) {
     assert(
         incremental, "Incremental compiler required for use of 'kAcceptTag'");
-    compiler = lookupIncrementalCompiler(isolateId);
+    compiler = lookupIncrementalCompiler(isolateGroupId);
     // There are unit tests that invoke the IncrementalCompiler directly and
     // request a reload, meaning that we won't have a compiler for this isolate.
     if (compiler != null) {
@@ -841,7 +840,7 @@
   // watch the performance though.
   if (incremental) {
     compiler = await lookupOrBuildNewIncrementalCompiler(
-        isolateId, sourceFiles, platformKernelPath, platformKernel,
+        isolateGroupId, sourceFiles, platformKernelPath, platformKernel,
         enableAsserts: enableAsserts,
         nullSafety: nullSafety,
         experimentalFlags: experimentalFlags,
@@ -854,7 +853,7 @@
     FileSystem fileSystem = _buildFileSystem(
         sourceFiles, platformKernel, multirootFilepaths, multirootScheme);
     compiler = new SingleShotCompilerWrapper(
-        isolateId, fileSystem, platformKernelPath,
+        isolateGroupId, fileSystem, platformKernelPath,
         requireMain: false,
         enableAsserts: enableAsserts,
         nullSafety: nullSafety,
@@ -1007,7 +1006,7 @@
     false /* incremental */,
     false /* snapshot */,
     kNullSafetyOptionUnspecified /* null safety */,
-    1 /* isolateId chosen randomly */,
+    1 /* isolateGroupId chosen randomly */,
     [] /* source files */,
     false /* enable asserts */,
     null /* experimental_flags */,
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index d2de37c..a532bb4 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -886,10 +886,10 @@
                                    char** error) {
   const char* result = NULL;
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
-  if (isolate->HasTagHandler()) {
+  auto isolate_group = thread->isolate_group();
+  if (isolate_group->HasTagHandler()) {
     const Object& obj = Object::Handle(
-        isolate->CallTagHandler(Dart_kCanonicalizeUrl, library, uri));
+        isolate_group->CallTagHandler(Dart_kCanonicalizeUrl, library, uri));
     if (obj.IsString()) {
       result = String2UTF8(String::Cast(obj));
     } else if (obj.IsError()) {
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index a245378..2cc879a 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -662,7 +662,7 @@
 DEFINE_NATIVE_ENTRY(IsolateMirror_loadUri, 0, 1) {
   GET_NON_NULL_NATIVE_ARGUMENT(String, uri, arguments->NativeArgAt(0));
 
-  if (!isolate->HasTagHandler()) {
+  if (!isolate->group()->HasTagHandler()) {
     ThrowLanguageError("no library handler registered");
   }
 
@@ -675,7 +675,7 @@
   } else {
     isolate->BlockClassFinalization();
     const Object& result = Object::Handle(
-        zone, isolate->CallTagHandler(
+        zone, isolate->group()->CallTagHandler(
                   Dart_kCanonicalizeUrl,
                   Library::Handle(
                       zone, isolate->group()->object_store()->root_library()),
@@ -703,7 +703,7 @@
   // Request the embedder to load the library.
   isolate->BlockClassFinalization();
   Object& result = Object::Handle(
-      zone, isolate->CallTagHandler(
+      zone, isolate->group()->CallTagHandler(
                 Dart_kImportTag,
                 Library::Handle(
                     zone, isolate->group()->object_store()->root_library()),
diff --git a/runtime/vm/BUILD.gn b/runtime/vm/BUILD.gn
index 93534de..49c4036 100644
--- a/runtime/vm/BUILD.gn
+++ b/runtime/vm/BUILD.gn
@@ -74,7 +74,7 @@
   if (is_fuchsia) {
     if (using_fuchsia_gn_sdk) {
       extra_deps = [
-        "$fuchsia_sdk_root/fidl/fuchsia.deprecatedtimezone",
+        "$fuchsia_sdk_root/fidl/fuchsia.intl",
         "$fuchsia_sdk_root/pkg/async",
         "$fuchsia_sdk_root/pkg/async-default",
         "$fuchsia_sdk_root/pkg/async-loop",
@@ -87,7 +87,7 @@
       ]
     } else if (using_fuchsia_sdk) {
       extra_deps = [
-        "$fuchsia_sdk_root/fidl:fuchsia.deprecatedtimezone",
+        "$fuchsia_sdk_root/fidl:fuchsia.intl",
         "$fuchsia_sdk_root/pkg:async-loop",
         "$fuchsia_sdk_root/pkg:async-loop-default",
         "$fuchsia_sdk_root/pkg:inspect",
@@ -98,9 +98,7 @@
       ]
     } else {
       extra_deps = [
-        # TODO(US-399): Remove time_service specific code when it is no longer
-        # necessary.
-        "//sdk/fidl/fuchsia.deprecatedtimezone",
+        "//sdk/fidl/fuchsia.intl",
         "//sdk/lib/sys/cpp",
         "//sdk/lib/sys/inspect/cpp",
         "//zircon/public/lib/fbl",
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 97dab6c..f371e1b 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1895,9 +1895,9 @@
   return mutator_thread_;
 }
 
-ObjectPtr Isolate::CallTagHandler(Dart_LibraryTag tag,
-                                  const Object& arg1,
-                                  const Object& arg2) {
+ObjectPtr IsolateGroup::CallTagHandler(Dart_LibraryTag tag,
+                                       const Object& arg1,
+                                       const Object& arg2) {
   Thread* thread = Thread::Current();
   Api::Scope api_scope(thread);
   Dart_Handle api_arg1 = Api::NewHandle(thread, arg1.ptr());
@@ -1906,7 +1906,7 @@
   {
     TransitionVMToNative transition(thread);
     ASSERT(HasTagHandler());
-    api_result = group()->library_tag_handler()(tag, api_arg1, api_arg2);
+    api_result = library_tag_handler()(tag, api_arg1, api_arg2);
   }
   return Api::UnwrapHandle(api_result);
 }
@@ -2473,7 +2473,6 @@
     StackZone zone(thread);
     HandleScope handle_scope(thread);
     ServiceIsolate::SendIsolateShutdownMessage();
-    KernelIsolate::NotifyAboutIsolateShutdown(this);
 #if !defined(PRODUCT)
     debugger()->Shutdown();
 #endif
@@ -2545,6 +2544,8 @@
   const bool shutdown_group =
       isolate_group->UnregisterIsolateDecrementCount(isolate);
   if (shutdown_group) {
+    KernelIsolate::NotifyAboutIsolateGroupShutdown(isolate_group);
+
 #if !defined(DART_PRECOMPILED_RUNTIME)
     if (!is_vm_isolate) {
       Thread::EnterIsolateGroupAsHelper(isolate_group, Thread::kUnknownTask,
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index aa8575f..96f6908 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -600,6 +600,10 @@
   void IncreaseMutatorCount(Isolate* mutator);
   void DecreaseMutatorCount(Isolate* mutator);
 
+  bool HasTagHandler() const { return library_tag_handler() != nullptr; }
+  ObjectPtr CallTagHandler(Dart_LibraryTag tag,
+                           const Object& arg1,
+                           const Object& arg2);
   Dart_LibraryTagHandler library_tag_handler() const {
     return library_tag_handler_;
   }
@@ -724,7 +728,7 @@
 #endif
   }
 
-  uint64_t id() { return id_; }
+  uint64_t id() const { return id_; }
 
   static void Init();
   static void Cleanup();
@@ -1102,12 +1106,6 @@
     environment_callback_ = value;
   }
 
-  bool HasTagHandler() const {
-    return group()->library_tag_handler() != nullptr;
-  }
-  ObjectPtr CallTagHandler(Dart_LibraryTag tag,
-                           const Object& arg1,
-                           const Object& arg2);
   bool HasDeferredLoadHandler() const {
     return group()->deferred_load_handler() != nullptr;
   }
diff --git a/runtime/vm/kernel_binary.cc b/runtime/vm/kernel_binary.cc
index 00ceb7d..9e6e614 100644
--- a/runtime/vm/kernel_binary.cc
+++ b/runtime/vm/kernel_binary.cc
@@ -194,18 +194,18 @@
 std::unique_ptr<Program> Program::ReadFromFile(
     const char* script_uri, const char** error /* = nullptr */) {
   Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   if (script_uri == NULL) {
     return nullptr;
   }
-  if (!isolate->HasTagHandler()) {
+  if (!isolate_group->HasTagHandler()) {
     return nullptr;
   }
   std::unique_ptr<kernel::Program> kernel_program;
 
   const String& uri = String::Handle(String::New(script_uri));
-  const Object& ret = Object::Handle(
-      isolate->CallTagHandler(Dart_kKernelTag, Object::null_object(), uri));
+  const Object& ret = Object::Handle(isolate_group->CallTagHandler(
+      Dart_kKernelTag, Object::null_object(), uri));
   if (ret.IsExternalTypedData()) {
     const auto& typed_data = ExternalTypedData::Handle(
         thread->zone(), ExternalTypedData::RawCast(ret.ptr()));
diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc
index 168cb76..9eab978 100644
--- a/runtime/vm/kernel_isolate.cc
+++ b/runtime/vm/kernel_isolate.cc
@@ -559,13 +559,12 @@
     is_static_object.type = Dart_CObject_kBool;
     is_static_object.value.as_bool = is_static;
 
-    auto isolate = thread->isolate();
     auto isolate_group = thread->isolate_group();
     auto source = isolate_group->source();
 
     Dart_CObject isolate_id;
     isolate_id.type = Dart_CObject_kInt64;
-    isolate_id.value.as_int64 = static_cast<int64_t>(isolate->main_port());
+    isolate_id.value.as_int64 = static_cast<int64_t>(isolate_group->id());
 
     intptr_t num_dills = 0;
     if (source->kernel_buffer != nullptr) {
@@ -756,16 +755,16 @@
     // compilation logic out of CreateIsolateAndSetupHelper and into
     // IsolateSetupHelper in main.cc.
     auto thread = Thread::Current();
-    auto isolate = thread != nullptr ? thread->isolate() : nullptr;
     auto isolate_group = thread != nullptr ? thread->isolate_group() : nullptr;
 
     if (incremental_compile) {
-      ASSERT(isolate != NULL);
+      ASSERT(isolate_group != nullptr);
     }
     Dart_CObject isolate_id;
     isolate_id.type = Dart_CObject_kInt64;
-    isolate_id.value.as_int64 =
-        isolate != nullptr ? static_cast<int64_t>(isolate->main_port()) : 0;
+    isolate_id.value.as_int64 = isolate_group != nullptr
+                                    ? static_cast<int64_t>(isolate_group->id())
+                                    : 0;
 
     Dart_CObject message;
     message.type = Dart_CObject_kArray;
@@ -1155,7 +1154,8 @@
       Dart_KernelCompilationVerbosityLevel_Error);
 }
 
-void KernelIsolate::NotifyAboutIsolateShutdown(const Isolate* isolate) {
+void KernelIsolate::NotifyAboutIsolateGroupShutdown(
+    const IsolateGroup* isolate_group) {
   if (!KernelIsolate::IsRunning()) {
     return;
   }
@@ -1170,8 +1170,7 @@
 
   Dart_CObject isolate_id;
   isolate_id.type = Dart_CObject_kInt64;
-  isolate_id.value.as_int64 =
-      isolate != NULL ? static_cast<int64_t>(isolate->main_port()) : 0;
+  isolate_id.value.as_int64 = static_cast<int64_t>(isolate_group->id());
 
   Dart_CObject message;
   message.type = Dart_CObject_kArray;
diff --git a/runtime/vm/kernel_isolate.h b/runtime/vm/kernel_isolate.h
index 438e883..2ba4f2c 100644
--- a/runtime/vm/kernel_isolate.h
+++ b/runtime/vm/kernel_isolate.h
@@ -79,7 +79,8 @@
 
   static Dart_KernelCompilationResult ListDependencies();
 
-  static void NotifyAboutIsolateShutdown(const Isolate* isolate);
+  static void NotifyAboutIsolateGroupShutdown(
+      const IsolateGroup* isolate_group);
 
   static void AddExperimentalFlag(const char* value);
   static bool GetExperimentalFlag(ExperimentalFeature feature);
@@ -115,7 +116,8 @@
   static bool IsRunning() { return false; }
   static void Shutdown() {}
   static bool IsKernelIsolate(const Isolate* isolate) { return false; }
-  static void NotifyAboutIsolateShutdown(const Isolate* isolate) {}
+  static void NotifyAboutIsolateGroupShutdown(
+      const IsolateGroup* isolate_group) {}
   static bool GetExperimentalFlag(const char* value) { return false; }
 
  protected:
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index 977686f..7dc46ad 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -704,13 +704,13 @@
 void KernelLoader::LoadNativeExtension(const Library& library,
                                        const String& uri_path) {
 #if !defined(DART_PRECOMPILER)
-  if (!I->HasTagHandler()) {
+  if (!IG->HasTagHandler()) {
     H.ReportError("no library handler registered.");
   }
 
   I->BlockClassFinalization();
   const auto& result = Object::Handle(
-      Z, I->CallTagHandler(Dart_kImportExtensionTag, library, uri_path));
+      Z, IG->CallTagHandler(Dart_kImportExtensionTag, library, uri_path));
   I->UnblockClassFinalization();
 
   if (result.IsError()) {
diff --git a/runtime/vm/os_fuchsia.cc b/runtime/vm/os_fuchsia.cc
index e79827b..674bdd5 100644
--- a/runtime/vm/os_fuchsia.cc
+++ b/runtime/vm/os_fuchsia.cc
@@ -10,8 +10,10 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <stdint.h>
+#include <string.h>
+#include <unistd.h>
 
-#include <fuchsia/deprecatedtimezone/cpp/fidl.h>
+#include <fuchsia/intl/cpp/fidl.h>
 #include <lib/async-loop/default.h>
 #include <lib/async-loop/loop.h>
 #include <lib/async/default.h>
@@ -22,21 +24,49 @@
 #include <zircon/process.h>
 #include <zircon/syscalls.h>
 #include <zircon/syscalls/object.h>
-#include <zircon/time.h>
 #include <zircon/threads.h>
+#include <zircon/time.h>
 #include <zircon/types.h>
 
+#include <set>
+
+#include "unicode/errorcode.h"
+#include "unicode/timezone.h"
+#include "unicode/umachine.h"
+
 #include "platform/assert.h"
+#include "platform/syslog.h"
 #include "platform/utils.h"
+#include "vm/lockers.h"
+#include "vm/os_thread.h"
 #include "vm/zone.h"
 
 namespace {
 
+using dart::Mutex;
+using dart::MutexLocker;
+using dart::Syslog;
+using dart::Zone;
+
+// This is the default timezone returned if it could not be obtained.  For
+// Fuchsia, the default device timezone is always UTC.
+static const char kDefaultTimezone[] = "UTC";
+
+static constexpr int32_t kMsPerSec = 1000;
+
 // The data directory containing ICU timezone data files.
 static constexpr char kICUTZDataDir[] = "/config/data/tzdata/icu/44/le";
 
+// This is the general OK status.
+static constexpr int32_t kOk = 0;
+
+// This status means that the error code is not initialized yet ("set" was not
+// yet called).  Error codes are usually either 0 (kOk), or negative.
+static constexpr int32_t kUninitialized = 1;
+
 // The status codes for tzdata file open and read.
 enum class TZDataStatus {
+  // The operation completed without error.
   OK = 0,
   // The open call for the tzdata file did not succeed.
   COULD_NOT_OPEN = -1,
@@ -46,6 +76,8 @@
 
 // Adds a facility for introspecting timezone data errors.  Allows insight into
 // the internal state of the VM even if error reporting facilities fail.
+//
+// Under normal operation, all metric values below should be zero.
 class InspectMetrics {
  public:
   // Does not take ownership of inspector.
@@ -53,9 +85,27 @@
       : inspector_(inspector),
         root_(inspector_->GetRoot()),
         metrics_(root_.CreateChild("os")),
-        dst_status_(metrics_.CreateInt("dst_status", 0)),
-        tz_data_status_(metrics_.CreateInt("tz_data_status", 0)),
-        tz_data_close_status_(metrics_.CreateInt("tz_data_close_status", 0)) {}
+        dst_status_(metrics_.CreateInt("dst_status", kUninitialized)),
+        tz_data_status_(metrics_.CreateInt("tz_data_status", kUninitialized)),
+        tz_data_close_status_(
+            metrics_.CreateInt("tz_data_close_status", kUninitialized)),
+        get_profile_status_(
+            metrics_.CreateInt("get_profile_status", kUninitialized)),
+        profiles_timezone_content_status_(
+            metrics_.CreateInt("timezone_content_status", kOk)),
+        num_get_profile_calls_(metrics_.CreateInt("num_get_profile_calls", 0)),
+        num_on_change_calls_(metrics_.CreateInt("num_on_change_calls", 0)),
+        num_intl_provider_errors_(
+            metrics_.CreateInt("num_intl_provider_errors", 0)) {}
+
+  // Registers a single call to GetProfile callback.
+  void RegisterGetProfileCall() { num_get_profile_calls_.Add(1); }
+
+  // Registers a single call to OnChange callback.
+  void RegisterOnChangeCall() { num_on_change_calls_.Add(1); }
+
+  // Registers a provider error.
+  void RegisterIntlProviderError() { num_intl_provider_errors_.Add(1); }
 
   // Sets the last status code for DST offset calls.
   void SetDSTOffsetStatus(zx_status_t status) {
@@ -69,6 +119,17 @@
     tz_data_close_status_.Set(status);
   }
 
+  // Sets the last status code for the call to PropertyProvider::GetProfile.
+  void SetProfileStatus(zx_status_t status) {
+    get_profile_status_.Set(static_cast<int32_t>(status));
+  }
+
+  // Sets the last status seen while examining timezones returned from
+  // PropertyProvider::GetProfile.
+  void SetTimeZoneContentStatus(zx_status_t status) {
+    profiles_timezone_content_status_.Set(static_cast<int32_t>(status));
+  }
+
  private:
   // The inspector that all metrics are being reported into.
   inspect::Inspector* inspector_;
@@ -87,11 +148,166 @@
 
   // The return code for the close() call for tzdata files.
   inspect::IntProperty tz_data_close_status_;
+
+  // The return code of the GetProfile call in GetTimeZoneName.  If this is
+  // nonzero, then os_fuchsia.cc reported a default timezone as a fallback.
+  inspect::IntProperty get_profile_status_;
+
+  // U_ILLEGAL_ARGUMENT_ERROR(=1) if timezones read from ProfileProvider were
+  // incorrect. Otherwise 0.  If this metric reports U_ILLEGAL_ARGUMENT_ERROR,
+  // the os_fuchsia.cc module reported a default timezone as a fallback.
+  inspect::IntProperty profiles_timezone_content_status_;
+
+  // Keeps a number of get_profile update calls.
+  inspect::IntProperty num_get_profile_calls_;
+
+  // Number of "on change" callback calls.
+  inspect::IntProperty num_on_change_calls_;
+
+  // Keeps a number of errors encountered in intl provider.
+  inspect::IntProperty num_intl_provider_errors_;
 };
 
+// Thread-safe storage for the current timezone name.
+//
+// Keeps an up to date timezone cache, updating if needed through the
+// asynchronous update interface.  Access to this class is thread-safe.
+class TimezoneName final {
+ public:
+  // Creates a new instance of TimezoneName.  Does not take ownership of
+  // metrics.
+  static std::shared_ptr<TimezoneName> New(
+      fuchsia::intl::PropertyProviderPtr proxy,
+      std::weak_ptr<InspectMetrics> metrics) {
+    auto timezone_name =
+        std::make_shared<TimezoneName>(std::move(proxy), metrics);
+    timezone_name->InitHandlers(timezone_name);
+    return timezone_name;
+  }
+
+  TimezoneName(fuchsia::intl::PropertyProviderPtr proxy,
+               std::weak_ptr<InspectMetrics> metrics)
+      : m_(),
+        metrics_(std::move(metrics)),
+        proxy_(std::move(proxy)),
+        timezone_name_(kDefaultTimezone) {
+    ASSERT(metrics_.lock() != nullptr);
+  }
+
+  // Gets the current timezone name.  Repeated calls may retrieve updated
+  // values.
+  std::string Get() const {
+    MutexLocker lock(&m_);
+    // Returns a copy, to avoid a data race with async updates.
+    return timezone_name_;
+  }
+
+ private:
+  // Sets the event handlers in this resolver.  Intended to resolve a circular
+  // reference between the shared timezone name and this.
+  void InitHandlers(std::shared_ptr<TimezoneName> timezone_name) {
+    ASSERT(timezone_name.get() == this);
+    timezone_name->proxy_.set_error_handler(
+        [weak_this =
+             std::weak_ptr<TimezoneName>(timezone_name)](zx_status_t status) {
+          if (!weak_this.expired()) {
+            weak_this.lock()->ErrorHandler(status);
+          }
+        });
+    timezone_name->proxy_.events().OnChange =
+        [weak_this = std::weak_ptr<TimezoneName>(timezone_name)]() {
+          if (!weak_this.expired()) {
+            weak_this.lock()->OnChangeCallback();
+          }
+        };
+    timezone_name->proxy_->GetProfile(
+        [weak_this = std::weak_ptr<TimezoneName>(timezone_name)](
+            fuchsia::intl::Profile profile) {
+          if (!weak_this.expired()) {
+            weak_this.lock()->GetProfileCallback(std::move(profile));
+          }
+        });
+  }
+
+  // Called on a profile provider error in the context of the event loop
+  // thread.
+  void ErrorHandler(zx_status_t status) {
+    MutexLocker lock(&m_);
+    WithMetrics([status](std::shared_ptr<InspectMetrics> metrics) {
+      metrics->SetProfileStatus(status);
+      metrics->RegisterIntlProviderError();
+    });
+  }
+
+  // Called when an OnChange event is received in the context of the event loop
+  // thread.  The only action here is to trigger an asynchronous update of the
+  // intl profile.
+  void OnChangeCallback() {
+    MutexLocker lock(&m_);
+    WithMetrics([](std::shared_ptr<InspectMetrics> metrics) {
+      metrics->RegisterOnChangeCall();
+    });
+    proxy_->GetProfile([this](fuchsia::intl::Profile profile) {
+      this->GetProfileCallback(std::move(profile));
+    });
+  }
+
+  // Called when a GetProfile async request is resolved, in the context of the
+  // event loop thread.
+  void GetProfileCallback(fuchsia::intl::Profile profile) {
+    MutexLocker lock(&m_);
+    WithMetrics([](std::shared_ptr<InspectMetrics> metrics) {
+      metrics->RegisterGetProfileCall();
+    });
+    const std::vector<fuchsia::intl::TimeZoneId>& timezones =
+        profile.time_zones();
+    if (timezones.empty()) {
+      WithMetrics([](std::shared_ptr<InspectMetrics> metrics) {
+        metrics->SetTimeZoneContentStatus(U_ILLEGAL_ARGUMENT_ERROR);
+      });
+      // Empty timezone array is not up to fuchsia::intl spec.  The serving
+      // endpoint is broken and should be fixed.
+      Syslog::PrintErr("got empty timezone value\n");
+      return;
+    }
+    WithMetrics([](std::shared_ptr<InspectMetrics> metrics) {
+      metrics->SetProfileStatus(ZX_OK);
+      metrics->SetTimeZoneContentStatus(ZX_OK);
+    });
+
+    timezone_name_ = timezones[0].id;
+  }
+
+  // Runs the provided function only on valid metrics.
+  void WithMetrics(std::function<void(std::shared_ptr<InspectMetrics> m)> f) {
+    std::shared_ptr<InspectMetrics> l = metrics_.lock();
+    if (l != nullptr) {
+      f(l);
+    }
+  }
+
+  // Guards timezone_name_ because the callbacks will be called in an
+  // asynchronous thread.
+  mutable Mutex m_;
+
+  // Used to keep tally on the update events. Not owned.
+  std::weak_ptr<InspectMetrics> metrics_;
+
+  // A client-side proxy for a connection to the property provider service.
+  fuchsia::intl::PropertyProviderPtr proxy_;
+
+  // Caches the current timezone name.  This is updated asynchronously through
+  // GetProfileCallback.
+  std::string timezone_name_;
+};
+
+// The timezone names encountered so far.  The timezone names must live forever.
+std::set<const std::string> timezone_names;
+
 // Initialized on OS:Init(), deinitialized on OS::Cleanup.
 std::unique_ptr<sys::ComponentInspector> component_inspector;
-std::unique_ptr<InspectMetrics> metrics;
+std::shared_ptr<InspectMetrics> metrics;
+std::shared_ptr<TimezoneName> timezone_name;
 async_loop_t* message_loop = nullptr;
 
 // Initializes the source of timezone data if available.  Timezone data file in
@@ -153,35 +369,57 @@
 // Putting this hack right now due to CP-120 as I need to remove
 // component:ConnectToEnvironmentServices and this is the only thing that is
 // blocking it and FL-98 will take time.
-static fuchsia::deprecatedtimezone::TimezoneSyncPtr tz;
+static fuchsia::intl::PropertyProviderPtr property_provider;
 
 static zx_status_t GetLocalAndDstOffsetInSeconds(int64_t seconds_since_epoch,
                                                  int32_t* local_offset,
                                                  int32_t* dst_offset) {
-  zx_status_t status = tz->GetTimezoneOffsetMinutes(seconds_since_epoch * 1000,
-                                                    local_offset, dst_offset);
-  metrics->SetDSTOffsetStatus(status);
-  if (status != ZX_OK) {
-    return status;
+  const char* timezone_id = OS::GetTimeZoneName(seconds_since_epoch);
+  std::unique_ptr<icu::TimeZone> timezone(
+      icu::TimeZone::createTimeZone(timezone_id));
+  UErrorCode error = U_ZERO_ERROR;
+  const auto ms_since_epoch =
+      static_cast<UDate>(kMsPerSec * seconds_since_epoch);
+  // The units of time that local_offset and dst_offset are returned from this
+  // function is, usefully, not documented, but it seems that the units are
+  // milliseconds.  Add these variables here for clarity.
+  int32_t local_offset_ms = 0;
+  int32_t dst_offset_ms = 0;
+  timezone->getOffset(ms_since_epoch, /*local_time=*/0, local_offset_ms,
+                      dst_offset_ms, error);
+  metrics->SetDSTOffsetStatus(error);
+  if (error != U_ZERO_ERROR) {
+    icu::ErrorCode icu_error;
+    icu_error.set(error);
+    Syslog::PrintErr("could not get DST offset: %s\n", icu_error.errorName());
+    return ZX_ERR_INTERNAL;
   }
-  *local_offset *= 60;
-  *dst_offset *= 60;
+  // We must return offset in seconds, so convert.
+  *local_offset = local_offset_ms / kMsPerSec;
+  *dst_offset = dst_offset_ms / kMsPerSec;
   return ZX_OK;
 }
 
+// Returns a C string with the time zone name. This module retains the
+// ownership of the pointer.
 const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) {
-  // TODO(abarth): Handle time zone changes.
-  static const auto* tz_name = new std::string([] {
-    std::string result;
-    tz->GetTimezoneId(&result);
-    return result;
-  }());
-  return tz_name->c_str();
+  ASSERT(timezone_name != nullptr);
+
+  // Sadly, since we do not know how long the timezone name will be needed, we
+  // can not ever deallocate it. So instead, we put it into a a set that will
+  // not move it around in memory and return a pointer to it.  Since the number
+  // of timezones is finite, this ensures that the memory taken up by timezones
+  // does not grow indefinitely, even if we end up retaining all the timezones
+  // there are.
+  const auto i = timezone_names.insert(timezone_name->Get());
+  ASSERT(i.first != timezone_names.end());
+  return i.first->c_str();
 }
 
 int OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch) {
-  int32_t local_offset, dst_offset;
-  zx_status_t status = GetLocalAndDstOffsetInSeconds(
+  int32_t local_offset = 0;
+  int32_t dst_offset = 0;
+  const zx_status_t status = GetLocalAndDstOffsetInSeconds(
       seconds_since_epoch, &local_offset, &dst_offset);
   return status == ZX_OK ? local_offset + dst_offset : 0;
 }
@@ -211,7 +449,7 @@
 }
 
 int64_t OS::GetCurrentMonotonicMicros() {
-  int64_t ticks = GetCurrentMonotonicTicks();
+  const int64_t ticks = GetCurrentMonotonicTicks();
   ASSERT(GetCurrentMonotonicFrequency() == kNanosecondsPerSecond);
   return ticks / kNanosecondsPerMicrosecond;
 }
@@ -231,6 +469,9 @@
   return -1;
 }
 
+// The timezone names encountered so far.  The timezone names must live forever.
+std::set<const std::string> timezone_names;
+
 // TODO(5411554):  May need to hoist these architecture dependent code
 // into a architecture specific file e.g: os_ia32_fuchsia.cc
 intptr_t OS::ActivationFrameAlignment() {
@@ -300,7 +541,7 @@
   va_end(measure_args);
 
   char* buffer;
-  if (zone) {
+  if (zone != nullptr) {
     buffer = zone->Alloc<char>(len + 1);
   } else {
     buffer = reinterpret_cast<char*>(malloc(len + 1));
@@ -364,19 +605,22 @@
 
   sys::ComponentContext* context = dart::ComponentContext();
   component_inspector = std::make_unique<sys::ComponentInspector>(context);
-  metrics = std::make_unique<InspectMetrics>(component_inspector->inspector());
+  metrics = std::make_shared<InspectMetrics>(component_inspector->inspector());
 
   InitializeTZData();
-  context->svc()->Connect(tz.NewRequest());
+  auto services = sys::ServiceDirectory::CreateFromNamespace();
+  services->Connect(property_provider.NewRequest());
+
+  timezone_name = TimezoneName::New(std::move(property_provider), metrics);
 }
 
 void OS::Cleanup() {
   if (message_loop != nullptr) {
     async_loop_shutdown(message_loop);
   }
-
-  metrics = nullptr;
-  component_inspector = nullptr;
+  timezone_name.reset();
+  metrics.reset();
+  component_inspector.reset();
 
   if (message_loop != nullptr) {
     // Check message_loop is still the default dispatcher before clearing it.
diff --git a/tools/VERSION b/tools/VERSION
index cfed773..a6a98c2 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 48
+PRERELEASE 49
 PRERELEASE_PATCH 0
\ No newline at end of file