Snap for 5031425 from ef07d14701d450e0a7b30c30c80624791852cf97 to emu-master-qemu-release

Change-Id: Iecf39673b27b9651d8781fc5312c60437c244e52
diff --git a/build/masters/master.client.adt/builders.pyl b/build/masters/master.client.adt/builders.pyl
index bb4f684..c430752 100644
--- a/build/masters/master.client.adt/builders.pyl
+++ b/build/masters/master.client.adt/builders.pyl
@@ -143,14 +143,24 @@
        "branch": "all",
        "name_identifier": "system-images",
     },
+     "sys_image_oc_mr1_iot_dev_poller": {
+       "gs_bucket": "android-build-emu-sysimage",
+       "gs_path": ["builds/git_oc-mr1-iot-dev-linux-sdk_google_iot_x86-userdebug/"],
+       "pollInterval": 1029,
+       "type": "GSMultiPoller",
+       "project": "oc-mr1-iot-dev",
+       "branch": "all",
+       "name_identifier": "system-images",
+     },
     "sys_image_pi_dev_poller": {
        "gs_bucket": "android-build-emu-sysimage",
-       "gs_path": ["builds/git_pi-dev-mac-sdk_gphone_x86-sdk_addon_mac/",
-		   "builds/git_pi-dev-linux-sdk_gphone_x86_64-userdebug/",
-		   "builds/git_pi-dev-linux-sdk_gwear_x86-sdk/"],
+       "gs_path": ["builds/git_pi-emu-dev-mac-sdk_gphone_x86-sdk_addon_mac/",
+		   "builds/git_pi-emu-dev-linux-sdk_gphone_x86_64-userdebug/",
+		   "builds/git_pi-emu-dev-linux-sdk_gwear_x86-sdk/",
+		   "builds/git_pi-emu-dev-linux-sdk_gphone_x86-user/"],
        "pollInterval": 597,
        "type": "GSMultiPoller",
-       "project": "pi-dev",
+       "project": "pi-emu-dev",
        "branch": "all",
        "name_identifier": "system-images",
     },
@@ -318,7 +328,7 @@
        "id": "_10",
        "scheduler": "sysimg-aosp",
      },
-     "UI API P": {
+     "UI API 28": {
        "name": "system image UI test pi",
        "id": "_11",
        "scheduler": "sysimg-pi",
@@ -443,6 +453,11 @@
        "id": "_22",
        "scheduler": "sysimg-oc-mr1-car-support-release",
      },
+     "Boot API 27 - IoT": {
+       "name": "sysimg-oc-mr1-iot-dev",
+       "id": "_36",
+       "scheduler": "sysimg-oc-mr1-iot-dev",
+     },
      "Boot API P": {
        "name": "sysimg-pi",
        "id": "_33",
@@ -478,7 +493,7 @@
      "Ubuntu 14.04 HD 4400": {
        "recipe": "adt/adt",
        "slave_pools": ["ubuntu-14_04-intel-hd-4400"],
-       "categories": ["UI API 26", "UI API 27", "Cross Builds"],
+       "categories": ["UI API 26", "UI API 27", "UI API 28", "Cross Builds"],
        "tag": "Linux",
      },
      "Ubuntu 16.04 Thinkpad 2017": {
@@ -492,8 +507,8 @@
        "slave_pools": ["ubuntu-16_04-thinkpad-2016"],
        "categories": ["Boot API 15", "Boot API 16", "Boot API 17", "Boot API 18", "Boot API 19", "Boot API 21",
        		      "Boot API 22", "Boot API 23", "Boot API 24", "Boot API 25", "Boot API 26", "Boot API 27",
-		      "Boot API 27 - Auto", "Boot API P", "Boot API P - Auto", "Boot Master", "Boot Master - IoT", "Boot AOSP",
-		      "Cross Builds"],
+		      "Boot API 27 - Auto", "Boot API 27 - IoT", "Boot API P", "Boot API P - Auto", "Boot Master",
+          "Boot Master - IoT", "Boot AOSP", "Cross Builds"],
        "tag": "Linux",
      },
      "Ubuntu 14.04 Quadro 600": {
@@ -525,7 +540,8 @@
        "slave_pools": ["win-7-64-intel-hd-4400"],
        "categories": ["Boot API 15", "Boot API 16", "Boot API 17", "Boot API 18", "Boot API 19", "Boot API 21",
        		      "Boot API 22", "Boot API 23", "Boot API 24", "Boot API 25", "Boot API 26", "Boot API 27",
-		      "Boot API 27 - Auto", "Boot API P", "Boot API P - Auto", "Boot Master", "Boot Master - IoT", "Boot AOSP"],
+		      "Boot API 27 - Auto", "Boot API 27 - IoT", "Boot API P", "Boot API P - Auto", "Boot Master",
+          "Boot Master - IoT", "Boot AOSP"],
        "tag": "Win",
      },
      "Win 8 64-bit Quadro 600": {
@@ -537,7 +553,7 @@
      "Win 8.1 64-bit Quadro 600": {
        "recipe": "adt/adt",
        "slave_pools": ["win-8_1-64-nvidia-quadro-600"],
-       "categories": ["UI API 26", "UI API 27"],
+       "categories": ["UI API 26", "UI API 27", "UI API 28"],
        "tag": "Win",
      },
      "Win 10 64-bit Quadro 600": {
@@ -556,7 +572,7 @@
      "Mac 10.10.5 Intel HD 5000": {
        "recipe": "adt/adt",
        "slave_pools": ["mac-10-10-5-intel-hd-5000"],
-       "categories": ["UI API 26", "UI API 27"],
+       "categories": ["UI API 26", "UI API 27", "UI API 28"],
        "tag": "Mac",
      },
      "Mac 10.10.5 Iris Pro": {
@@ -570,7 +586,8 @@
        "slave_pools": ["mac-10-8-5-intel-hd-5000"],
        "categories": ["Boot API 15", "Boot API 16", "Boot API 17", "Boot API 18", "Boot API 19", "Boot API 21",
        		      "Boot API 22", "Boot API 23", "Boot API 24", "Boot API 25", "Boot API 26", "Boot API 27",
-		      "Boot API 27 - Auto", "Boot API P", "Boot API P - Auto", "Boot Master", "Boot Master - IoT", "Boot AOSP"],
+		      "Boot API 27 - Auto", "Boot API 27 - IoT", "Boot API P", "Boot API P - Auto", "Boot Master",
+          "Boot Master - IoT", "Boot AOSP"],
        "tag": "Mac",
      },
      "Mac 10.11.1 Iris Pro": {
@@ -675,6 +692,10 @@
       "project": ["oc-mr1-emu-dev"],
       "type": "emu_scheduler",
     },
+    "sysimg-oc-mr1-iot-dev": {
+      "project": ["oc-mr1-iot-dev"],
+      "type": "emu_scheduler",
+    },
     "sysimg-nyc": {
       "project": ["nyc-emu-dev"],
       "type": "emu_scheduler",
diff --git a/build/scripts/slave/recipes/adt/adt.py b/build/scripts/slave/recipes/adt/adt.py
index 4613cec..a7bf825 100644
--- a/build/scripts/slave/recipes/adt/adt.py
+++ b/build/scripts/slave/recipes/adt/adt.py
@@ -47,6 +47,7 @@
         'oc-mr1-car-support-release': bootStep('OC_MR1_CAR_SUPPORT', '{"ori": "oc-mr1-car-support"}'),
         'oc-emu-dev': bootStep('OC', '{"ori": "oc"}'),
         'oc-mr1-emu-dev': bootStep('OC_MR1', '{"ori": "oc-mr1"}'),
+        'oc-mr1-iot-dev': bootStep('OC_MR1_IOT', '{"ori": "oc-mr1-iot"}'),
         'lmp-emu-dev': bootStep('LMP', '{"ori": "lmp"}'),
         'klp-emu-dev': bootStep('KLP', '{"ori": "klp"}'),
         'gb-emu-dev': bootStep('GB', '{"ori": "gb"}'),
diff --git a/build/scripts/slave/recipes/adt/sysimage_release_psq.py b/build/scripts/slave/recipes/adt/sysimage_release_psq.py
index 8f81f5a..ad4c10d 100644
--- a/build/scripts/slave/recipes/adt/sysimage_release_psq.py
+++ b/build/scripts/slave/recipes/adt/sysimage_release_psq.py
@@ -50,7 +50,8 @@
   'git_nyc-preview-release': '25',  # Preview
   'git_nyc-mr1-emu-release': '25',
   'git_oc-emu-release': '26',
-  'git_oc-mr1-emu-release': '27'
+  'git_oc-mr1-emu-release': '27',
+  'git_pi-emu-release': '28'
 }
 
 # Variables needed to communicate with Gerrit REST API.
@@ -194,7 +195,7 @@
                            'test_ui.*',
                            'config.csv',
                            # We run only x86 images for UI tests.
-                           # Besides, UiAutomation framework only supports API 18 or plus.
+                           # UiAutomation framework only supports API 18 or greater.
                            '{"abi": "x86", "api": ">=18"}',
                            emulator_path,
                            env,
diff --git a/emu_test/config/boot_cfg.csv b/emu_test/config/boot_cfg.csv
index 4f15937..8d1582c 100644
--- a/emu_test/config/boot_cfg.csv
+++ b/emu_test/config/boot_cfg.csv
@@ -10,6 +10,7 @@
 API P,android-wear,x86,wear_square_320,1024,yes,pi,P,P,P,P,P,P,S,S,S
 API P,android-car,X86_64,Android Automotive,2048,yes,pi-car,P,P,P,P,P,P,S,S,S
 API 27,android-car,x86,Android Automotive,2048,yes,oc-mr1-car-support,P,P,P,P,P,P,S,S,S
+API 27,default,x86,Nexus 4,2048,yes,oc-mr1-iot,S,P,P,P,S,S,S,S,S
 API 27,android-tv,x86,Android TV (1080p),2048,yes,oc-mr1,P,P,P,P,P,P,S,S,S
 API 27,google_apis,x86,Nexus 6P,2048,yes,oc-mr1,P,P,P,P,P,P,S,S,S
 API 27,google_apis_playstore,x86,Nexus 6P,2048,yes,oc-mr1,P,P,P,P,P,P,S,S,S
diff --git a/emu_test/config/ui_cfg.csv b/emu_test/config/ui_cfg.csv
index 3cf006a..b10bcab 100644
--- a/emu_test/config/ui_cfg.csv
+++ b/emu_test/config/ui_cfg.csv
@@ -1,4 +1,5 @@
 Device Config,,,,,,,Linux,,,,,Windows,,,,,MacOSX,,,
 API*,TAG*,ABI*,DEVICE,RAM,GPU,ORI,Ubuntu 14.04 HD 4400,Win 8.1 64-bit Quadro 600,Mac 10.10.5 Intel HD 5000
+API 28,google_apis,x86,Pixel,2048,yes,pi,P,P,P
 API 27,google_apis_playstore,x86,Nexus 5X,2048,yes,oc-mr1,P,P,P
 API 26,google_apis_playstore,x86,Nexus 5X,2048,yes,oc,P,P,P
diff --git a/emu_test/test_avd/launch_avd.py b/emu_test/test_avd/launch_avd.py
index 2fd7286..2b0f96a 100644
--- a/emu_test/test_avd/launch_avd.py
+++ b/emu_test/test_avd/launch_avd.py
@@ -83,11 +83,13 @@
     return vars['process'].returncode, vars['output'], vars['err']
 
 
-def launch_emu(avd, emu_args, emu_log_stream):
+def launch_emu(avd, emu_args, emu_log_stream, additional_args=None):
     """Launch given avd and return immediately"""
     log.debug('call Launching AVD, ...: %s' % str(avd))
     exec_path = emu_args.emulator_exec
     launch_cmd = [exec_path, "-avd", str(avd), "-verbose", "-show-kernel"]
+    if additional_args:
+        launch_cmd.extend(additional_args)
 
     if "emu-master-dev" in exec_path:
         launch_cmd += ["-skip-adb-auth"]
@@ -104,12 +106,12 @@
     return start_proc
 
 
-def launch_emu_and_wait(avd, emu_args, emu_log_stream):
+def launch_emu_and_wait(avd, emu_args, emu_log_stream, additional_args=None):
     """Launch given avd and wait for boot completion, return boot time"""
     run_with_timeout(["adb", "kill-server"], 20)
     run_with_timeout(["adb", "start-server"], 20)
     pool = multiprocessing.pool.ThreadPool(processes = 1)
-    launcher_emu = pool.apply_async(launch_emu, [avd, emu_args, emu_log_stream])
+    launcher_emu = pool.apply_async(launch_emu, [avd, emu_args, emu_log_stream, additional_args])
     start_time = time.time()
     completed = "0"
     real_time_out = emu_args.timeout_in_seconds;
@@ -134,7 +136,7 @@
             continue
         if exit_code is 0:
             completed = output.strip()
-        if completed is "1":
+        if completed == "1":
             log.info('AVD %s is fully booted' % str(avd))
             break
         time.sleep(1)
@@ -150,6 +152,7 @@
     else:
         success = False
     emu_proc.terminate()
+    run_with_timeout(["adb", "kill-server"], 20)
     return success
 
 
diff --git a/emu_test/test_avd/launch_avd_no_window.py b/emu_test/test_avd/launch_avd_no_window.py
new file mode 100644
index 0000000..ca5b6f0
--- /dev/null
+++ b/emu_test/test_avd/launch_avd_no_window.py
@@ -0,0 +1,51 @@
+"""AVD Launch test.
+
+Verify the emulator launched in AVD without window can be detected.
+
+usage: launch_avd_no_window.py [-h] [-t TIMEOUT_IN_SECONDS] --avd AVD
+                               [--exec EMULATOR_EXEC]
+"""
+
+import logging
+import os
+import sys
+import unittest
+
+import emu_test
+from emu_test.utils import emu_argparser
+from emu_test.utils.emu_testcase import EmuBaseTestCase, AVDConfig
+
+from launch_avd import launch_emu_and_wait
+
+log = logging.getLogger('launch_avd_no_window')
+
+class LaunchAVDNoWindowTest(EmuBaseTestCase):
+    def launch_avd(self, avd_config):
+        self.avd_config = avd_config
+        self.assertEqual(self.create_avd(avd_config), 0)
+        test_name  = self.id().rsplit('.', 1)[-1]
+        emu_log_path = os.path.join(emu_argparser.emu_args.session_dir,
+                                    emu_argparser.emu_args.test_dir,
+                                    "%s_verbose.txt" % test_name)
+        args = ['-port', '5554', '-wipe-data', '-no-boot-anim', '-no-snapshot',
+                '-no-window', '-qemu', '-enable-kvm']
+        with open(emu_log_path, 'wb') as emu_log:
+            return launch_emu_and_wait(avd_config,
+                                       emu_argparser.emu_args,
+                                       emu_log,
+                                       additional_args = args)
+
+
+if emu_argparser.emu_args.config_file is None:
+    sys.exit(-1)
+else:
+    emu_test.utils.emu_testcase.create_test_case_from_file(
+        'launch_avd', LaunchAVDNoWindowTest, LaunchAVDNoWindowTest.launch_avd)
+
+
+if __name__ == '__main__':
+    os.environ['SHELL'] = '/bin/bash'
+    emu_argparser.emu_args = emu_argparser.get_parser().parse_args()
+    log.info(emu_argparser.emu_args)
+    sys.argv[1:] = emu_argparser.emu_args.unittest_args
+    unittest.main()
\ No newline at end of file
diff --git a/emu_test/test_console/testcase_auth.py b/emu_test/test_console/testcase_auth.py
index e09e682..ee2e121 100644
--- a/emu_test/test_console/testcase_auth.py
+++ b/emu_test/test_console/testcase_auth.py
@@ -41,8 +41,11 @@
                                False, '', expected_output, output)
 
   def _verify_auth_command_by_enter_help_command(self, expected_output):
