[ VM / Service ] Made VM timeline functionality public through the service API.

Change-Id: I8228f0417047af53edc6f570940d452da3155bbd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106061
Reviewed-by: Siva Annamalai <asiva@google.com>
diff --git a/runtime/observatory/lib/src/repositories/timeline.dart b/runtime/observatory/lib/src/repositories/timeline.dart
index a954bf6..c2695ef 100644
--- a/runtime/observatory/lib/src/repositories/timeline.dart
+++ b/runtime/observatory/lib/src/repositories/timeline.dart
@@ -7,21 +7,21 @@
 class TimelineRepository implements M.TimelineRepository {
   Future<M.TimelineFlags> getFlags(M.VMRef ref) async {
     S.VM vm = ref as S.VM;
-    S.ServiceMap response = await vm.invokeRpc('_getVMTimelineFlags', {});
+    S.ServiceMap response = await vm.invokeRpc('getVMTimelineFlags', {});
     return new S.TimelineFlags(response);
   }
 
   Future setRecordedStreams(M.VMRef ref, Iterable<M.TimelineStream> streams) {
     S.VM vm = ref as S.VM;
     assert(vm != null);
-    return vm.invokeRpc('_setVMTimelineFlags', {
+    return vm.invokeRpc('setVMTimelineFlags', {
       'recordedStreams': '[${streams.map((s) => s.name).join(', ')}]',
     });
   }
 
   Future clear(M.VMRef ref) {
     S.VM vm = ref as S.VM;
-    return vm.invokeRpc('_clearVMTimeline', {});
+    return vm.invokeRpc('clearVMTimeline', {});
   }
 
   Future<Map<String, dynamic>> getIFrameParams(M.VMRef ref) async {
diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart
index 8d0c9f4..287d311 100644
--- a/runtime/observatory/tests/service/get_version_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_version_rpc_test.dart
@@ -12,7 +12,7 @@
     var result = await vm.invokeRpcNoUpgrade('getVersion', {});
     expect(result['type'], equals('Version'));
     expect(result['major'], equals(3));
-    expect(result['minor'], equals(18));
+    expect(result['minor'], equals(19));
     expect(result['_privateMajor'], equals(0));
     expect(result['_privateMinor'], equals(0));
   },
diff --git a/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart b/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart
index 8a60ea3..cdd9344 100644
--- a/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_vm_timeline_rpc_test.dart
@@ -94,14 +94,14 @@
     }
     Map arguments = event['args'];
     expect(arguments, new isInstanceOf<Map>());
-    expect(arguments['isolateNumber'], new isInstanceOf<String>());
+    expect(arguments['isolateId'], new isInstanceOf<String>());
   }
 }
 
 var tests = <VMTest>[
   (VM vm) async {
-    Map result = await vm.invokeRpcNoUpgrade('_getVMTimeline', {});
-    expect(result['type'], equals('_Timeline'));
+    Map result = await vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
     expect(result['traceEvents'], new isInstanceOf<List>());
     final int numEvents = result['traceEvents'].length;
     List dartEvents = filterForDartEvents(result['traceEvents']);
@@ -121,7 +121,7 @@
     int origin = timeOrigin(dartEvents);
     int extent = timeDuration(dartEvents, origin);
     // Query for the timeline with the time window for Dart events.
-    result = await vm.invokeRpcNoUpgrade('_getVMTimeline',
+    result = await vm.invokeRpcNoUpgrade('getVMTimeline',
         {'timeOriginMicros': origin, 'timeExtentMicros': extent});
     // Verify that we received fewer events than before.
     expect(result['traceEvents'].length, lessThan(numEvents));
diff --git a/runtime/observatory/tests/service/vm_timeline_events_test.dart b/runtime/observatory/tests/service/vm_timeline_events_test.dart
index 9b2e550..9586771 100644
--- a/runtime/observatory/tests/service/vm_timeline_events_test.dart
+++ b/runtime/observatory/tests/service/vm_timeline_events_test.dart
@@ -45,7 +45,7 @@
   },
   (Isolate isolate) async {
     // Get the flags.
-    Map flags = await isolate.vm.invokeRpcNoUpgrade('_getVMTimelineFlags', {});
+    Map flags = await isolate.vm.invokeRpcNoUpgrade('getVMTimelineFlags', {});
     expect(flags['type'], 'TimelineFlags');
     // Confirm that 'Dart' is available.
     expect(flags['availableStreams'].contains('Dart'), isTrue);
@@ -54,7 +54,7 @@
   },
   (Isolate isolate) async {
     // Enable the Dart category.
-    await isolate.vm.invokeRpcNoUpgrade('_setVMTimelineFlags', {
+    await isolate.vm.invokeRpcNoUpgrade('setVMTimelineFlags', {
       "recordedStreams": ["Dart"]
     });
   },
diff --git a/runtime/observatory/tests/service/vm_timeline_flags_test.dart b/runtime/observatory/tests/service/vm_timeline_flags_test.dart
index 4e71d95..5f5365f 100644
--- a/runtime/observatory/tests/service/vm_timeline_flags_test.dart
+++ b/runtime/observatory/tests/service/vm_timeline_flags_test.dart
@@ -31,7 +31,7 @@
   hasStoppedAtBreakpoint,
   (Isolate isolate) async {
     // Get the flags.
-    Map flags = await isolate.vm.invokeRpcNoUpgrade('_getVMTimelineFlags', {});
+    Map flags = await isolate.vm.invokeRpcNoUpgrade('getVMTimelineFlags', {});
     expect(flags['type'], 'TimelineFlags');
     // Confirm that 'Dart' is available.
     expect(flags['availableStreams'].contains('Dart'), isTrue);
@@ -40,21 +40,21 @@
   },
   (Isolate isolate) async {
     // Get the timeline.
-    Map result = await isolate.vm.invokeRpcNoUpgrade('_getVMTimeline', {});
-    expect(result['type'], equals('_Timeline'));
+    Map result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
     expect(result['traceEvents'], new isInstanceOf<List>());
     // Confirm that it as no non-meta data events.
     expect(filterEvents(result['traceEvents'], isNotMetaData).length, 0);
   },
   (Isolate isolate) async {
     // Enable the Dart category.
-    await isolate.vm.invokeRpcNoUpgrade('_setVMTimelineFlags', {
+    await isolate.vm.invokeRpcNoUpgrade('setVMTimelineFlags', {
       "recordedStreams": ["Dart"]
     });
   },
   (Isolate isolate) async {
     // Get the flags.
-    Map flags = await isolate.vm.invokeRpcNoUpgrade('_getVMTimelineFlags', {});
+    Map flags = await isolate.vm.invokeRpcNoUpgrade('getVMTimelineFlags', {});
     expect(flags['type'], 'TimelineFlags');
     // Confirm that only Dart is being recorded.
     expect(flags['recordedStreams'].length, equals(1));
@@ -65,8 +65,8 @@
   hasStoppedAtBreakpoint,
   (Isolate isolate) async {
     // Get the timeline.
-    Map result = await isolate.vm.invokeRpcNoUpgrade('_getVMTimeline', {});
-    expect(result['type'], equals('_Timeline'));
+    Map result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
     expect(result['traceEvents'], new isInstanceOf<List>());
     print(result['traceEvents']);
     // Confirm that Dart events are added.
@@ -79,16 +79,16 @@
   (Isolate isolate) async {
     // Disable the Dart category.
     await isolate.vm
-        .invokeRpcNoUpgrade('_setVMTimelineFlags', {"recordedStreams": []});
+        .invokeRpcNoUpgrade('setVMTimelineFlags', {"recordedStreams": []});
     // Grab the timeline and remember the number of Dart events.
-    Map result = await isolate.vm.invokeRpcNoUpgrade('_getVMTimeline', {});
-    expect(result['type'], equals('_Timeline'));
+    Map result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
     expect(result['traceEvents'], new isInstanceOf<List>());
     dartEventCount = filterEvents(result['traceEvents'], isDart).length;
   },
   (Isolate isolate) async {
     // Get the flags.
-    Map flags = await isolate.vm.invokeRpcNoUpgrade('_getVMTimelineFlags', {});
+    Map flags = await isolate.vm.invokeRpcNoUpgrade('getVMTimelineFlags', {});
     expect(flags['type'], 'TimelineFlags');
     // Confirm that 'Dart' is not being recorded.
     expect(flags['recordedStreams'].length, equals(0));
@@ -97,8 +97,8 @@
   hasStoppedAtBreakpoint,
   (Isolate isolate) async {
     // Grab the timeline and verify that we haven't added any new Dart events.
-    Map result = await isolate.vm.invokeRpcNoUpgrade('_getVMTimeline', {});
-    expect(result['type'], equals('_Timeline'));
+    Map result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
     expect(result['traceEvents'], new isInstanceOf<List>());
     expect(filterEvents(result['traceEvents'], isDart).length, dartEventCount);
     // Confirm that zero non-Dart events are added.
diff --git a/runtime/observatory/tests/service/write_cpu_profile_timeline_rpc_test.dart b/runtime/observatory/tests/service/write_cpu_profile_timeline_rpc_test.dart
index ea0a6ef..fad0eda 100644
--- a/runtime/observatory/tests/service/write_cpu_profile_timeline_rpc_test.dart
+++ b/runtime/observatory/tests/service/write_cpu_profile_timeline_rpc_test.dart
@@ -27,8 +27,8 @@
     print(result);
     expect(result['type'], equals('Success'));
 
-    result = await isolate.vm.invokeRpcNoUpgrade('_getVMTimeline', {});
-    expect(result['type'], equals('_Timeline'));
+    result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
     expect(result['traceEvents'], new isInstanceOf<List>());
 
     var events = result['traceEvents'];
diff --git a/runtime/vm/json_stream.cc b/runtime/vm/json_stream.cc
index aeaa9d0..30cddd2 100644
--- a/runtime/vm/json_stream.cc
+++ b/runtime/vm/json_stream.cc
@@ -136,6 +136,9 @@
       return "File system does not exist";
     case kFileDoesNotExist:
       return "File does not exist";
+    case kInvalidTimelineRequest:
+      return "The timeline related request could not be completed due to the "
+             "current configuration";
     default:
       return "Extension error";
   }
diff --git a/runtime/vm/json_stream.h b/runtime/vm/json_stream.h
index 98d39f3..b9fdef7 100644
--- a/runtime/vm/json_stream.h
+++ b/runtime/vm/json_stream.h
@@ -61,6 +61,7 @@
   kServiceAlreadyRegistered = 111,
   kServiceDisappeared = 112,
   kExpressionCompilationError = 113,
+  kInvalidTimelineRequest = 114,
 
   // Experimental (used in private rpcs).
   kFileSystemAlreadyExists = 1001,
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index eca7158..ed4dcc2 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -3656,6 +3656,18 @@
   TimelineEventRecorder* timeline_recorder = Timeline::recorder();
   // TODO(johnmccutchan): Return an error.
   ASSERT(timeline_recorder != NULL);
+  const char* name = timeline_recorder->name();
+  if ((strcmp(name, FUCHSIA_RECORDER_NAME) == 0) ||
+      (strcmp(name, SYSTRACE_RECORDER_NAME) == 0)) {
+    js->PrintError(kInvalidTimelineRequest,
+                   "A recorder of type \"%s\" is "
+                   "currently in use. As a result, timeline events are handled "
+                   "by the OS rather than the VM. See the VM service "
+                   "documentation for more details on where timeline events "
+                   "can be found for this recorder type.",
+                   timeline_recorder->name());
+    return true;
+  }
   int64_t time_origin_micros =
       Int64Parameter::Parse(js->LookupParam("timeOriginMicros"));
   int64_t time_extent_micros =
@@ -4882,7 +4894,7 @@
     build_expression_evaluation_scope_params },
   { "_clearCpuProfile", ClearCpuProfile,
     clear_cpu_profile_params },
-  { "_clearVMTimeline", ClearVMTimeline,
+  { "clearVMTimeline", ClearVMTimeline,
     clear_vm_timeline_params, },
   { "_compileExpression", CompileExpression, compile_expression_params },
   { "_enableProfiler", EnableProfiler,
@@ -4959,9 +4971,9 @@
     get_vm_metric_params },
   { "_getVMMetricList", GetVMMetricList,
     get_vm_metric_list_params },
-  { "_getVMTimeline", GetVMTimeline,
+  { "getVMTimeline", GetVMTimeline,
     get_vm_timeline_params },
-  { "_getVMTimelineFlags", GetVMTimelineFlags,
+  { "getVMTimelineFlags", GetVMTimelineFlags,
     get_vm_timeline_flags_params },
   { "invoke", Invoke, invoke_params },
   { "kill", Kill, kill_params },
@@ -4991,7 +5003,7 @@
     set_trace_class_allocation_params },
   { "setVMName", SetVMName,
     set_vm_name_params },
-  { "_setVMTimelineFlags", SetVMTimelineFlags,
+  { "setVMTimelineFlags", SetVMTimelineFlags,
     set_vm_timeline_flags_params },
   { "_collectAllGarbage", CollectAllGarbage,
     collect_all_garbage_params },
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index 9546d75..6e99782 100644
--- a/runtime/vm/service.h
+++ b/runtime/vm/service.h
@@ -15,7 +15,7 @@
 namespace dart {
 
 #define SERVICE_PROTOCOL_MAJOR_VERSION 3
-#define SERVICE_PROTOCOL_MINOR_VERSION 18
+#define SERVICE_PROTOCOL_MINOR_VERSION 19
 
 class Array;
 class EmbedderServiceHandler;
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index 239c6fc..52eea6b 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -1,8 +1,8 @@
-# Dart VM Service Protocol 3.18
+# Dart VM Service Protocol 3.19
 
 > Please post feedback to the [observatory-discuss group][discuss-list]
 
-This document describes of _version 3.18_ of the Dart VM Service Protocol. This
+This document describes of _version 3.19_ of the Dart VM Service Protocol. This
 protocol is used to communicate with a running Dart Virtual Machine.
 
 To use the Service Protocol, start the VM with the *--observe* flag.
@@ -27,6 +27,7 @@
   - [addBreakpoint](#addbreakpoint)
   - [addBreakpointWithScriptUri](#addbreakpointwithscripturi)
   - [addBreakpointAtEntry](#addbreakpointatentry)
+  - [clearVMTimeline](#clearvmtimeline)
   - [evaluate](#evaluate)
   - [evaluateInFrame](#evaluateinframe)
   - [getAllocationProfile](#getallocationprofile)
@@ -39,6 +40,8 @@
   - [getStack](#getstack)
   - [getVersion](#getversion)
   - [getVM](#getvm)
+  - [getVMTimeline](#getvmtimeline)
+  - [getVMTimelineFlags](#getvmtimelineflags)
   - [invoke](#invoke)
   - [pause](#pause)
   - [kill](#kill)
@@ -50,6 +53,7 @@
   - [setLibraryDebuggable](#setlibrarydebuggable)
   - [setName](#setname)
   - [setVMName](#setvmname)
+  - [setVMTimelineFlags](#setvmtimelineflags)
   - [streamCancel](#streamcancel)
   - [streamListen](#streamlisten)
 - [Public Types](#public-types)
@@ -98,6 +102,9 @@
   - [Stack](#stack)
   - [StepOption](#stepoption)
   - [Success](#success)
+  - [Timeline](#timeline)
+  - [TimelineEvent](#timelineevent)
+  - [TimelineFlags](#timelineflags)
   - [TypeArguments](#typearguments)
   - [UresolvedSourceLocation](#unresolvedsourcelocation)
   - [Version](#version)
@@ -203,8 +210,7 @@
 111 | Service already registered | Service with such name has already been registered by this client
 112 | Service disappeared | Failed to fulfill service request, likely service handler is no longer available
 113 | Expression compilation error | Request to compile expression failed
-
-
+114 | Invalid timeline request | The timeline related request could not be completed due to the current configuration
 
 ## Events
 
@@ -479,6 +485,17 @@
 
 Note that breakpoints are added and removed on a per-isolate basis.
 
+
+### clearVMTimeline
+
+``` 
+Success clearVMTimeline()
+```
+
+Clears all VM timeline events.
+
+See [Success](#success).
+
 ### invoke
 
 ```
@@ -768,6 +785,42 @@
 
 See [VM](#vm).
 
+
+### getVMTimeline
+
+```
+Timeline getVMTimeline(int timeOriginMicros,
+                       int timeExtentMicros)
+```
+
+The _getVMTimeline_ RPC is used to retrieve an object which contains VM timeline
+events.
+
+The _timeOriginMicros_ parameter is the beginning of the time range used to filter
+timeline events.
+
+The _timeExtentMicros_ parameter specifies how large the time range used to filter
+timeline events should be.
+
+For example, given _timeOriginMicros_ and _timeExtentMicros_, only timeline events
+from the following time range will be returned: `(timeOriginMicros, timeOriginMicros + timeExtentMicros)`.
+
+If _getVMTimeline_ is invoked while the current recorder is one of Fuchsia or
+Systrace, the _114_ error code, invalid timeline request, will be returned as
+timeline events are handled by the OS in these modes.
+
+### getVMTimelineFlags
+
+```
+TimelineFlags getVMTimelineFlags()
+```
+
+The _getVMTimelineFlags_ RPC returns information about the current VM timeline configuration.
+
+To change which timeline streams are currently enabled, see [setVMTimelineFlags](#setvmtimelineflags).
+
+See [TimelineFlags](#timelineflags).
+
 ### pause
 
 ```
@@ -794,7 +847,6 @@
 
 ### reloadSources
 
-
 ```
 ReloadReport reloadSources(string isolateId,
                            bool force [optional],
@@ -934,6 +986,22 @@
 
 See [Success](#success).
 
+### setVMTimelineFlags
+
+```
+Success setVMTimelineFlags(string[] recordedStreams)
+```
+
+The _setVMTimelineFlags_ RPC is used to set which timeline streams are enabled.
+
+The _recordedStreams_ parameter is the list of all timeline streams which are
+to be enabled. Streams not explicitly specified will be disabled. Invalid stream
+names are ignored.
+
+To get the list of currently enabled timeline streams, see [getVMTimelineFlags](#getvmtimelineflags).
+
+See [Success](#success).
+
 ### streamCancel
 
 ```
@@ -2767,6 +2835,21 @@
 
 The _Success_ type is used to indicate that an operation completed successfully.
 
+### Timeline
+
+```
+class Timeline extends Response {
+  // A list of timeline events.
+  TimelineEvent[] traceEvents;
+
+  // The start of the period of time in which traceEvents were collected.
+  int timeOriginMicros;
+
+  // The duration of time covered by the timeline.
+  int timeExtentMicros;
+}
+```
+
 ### TimelineEvent
 
 ```
@@ -2776,6 +2859,23 @@
 
 An _TimelineEvent_ is an arbitrary map that contains a [Trace Event Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview) event.
 
+### TimelineFlags
+
+```
+class TimelineFlags extends Response {
+  // The name of the recorder currently in use. Recorder types include, but are
+  // not limited to: Callback, Endless, Fuchsia, Ring, Startup, and Systrace.
+  // Set to "null" if no recorder is currently set. 
+  string recorderName;
+
+  // The list of all available timeline streams.
+  string[] availableStreams;
+
+  // The list of timeline streams that are currently enabled.
+  string[] recordedStreams;
+}
+```
+
 ### TypeArguments
 
 ```
@@ -2922,5 +3022,6 @@
 3.16 | Add 'getMemoryUsage' RPC and 'MemoryUsage' object.
 3.17 | Add 'Logging' event kind and the LogRecord class.
 3.18 | Add 'getAllocationProfile' RPC and 'AllocationProfile' and 'ClassHeapStats' objects.
+3.19 | Add 'clearVMTimeline', 'getVMTimeline', 'getVMTimelineFlags', 'setVMTimelineFlags', 'Timeline', and 'TimelineFlags'.
 
 [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
diff --git a/runtime/vm/service/service_dev.md b/runtime/vm/service/service_dev.md
index 36aeb96..148ee39 100644
--- a/runtime/vm/service/service_dev.md
+++ b/runtime/vm/service/service_dev.md
@@ -1,8 +1,8 @@
-# Dart VM Service Protocol 3.19-dev
+# Dart VM Service Protocol 3.20-dev
 
 > Please post feedback to the [observatory-discuss group][discuss-list]
 
-This document describes of _version 3.19-dev_ of the Dart VM Service Protocol. This
+This document describes of _version 3.20-dev_ of the Dart VM Service Protocol. This
 protocol is used to communicate with a running Dart Virtual Machine.
 
 To use the Service Protocol, start the VM with the *--observe* flag.
@@ -27,6 +27,7 @@
   - [addBreakpoint](#addbreakpoint)
   - [addBreakpointWithScriptUri](#addbreakpointwithscripturi)
   - [addBreakpointAtEntry](#addbreakpointatentry)
+  - [clearVMTimeline](#clearvmtimeline)
   - [evaluate](#evaluate)
   - [evaluateInFrame](#evaluateinframe)
   - [getAllocationProfile](#getallocationprofile)
@@ -39,6 +40,8 @@
   - [getStack](#getstack)
   - [getVersion](#getversion)
   - [getVM](#getvm)
+  - [getVMTimeline](#getvmtimeline)
+  - [getVMTimelineFlags](#getvmtimelineflags)
   - [invoke](#invoke)
   - [pause](#pause)
   - [kill](#kill)
@@ -50,6 +53,7 @@
   - [setLibraryDebuggable](#setlibrarydebuggable)
   - [setName](#setname)
   - [setVMName](#setvmname)
+  - [setVMTimelineFlags](#setvmtimelineflags)
   - [streamCancel](#streamcancel)
   - [streamListen](#streamlisten)
 - [Public Types](#public-types)
@@ -98,6 +102,9 @@
   - [Stack](#stack)
   - [StepOption](#stepoption)
   - [Success](#success)
+  - [Timeline](#timeline)
+  - [TimelineEvent](#timelineevent)
+  - [TimelineFlags](#timelineflags)
   - [TypeArguments](#typearguments)
   - [UresolvedSourceLocation](#unresolvedsourcelocation)
   - [Version](#version)
@@ -203,8 +210,7 @@
 111 | Service already registered | Service with such name has already been registered by this client
 112 | Service disappeared | Failed to fulfill service request, likely service handler is no longer available
 113 | Expression compilation error | Request to compile expression failed
-
-
+114 | Invalid timeline request | The timeline related request could not be completed due to the current configuration
 
 ## Events
 
@@ -479,6 +485,17 @@
 
 Note that breakpoints are added and removed on a per-isolate basis.
 
+
+### clearVMTimeline
+
+``` 
+Success clearVMTimeline()
+```
+
+Clears all VM timeline events.
+
+See [Success](#success).
+
 ### invoke
 
 ```
@@ -768,6 +785,42 @@
 
 See [VM](#vm).
 
+
+### getVMTimeline
+
+```
+Timeline getVMTimeline(int timeOriginMicros,
+                       int timeExtentMicros)
+```
+
+The _getVMTimeline_ RPC is used to retrieve an object which contains VM timeline
+events.
+
+The _timeOriginMicros_ parameter is the beginning of the time range used to filter
+timeline events.
+
+The _timeExtentMicros_ parameter specifies how large the time range used to filter
+timeline events should be.
+
+For example, given _timeOriginMicros_ and _timeExtentMicros_, only timeline events
+from the following time range will be returned: `(timeOriginMicros, timeOriginMicros + timeExtentMicros)`.
+
+If _getVMTimeline_ is invoked while the current recorder is one of Fuchsia or
+Systrace, the _114_ error code, invalid timeline request, will be returned as
+timeline events are handled by the OS in these modes.
+
+### getVMTimelineFlags
+
+```
+TimelineFlags getVMTimelineFlags()
+```
+
+The _getVMTimelineFlags_ RPC returns information about the current VM timeline configuration.
+
+To change which timeline streams are currently enabled, see [setVMTimelineFlags](#setvmtimelineflags).
+
+See [TimelineFlags](#timelineflags).
+
 ### pause
 
 ```
@@ -794,7 +847,6 @@
 
 ### reloadSources
 
-
 ```
 ReloadReport reloadSources(string isolateId,
                            bool force [optional],
@@ -934,6 +986,22 @@
 
 See [Success](#success).
 
+### setVMTimelineFlags
+
+```
+Success setVMTimelineFlags(string[] recordedStreams)
+```
+
+The _setVMTimelineFlags_ RPC is used to set which timeline streams are enabled.
+
+The _recordedStreams_ parameter is the list of all timeline streams which are
+to be enabled. Streams not explicitly specified will be disabled. Invalid stream
+names are ignored.
+
+To get the list of currently enabled timeline streams, see [getVMTimelineFlags](#getvmtimelineflags).
+
+See [Success](#success).
+
 ### streamCancel
 
 ```
@@ -2767,6 +2835,21 @@
 
 The _Success_ type is used to indicate that an operation completed successfully.
 
+### Timeline
+
+```
+class Timeline extends Response {
+  // A list of timeline events.
+  TimelineEvent[] traceEvents;
+
+  // The start of the period of time in which traceEvents were collected.
+  int timeOriginMicros;
+
+  // The duration of time covered by the timeline.
+  int timeExtentMicros;
+}
+```
+
 ### TimelineEvent
 
 ```
@@ -2776,6 +2859,23 @@
 
 An _TimelineEvent_ is an arbitrary map that contains a [Trace Event Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview) event.
 
+### TimelineFlags
+
+```
+class TimelineFlags extends Response {
+  // The name of the recorder currently in use. Recorder types include, but are
+  // not limited to: Callback, Endless, Fuchsia, Ring, Startup, and Systrace.
+  // Set to "null" if no recorder is currently set. 
+  string recorderName;
+
+  // The list of all available timeline streams.
+  string[] availableStreams;
+
+  // The list of timeline streams that are currently enabled.
+  string[] recordedStreams;
+}
+```
+
 ### TypeArguments
 
 ```
@@ -2922,5 +3022,6 @@
 3.16 | Add 'getMemoryUsage' RPC and 'MemoryUsage' object.
 3.17 | Add 'Logging' event kind and the LogRecord class.
 3.18 | Add 'getAllocationProfile' RPC and 'AllocationProfile' and 'ClassHeapStats' objects.
+3.19 | Add 'clearVMTimeline', 'getVMTimeline', 'getVMTimelineFlags', 'setVMTimelineFlags', 'Timeline', and 'TimelineFlags'.
 
 [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc
index 329f488..8b9a644 100644
--- a/runtime/vm/timeline.cc
+++ b/runtime/vm/timeline.cc
@@ -17,6 +17,7 @@
 #include "vm/lockers.h"
 #include "vm/log.h"
 #include "vm/object.h"
+#include "vm/service.h"
 #include "vm/service_event.h"
 #include "vm/thread.h"
 
@@ -665,7 +666,7 @@
     if (isolate_id_ != ILLEGAL_PORT) {
       // If we have one, append the isolate id.
       stream->UncloseObject();
-      stream->PrintfProperty("isolateNumber", "%" Pd64 "",
+      stream->PrintfProperty("isolateId", ISOLATE_SERVICE_ID_FORMAT_STRING,
                              static_cast<int64_t>(isolate_id_));
       stream->CloseObject();
     }
@@ -677,7 +678,7 @@
     }
     if (isolate_id_ != ILLEGAL_PORT) {
       // If we have one, append the isolate id.
-      args.AddPropertyF("isolateNumber", "%" Pd64 "",
+      args.AddPropertyF("isolateId", ISOLATE_SERVICE_ID_FORMAT_STRING,
                         static_cast<int64_t>(isolate_id_));
     }
   }
@@ -1199,7 +1200,7 @@
     return;
   }
   JSONObject topLevel(js);
-  topLevel.AddProperty("type", "_Timeline");
+  topLevel.AddProperty("type", "Timeline");
   {
     JSONArray events(&topLevel, "traceEvents");
     PrintJSONMeta(&events);
@@ -1294,11 +1295,13 @@
     return;
   }
   JSONObject topLevel(js);
-  topLevel.AddProperty("type", "_Timeline");
+  topLevel.AddProperty("type", "Timeline");
   {
     JSONArray events(&topLevel, "traceEvents");
     PrintJSONMeta(&events);
   }
+  topLevel.AddPropertyTimeMicros("timeOriginMicros", TimeOriginMicros());
+  topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
 }
 
 void TimelineEventCallbackRecorder::PrintTraceEvent(
@@ -1332,11 +1335,13 @@
     return;
   }
   JSONObject topLevel(js);
-  topLevel.AddProperty("type", "_Timeline");
+  topLevel.AddProperty("type", "Timeline");
   {
     JSONArray events(&topLevel, "traceEvents");
     PrintJSONMeta(&events);
   }
+  topLevel.AddPropertyTimeMicros("timeOriginMicros", TimeOriginMicros());
+  topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
 }
 
 void TimelineEventPlatformRecorder::PrintTraceEvent(
@@ -1380,7 +1385,7 @@
     return;
   }
   JSONObject topLevel(js);
-  topLevel.AddProperty("type", "_Timeline");
+  topLevel.AddProperty("type", "Timeline");
   {
     JSONArray events(&topLevel, "traceEvents");
     PrintJSONMeta(&events);
diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h
index 9d43824..68d85c2 100644
--- a/runtime/vm/timeline.h
+++ b/runtime/vm/timeline.h
@@ -36,6 +36,13 @@
 class VirtualMemory;
 class Zone;
 
+#define CALLBACK_RECORDER_NAME "Callback"
+#define ENDLESS_RECORDER_NAME "Endless"
+#define FUCHSIA_RECORDER_NAME "Fuchsia"
+#define RING_RECORDER_NAME "Ring"
+#define STARTUP_RECORDER_NAME "Startup"
+#define SYSTRACE_RECORDER_NAME "Systrace"
+
 // (name, fuchsia_name).
 #define TIMELINE_STREAM_LIST(V)                                                \
   V(API, "dart:api")                                                           \
@@ -787,7 +794,7 @@
       : TimelineEventFixedBufferRecorder(capacity) {}
   virtual ~TimelineEventRingRecorder() {}
 
-  const char* name() const { return "Ring"; }
+  const char* name() const { return RING_RECORDER_NAME; }
 
  protected:
   TimelineEventBlock* GetNewBlockLocked();
@@ -801,7 +808,7 @@
       : TimelineEventFixedBufferRecorder(capacity) {}
   virtual ~TimelineEventStartupRecorder() {}
 
-  const char* name() const { return "Startup"; }
+  const char* name() const { return STARTUP_RECORDER_NAME; }
 
  protected:
   TimelineEventBlock* GetNewBlockLocked();
@@ -823,7 +830,7 @@
   // |event| as it may be freed as soon as this function returns.
   virtual void OnEvent(TimelineEvent* event) = 0;
 
-  const char* name() const { return "Callback"; }
+  const char* name() const { return CALLBACK_RECORDER_NAME; }
 
  protected:
   TimelineEventBlock* GetNewBlockLocked() { return NULL; }
@@ -846,7 +853,7 @@
   void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter);
 #endif
 
-  const char* name() const { return "Endless"; }
+  const char* name() const { return ENDLESS_RECORDER_NAME; }
 
  protected:
   TimelineEvent* StartEvent();
@@ -919,7 +926,7 @@
   TimelineEventFuchsiaRecorder() {}
   virtual ~TimelineEventFuchsiaRecorder() {}
 
-  const char* name() const { return "Fuchsia"; }
+  const char* name() const { return FUCHSIA_RECORDER_NAME; }
 
  private:
   void OnEvent(TimelineEvent* event);
@@ -939,7 +946,7 @@
                                 char* buffer,
                                 intptr_t buffer_size);
 
-  const char* name() const { return "Systrace"; }
+  const char* name() const { return SYSTRACE_RECORDER_NAME; }
 
  private:
   void OnEvent(TimelineEvent* event);
diff --git a/runtime/vm/timeline_test.cc b/runtime/vm/timeline_test.cc
index eaf09f6..56313ab 100644
--- a/runtime/vm/timeline_test.cc
+++ b/runtime/vm/timeline_test.cc
@@ -256,8 +256,8 @@
   JSONStream js;
   TimelineEventFilter filter;
   recorder->PrintJSON(&js, &filter);
-  // Check the type. This test will fail if we ever make Timeline public.
-  EXPECT_SUBSTRING("\"type\":\"_Timeline\"", js.ToCString());
+  // Check the type.
+  EXPECT_SUBSTRING("\"type\":\"Timeline\"", js.ToCString());
   // Check that there is a traceEvents array.
   EXPECT_SUBSTRING("\"traceEvents\":[", js.ToCString());
 }
diff --git a/sdk/lib/vmservice/vmservice.dart b/sdk/lib/vmservice/vmservice.dart
index 4237f28..636feb9 100644
--- a/sdk/lib/vmservice/vmservice.dart
+++ b/sdk/lib/vmservice/vmservice.dart
@@ -69,6 +69,7 @@
 const kServiceAlreadyRegistered = 111;
 const kServiceDisappeared = 112;
 const kExpressionCompilationError = 113;
+const kInvalidTimelineRequest = 114;
 
 // Experimental (used in private rpcs).
 const kFileSystemAlreadyExists = 1001;
@@ -87,6 +88,8 @@
   kServiceAlreadyRegistered: 'Service already registered',
   kServiceDisappeared: 'Service has disappeared',
   kExpressionCompilationError: 'Expression compilation error',
+  kInvalidTimelineRequest: 'The timeline related request could not be completed'
+      'due to the current configuration',
 };
 
 String encodeRpcError(Message message, int code, {String details}) {