Fix built-in controller registration flow.

Built-in controller registration flow is wrong, breaking the use
case where more than one test class is executed in a test run.
It should follow the same flow as controller modules registered
by the test classes.

The import, registration, and unregistration should happen once for
each test class in each test run, for all controller modules.

Added a unit test for this use case as well.

Bug=29196491

Cherry-pick of https://android-review.googlesource.com/#/c/237131/
Change-Id: Icdda688e8569686589b27a7234ea982401f4eb9d
diff --git a/acts/framework/acts/test_runner.py b/acts/framework/acts/test_runner.py
index 72f5281..ca50ff7 100644
--- a/acts/framework/acts/test_runner.py
+++ b/acts/framework/acts/test_runner.py
@@ -134,6 +134,28 @@
                             test_classes[member_name] = test_class
         return test_classes
 
+    def _import_builtin_controllers(self):
+        """Import built-in controller modules.
+
+        Go through the testbed configs, find any built-in controller configs
+        and import the corresponding controller module from acts.controllers
+        package.
+
+        TODO(angli): Remove this when all scripts change to explicitly declare
+                     controller dependency.
+
+        Returns:
+            A list of controller modules.
+        """
+        builtin_controllers = []
+        for ctrl_name in keys.Config.builtin_controller_names.value:
+            if ctrl_name in self.testbed_configs:
+                module_name = keys.get_module_name(ctrl_name)
+                module = importlib.import_module("acts.controllers.%s" %
+                                                 module_name)
+                builtin_controllers.append(module)
+        return builtin_controllers
+
     @staticmethod
     def verify_controller_module(module):
         """Verifies a module object follows the required interface for
@@ -167,7 +189,7 @@
         module will be instantiated with corresponding configs in the test
         config file. The module should be imported first.
 
-        Params:
+        Args:
             module: A module that follows the controller module interface.
             required: A bool. If True, failing to register the specified
                       controller module raises exceptions. If False, returns
@@ -255,14 +277,8 @@
         Args:
             test_configs: A json object representing the test configurations.
         """
-        self.test_run_info[keys.Config.ikey_testbed_name.value] = self.testbed_name
-        # Instantiate builtin controllers
-        for ctrl_name in keys.Config.builtin_controller_names.value:
-            if ctrl_name in self.testbed_configs:
-                module_name = keys.get_module_name(ctrl_name)
-                module = importlib.import_module("acts.controllers.%s" %
-                                                 module_name)
-                self.register_controller(module)
+        self.test_run_info[
+            keys.Config.ikey_testbed_name.value] = self.testbed_name
         # Unpack other params.
         self.test_run_info["register_controller"] = self.register_controller
         self.test_run_info[keys.Config.ikey_logpath.value] = self.log_path
@@ -347,24 +363,26 @@
         t_configs = self.test_configs[keys.Config.key_test_paths.value]
         self.test_classes = self.import_test_modules(t_configs)
         self.log.debug("Executing run list %s.", self.run_list)
-        try:
-            for test_cls_name, test_case_names in self.run_list:
-                if not self.running:
-                    break
-                if test_case_names:
-                    self.log.debug("Executing test cases %s in test class %s.",
-                                   test_case_names,
-                                   test_cls_name)
-                else:
-                    self.log.debug("Executing test class %s", test_cls_name)
-                try:
-                    self.run_test_class(test_cls_name, test_case_names)
-                except signals.TestAbortAll as e:
-                    self.log.warning(("Abort all subsequent test classes. Reason: "
-                                      "%s"), e)
-                    raise
-        finally:
-            self.unregister_controllers()
+        for test_cls_name, test_case_names in self.run_list:
+            if not self.running:
+                break
+            if test_case_names:
+                self.log.debug("Executing test cases %s in test class %s.",
+                               test_case_names, test_cls_name)
+            else:
+                self.log.debug("Executing test class %s", test_cls_name)
+            try:
+                # Import and register the built-in controller modules specified
+                # in testbed config.
+                for module in self._import_builtin_controllers():
+                    self.register_controller(module)
+                self.run_test_class(test_cls_name, test_case_names)
+            except signals.TestAbortAll as e:
+                self.log.warning(
+                    "Abort all subsequent test classes. Reason: %s", e)
+                raise
+            finally:
+                self.unregister_controllers()
 
     def stop(self):
         """Releases resources from test run. Should always be called after
diff --git a/acts/framework/tests/acts_test_runner_test.py b/acts/framework/tests/acts_test_runner_test.py
index fccb081..2126ca0 100755
--- a/acts/framework/tests/acts_test_runner_test.py
+++ b/acts/framework/tests/acts_test_runner_test.py
@@ -22,6 +22,8 @@
 from acts import keys
 from acts import signals
 from acts import test_runner
+
+import acts_android_device_test
 import mock_controller
 
 
@@ -183,12 +185,9 @@
                      {"serial": "xxxx", "magic": "Magic2"}]
         mock_test_config[tb_key][mock_ctrlr_config_name] = my_config
         mock_test_config[tb_key]["AndroidDevice"] = [
-            {"serial": "1",
-             "skip_sl4a": True}
-        ]
+            {"serial": "1", "skip_sl4a": True}]
         tr = test_runner.TestRunner(mock_test_config,
-                                    [('IntegrationTest', None),
-                                     ('IntegrationTest', None)])
+            [('IntegrationTest', None), ('IntegrationTest', None)])
         tr.run()
         tr.stop()
         self.assertFalse(tr.controller_registry)