-    is_command_successful, output = util.execute_console_command(
-        self.telnet, util.CMD_HELP, expected_output)
+    output = util.execute_help_command(self.telnet, util.CMD_HELP)
+    is_command_successful = True
+    for cmd in expected_output:
+      if cmd not in output:
+        is_command_successful = False
 
     self.assert_cmd_successful(
         is_command_successful, 'Failed to properly list all command options.',
@@ -90,7 +93,7 @@
     print 'Running test: %s' % (inspect.stack()[0][3])
     self.telnet = util.telnet_emulator()
     self._verify_auth_command_by_enter_help_command(
-        util.REGEX_HELP_DISPLAY_NO_AUTH)
+        util.CMDS_FOR_HELP_NO_AUTH)
     util.exit_emulator_console(self.telnet)
 
   def test_auth_user_with_random_auth_token(self):
@@ -153,7 +156,7 @@
     valid_auth_cmd = '%s %s\n' % (util.AUTH, auth_token)
     self._auth_user_for_emulator_console(valid_auth_cmd, AUTH_OUTPUT)
     self._verify_auth_command_by_enter_help_command(
-        util.REGEX_HELP_DISPLAY_AUTH)
+        util.CMDS_FOR_HELP_AUTH)
     util.exit_emulator_console(self.telnet)
 
   def test_auth_empty_auth_token_file(self):
@@ -183,7 +186,7 @@
       # telnet and verify
       self.telnet = util.telnet_emulator()
       self._verify_auth_command_by_enter_help_command(
-          util.REGEX_HELP_DISPLAY_AUTH)
+          util.CMDS_FOR_HELP_AUTH)
 
       # reset auth token file
       f = open(util.TOKEN_PATH, 'w')
diff --git a/emu_test/test_console/testcase_help.py b/emu_test/test_console/testcase_help.py
index 7919306..bb197ea 100644
--- a/emu_test/test_console/testcase_help.py
+++ b/emu_test/test_console/testcase_help.py
@@ -33,8 +33,11 @@
     Args:
         expected_output: Expected console output for help commands.
     """
-    is_command_successful, output = util.execute_console_command(
-        self.telnet, util.CMD_HELP, expected_output)
+    output = util.execute_help_command(self.telnet, util.CMD_HELP)
+    is_command_successful = True
+    for cmd in expected_output:
+      if cmd not in output:
+        is_command_successful = False
 
     self.assert_cmd_successful(
         is_command_successful,
@@ -47,8 +50,11 @@
     Args:
         expected_output: Expected console output for help-vebose command.
     """
