Include startup time according to adb start-activity

Change-Id: I9d967848492457d266b3346e86b096a9303aefe7
diff --git a/tools/startup/adb_utils.py b/tools/startup/adb_utils.py
index 41717cf..136c99a 100644
--- a/tools/startup/adb_utils.py
+++ b/tools/startup/adb_utils.py
@@ -180,13 +180,25 @@
   assert len(stdout) == 0
   time.sleep(sleep_in_seconds)
 
-def launch_activity(app_id, activity, device_id=None):
-  cmd = create_adb_cmd(
-    'shell am start -n %s/%s' % (app_id, activity), device_id)
+def launch_activity(
+    app_id, activity, device_id=None, wait_for_activity_to_launch=False):
+  args = ['shell', 'am', 'start', '-n', '%s/%s' % (app_id, activity)]
+  if wait_for_activity_to_launch:
+    args.append('-W')
+  cmd = create_adb_cmd(args, device_id)
   stdout = subprocess.check_output(cmd).decode('utf-8').strip()
   expected_stdout = (
       'Starting: Intent { cmp=%s/.%s }' % (app_id, activity[len(app_id)+1:]))
-  assert stdout == expected_stdout, 'was %s' % stdout
+  assert stdout.startswith(expected_stdout), 'was %s' % stdout
+  lines = stdout.splitlines()
+  result = {}
+  for line in lines:
+    if line.startswith('TotalTime: '):
+      total_time_str = line.removeprefix('TotalTime: ')
+      assert total_time_str.isdigit()
+      result['total_time'] = int(total_time_str)
+  assert not wait_for_activity_to_launch or 'total_time' in result
+  return result
 
 def prepare_for_interaction_with_device(device_id=None, device_pin=None):
   # Increase screen off timeout to avoid device screen turns off.
diff --git a/tools/startup/trace_generator.py b/tools/startup/trace_generator.py
index b4a4089..37f5c71 100755
--- a/tools/startup/trace_generator.py
+++ b/tools/startup/trace_generator.py
@@ -89,14 +89,17 @@
       out_dir, tmp_dir)
 
   # Launch main activity.
-  adb_utils.launch_activity(
-      options.app_id, options.main_activity, options.device_id)
+  launch_activity_result = adb_utils.launch_activity(
+      options.app_id,
+      options.main_activity,
+      options.device_id,
+      wait_for_activity_to_launch=True)
 
   # Wait for perfetto trace collector to stop.
   perfetto_utils.stop_record_android_trace(perfetto_process, out_dir)
 
   # Get minor and major page faults from app process.
-  data = compute_data(perfetto_trace_path, options)
+  data = compute_data(launch_activity_result, perfetto_trace_path, options)
   write_data(out_dir, data)
   return data
 
@@ -104,7 +107,7 @@
   for key, value in data.items():
     if key == 'time':
       continue
-    if hasattr(sum_data, key):
+    if key in sum_data:
       if key == 'app_id':
         assert sum_data[key] == value
       else:
@@ -115,7 +118,7 @@
     else:
       sum_data[key] = value
 
-def compute_data(perfetto_trace_path, options):
+def compute_data(launch_activity_result, perfetto_trace_path, options):
   minfl, majfl = adb_utils.get_minor_major_page_faults(
       options.app_id, options.device_id)
   data = {
@@ -124,36 +127,45 @@
     'minfl': minfl,
     'majfl': majfl
   }
-  startup_data = compute_startup_data(perfetto_trace_path, options)
+  startup_data = compute_startup_data(
+      launch_activity_result, perfetto_trace_path, options)
   return data | startup_data
 
-def compute_startup_data(perfetto_trace_path, options):
-  trace_processor = TraceProcessor(file_path=perfetto_trace_path)
-
-  # Compute time to first frame according to the builtin android_startup metric.
-  startup_metric = trace_processor.metric(['android_startup'])
-  time_to_first_frame_ms = \
-      startup_metric.android_startup.startup[0].to_first_frame.dur_ms
-
-  # Compute time to first and last doFrame event.
-  bind_application_slice = perfetto_utils.find_unique_slice_by_name(
-      'bindApplication', options, trace_processor)
-  activity_start_slice = perfetto_utils.find_unique_slice_by_name(
-      'activityStart', options, trace_processor)
-  do_frame_slices = perfetto_utils.find_slices_by_name(
-      'Choreographer#doFrame', options, trace_processor)
-  first_do_frame_slice = next(do_frame_slices)
-  *_, last_do_frame_slice = do_frame_slices
-
-  return {
-    'time_to_first_frame_ms': time_to_first_frame_ms,
-    'time_to_first_choreographer_do_frame_ms':
-        perfetto_utils.get_slice_end_since_start(
-            first_do_frame_slice, bind_application_slice),
-    'time_to_last_choreographer_do_frame_ms':
-        perfetto_utils.get_slice_end_since_start(
-            last_do_frame_slice, bind_application_slice)
+def compute_startup_data(launch_activity_result, perfetto_trace_path, options):
+  startup_data = {
+    'time_to_activity_started_ms': launch_activity_result.get('total_time')
   }
+  perfetto_startup_data = {}
+  if not options.no_perfetto:
+    trace_processor = TraceProcessor(file_path=perfetto_trace_path)
+
+    # Compute time to first frame according to the builtin android_startup metric.
+    startup_metric = trace_processor.metric(['android_startup'])
+    time_to_first_frame_ms = \
+        startup_metric.android_startup.startup[0].to_first_frame.dur_ms
+
+    # Compute time to first and last doFrame event.
+    bind_application_slice = perfetto_utils.find_unique_slice_by_name(
+        'bindApplication', options, trace_processor)
+    activity_start_slice = perfetto_utils.find_unique_slice_by_name(
+        'activityStart', options, trace_processor)
+    do_frame_slices = perfetto_utils.find_slices_by_name(
+        'Choreographer#doFrame', options, trace_processor)
+    first_do_frame_slice = next(do_frame_slices)
+    *_, last_do_frame_slice = do_frame_slices
+
+    perfetto_startup_data = {
+      'time_to_first_frame_ms': round(time_to_first_frame_ms),
+      'time_to_first_choreographer_do_frame_ms':
+          round(perfetto_utils.get_slice_end_since_start(
+              first_do_frame_slice, bind_application_slice)),
+      'time_to_last_choreographer_do_frame_ms':
+          round(perfetto_utils.get_slice_end_since_start(
+              last_do_frame_slice, bind_application_slice))
+    }
+
+  # Return combined startup data.
+  return startup_data | perfetto_startup_data
 
 def write_data(out_dir, data):
   data_path = os.path.join(out_dir, 'data.txt')
@@ -193,6 +205,10 @@
   result.add_argument('--main-activity',
                       help='Main activity class name',
                       required=True)
+  result.add_argument('--no-perfetto',
+                      help='Disables perfetto trace generation',
+                      action='store_true',
+                      default=False)
   result.add_argument('--out-dir',
                       help='Directory to store trace files in',
                       required=True)