-    is_command_successful, output = util.execute_console_command(
-        self.telnet, util.CMD_HELP_VERBOSE, expected_output)
+    output = util.execute_help_command(self.telnet, util.CMD_HELP_VERBOSE)
+    is_command_successful = True
+    for cmd in expected_output:
+      if cmd not in output:
+        is_command_successful = False
 
     self.assert_cmd_successful(
         is_command_successful,
@@ -82,13 +88,13 @@
          gsm and rotate commands are available
     """
     print 'Running test: %s' % (inspect.stack()[0][3])
-    self._help_command(util.REGEX_HELP_DISPLAY_NO_AUTH)
-    self._help_verbose_command(util.REGEX_HELP_VERBOSE_DISPLAY_NO_AUTH)
+    self._help_command(util.CMDS_FOR_HELP_NO_AUTH)
+    self._help_verbose_command(util.CMDS_FOR_HELP_VERBOSE_NO_AUTH)
 
     self._auth_user_for_emulator_console()
 
-    self._help_command(util.REGEX_HELP_DISPLAY_AUTH)
-    self._help_verbose_command(util.REGEX_HELP_VERBOSE_DISPLAY_AUTH)
+    self._help_command(util.CMDS_FOR_HELP_AUTH)
+    self._help_verbose_command(util.CMDS_FOR_HELP_VERBOSE_DISPLAY_AUTH)
 
 
 if __name__ == '__main__':
diff --git a/emu_test/test_console/testcase_restart.py b/emu_test/test_console/testcase_restart.py
new file mode 100644
index 0000000..ab42518
--- /dev/null
+++ b/emu_test/test_console/testcase_restart.py
@@ -0,0 +1,64 @@
+"""Test for restart-related commands."""
+
+import inspect
+import sys
+import time
+import unittest
+
+import testcase_base
+from utils import util
+
+RESTART_CMD = 'restart\n'
+RESTART_RESPONSE = 'OK: restarting emulator, bye bye.*\n.*OK'
+RESTART_WAIT_TIMEOUT_S = 30 # This timeout needs to calibrate on buildbot.
+
+
+class RestartTest(testcase_base.BaseConsoleTest):
+  """This class aims to test restart-related emulator console commands."""
+
+  def __init__(self, method_name=None, avd=None, builder_name=None):
+    if method_name:
+      super(RestartTest, self).__init__(method_name)
+    else:
+      super(RestartTest, self).__init__()
+    self.avd = avd
+    self.builder_name = builder_name
+
+  def _telnet_to_emulator_and_auth(self):
+    """Telnet to emulator and auth token"""
+    auth_token = util.get_auth_token()
+    self.telnet = util.telnet_emulator()
+    self.telnet.write('%s %s\n' % (util.AUTH, auth_token))
+    util.wait_on_windows()
+    if (not util.check_read_until(
+            self.telnet.read_until(util.OK, util.TIMEOUT_S))):
+      sys.exit(-1)
+
+  def test_restart_command(self):
+    """Test command for: restart.
+
+    TODO: TT ID:
+    Test steps:
+      1. Launch an emulator avd
+      2. From command prompt, run: telnet localhost <port>
+      3. Run: auth <token>
+      4. Run: restart, and verify output
+      5. Repeat step 2-3, and verify it works, which means emulator restarted
+    """
+    if util.WIN_BUILDER_NAME in self.builder_name:
+      print 'Skip restart test on Win.'
+      pass
+      return
+
+    print 'Running test: %s' % (inspect.stack()[0][3])
+
+    self.telnet.write(RESTART_CMD)
+    self.assertTrue(RESTART_RESPONSE, self.telnet.read_all())
+
+    time.sleep(RESTART_WAIT_TIMEOUT_S)
+    self._telnet_to_emulator_and_auth()
+
+
+if __name__ == '__main__':
+  print '======= restart Test ======='
+  unittest.main()
diff --git a/emu_test/test_console/utils/util.py b/emu_test/test_console/utils/util.py
index a929cd0..6c58d94 100644
--- a/emu_test/test_console/utils/util.py
+++ b/emu_test/test_console/utils/util.py
@@ -57,25 +57,19 @@
 PORT_NO_REDIR = 'no active redirections\r\nOK'
 PORT_REDIR_ADD = 'tcp:5556  => 5554 \r\nOK'
 CMD_HELP = 'help\n'
-REGEX_HELP_DISPLAY_NO_AUTH = (r'.*\n.*help.*\n.*help-verbose.*\n.*ping.*\n'
-                              r'.*avd.*\n.*auth.*\n.*quit\|exit.*\n.*\n.*\n.*\nOK')
-REGEX_HELP_DISPLAY_AUTH = (r'.*\n.*help.*\n.*help-verbose.*\n.*ping.*\n.*event.*\n'
-                           r'.*geo.*\n.*gsm.*\n.*cdma.*\n.*crash.*\n.*crash-on-exit.*\n'
-                           r'.*kill.*\n.*restart.*\n.*network.*\n'
-                           r'.*power.*\n.*quit\|exit.*\n.*redir.*\n'
-                           r'.*sms.*\n.*avd.*\n.*qemu.*\n.*sensor.*\n.*physics.*\n'
-                           r'.*finger.*\n.*debug.*\n.*rotate.*\n.*screenrecord.*\n.*\n.*\n.*\nOK')
 CMD_HELP_VERBOSE = 'help-verbose\n'
-REGEX_HELP_VERBOSE_DISPLAY_NO_AUTH = (
-        r'.*\n.*\n.*help.*\n.*help-verbose.*\n.*ping.*\n'
-        r'.*avd.*\n.*auth.*\n.*quit\|exit.*\n.*\n.*\nOK')
-REGEX_HELP_VERBOSE_DISPLAY_AUTH = (
-        r'.*\n.*\n.*help.*\n.*help-verbose.*\n.*ping.*\n.*event.*\n'
-        r'.*geo.*\n.*gsm.*\n.*cdma.*\n.*crash.*\n.*crash-on-exit.*\n'
-        r'.*kill.*\n.*restart.*\n.*network.*\n'
-        r'.*power.*\n.*quit\|exit.*\n.*redir.*\n'
-        r'.*sms.*\n.*avd.*\n.*qemu.*\n.*sensor.*\n.*physics.*\n'
-        r'.*finger.*\n.*debug.*\n.*rotate.*\n.*screenrecord.*\n.*\n.*\nOK')
+CMDS_FOR_HELP_NO_AUTH = [
+  'help', 'help-verbose', 'ping', 'avd', 'auth', 'quit', 'exit'
+]
+CMDS_FOR_HELP_AUTH = [
+  'help', 'help-verbose', 'ping', 'event', 'geo', 'gsm', 'cdma', 'crash',
+  'crash-on-exit', 'kill', 'restart', 'network', 'power', 'quit', 'exit', 'redir',
+  'sms', 'avd', 'qemu', 'sensor', 'physics', 'finger', 'debug', 'rotate',
+  'screenrecord'
+]
+CMDS_FOR_HELP_VERBOSE_NO_AUTH = CMDS_FOR_HELP_NO_AUTH
+CMDS_FOR_HELP_VERBOSE_DISPLAY_AUTH = CMDS_FOR_HELP_AUTH
+
 AUTH = 'auth'
 CMD_RANDOM_AUTH_TOKEN = '%s axxB123cc\n' % AUTH
 CMD_EMPTY_AUTH_TOKEN = '%s \n' % AUTH
@@ -269,6 +263,41 @@
   return is_command_successful, output
 
 
+def execute_help_command(telnet, command):
+  """Executes emulator console help related command.
+
+  Executes emulator console command through telnet connection,
+  compare command output and expected command output.
+
+  Args:
+    telnet: The telnet connection to emulator.
+    command: The console command to execute.
+
+  Returns:
+    output: The command output in the terminal.
+  """
+
+  print 'execute console command: %s' % (command.strip())
+
+  telnet.write(command)
+  time.sleep(CMD_WAIT_TIMEOUT_S)
+
+  if command == 'crash\n':
+    output = telnet.read_all()
+  elif command == CMD_ROTATE: # No 'OK' output showing, only new line.
+    print 'command is rotate'
+    output = telnet.read_until('\n', 10)
+    print 'output = "%s"' % output
+  elif command == CMD_EMPTY_AUTH_TOKEN:
+    output = telnet.read_until('missing authentication token').strip()
+  elif command == CMD_RANDOM_AUTH_TOKEN:
+    output = telnet.read_until('emulator_console_auth_token').strip()
+  else:
+    output = parse_output(telnet)
+
+  return output
+
+
 def get_auth_token():
   """Gets auth token value from auth token file.
 
diff --git a/emu_test/test_mttf/__init__.py b/emu_test/test_mttf/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/emu_test/test_mttf/__init__.py
diff --git a/emu_test/test_mttf/action.py b/emu_test/test_mttf/action.py
new file mode 100644
index 0000000..fca1f49
--- /dev/null
+++ b/emu_test/test_mttf/action.py
@@ -0,0 +1,157 @@
+"""Contains the individual actions that can be send to the telnet console.
+"""
+import random
+import time
+
+
+class Action(object):
+  """Parent class of every action that can be send to the emulator."""
+
+  def __init__(self, interval):
+    self.interval = interval
+
+  def __lt__(self, other):
+    return self.interval < other.interval
+
+
+class MouseEvent(Action):
+  """Sends one mouse event every 10 ms."""
+
+  def __init__(self, width, height):
+    super(MouseEvent, self).__init__(0.01)
+    self.width = int(width)
+    self.height = int(height)
+    self.x = random.randint(0, self.width)
+    self.y = random.randint(0, self.height)
+
+  def produce_event(self):
+    # This makes sure we jump to a different region on the screen
+    self.x = self.x if random.random() < 0.9 else random.randint(0, self.width)
+    self.y = self.y if random.random() < 0.9 else random.randint(0, self.height)
+
+    # This keeps the movement relatively local
+    self.y = min(self.height, max(0, self.y + random.randint(-5, 5)))
+    self.x = min(self.width, max(0, self.x + random.randint(-5, 5)))
+
+    # 80% chance of a mouse down.
+    btn = 1 if random.random() < 0.8 else 0
+
+    return "event mouse {0} {1} 0 {2}".format(self.x, self.y, btn)
+
+
+class SmsEvent(Action):
+  """Sms every 10 seconds.."""
+  NAMES = [ "Falstaff", "Julius", "Marc-Anthony", "Hamlet",
+      "Othello", "Iago", "Antony", "Timon", "Cleopatra",
+      "Rosalind", "Brutus", "Vincentio", "Coriolanus"
+  ]
+
+  WORDLIST = [
+      "a", "about", "all", "also", "and", "as", "at", "be", "because", "but",
+      "by", "can", "come", "could", "day", "do", "even", "find", "first", "for",
+      "from", "get", "give", "go", "have", "he", "her", "here", "him", "his",
+      "how", "I", "if", "in", "into", "it", "its", "just", "know", "like",
+      "look", "make", "man", "many", "me", "more", "my", "new", "no", "not",
+      "now", "of", "on", "one", "only", "or", "other", "our", "out", "people",
+      "say", "see", "she", "so", "some", "take", "tell", "than", "that", "the",
+      "their", "them", "then", "there", "these", "they", "thing", "think",
+      "this", "those", "time", "to", "two", "up", "use", "very", "want", "way",
+      "we", "well", "what", "when", "which", "who", "will", "with", "would",
+      "year", "you", "your"
+  ]
+
+  def __init__(self):
+    super(SmsEvent, self).__init__(10)
+
+  def produce_event(self):
+    return "sms send {} {} {} {} {}".format(
+        random.choice(SmsEvent.NAMES), random.choice(SmsEvent.WORDLIST),
+        random.choice(SmsEvent.WORDLIST), random.choice(SmsEvent.WORDLIST),
+        random.choice(SmsEvent.WORDLIST))
+
+
+class GeoEvent(Action):
+  """Geo every 1 second."""
+
+  def __init__(self):
+    super(GeoEvent, self).__init__(1)
+    # Start wandering along the googleplex..
+    self.lat = 37.422206
+    self.long = -122.086088
+
+  def produce_event(self):
+    self.long = min(180, max(0, self.long + random.random() - 0.5))
+    self.lat = min(90, max(-90, self.lat + random.random() - 0.5))
+    return "geo fix {0} {1}".format(self.long, self.lat)
+
+
+class PowerEvent(Action):
+  """Power event every minute."""
+
+  def __init__(self):
+    super(PowerEvent, self).__init__(60)
+
+  def produce_event(self):
+    if random.random() > 0.9:
+      ac = "on" if bool(random.getrandbits(1)) else "off"
+      return "power ac {0}".format(ac)
+
+    return "power capacity {0}".format(random.randint(0, 100))
+
+
+class ScreenRecord(Action):
+  """Screen record every minute."""
+
+  def __init__(self):
+    super(ScreenRecord, self).__init__(60)
+
+  def produce_event(self):
+    if random.random() > 0.5:
+      return "screenrecord start a.webm"
+    else:
+      return "screenrecord stop"
+
+
+class Finger(Action):
+  """Hit the finger sensor every minute"""
+
+  def __init__(self):
+    super(Finger, self).__init__(60)
+
+  def produce_event(self):
+    if random.random() > 0.5:
+      return "finger touch 1"
+    else:
+      return "finger remove"
+
+
+class Snapshot(Action):
+  """Every other minute take/load a snapshot."""
+
+  def __init__(self):
+    super(Snapshot, self).__init__(120)
+
+  def produce_event(self):
+    if random.random() > 0.5:
+      return "avd snapshot save monkey"
+    else:
+      return "avd snapshot load monkey"
+
+
+class Sensor(Action):
+  """Sensor status every second."""
+
+  def __init__(self):
+    super(Sensor, self).__init__(1)
+    self.sensors = [
+        "acceleration", "gyroscope", "magnetic-field", "orientation",
+        "temperature", "proximity", "light", "pressure", "humidity",
+        "magnetic-field-uncalibrated", "gyroscope-uncalibrated"
+    ]
+
+  def produce_event(self):
+    a = random.randint(0, 256)
+    b = random.randint(0, 256)
+    c = random.randint(0, 256)
+    return "sensor set {0} {1}:{2}:{3}".format(
+        random.choice(self.sensors), a, b, c)
diff --git a/emu_test/test_mttf/emulator_connection.py b/emu_test/test_mttf/emulator_connection.py
new file mode 100644
index 0000000..255f779
--- /dev/null
+++ b/emu_test/test_mttf/emulator_connection.py
@@ -0,0 +1,110 @@
+"""A Connection to the emulator telnet console. Python3
+"""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import sys
+import time
+import datetime
+from threading import Thread
+
+try:
+  from absl import logging
+except ImportError:
+  import logging
+
+try:
+  import asyncio
+except ImportError:
+  logging.error("Yikes, no asyncio, reverting to trollius!")
+  import trollius as asyncio
+  from trollius import From
+
+
+class EmulatorConnection(asyncio.Protocol):
+  """Connects to the emulator telnet console.
+
+  It will authenticate immediately.
+  """
+
+  def __init__(self, loop, callback):
+    self.loop = loop
+    self.transport = None
+    self.callback = callback
+    self.start = time.time()
+    self.connected = False
+
+  def is_connected(self):
+    return self.connected
+
+  def connection_made(self, transport):
+    logging.info("Connected to emulator: %s", self)
+    self.transport = transport
+    self.connected = True
+    self.start = time.time()
+
+  @asyncio.coroutine
+  def auth(self, fname):
+    logging.info("Authenticating using %s", fname)
+    with open(fname[1:-1], "r") as authfile:
+      token = authfile.read()
+      msg = "auth {}".format(token).strip()
+      yield From(self.send(msg))
+
+  def data_received(self, data):
+    msg = data.decode()
+    # send the auth token if needed
+    if "Android Console: you can find your <auth_token> in" in msg:
+      fname = msg.split("\n")[-3].strip()
+      asyncio.async(self.auth(fname), self.loop)
+
+    # do something with the received data
+    if self.callback:
+      asyncio.async(self.callback(msg))
+
+  def connection_lost(self, exc):
+    total = time.time() - self.start
+    logging.error("The emulator is gone, we were alive for: %d seconds (%s)!",
+                  total, str(datetime.timedelta(seconds=total)))
+    self.connected = False
+    self.loop.stop()
+    try:
+      # Best effort cleanup
+      pending = asyncio.Task.all_tasks()
+      for task in pending:
+        task.cancel()
+        # Now we should await task to execute its cancellation.
+        # Cancelled task raises asyncio.CancelledError that we can suppress:
+        try:
+          self.loop.run_until_complete(task)
+        except (asyncio.CancelledError):
+          pass
+    except RuntimeError:
+      pass
+
+  @asyncio.coroutine
+  def send(self, msg):
+    """Sends plain text to the emulator"""
+    if self.connected:
+      self.transport.write("{}\n".format(msg).encode())
+
+  @staticmethod
+  def connect(port, callback=None):
+    """Connects to the telnet console on the given port and authenticates.
+
+    Args:
+      port:     The port to which to connect to the emulator.
+      callback: Function to be called when the telnet console has data
+
+    Returns:
+      Thread that is running the event loop
+    """
+    loop = asyncio.new_event_loop()
+    emulator = EmulatorConnection(loop, callback)
+    coro = loop.create_connection(lambda: emulator, "127.0.0.1", port)
+    loop.run_until_complete(coro)
+    t = Thread(target=lambda loop: loop.run_forever(), args=(loop,))
+    t.start()
+    return emulator
diff --git a/emu_test/test_mttf/simple_mttf.py b/emu_test/test_mttf/simple_mttf.py
new file mode 100644
index 0000000..d60be9f
--- /dev/null
+++ b/emu_test/test_mttf/simple_mttf.py
@@ -0,0 +1,158 @@
+"""A python 3 ript to run a mean time to failure test.
+
+Mean time to failure is defined as the emulator crashing, not becoming
+unresponsive.
+
+We have a list of actions, that have an interval/frequency of when they should
+be send to the emulator. When an event is due it is produced (i.e. converted to
+a telnet command) and send to the emulator.
+
+It will connect to a running emulator if one is available, otherwise it will
+launch one. You must have the 'adb' executable available on the path.
+
+Running against the emulator build in the current branch for example:
+
+ $ python simple_mttf.py --emulator $PWD/../../../qemu/objs/emulator -avd my_avd
+"""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+try:
+  from absl import app
+  from absl import flags
+  from absl import logging
+except ImportError:
+  print('Make sure to: pip install absl-py')
+
+from action import *
+from emulator_connection import EmulatorConnection
+import os
+import random
+import re
+import psutil
+import subprocess
+
+try:
+  import trollius as asyncio
+except ImportError:
+  print('Make sure to: pip install trollius')
+
+import heapq
+import time
+
+FLAGS = flags.FLAGS
+flags.DEFINE_string('avd', None,
+                    'The avd to test (or pick first available from emulator)')
+flags.DEFINE_string(
+    'emulator',
+    os.path.join(os.environ['ANDROID_SDK_ROOT'], 'emulator', 'emulator'),
+    'Emulator executable')
+flags.DEFINE_integer('port', 5554, 'Emulator telnet port')
+flags.DEFINE_integer('maxboot', 120, 'Maximum time to start emulator')
+
+
+@asyncio.coroutine
+def randomizer(connection, actions):
+  # Priority queue ordered by when the event should be sent
+  # to the emulator
+  heap = []
+  tm = time.time()
+  for a in actions:
+    heapq.heappush(heap, (tm + a.interval, a))
+
+  while connection.is_connected():
+    now = time.time()
+    evt_time, action = heapq.heappop(heap)
+    timeout = evt_time - now
+
+    # Wait until this event is ready to be fired.
+    if timeout > 0:
+      time.sleep(timeout)
+
+    # Produce the event, if any
+    event = action.produce_event()
+    if event:
+      yield connection.send(event)
+
+    # Schedule it for future execution.
+    heapq.heappush(heap, (time.time() + action.interval, action))
+  logging.info('Completed')
+
+
+def get_screen_size(adb):
+  # this will give something like: 'Physical size: 1080x1920\n'
+  screen_info = subprocess.check_output([adb, 'shell', 'wm', 'size'])
+  match = re.match('.*: (\d+)x(\d+)', screen_info)
+  w = 1080
+  h = 1920
+  if match:
+    w = match.group(1)
+    h = match.group(2)
+  return w, h
+
+
+def get_avd():
+  """Just pick the first reported avd from the emulator."""
+  return subprocess.check_output([FLAGS.emulator, '-list-avds']).split()[0]
+
+
+def is_alive():
+  """Returns true if the emulator has booted."""
+  cmd = ['adb', 'shell', 'getprop', 'sys.boot_completed']
+  try:
+    return '1' in subprocess.check_output(cmd).decode("utf-8")
+  except subprocess.CalledProcessError:
+    return False
+
+
+def launch_emu_and_wait(avd):
+  """Super simplistic, poor man's emulator launcher..."""
+  cmd = [
+      FLAGS.emulator,
+      '-avd',
+      avd,  # '-verbose', '-show-kernel',
+      '-detect-image-hang',
+      '-wipe-data',
+      '-dns-server',
+      '8.8.8.8',
+      '-skip-adb-auth'
+  ]
+  proc = psutil.Popen(cmd)
+  max_wait = FLAGS.maxboot
+  while (proc.status() not in [
+      psutil.STATUS_DEAD, psutil.STATUS_ZOMBIE, psutil.STATUS_STOPPED
+  ] and max_wait > 0 and not is_alive()):
+    time.sleep(1)
+    max_wait = max_wait - 1
+
+
+def main(argv):
+  # launch the emulator if needed
+  if not is_alive():
+    avd = FLAGS.avd or get_avd()
+    launch_emu_and_wait(avd)
+
+  # Get the screen size from the device.
+  w, h = get_screen_size('adb')
+  # Set of actions we want to send to the emulator
+  action_set = [
+      MouseEvent(w, h),
+      SmsEvent(),
+      GeoEvent(),
+      PowerEvent(),
+      ScreenRecord(),
+      Sensor(),
+      Finger(),
+      Snapshot()
+  ]
+  # Connection to the emulator
+  connection = EmulatorConnection.connect(FLAGS.port)
+  loop = asyncio.new_event_loop()
+  asyncio.set_event_loop(loop)
+  loop.run_until_complete(randomizer(connection, action_set))
+
+
+if __name__ == '__main__':
+  app.run(main)
diff --git a/emu_test/test_mttf/test_mttf.py b/emu_test/test_mttf/test_mttf.py
new file mode 100644
index 0000000..f5dcb1a
--- /dev/null
+++ b/emu_test/test_mttf/test_mttf.py
@@ -0,0 +1,156 @@
+"""Test the emulator mttf time"""
+
+import heapq
+import os
+import psutil
+import re
+import shutil
+import subprocess
+import time
+import time
+import traceback
+import unittest
+
+try:
+  import asyncio
+except ImportError:
+  import trollius as asyncio
+
+from emu_test.utils.emu_error import TimeoutError
+from emu_test.utils.emu_argparser import emu_args
+import emu_test.utils.emu_testcase
+from emu_test.utils.emu_testcase import EmuBaseTestCase, AVDConfig
+import emu_test.utils.path_utils as path_utils
+from emu_test.test_mttf.emulator_connection import EmulatorConnection
+from emu_test.test_mttf.action import *
+
+
+class MttfTestCase(EmuBaseTestCase):
+  """Mean time to failure test.
+
+     This test will just send random clicks to the emulator until it dies.
+  """
+
+  def __init__(self, *args, **kwargs):
+    super(MttfTestCase, self).__init__(*args, **kwargs)
+    self.avd_config = None
+    self.mttf_time = time.time()
+    self.connection = None
+
+  @classmethod
+  def setUpClass(cls):
+    super(MttfTestCase, cls).setUpClass()
+
+  def kill_emulator(self):
+    self.m_logger.debug('First try - quit emulator by adb emu kill')
+    adb_binary = path_utils.get_adb_binary()
+    psutil.Popen([adb_binary, 'emu', 'kill']).communicate()
+    # check emulator process is terminated
+    result = self.term_check(timeout=5)
+    if not result:
+      self.m_logger.info('Second try - quit emulator by psutil')
+      self.kill_proc_by_name(['emulator', 'qemu-system'])
+      result = self.term_check(timeout=10)
+      self.m_logger.info('term_check after psutil.kill - %s' % result)
+    return result
+
+  def tearDown(self):
+    result = self.kill_emulator()
+    self.m_logger.info('Remove AVD inside of tear down')
+    # avd should be found $HOME/.android/avd/
+    avd_dir = os.path.join(os.path.expanduser('~'), '.android', 'avd')
+    try:
+      if result and self.start_proc:
+        self.start_proc.wait()
+      time.sleep(1)
+      self.kill_proc_by_name(['crash-service', 'adb'])
+      os.remove(os.path.join(avd_dir, '%s.ini' % self.avd_config.name()))
+      shutil.rmtree(
+          os.path.join(avd_dir, '%s.avd' % self.avd_config.name()),
+          ignore_errors=True)
+    except Exception, e:
+      self.m_logger.error('Error in cleanup - %r' % e)
+
+  def mttf_check(self, avd):
+
+    @asyncio.coroutine
+    def randomizer(connection, actions):
+      # Priority queue ordered by when the event should be sent
+      # to the emulator.
+      heap = []
+      tm = time.time()
+      for a in actions:
+        heapq.heappush(heap, (tm + a.interval, a))
+
+      while connection.is_connected():
+        now = time.time()
+        evt_time, action = heapq.heappop(heap)
+        timeout = evt_time - now
+
+        # Wait until this event is ready to be fired.
+        if timeout > 0:
+          time.sleep(timeout)
+
+        # Produce the event, if any
+        event = action.produce_event()
+        if event:
+          yield connection.send(event)
+
+        # Schedule it for future execution.
+        heapq.heappush(heap, (time.time() + action.interval, action))
+
+    def get_screen_size(adb):
+      # this will give something like: 'Physical size: 1080x1920\n'
+      screen_info = subprocess.check_output([adb, 'shell', 'wm', 'size'])
+      match = re.match('.*: (\d+)x(\d+)', screen_info)
+      w = 1080
+      h = 1920
+      if match:
+        w = match.group(1)
+        h = match.group(2)
+      return w, h
+
+    try:
+      self.launch_emu_and_wait(avd)
+    except TimeoutError:
+      self.m_logger.error('AVD %s, time out, try one more time' % str(avd))
+    except:
+      self.m_logger.error('AVD %s, exception, try one more time' % str(avd))
+      self.m_logger.error(traceback.format_exc())
+
+    adb = path_utils.get_adb_binary()
+    w, h = get_screen_size(adb)
+    action_set = [
+        MouseEvent(w, h),
+        SmsEvent(),
+        GeoEvent(),
+        PowerEvent(),
+        ScreenRecord(),
+        Sensor(),
+        Finger(),
+        Snapshot()
+    ]
+    connection = EmulatorConnection.connect(5554)
+    loop = asyncio.new_event_loop()
+    asyncio.set_event_loop(loop)
+    loop.run_until_complete(randomizer(connection, action_set))
+
+  def run_mttf_test(self, avd_config):
+    pass
+    # TODO(jansene): These tests can run for hours/(days?) so are disabled for now
+    # self.avd_config = avd_config
+    # if self.create_avd(avd_config) == 0:
+    #   self.mttf_check(avd_config)
+
+
+def create_test_case_for_avds():
+  pass
+  # TODO(jansene): These tests can run for hours/(days?) so are disabled for now
+  # create_test_case_from_file("mttf", MttfTestCase, MttfTestCase.run_mttf_test)
+
+
+if emu_args.config_file is None:
+  create_test_case_for_avds()
+else:
+  emu_test.utils.emu_testcase.create_test_case_from_file(
+      'mttf', MttfTestCase, MttfTestCase.run_mttf_test)
diff --git a/emu_test/test_ui/test_ui.py b/emu_test/test_ui/test_ui.py
index a5ddea1..3d590c5 100644
--- a/emu_test/test_ui/test_ui.py
+++ b/emu_test/test_ui/test_ui.py
@@ -86,9 +86,15 @@
         Returns the process.
 
         """
+        if 'android-tv' in test_tag:
+           pkg = 'tv'
+        elif 'android-wear' in test_tag:
+           pkg = 'wear'
+        else:
+           pkg = 'smoke'
         test_args_prefix = '-Pandroid.testInstrumentationRunnerArguments'
-        test_class = test_args_prefix + '.class=com.android.devtools.systemimage.uitest.smoke.' +\
-                     'api' + avd.api + '.' + class_name
+        test_class = test_args_prefix + '.class=com.android.devtools.systemimage.uitest.' +\
+                     pkg + '.api' + avd.api + '.' + class_name
         test_api = '%s.api=%s' % (test_args_prefix, avd.api)
         test_abi = test_args_prefix + '.abi=' + avd.abi
         test_tag = test_args_prefix + '.tag=' + avd.tag
@@ -101,9 +107,15 @@
                             shell=self.use_shell)
 
     def _launch_ui_test_with_avd_configs(self, avd):
+        if 'android-tv' in test_tag:
+           pkg = 'tv'
+        elif 'android-wear' in test_tag:
+           pkg = 'wear'
+        else:
+           pkg = 'smoke'
         test_args_prefix = '-Pandroid.testInstrumentationRunnerArguments'
-        test_package = test_args_prefix + '.package=com.android.devtools.systemimage.uitest.smoke.' +\
-                       'api' + avd.api
+        test_package = test_args_prefix + '.package=com.android.devtools.systemimage.uitest.' +\
+                       pkg + '.api' + avd.api
         test_api = test_args_prefix + '.api=' + avd.api
         test_abi = test_args_prefix + '.abi=' + avd.abi
         test_tag = test_args_prefix + '.tag=' + avd.tag
diff --git a/emu_test/utils/android_cl_scan.py b/emu_test/utils/android_cl_scan.py
index be1226c..29c0d27 100644
--- a/emu_test/utils/android_cl_scan.py
+++ b/emu_test/utils/android_cl_scan.py
@@ -112,6 +112,8 @@
     return 'git_oc-emu-dev', 'sdk_gphone_x86-sdk_addon'
   elif 'sys_image_oc_mr1_dev_poller' in poller:
     return 'git_oc-mr1-emu-dev', 'sdk_gphone_x86-sdk_addon'
+  elif 'sys_image_oc_mr1_iot_dev_poller' in poller:
+    return 'git_oc-mr1-iot-dev', 'sdk_google_iot_x86-userdebug'
   elif 'sys_image_nyc_mr1_dev_poller' in poller:
     return 'git_nyc-mr1-emu-dev', 'sdk_google_phone_x86-sdk_addon'
   elif 'sys_image_klp_poller' in poller:
diff --git a/emu_test/utils/download_unzip_image.py b/emu_test/utils/download_unzip_image.py
index 6c8e885..de5d408 100644
--- a/emu_test/utils/download_unzip_image.py
+++ b/emu_test/utils/download_unzip_image.py
@@ -64,7 +64,7 @@
     elif 'jb-mr2-emu' in branch_name:
       api = '18'
     elif 'pi' in branch_name:
-      api = 'P'
+      api = '28'
     elif 'aosp-master' in branch_name:
       api = 'P'
     elif 'master' in branch_name:
diff --git a/emu_test/utils/emu_testcase.py b/emu_test/utils/emu_testcase.py
index 05e5a61..bf510e9 100644
--- a/emu_test/utils/emu_testcase.py
+++ b/emu_test/utils/emu_testcase.py
@@ -902,18 +902,26 @@
         test_name = "test_%s%s_test_%s%s" % (variant_str, str(avd_config), desc, qemu_str)
         setattr(testcase_class, test_name, func)
 
-    def get_ui_test_class_names(api):
-        """Get the names of test classes in the com.android.devtools.systemimage.uitest.smoke package.
+    def get_ui_test_class_names(api, tag):
+        """Get the names of test classes in the com.android.devtools.systemimage.uitest package.
 
         Takes the directory listing of all files that end in '.java'.
 
+        :param api: api id
+        :param tag: tag for the test
         Return: The name of the test classes in the package.
 
         """
+        if 'android-tv' in tag:
+           pkg = 'tv'
+        elif 'android-wear' in test_tag:
+           pkg = 'wear'
+        else:
+           pkg = 'smoke'
         uitest_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                   '..', '..', 'system_image_uitests')
         package_path = os.path.join(uitest_dir, 'app', 'src', 'androidTest', 'java', 'com',
-                                    'android', 'devtools', 'systemimage', 'uitest', 'smoke', 'api'+api)
+                                    'android', 'devtools', 'systemimage', 'uitest', pkg, 'api'+api)
         classes = [filename[:-5:] for filename in os.listdir(package_path)
                    if filename.endswith('.java')]
         return classes
@@ -975,7 +983,7 @@
                                            get_port(), is_cts, ori)
                     variants = None
                     if generate_test_class:
-                        variants = get_ui_test_class_names(api)
+                        variants = get_ui_test_class_names(api, tag)
                     for variant in variants or [None]:
                         create_test_case(avd_config, op, emu_argparser.emu_args.builder_name,
                                          emu_argparser.emu_args.pattern, variant)
diff --git a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api26/AppTest.java b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api26/AppTest.java
index 27803d0..37adef9 100644
--- a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api26/AppTest.java
+++ b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api26/AppTest.java
@@ -79,8 +79,10 @@
     @TestInfo(id = "14578823")
     public void installAppAndLaunch() throws Exception {
         Instrumentation instrumentation = testFramework.getInstrumentation();
+        UiDevice device = UiDevice.getInstance(instrumentation);
         String testPackageName = "com.example.android.rs.hellocompute";
         String apk = "HelloCompute.apk";
+        String appName = "RsHelloCompute";
         String result = "";
 
         // Install RsHelloCompute, if not already present.
@@ -89,6 +91,7 @@
 
         if (!isHelloComputeInstalled) {
             result = PackageInstallationUtil.installApk(instrumentation, apk);
+            new AppWatcher(device).checkForCondition();
             isHelloComputeInstalled = PackageInstallationUtil.
                     isPackageInstalled(instrumentation, testPackageName);
         }
@@ -96,10 +99,11 @@
         assertTrue("Application " + apk + " is not installed. Result: " + result,
                 isHelloComputeInstalled);
 
+        AppLauncher.launch(instrumentation, appName);
+        boolean hasApplication = testFramework.getDevice().findObject(new UiSelector().resourceId(
+                Res.APP_IMAGE_VIEW_ID)).waitForExists(5L);
 
-        AppLauncher.launch(instrumentation, "RsHelloCompute");
-        assertTrue(testFramework.getDevice().findObject(new UiSelector().resourceId(
-                Res.APP_IMAGE_VIEW_ID)).exists());
+        assertTrue("Application " + appName + " did not launch", hasApplication);
     }
 
     /**
diff --git a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api26/GoogleServicesTest.java b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api26/GoogleServicesTest.java
index bca9fb4..19b33a1 100644
--- a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api26/GoogleServicesTest.java
+++ b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api26/GoogleServicesTest.java
@@ -165,7 +165,6 @@
      *   2. Log in user prompt or user promo is present.
      *   </pre>
      */
-    @Ignore("Bug in API 26. b/110433630")
     @Test
     @TestInfo(id = "d7f5673a-a3d0-4f50-856a-dfa10ce5c21c")
     public void loginGoogleChrome() throws Exception {
@@ -173,6 +172,7 @@
         final UiDevice device = UiDevice.getInstance(instrumentation);
 
         final String username = "David Play";
+        final String email = "pstester1980@gmail.com";
 
         AppLauncher.launch(instrumentation, "Chrome");
         new AddGoogleAccountWatcher(device).checkForCondition();
@@ -184,10 +184,10 @@
         );
 
         if (new Wait().until(new Wait.ExpectedCondition() {
-                    @Override
-                    public boolean isTrue() {
-                        return chromeUpdateButton.exists();
-                }})) {
+            @Override
+            public boolean isTrue() {
+                return chromeUpdateButton.exists();
+            }})) {
             chromeUpdateButton.clickAndWaitForNewWindow();
         }
 
@@ -195,10 +195,10 @@
                 new UiSelector().resourceId(Res.CHROME_MENU_BUTTON_RES));
 
         if (new Wait().until(new Wait.ExpectedCondition() {
-                    @Override
-                    public boolean isTrue() {
-                        return chromeMenuButton.exists();
-                }})) {
+            @Override
+            public boolean isTrue() {
+                return chromeMenuButton.exists();
+            }})) {
             chromeMenuButton.clickAndWaitForNewWindow();
         }
 
@@ -235,16 +235,33 @@
 
         GoogleServiceTestUtil.signInToChrome(instrumentation);
 
-        final UiObject signedInLabel = device.findObject(new UiSelector().text(username));
-
-        assertTrue("Google log in was unsuccessful", new Wait().
+        final UiObject signedInName = device.findObject(new UiSelector().text(username));
+        boolean hasSignInName = new Wait().
                 until(new Wait.ExpectedCondition() {
                     @Override
                     public boolean isTrue() {
-                        return signedInLabel.exists();
-                    }})
-        );
-        signedInLabel.clickAndWaitForNewWindow();
+                        return signedInName.exists();
+                    }
+                });
+
+        if (hasSignInName) {
+            signedInName.clickAndWaitForNewWindow();
+        } else {
+            final UiObject signedInEmail = device.findObject(new UiSelector().text(email));
+            boolean hasSignInEmail = new Wait().
+                    until(new Wait.ExpectedCondition() {
+                        @Override
+                        public boolean isTrue() {
+                            return signedInEmail.exists();
+                        }
+                    });
+            if (hasSignInEmail) {
+                signedInEmail.clickAndWaitForNewWindow();
+            } else {
+                assertTrue("Google log in was unsuccessful", false);
+            }
+        }
+
         final UiObject signOutLabel = device.findObject(new UiSelector().text("Sign out of Chrome"));
 
         if (new Wait().until(new Wait.ExpectedCondition() {
diff --git a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api27/AppTest.java b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api27/AppTest.java
index 8c24db7..8377f82 100644
--- a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api27/AppTest.java
+++ b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api27/AppTest.java
@@ -79,8 +79,10 @@
     @TestInfo(id = "14578823")
     public void installAppAndLaunch() throws Exception {
         Instrumentation instrumentation = testFramework.getInstrumentation();
+        UiDevice device = UiDevice.getInstance(instrumentation);
         String testPackageName = "com.example.android.rs.hellocompute";
         String apk = "HelloCompute.apk";
+        String appName = "RsHelloCompute";
         String result = "";
 
         // Install RsHelloCompute, if not already present.
@@ -89,6 +91,7 @@
 
         if (!isHelloComputeInstalled) {
             result = PackageInstallationUtil.installApk(instrumentation, apk);
+            new AppWatcher(device).checkForCondition();
             isHelloComputeInstalled = PackageInstallationUtil.
                     isPackageInstalled(instrumentation, testPackageName);
         }
@@ -96,10 +99,11 @@
         assertTrue("Application " + apk + " is not installed. Result: " + result,
                 isHelloComputeInstalled);
 
+        AppLauncher.launch(instrumentation, appName);
+        boolean hasApplication = testFramework.getDevice().findObject(new UiSelector().resourceId(
+                Res.APP_IMAGE_VIEW_ID)).waitForExists(5L);
 
-        AppLauncher.launch(instrumentation, "RsHelloCompute");
-        assertTrue(testFramework.getDevice().findObject(new UiSelector().resourceId(
-                Res.APP_IMAGE_VIEW_ID)).exists());
+        assertTrue("Application " + appName + " did not launch", hasApplication);
     }
 
     /**
diff --git a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api27/GoogleServicesTest.java b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api27/GoogleServicesTest.java
index cad7dde..457792b 100644
--- a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api27/GoogleServicesTest.java
+++ b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api27/GoogleServicesTest.java
@@ -165,7 +165,6 @@
      *   2. Log in user prompt or user promo is present.
      *   </pre>
      */
-    @Ignore("Bug in API 26. b/110433630")
     @Test
     @TestInfo(id = "d7f5673a-a3d0-4f50-856a-dfa10ce5c21c")
     public void loginGoogleChrome() throws Exception {
@@ -173,6 +172,7 @@
         final UiDevice device = UiDevice.getInstance(instrumentation);
 
         final String username = "David Play";
+        final String email = "pstester1980@gmail.com";
 
         AppLauncher.launch(instrumentation, "Chrome");
         new AddGoogleAccountWatcher(device).checkForCondition();
@@ -184,10 +184,10 @@
         );
 
         if (new Wait().until(new Wait.ExpectedCondition() {
-                    @Override
-                    public boolean isTrue() {
-                        return chromeUpdateButton.exists();
-                }})) {
+            @Override
+            public boolean isTrue() {
+                return chromeUpdateButton.exists();
+            }})) {
             chromeUpdateButton.clickAndWaitForNewWindow();
         }
 
@@ -195,10 +195,10 @@
                 new UiSelector().resourceId(Res.CHROME_MENU_BUTTON_RES));
 
         if (new Wait().until(new Wait.ExpectedCondition() {
-                    @Override
-                    public boolean isTrue() {
-                        return chromeMenuButton.exists();
-                }})) {
+            @Override
+            public boolean isTrue() {
+                return chromeMenuButton.exists();
+            }})) {
             chromeMenuButton.clickAndWaitForNewWindow();
         }
 
@@ -235,16 +235,33 @@
 
         GoogleServiceTestUtil.signInToChrome(instrumentation);
 
-        final UiObject signedInLabel = device.findObject(new UiSelector().text(username));
-
-        assertTrue("Google log in was unsuccessful", new Wait().
+        final UiObject signedInName = device.findObject(new UiSelector().text(username));
+        boolean hasSignInName = new Wait().
                 until(new Wait.ExpectedCondition() {
                     @Override
                     public boolean isTrue() {
-                        return signedInLabel.exists();
-                    }})
-        );
-        signedInLabel.clickAndWaitForNewWindow();
+                        return signedInName.exists();
+                    }
+                });
+
+        if (hasSignInName) {
+            signedInName.clickAndWaitForNewWindow();
+        } else {
+            final UiObject signedInEmail = device.findObject(new UiSelector().text(email));
+            boolean hasSignInEmail = new Wait().
+                    until(new Wait.ExpectedCondition() {
+                        @Override
+                        public boolean isTrue() {
+                            return signedInEmail.exists();
+                        }
+                    });
+            if (hasSignInEmail) {
+                signedInEmail.clickAndWaitForNewWindow();
+            } else {
+                assertTrue("Google log in was unsuccessful", false);
+            }
+        }
+
         final UiObject signOutLabel = device.findObject(new UiSelector().text("Sign out of Chrome"));
 
         if (new Wait().until(new Wait.ExpectedCondition() {
diff --git a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/AppTest.java b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/AppTest.java
index edc34e3..782eb0f 100644
--- a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/AppTest.java
+++ b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/AppTest.java
@@ -79,8 +79,10 @@
     @TestInfo(id = "14578823")
     public void installAppAndLaunch() throws Exception {
         Instrumentation instrumentation = testFramework.getInstrumentation();
+        UiDevice device = UiDevice.getInstance(instrumentation);
         String testPackageName = "com.example.android.rs.hellocompute";
         String apk = "HelloCompute.apk";
+        String appName = "RsHelloCompute";
         String result = "";
 
         // Install RsHelloCompute, if not already present.
@@ -89,6 +91,7 @@
 
         if (!isHelloComputeInstalled) {
             result = PackageInstallationUtil.installApk(instrumentation, apk);
+            new AppWatcher(device).checkForCondition();
             isHelloComputeInstalled = PackageInstallationUtil.
                     isPackageInstalled(instrumentation, testPackageName);
         }
@@ -96,9 +99,11 @@
         assertTrue("Application " + apk + " is not installed. Result: " + result,
                 isHelloComputeInstalled);
 
-        AppLauncher.launch(instrumentation, "RsHelloCompute");
-        assertTrue(testFramework.getDevice().findObject(new UiSelector().resourceId(
-                Res.APP_IMAGE_VIEW_ID)).exists());
+        AppLauncher.launch(instrumentation, appName);
+        boolean hasApplication = testFramework.getDevice().findObject(new UiSelector().resourceId(
+                Res.APP_IMAGE_VIEW_ID)).waitForExists(5L);
+
+        assertTrue("Application " + appName + " did not launch", hasApplication);
     }
 
     /**
diff --git a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/CameraTest.java b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/CameraTest.java
index 8e28237..580503d 100644
--- a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/CameraTest.java
+++ b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/CameraTest.java
@@ -47,7 +47,7 @@
     public final SystemImageTestFramework testFramework = new SystemImageTestFramework();
 
     @Rule
-    public Timeout globalTimeout = Timeout.seconds(120);
+    public Timeout globalTimeout = Timeout.seconds(240);
 
     /**
      * Tests the photo capture functionality of the camera application.
diff --git a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/GoogleServicesTest.java b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/GoogleServicesTest.java
index 7fc0e50..8b04b04 100644
--- a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/GoogleServicesTest.java
+++ b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/GoogleServicesTest.java
@@ -139,8 +139,6 @@
 
         assertTrue("Cannot find location toggle button", device.findObject(
                 new UiSelector().className("android.widget.Switch")).exists());
-        assertTrue("Cannot find mode", device.findObject(new UiSelector().text(
-                "Mode")).exists());
         assertTrue("Cannot find recent location", device.findObject(new UiSelector().text(
                 "Recent location requests")).exists());
     }
@@ -165,7 +163,6 @@
      *   2. Log in user prompt or user promo is present.
      *   </pre>
      */
-    @Ignore("Bug in API 26. b/110433630")
     @Test
     @TestInfo(id = "d7f5673a-a3d0-4f50-856a-dfa10ce5c21c")
     public void loginGoogleChrome() throws Exception {
@@ -173,6 +170,7 @@
         final UiDevice device = UiDevice.getInstance(instrumentation);
 
         final String username = "David Play";
+        final String email = "pstester1980@gmail.com";
 
         AppLauncher.launch(instrumentation, "Chrome");
         new AddGoogleAccountWatcher(device).checkForCondition();
@@ -184,10 +182,10 @@
         );
 
         if (new Wait().until(new Wait.ExpectedCondition() {
-                    @Override
-                    public boolean isTrue() {
-                        return chromeUpdateButton.exists();
-                }})) {
+            @Override
+            public boolean isTrue() {
+                return chromeUpdateButton.exists();
+            }})) {
             chromeUpdateButton.clickAndWaitForNewWindow();
         }
 
@@ -195,10 +193,10 @@
                 new UiSelector().resourceId(Res.CHROME_MENU_BUTTON_RES));
 
         if (new Wait().until(new Wait.ExpectedCondition() {
-                    @Override
-                    public boolean isTrue() {
-                        return chromeMenuButton.exists();
-                }})) {
+            @Override
+            public boolean isTrue() {
+                return chromeMenuButton.exists();
+            }})) {
             chromeMenuButton.clickAndWaitForNewWindow();
         }
 
@@ -235,24 +233,31 @@
 
         GoogleServiceTestUtil.signInToChrome(instrumentation);
 
-        final UiObject signedInLabel = device.findObject(new UiSelector().text(username));
-
-        assertTrue("Google log in was unsuccessful", new Wait().
+        final UiObject signedInName = device.findObject(new UiSelector().text(username));
+        boolean hasSignInName = new Wait().
                 until(new Wait.ExpectedCondition() {
                     @Override
                     public boolean isTrue() {
-                        return signedInLabel.exists();
-                    }})
-        );
-        signedInLabel.clickAndWaitForNewWindow();
-        final UiObject signOutLabel = device.findObject(new UiSelector().text("Sign out of Chrome"));
+                        return signedInName.exists();
+                    }
+                });
 
-        if (new Wait().until(new Wait.ExpectedCondition() {
-            @Override
-            public boolean isTrue() {
-                return signOutLabel.exists();
-            }})) {
-            signOutLabel.clickAndWaitForNewWindow();
+        if (hasSignInName) {
+            signedInName.clickAndWaitForNewWindow();
+        } else {
+            final UiObject signedInEmail = device.findObject(new UiSelector().text(email));
+            boolean hasSignInEmail = new Wait().
+                    until(new Wait.ExpectedCondition() {
+                        @Override
+                        public boolean isTrue() {
+                            return signedInEmail.exists();
+                        }
+                    });
+            if (hasSignInEmail) {
+                signedInEmail.clickAndWaitForNewWindow();
+            } else {
+                assertTrue("Google log in was unsuccessful", false);
+            }
         }
 
         final UiObject signOutButton = device.findObject(new UiSelector().text("SIGN OUT"));
diff --git a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/ShellUtilTest.java b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/ShellUtilTest.java
index c01ce52..446b3ed 100644
--- a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/ShellUtilTest.java
+++ b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/api28/ShellUtilTest.java
@@ -54,7 +54,7 @@
     public final SystemImageTestFramework testFramework = new SystemImageTestFramework();
 
     @Rule
-    public Timeout globalTimeout = Timeout.seconds(120);
+    public Timeout globalTimeout = Timeout.seconds(240);
 
     /**
      * Tests the integrity of Shell utilities.
@@ -124,7 +124,8 @@
             DeveloperOptionsManager.enableDeveloperOptions_v2(testFramework);
         }
 
-        AppLauncher.launchPath(instrumentation, new String[] {"Settings", "System", "Developer options"});
+        AppLauncher.launchPath(instrumentation, new String[] {
+                "Settings", "System", "Advanced", "Developer options"});
         // Remove bug report files even if the test fails.
         try {
             device.findObject(
diff --git a/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/tv/api28/SettingsTest.java b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/tv/api28/SettingsTest.java
new file mode 100644
index 0000000..0cc94e4
--- /dev/null
+++ b/system_image_uitests/app/src/androidTest/java/com/android/devtools/systemimage/uitest/smoke/tv/api28/SettingsTest.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.devtools.systemimage.uitest.smoke.tv.api28;
+
+import android.app.Instrumentation;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
+
+import com.android.devtools.systemimage.uitest.annotations.TestInfo;
+import com.android.devtools.systemimage.uitest.common.Res;
+import com.android.devtools.systemimage.uitest.framework.SystemImageTestFramework;
+import com.android.devtools.systemimage.uitest.utils.Wait;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test class for Settings page on Android TV API images.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SettingsTest {
+    @Rule
+    public final SystemImageTestFramework testFramework = new SystemImageTestFramework();
+
+    private Instrumentation instrumentation = testFramework.getInstrumentation();
+    private UiDevice device = UiDevice.getInstance(instrumentation);
+
+    private final static String TAG = "SettingsTest";
+
+    @Rule
+    public Timeout globalTimeout = Timeout.seconds(120);
+
+    /**
+     * Verifies 24-hour format is enabled.
+     * <p>
+     * This is run to qualify releases. Please involve the test team in substantial changes.
+     * <p>
+     *   <pre>
+     *   1. Start the emulator.
+     *   2. Open Settings > Device Preferences > Date & time
+     *   3. Disable 24-hour format.
+     *   4. Verify example time on screen shows 1:00 PM.
+     *   5. Enable 24-hour format.
+     *   Verify:
+     *   Example time on screen shows 13:00.
+     *   </pre>
+     */
+    @Test
+    @TestInfo(id = "f83bf063-2a8c-4d1b-808b-20fd76933135")
+    public void enableTwentyFourHourFormat() throws Exception {
+        this.openDateTimeSettings();
+
+        UiObject use24 = device.findObject(new UiSelector().text("Use 24-hour format"));
+        assertTrue("Failed to find Use 24-hour format switch.", use24.waitForExists(5L));
+
+        boolean useTwentyFourWasEnabled = false;
+        final UiObject thirteenHundredLabel = device.findObject(new UiSelector().text("13:00"));
+
+        // Initialize 24-hour format option to disabled state.
+        if (thirteenHundredLabel.exists()) {
+            useTwentyFourWasEnabled = true;
+            use24.click();
+        }
+        assertTrue("Failed to find Use 24-hour format label.",
+                new Wait().until(new Wait.ExpectedCondition() {
+                    @Override
+                    public boolean isTrue() {
+                        return device.findObject(
+                                new UiSelector().text("Use 24-hour format")).exists();
+                    }
+                })
+        );
+        assertTrue("Failed to find 1:00 PM label.",
+                new Wait().until(new Wait.ExpectedCondition() {
+                    @Override
+                    public boolean isTrue() {
+                        return device.findObject(new UiSelector().text("1:00 PM")).exists();
+                    }
+                })
+        );
+        // Enable 24-hour format.
+        use24.click();
+        assertTrue("Failed to find 13:00 label.",
+                new Wait().until(new Wait.ExpectedCondition() {
+                    @Override
+                    public boolean isTrue(){
+                        return thirteenHundredLabel.waitForExists(5L);
+                    }
+                })
+        );
+
+        // Clean up by disabling 24-hour format option.
+        if (!useTwentyFourWasEnabled) {
+            use24.click();
+        }
+    }
+
+    /**
+     * Verifies Time Zone can be set correctly.
+     * <p>
+     * This is run to qualify releases. Please involve the test team in substantial changes.
+     * <p>
+     *   <pre>
+     *   1. Start the emulator.
+     *   2. Open Settings > Device Preferences > Date & time
+     *   3. Store original time zone offset.
+     *   4. Open Set time zone
+     *   4. Set the time zone to GMT-5:00.
+     *   5. Set the time zone back to the original offset.
+     *   Verify:
+     *   1. Time zone was set to new value successfully.
+     *   2. Time zone was reset successfully.
+     *   </pre>
+     */
+    @Test
+    @TestInfo(id = "f83bf063-2a8c-4d1b-808b-20fd76933135")
+    public void setTimeZone() throws Exception {
+        this.openDateTimeSettings();
+
+        UiObject originalTimeZone = device.findObject(new UiSelector().textContains("GMT"));
+        String originalLabel = originalTimeZone.getText();
+        String originalOffset = originalLabel.substring(0, originalLabel.indexOf(" "));
+
+        UiObject setTimeZone = device.findObject(new UiSelector().text("Set time zone"));
+        if (setTimeZone.waitForExists(5L)) {
+            setTimeZone.clickAndWaitForNewWindow();
+        }
+
+        UiScrollable itemList = new UiScrollable(new UiSelector().resourceId(Res.TV_MAIN_FRAME));
+        itemList.setAsVerticalList();
+
+        String targetOffset = "GMT-05:00";
+        UiSelector targetSelector = new UiSelector().textContains(targetOffset);
+        boolean targetFound = itemList.scrollIntoView(targetSelector);
+        assertTrue("Target time zone label not found", targetFound);
+
+        UiObject targetTimeZone = device.findObject(targetSelector);
+        targetTimeZone.click();
+        assertTrue("Target time zone not set", targetTimeZone.waitForExists(5L));
+
+        if (setTimeZone.waitForExists(5L)) {
+            setTimeZone.clickAndWaitForNewWindow();
+        }
+
+        UiSelector originalSelector = new UiSelector().textContains(originalOffset);
+        boolean originalFound = itemList.scrollIntoView(originalSelector);
+        assertTrue("Original time zone label not found", originalFound);
+
+        originalTimeZone = device.findObject(originalSelector);
+        originalTimeZone.click();
+        assertTrue("Original time zone not set", originalTimeZone.waitForExists(5L));
+    }
+
+    /**
+     * Verifies set date and set time fields are editable.
+     * <p>
+     * This is run to qualify releases. Please involve the test team in substantial changes.
+     * <p>
+     *   <pre>
+     *   1. Start the emulator.
+     *   2. Open Settings > Device Preferences > Date & time.
+     *   3. Ensure that Automatic date & time option is enabled.
+     *   4. Disable Automatic date & time option.
+     *   5. Set date and Set time options are enabled.
+     *   6. Click on Set date option and Set time option.
+     *   Verify:
+     *   1. Automatic date & time is enabled.
+     *   2. Automatic date & time is disabled, Set date and Set time are enabled.
+     *   3. Calendar date picker is found.
+     *   4. Clock time picker is found.
+     *   5. Automatic date & time is re-enabled, Set date and Set time are disabled.
+     *   </pre>
+     */
+    @Test
+    @TestInfo(id = "f83bf063-2a8c-4d1b-808b-20fd76933135")
+    public void enableSetDateAndSetTime() throws Exception {
+
+        this.openDateTimeSettings();
+
+        // Test requires "Automatic date & time" widget to start in the enabled state.
+
+        final UiObject automaticDateTime = device.findObject(new UiSelector().text("Automatic date & time"));
+        final UiObject networkProvidedLabel = device.findObject(new UiSelector().text("Use network-provided time"));
+        final UiObject setDate = device.findObject(new UiSelector().text("Set date"));
+        final UiObject setTime = device.findObject(new UiSelector().text("Set time"));
+
+        if (automaticDateTime.waitForExists(5L) && !networkProvidedLabel.waitForExists(5L)) {
+            automaticDateTime.clickAndWaitForNewWindow();
+        }
+
+        if (networkProvidedLabel.waitForExists(5L)) {
+            networkProvidedLabel.clickAndWaitForNewWindow();
+        }
+
+        assertTrue("Failed to enable automatic date & time.",
+                new Wait().until(new Wait.ExpectedCondition() {
+                    @Override
+                    public boolean isTrue() {
+                        return automaticDateTime.waitForExists(5L) &&
+                                networkProvidedLabel.waitForExists(5L);
+                    }
+                })
+        );
+
+        automaticDateTime.clickAndWaitForNewWindow();
+        final UiObject offLabel = device.findObject(new UiSelector().text("Off"));
+        if (offLabel.waitForExists(5L)) {
+            offLabel.clickAndWaitForNewWindow();
+        }
+
+        assertTrue("Failed to disable automatic date & time.",
+                new Wait().until(new Wait.ExpectedCondition() {
+                    @Override
+                    public boolean isTrue() throws Exception {
+                        return automaticDateTime.waitForExists(5L) &&
+                                offLabel.waitForExists(5L) &&
+                                setDate.isEnabled() && setTime.isEnabled();
+                    }
+                })
+        );
+
+        setDate.clickAndWaitForNewWindow();
+        final UiObject datePicker = device.findObject(
+                new UiSelector().resourceId(Res.TV_DATE_PICKER));
+        assertTrue("Date picker not found",
+                new Wait().until(new Wait.ExpectedCondition() {
+                    @Override
+                    public boolean isTrue() {
+                        return datePicker.waitForExists(5L);
+                    }
+                })
+        );
+
+        this.openDateTimeSettings();
+
+        setTime.clickAndWaitForNewWindow();
+        final UiObject timePicker = device.findObject(
+                new UiSelector().resourceId(Res.TV_TIME_PICKER));
+        assertTrue("Time picker not found",
+                new Wait().until(new Wait.ExpectedCondition() {
+                    @Override
+                    public boolean isTrue() {
+                        return timePicker.waitForExists(5L);
+                    }
+                })
+        );
+
+        this.openDateTimeSettings();
+
+        if (automaticDateTime.waitForExists(5L) && !networkProvidedLabel.waitForExists(5L)) {
+            automaticDateTime.clickAndWaitForNewWindow();
+        }
+
+        if (networkProvidedLabel.waitForExists(5L)) {
+            networkProvidedLabel.clickAndWaitForNewWindow();
+        }
+
+        assertTrue("Failed to re-enable automatic date & time.",
+                new Wait().until(new Wait.ExpectedCondition() {
+                    @Override
+                    public boolean isTrue() throws Exception {
+                        return automaticDateTime.waitForExists(5L) &&
+                                networkProvidedLabel.waitForExists(5L) &&
+                                !setDate.isEnabled() && !setTime.isEnabled();
+                    }
+                })
+        );
+    }
+
+    private void openDateTimeSettings() throws UiObjectNotFoundException {
+        device.pressHome();
+
+        UiObject tvLauncher = device.findObject(new UiSelector().resourceId(Res.TV_LAUNCHER));
+        if (tvLauncher.waitForExists(5L)) {
+            tvLauncher.clickAndWaitForNewWindow();
+        }
+
+        UiObject devicePreferences = device.findObject(new UiSelector().text("Device Preferences"));
+        if (devicePreferences.waitForExists(5L)) {
+            devicePreferences.clickAndWaitForNewWindow();
+        }
+
+        UiObject dateTime = device.findObject(new UiSelector().text("Date & time"));
+        if (dateTime.waitForExists(5L)) {
+            dateTime.clickAndWaitForNewWindow();
+        }
+    }
+}
\ No newline at end of file
diff --git a/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/common/Res.java b/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/common/Res.java
index 174fa99..7fc6c84 100644
--- a/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/common/Res.java
+++ b/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/common/Res.java
@@ -114,4 +114,10 @@
     public static final String ANDROID_LIST_RES = "android:id/list";
     public static final String ANDROID_CONTENT_RES = "android:id/content";
     public static final String ANDROID_TITLE_RES = "android:id/title";
+
+    // Android TV Resources.
+    public static final String TV_LAUNCHER = "com.google.android.tvlauncher:id/button_icon";
+    public static final String TV_MAIN_FRAME = "com.android.tv.settings:id/main_frame";
+    public static final String TV_DATE_PICKER = "com.android.tv.settings:id/date_picker";
+    public static final String TV_TIME_PICKER = "com.android.tv.settings:id/time_picker";
 }
diff --git a/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/utils/CameraTestUtil.java b/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/utils/CameraTestUtil.java
index c82955d..23331f4 100644
--- a/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/utils/CameraTestUtil.java
+++ b/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/utils/CameraTestUtil.java
@@ -52,18 +52,14 @@
         AppLauncher.launchPath(instrumentation, new String[]{"Camera"});
         UiObject cameraFrame = device.findObject(new UiSelector().resourceId(Res.CAMERA_FRAME_RES));
         if (cameraFrame.waitForExists(5L)) {
-            cameraFrame.click();
-            cameraFrame.swipeRight(3);
+            cameraFrame.longClick();
+            cameraFrame.swipeRight(20);
         }
 
         new CameraAccessPermissionsWatcher(device).checkForCondition();
 
-        boolean cameraModeButtonExists = new Wait().until(new Wait.ExpectedCondition() {
-            @Override
-            public boolean isTrue() {
-                return device.findObject(new UiSelector().descriptionStartsWith("Switch to")).exists();
-            }
-        });
+        boolean cameraModeButtonExists = device.findObject(new UiSelector()
+                .descriptionStartsWith("Switch to")).waitForExists(5L);
 
         org.junit.Assert.assertTrue("Button to select " + mode + " mode not found", cameraModeButtonExists);
 
diff --git a/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/watchers/AppWatcher.java b/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/watchers/AppWatcher.java
index 20a6325..3f29574 100644
--- a/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/watchers/AppWatcher.java
+++ b/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/watchers/AppWatcher.java
@@ -43,6 +43,12 @@
                 mDevice.findObject(new UiSelector().textMatches(("(?i)no thanks(?-i)"))).click();
                 condition = true;
             }
+            isSuccess = mDevice.findObject(new UiSelector().textMatches(("(?i)ok(?-i)")))
+                    .waitForExists(TimeUnit.MILLISECONDS.convert(3L, TimeUnit.SECONDS));
+            if (isSuccess) {
+                mDevice.findObject(new UiSelector().textMatches(("(?i)ok(?-i)"))).click();
+                condition = true;
+            }
         }
         catch (UiObjectNotFoundException e) {
             throw new AssertionError("Failed to dismiss the AppTest popup dialog");
diff --git a/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/watchers/CameraAccessPermissionsWatcher.java b/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/watchers/CameraAccessPermissionsWatcher.java
index 252708d..82af22f 100644
--- a/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/watchers/CameraAccessPermissionsWatcher.java
+++ b/system_image_uitests/app/src/main/java/com/android/devtools/systemimage/uitest/watchers/CameraAccessPermissionsWatcher.java
@@ -57,6 +57,12 @@
                 mDevice.findObject(new UiSelector().textMatches("(?i)got it(?-i)")).clickAndWaitForNewWindow();
                 condition = true;
             }
+            hasPopup = mDevice.findObject(new UiSelector().textMatches("(?i)continue(?-i)"))
+                    .waitForExists(TimeUnit.MILLISECONDS.convert(3L, TimeUnit.SECONDS));
+            if (hasPopup) {
+                mDevice.findObject(new UiSelector().textMatches("(?i)continue(?-i)")).clickAndWaitForNewWindow();
+                condition = true;
+            }
         }
         catch (UiObjectNotFoundException e) {
             throw new AssertionError("Unable to grant camera privilege");