Integrate HAL client into AndroidDevice class.

Clean up AndroidDevice class.
Reorg existing mirror modules.
Create a manager class for HAL-level mirror objects.
Integrate the manager class in AndroidDevice.

Bug=29119971

Change-Id: I3d5d2b55128193385977de982af2244b595c2a95
diff --git a/utils/python/controllers/adb.py b/utils/python/controllers/adb.py
index 9e8859a..40b64c2 100644
--- a/utils/python/controllers/adb.py
+++ b/utils/python/controllers/adb.py
@@ -150,34 +150,6 @@
         """
         self.forward("tcp:{} tcp:{}".format(host_port, device_port))
 
-    def start_sl4a(self, port=8080):
-        """Starts sl4a server on the android device.
-
-        Args:
-            port: Port number to use on the android device.
-        """
-        MAX_SL4A_WAIT_TIME = 10
-        print(self.shell(SL4A_LAUNCH_CMD.format(port)))
-
-        for _ in range(MAX_SL4A_WAIT_TIME):
-            time.sleep(1)
-            if self.is_sl4a_running():
-                return
-        raise AdbError(
-            "com.googlecode.android_scripting process never started.")
-
-    def is_sl4a_running(self):
-        """Checks if the sl4a app is running on an android device.
-
-        Returns:
-            True if the sl4a app is running, False otherwise.
-        """
-        #Grep for process with a preceding S which means it is truly started.
-        out = self.shell('ps | grep "S com.googlecode.android_scripting"')
-        if len(out) == 0:
-            return False
-        return True
-
     def __getattr__(self, name):
         def adb_call(*args):
             clean_name = name.replace('_', '-')
diff --git a/utils/python/controllers/android_device.py b/utils/python/controllers/android_device.py
index 01a2368..e7c33ca 100644
--- a/utils/python/controllers/android_device.py
+++ b/utils/python/controllers/android_device.py
@@ -17,6 +17,7 @@
 from builtins import str
 from builtins import open
 
+import logging
 import os
 import time
 import traceback
@@ -24,10 +25,11 @@
 from vts.runners.host import logger as vts_logger
 from vts.runners.host import signals
 from vts.runners.host import utils
-from vts.runners.utils.pythoncontrollers import adb
-from vts.runners.utils.pythoncontrollers import android
-from vts.runners.utils.pythoncontrollers import event_dispatcher
-from vts.runners.utils.pythoncontrollers import fastboot
+from vts.utils.python.controllers import adb
+from vts.utils.python.controllers import event_dispatcher
+from vts.utils.python.controllers import fastboot
+
+from vts.utils.python.mirror import hal_mirror
 
 VTS_CONTROLLER_CONFIG_NAME = "AndroidDevice"
 VTS_CONTROLLER_REFERENCE_NAME = "android_devices"
@@ -43,19 +45,19 @@
     pass
 
 
-def create(configs, logger):
+def create(configs):
     if not configs:
         raise AndroidDeviceError(ANDROID_DEVICE_EMPTY_CONFIG_MSG)
     elif configs == ANDROID_DEVICE_PICK_ALL_TOKEN:
-        ads = get_all_instances(logger=logger)
+        ads = get_all_instances()
     elif not isinstance(configs, list):
         raise AndroidDeviceError(ANDROID_DEVICE_NOT_LIST_CONFIG_MSG)
     elif isinstance(configs[0], str):
         # Configs is a list of serials.
-        ads = get_instances(configs, logger)
+        ads = get_instances(configs)
     else:
         # Configs is a list of dicts.
-        ads = get_instances_with_configs(configs, logger)
+        ads = get_instances_with_configs(configs)
     connected_ads = list_adb_devices()
     for ad in ads:
         if ad.serial not in connected_ads:
@@ -63,16 +65,6 @@
                 ("Android device %s is specified in config"
                  " but is not attached.") % ad.serial)
         ad.startAdbLogcat()
-        try:
-            ad.getSl4aClient()
-            ad.ed.start()
-        except:
-            # This exception is logged here to help with debugging under py2,
-            # because "exception raised while processing another exception" is
-            # only printed under py3.
-            msg = "Failed to start sl4a on %s" % ad.serial
-            logger.exception(msg)
-            raise AndroidDeviceError(msg)
     return ads
 
 
@@ -128,23 +120,22 @@
     return _parse_device_list(out, "fastboot")
 
 
-def get_instances(serials, logger=None):
+def get_instances(serials):
     """Create AndroidDevice instances from a list of serials.
 
     Args:
         serials: A list of android device serials.
-        logger: A logger to be passed to each instance.
 
     Returns:
         A list of AndroidDevice objects.
     """
     results = []
     for s in serials:
-        results.append(AndroidDevice(s, logger=logger))
+        results.append(AndroidDevice(s))
     return results
 
 
-def get_instances_with_configs(configs, logger=None):
+def get_instances_with_configs(configs):
     """Create AndroidDevice instances from a list of json configs.
 
     Each config should have the required key-value pair "serial".
@@ -152,7 +143,6 @@
     Args:
         configs: A list of dicts each representing the configuration of one
             android device.
-        logger: A logger to be passed to each instance.
 
     Returns:
         A list of AndroidDevice objects.
@@ -164,18 +154,17 @@
         except KeyError:
             raise AndroidDeviceError(('Required value "serial" is missing in '
                                       'AndroidDevice config %s.') % c)
-        ad = AndroidDevice(serial, logger=logger)
+        ad = AndroidDevice(serial)
         ad.loadConfig(c)
         results.append(ad)
     return results
 
 
-def get_all_instances(include_fastboot=False, logger=None):
+def get_all_instances(include_fastboot=False):
     """Create AndroidDevice instances for all attached android devices.
 
     Args:
         include_fastboot: Whether to include devices in bootloader mode or not.
-        logger: A logger to be passed to each instance.
 
     Returns:
         A list of AndroidDevice objects each representing an android device
@@ -183,8 +172,8 @@
     """
     if include_fastboot:
         serial_list = list_adb_devices() + list_fastboot_devices()
-        return get_instances(serial_list, logger=logger)
-    return get_instances(list_adb_devices(), logger=logger)
+        return get_instances(serial_list)
+    return get_instances(list_adb_devices())
 
 
 def filter_devices(ads, func):
@@ -267,7 +256,7 @@
     utils.concurrent_exec(take_br, args)
 
 
-class AndroidDevice:
+class AndroidDevice(object):
     """Class representing an android device.
 
     Each object of this class represents one Android device in ACTS, including
@@ -277,11 +266,8 @@
 
     Attributes:
         serial: A string that's the serial number of the Androi device.
-        h_port: An integer that's the port number for adb port forwarding used
-                on the computer the Android device is connected
         d_port: An integer  that's the port number used on the Android device
                 for adb port forwarding.
-        log: A LoggerProxy object used for the class's internal logging.
         log_path: A string that is the path where all logs collected on this
                   android device should be stored.
         adb_logcat_process: A process that collects the adb logcat.
@@ -292,29 +278,25 @@
                   via fastboot.
     """
 
-    def __init__(self,
-                 serial="",
-                 host_port=None,
-                 device_port=8080,
-                 logger=None):
+    def __init__(self, serial="", device_port=5001):
         self.serial = serial
-        self.h_port = host_port
-        self.d_port = device_port
+        self.device_port = device_port
         self.log = logging.getLogger()
-        lp = self.log.log_path
-        self.log_path = os.path.join(lp, "AndroidDevice%s" % serial)
-        self._droid_sessions = {}
-        self._event_dispatchers = {}
+        base_log_path = getattr(self.log, "log_path", "/tmp/logs/")
+        self.log_path = os.path.join(base_log_path, "AndroidDevice%s" % serial)
         self.adb_logcat_process = None
         self.adb_logcat_file_path = None
         self.adb = adb.AdbProxy(serial)
         self.fastboot = fastboot.FastbootProxy(serial)
         if not self.isBootloaderMode:
             self.rootAdb()
+        self.host_port = adb.get_available_host_port()
+        self.adb.tcp_forward(self.host_port, self.device_port)
+        self.hal = hal_mirror.HalMirror(self.host_port)
 
     def __del__(self):
-        if self.h_port:
-            self.adb.forward("--remove tcp:%d" % self.h_port)
+        if self.host_port:
+            self.adb.forward("--remove tcp:%s" % self.host_port)
         if self.adb_logcat_process:
             self.stopAdbLogcat()
 
@@ -355,53 +337,6 @@
             return model
 
     @property
-    def droid(self):
-        """The first sl4a session initiated on this device. None if there isn't
-        one.
-        """
-        try:
-            session_id = sorted(self._droid_sessions)[0]
-            return self._droid_sessions[session_id][0]
-        except IndexError:
-            return None
-
-    @property
-    def ed(self):
-        """The first event_dispatcher instance created on this device. None if
-        there isn't one.
-        """
-        try:
-            session_id = sorted(self._event_dispatchers)[0]
-            return self._event_dispatchers[session_id]
-        except IndexError:
-            return None
-
-    @property
-    def droids(self):
-        """A list of the active sl4a sessions on this device.
-
-        If multiple connections exist for the same session, only one connection
-        is listed.
-        """
-        keys = sorted(self._droid_sessions)
-        results = []
-        for k in keys:
-            results.append(self._droid_sessions[k][0])
-        return results
-
-    @property
-    def eds(self):
-        """A list of the event_dispatcher objects on this device.
-
-        The indexing of the list matches that of the droids property.
-        """
-        keys = sorted(self._event_dispatchers)
-        results = []
-        for k in keys:
-            results.append(self._event_dispatchers[k])
-        return results
-
-    @property
     def isAdbLogcatOn(self):
         """Whether there is an ongoing adb logcat collection.
         """
@@ -421,9 +356,9 @@
         """
         for k, v in config.items():
             if hasattr(self, k):
-                raise AndroidDeviceError(("Attempting to set existing "
-                                          "attribute %s on %s") %
-                                         (k, self.serial))
+                raise AndroidDeviceError(
+                    "Attempting to set existing attribute %s on %s" %
+                    (k, self.serial))
             setattr(self, k, v)
 
     def rootAdb(self):
@@ -435,138 +370,24 @@
             self.adb.remount()
             self.adb.wait_for_device()
 
-    def getSl4aClient(self, handle_event=True):
-        """Create an sl4a connection to the device.
-
-        Return the connection handler 'droid'. By default, another connection
-        on the same session is made for EventDispatcher, and the dispatcher is
-        returned to the caller as well.
-        If sl4a server is not started on the device, try to start it.
-
-        Args:
-            handle_event: True if this droid session will need to handle
-                events.
-
-        Returns:
-            droid: Android object used to communicate with sl4a on the android
-                device.
-            ed: An optional EventDispatcher to organize events for this droid.
-
-        Examples:
-            Don't need event handling:
-            >>> ad = AndroidDevice()
-            >>> droid = ad.getSl4aClient(False)
-
-            Need event handling:
-            >>> ad = AndroidDevice()
-            >>> droid, ed = ad.getSl4aClient()
-        """
-        if not self.h_port or not adb.is_port_available(self.h_port):
-            self.h_port = adb.get_available_host_port()
-        self.adb.tcp_forward(self.h_port, self.d_port)
-        try:
-            droid = self.start_new_session()
-        except:
-            self.adb.start_sl4a()
-            droid = self.start_new_session()
-        if handle_event:
-            ed = self.getSl4aEventDispatcher(droid)
-            return droid, ed
-        return droid
-
-    def getSl4aEventDispatcher(self, droid):
-        """Return an EventDispatcher for an sl4a session
-
-        Args:
-            droid: Session to create EventDispatcher for.
-
-        Returns:
-            ed: An EventDispatcher for specified session.
-        """
-        ed_key = self.serial + str(droid.uid)
-        if ed_key in self._event_dispatchers:
-            if self._event_dispatchers[ed_key] is None:
-                raise AndroidDeviceError("EventDispatcher Key Empty")
-            self.log.debug("Returning existing key %s for event dispatcher!",
-                           ed_key)
-            return self._event_dispatchers[ed_key]
-        event_droid = self.add_new_connection_to_session(droid.uid)
-        ed = event_dispatcher.EventDispatcher(event_droid)
-        self._event_dispatchers[ed_key] = ed
-        return ed
-
-    def _is_timestamp_in_range(self, target, begin_time, end_time):
-        low = vts_logger.logLineTimestampComparator(begin_time, target) <= 0
-        high = vts_logger.logLineTimestampComparator(end_time, target) >= 0
-        return low and high
-
-    def takeAdbLogExcerpt(self, tag, begin_time):
-        """Takes an excerpt of the adb logcat log from a certain time point to
-        current time.
-
-        Args:
-            tag: An identifier of the time period, usualy the name of a test.
-            begin_time: Logline format timestamp of the beginning of the time
-                period.
-        """
-        if not self.adb_logcat_file_path:
-            raise AndroidDeviceError(
-                ("Attempting to cat adb log when none has"
-                 " been collected on Android device %s.") % self.serial)
-        end_time = vts_logger.getLogLineTimestamp()
-        self.log.debug("Extracting adb log from logcat.")
-        adb_excerpt_path = os.path.join(self.log_path, "AdbLogExcerpts")
-        utils.create_dir(adb_excerpt_path)
-        f_name = os.path.basename(self.adb_logcat_file_path)
-        out_name = f_name.replace("adblog,", "").replace(".txt", "")
-        out_name = ",{},{}.txt".format(begin_time, out_name)
-        tag_len = utils.MAX_FILENAME_LEN - len(out_name)
-        tag = tag[:tag_len]
-        out_name = tag + out_name
-        full_adblog_path = os.path.join(adb_excerpt_path, out_name)
-        with open(full_adblog_path, 'w', encoding='utf-8') as out:
-            in_file = self.adb_logcat_file_path
-            with open(in_file, 'r', encoding='utf-8', errors='replace') as f:
-                in_range = False
-                while True:
-                    line = None
-                    try:
-                        line = f.readline()
-                        if not line:
-                            break
-                    except:
-                        continue
-                    line_time = line[:vts_logger.log_line_timestamp_len]
-                    if not vts_logger.isValidLogLineTimestamp(line_time):
-                        continue
-                    if self._is_timestamp_in_range(line_time, begin_time,
-                                                   end_time):
-                        in_range = True
-                        if not line.endswith('\n'):
-                            line += '\n'
-                        out.write(line)
-                    else:
-                        if in_range:
-                            break
-
     def startAdbLogcat(self):
         """Starts a standing adb logcat collection in separate subprocesses and
         save the logcat in a file.
         """
         if self.isAdbLogcatOn:
-            raise AndroidDeviceError(("Android device {} already has an adb "
+            raise AndroidDeviceError(("Android device %s already has an adb "
                                       "logcat thread going on. Cannot start "
-                                      "another one.").format(self.serial))
+                                      "another one.") % self.serial)
         # Disable adb log spam filter.
         self.adb.shell("logpersist.start")
-        f_name = "adblog,{},{}.txt".format(self.model, self.serial)
+        f_name = "adblog,%s,%s.txt" % (self.model, self.serial)
         utils.create_dir(self.log_path)
         logcat_file_path = os.path.join(self.log_path, f_name)
         try:
             extra_params = self.adb_logcat_param
         except AttributeError:
             extra_params = "-b all"
-        cmd = "adb -s {} logcat -v threadtime {} >> {}".format(
+        cmd = "adb -s %s logcat -v threadtime %s >> %s" % (
             self.serial, extra_params, logcat_file_path)
         self.adb_logcat_process = utils.start_standing_subprocess(cmd)
         self.adb_logcat_file_path = logcat_file_path
@@ -575,9 +396,9 @@
         """Stops the adb logcat collection subprocess.
         """
         if not self.isAdbLogcatOn:
-            raise AndroidDeviceError(("Android device {} does not have an "
-                                      "ongoing adb logcat collection.").format(
-                                          self.serial))
+            raise AndroidDeviceError(
+                "Android device %s does not have an ongoing adb logcat collection."
+                % self.serial)
         utils.stop_standing_subprocess(self.adb_logcat_process)
         self.adb_logcat_process = None
 
@@ -590,113 +411,14 @@
         """
         br_path = os.path.join(self.log_path, "BugReports")
         utils.create_dir(br_path)
-        base_name = ",{},{}.txt".format(begin_time, self.serial)
+        base_name = ",%s,%s.txt" % (begin_time, self.serial)
         test_name_len = utils.MAX_FILENAME_LEN - len(base_name)
         out_name = test_name[:test_name_len] + base_name
         full_out_path = os.path.join(br_path, out_name.replace(' ', '\ '))
         self.log.info("Taking bugreport for %s on %s", test_name, self.serial)
-        self.adb.bugreport(" > {}".format(full_out_path))
+        self.adb.bugreport(" > %s" % full_out_path)
         self.log.info("Bugreport for %s taken at %s", test_name, full_out_path)
 
-    def start_new_session(self):
-        """Start a new session in sl4a.
-
-        Also caches the droid in a dict with its uid being the key.
-
-        Returns:
-            An Android object used to communicate with sl4a on the android
-                device.
-
-        Raises:
-            SL4AException: Something is wrong with sl4a and it returned an
-            existing uid to a new session.
-        """
-        droid = android.Android(port=self.h_port)
-        if droid.uid in self._droid_sessions:
-            raise android.SL4AException(("SL4A returned an existing uid for a "
-                                         "new session. Abort."))
-        self._droid_sessions[droid.uid] = [droid]
-        return droid
-
-    def add_new_connection_to_session(self, session_id):
-        """Create a new connection to an existing sl4a session.
-
-        Args:
-            session_id: UID of the sl4a session to add connection to.
-
-        Returns:
-            An Android object used to communicate with sl4a on the android
-                device.
-
-        Raises:
-            AndroidDeviceError: Raised if the session it's trying to connect to
-            does not exist.
-        """
-        if session_id not in self._droid_sessions:
-            raise AndroidDeviceError("Session %d doesn't exist." % session_id)
-        droid = android.Android(cmd='continue',
-                                uid=session_id,
-                                port=self.h_port)
-        return droid
-
-    def closeOneSl4aSession(self, session_id):
-        """Terminate a session in sl4a.
-
-        Send terminate signal to sl4a server; stop dispatcher associated with
-        the session. Clear corresponding droids and dispatchers from cache.
-
-        Args:
-            session_id: UID of the sl4a session to terminate.
-        """
-        if self._droid_sessions and (session_id in self._droid_sessions):
-            for droid in self._droid_sessions[session_id]:
-                droid.closeSl4aSession()
-                droid.close()
-            del self._droid_sessions[session_id]
-        ed_key = self.serial + str(session_id)
-        if ed_key in self._event_dispatchers:
-            self._event_dispatchers[ed_key].clean_up()
-            del self._event_dispatchers[ed_key]
-
-    def closeAllSl4aSession(self):
-        """Terminate all sl4a sessions on the AndroidDevice instance.
-
-        Terminate all sessions and clear caches.
-        """
-        if self._droid_sessions:
-            session_ids = list(self._droid_sessions.keys())
-            for session_id in session_ids:
-                try:
-                    self.closeOneSl4aSession(session_id)
-                except:
-                    msg = "Failed to terminate session %d." % session_id
-                    self.log.exception(msg)
-                    self.log.error(traceback.format_exc())
-            if self.h_port:
-                self.adb.forward("--remove tcp:%d" % self.h_port)
-                self.h_port = None
-
-    def runIperfClient(self, server_host, extra_args=""):
-        """Start iperf client on the device.
-
-        Return status as true if iperf client start successfully.
-        And data flow information as results.
-
-        Args:
-            server_host: Address of the iperf server.
-            extra_args: A string representing extra arguments for iperf client,
-                e.g. "-i 1 -t 30".
-
-        Returns:
-            status: true if iperf client start successfully.
-            results: results have data flow information
-        """
-        out = self.adb.shell("iperf3 -c {} {}".format(server_host, extra_args))
-        clean_out = str(out, 'utf-8').strip().split('\n')
-        if "error" in clean_out[0].lower():
-            return False, clean_out
-        return True, clean_out
-
     @utils.timeout(15 * 60)
     def waitForBootCompletion(self):
         """Waits for Android framework to broadcast ACTION_BOOT_COMPLETED.
@@ -717,22 +439,11 @@
             time.sleep(5)
 
     def reboot(self):
-        """Reboots the device.
-
-        Terminate all sl4a sessions, reboot the device, wait for device to
-        complete booting, and restart an sl4a session.
-
-        This is a blocking method.
+        """Reboots the device and wait for device to complete booting.
 
         This is probably going to print some error messages in console. Only
         use if there's no other option.
 
-        Example:
-            droid, ed = ad.reboot()
-
-        Returns:
-            An sl4a session with an event_dispatcher.
-
         Raises:
             AndroidDeviceError is raised if waiting for completion timed
             out.
@@ -743,12 +454,9 @@
         has_adb_log = self.isAdbLogcatOn
         if has_adb_log:
             self.stopAdbLogcat()
-        self.closeAllSl4aSession()
         self.adb.reboot()
         self.waitForBootCompletion()
         self.rootAdb()
-        droid, ed = self.getSl4aClient()
-        ed.start()
         if has_adb_log:
             self.startAdbLogcat()
-        return droid, ed
+
diff --git a/utils/python/data_objects/__init__.py b/utils/python/data_objects/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/utils/python/data_objects/__init__.py
+++ /dev/null
diff --git a/utils/python/mirror_objects/__init__.py b/utils/python/mirror/__init__.py
similarity index 100%
rename from utils/python/mirror_objects/__init__.py
rename to utils/python/mirror/__init__.py
diff --git a/utils/python/mirror/hal_mirror.py b/utils/python/mirror/hal_mirror.py
new file mode 100644
index 0000000..5d133fd
--- /dev/null
+++ b/utils/python/mirror/hal_mirror.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 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.
+#
+
+import logging
+
+from google.protobuf import text_format
+
+from vts.runners.host import errors
+from vts.runners.host.proto import InterfaceSpecificationMessage_pb2 as IfaceSpecMsg
+from vts.runners.host.tcp_client import vts_tcp_client
+from vts.utils.python.mirror import mirror_object
+
+COMPONENT_CLASS_DICT = {"hal": 1,
+                        "sharedlib": 2,
+                        "hal_hidl": 3,
+                        "hal_submodule": 4,
+                        "legacy_hal": 5}
+
+COMPONENT_TYPE_DICT = {"audio": 1,
+                       "camera": 2,
+                       "gps": 3,
+                       "light": 4,
+                       "wifi": 5}
+
+_DEFAULT_TARGET_BASE_PATHS = ["/system/lib64/hw"]
+
+
+class HalMirror(object):
+    """The class that acts as the mirror to an Android device's HAL layer.
+
+    This class holds and manages the life cycle of multiple mirror objects that
+    map to different HAL components.
+
+    One can use this class to create and des
+
+    Attributes:
+        hal_level_mirrors: dict, key is HAL handler name, value is HAL
+                           mirror object.
+        host_port: int, the port number on the host side to use.
+    """
+    def __init__(self, host_port):
+        self._hal_level_mirrors = {}
+        self._host_port = host_port
+
+    def __del__(self):
+        for hal_mirror_name in self._hal_level_mirrors:
+            self.RemoveHal(hal_mirror_name)
+        self._hal_level_mirrors = {}
+
+    def InitConventionalHal(self,
+                            target_type,
+                            target_version,
+                            target_basepaths=_DEFAULT_TARGET_BASE_PATHS,
+                            handler_name=None,
+                            bits=64):
+        """Initiates a handler for a particular conventional HAL.
+
+        This will initiate a stub service for a HAL on the target side, create
+        the top level mirror object for a HAL, and register it in the manager.
+
+        Args:
+            target_type: string, the target type name (e.g., light, camera).
+            target_version: float, the target component version (e.g., 1.0).
+            target_basepaths: list of strings, the paths to look for target
+                             files in. Default is _DEFAULT_TARGET_BASE_PATHS.
+            handler_name: string, the name of the handler. target_type is used
+                          by default.
+            bits: integer, processor architecture indicator: 32 or 64.
+        """
+        self._CreateMirrorObject("hal",
+                                 target_type,
+                                 target_version,
+                                 target_basepaths,
+                                 handler_name=handler_name,
+                                 bits=bits)
+
+    def InitLegacyHal(self,
+                      target_type,
+                      target_version,
+                      target_basepaths=_DEFAULT_TARGET_BASE_PATHS,
+                      handler_name=None,
+                      bits=64):
+        """Initiates a handler for a particular legacy HAL that does not use
+        HIDL.
+
+        This will initiate a stub service for a HAL on the target side, create
+        the top level mirror object for a HAL, and register it in the manager.
+
+        Args:
+            target_type: string, the target type name (e.g., light, camera).
+            target_version: float, the target component version (e.g., 1.0).
+            target_basepaths: list of strings, the paths to look for target
+                             files in. Default is _DEFAULT_TARGET_BASE_PATHS.
+            handler_name: string, the name of the handler. target_type is used
+                          by default.
+            bits: integer, processor architecture indicator: 32 or 64.
+        """
+        self._CreateMirrorObject("legacy_hal",
+                                 target_type,
+                                 target_version,
+                                 target_basepaths,
+                                 handler_name=handler_name,
+                                 bits=bits)
+
+    def RemoveHal(self, handler_name):
+        hal_level_mirror = self._hal_level_mirrors[handler_name]
+        hal_level_mirror.CleanUp()
+
+    def _CreateMirrorObject(self,
+                            target_class,
+                            target_type,
+                            target_version,
+                            target_basepaths=_DEFAULT_TARGET_BASE_PATHS,
+                            handler_name=None,
+                            bits=64):
+        """Initiates the stub for a HAL on the target device and creates a top
+        level MirroObject for it.
+
+        Args:
+            target_class: string, the target class name (e.g., hal).
+            target_type: string, the target type name (e.g., light, camera).
+            target_version: float, the target component version (e.g., 1.0).
+            target_basepaths: list of strings, the paths to look for target
+                             files in. Default is _DEFAULT_TARGET_BASE_PATHS.
+            handler_name: string, the name of the handler. target_type is used
+                          by default.
+            bits: integer, processor architecture indicator: 32 or 64.
+
+        Raises:
+            errors.ComponentLoadingError is raised when error occurs trying to
+            create a MirrorObject.
+        """
+        if bits not in [32, 64]:
+            raise error.ComponentLoadingError("Invalid value for bits: %s" % bits)
+        client = vts_tcp_client.VtsTcpClient()
+        client.Connect(port=self._host_port)
+        if not handler_name:
+            handler_name = target_type
+        service_name = "vts_binder_%s" % handler_name
+
+        # Get all the HALs available on the target.
+        hal_list = client.ListHals(target_basepaths)
+        if not hal_list:
+            raise errors.ComponentLoadingError(
+                "Could not find any HAL under path %s" % target_basepaths)
+        logging.debug(hal_list)
+
+        # Find the corresponding filename for HAL target type.
+        target_filename = None
+        for name in hal_list:
+            if target_type in name:
+                # TODO: check more exactly (e.g., multiple hits).
+                target_filename = name
+        if not target_filename:
+            raise errors.ComponentLoadingError(
+                "No file found for HAL target type %s." % target_type)
+
+        # Check whether the requested binder service is already running.
+        # if client.CheckStubService(service_name=service_name):
+        #     raise errors.ComponentLoadingError("A stub for %s already exists" %
+        #                                        service_name)
+
+        # Launch the corresponding stub of the requested HAL on the target.
+        logging.info("Init the stub service for %s", target_type)
+        target_class_id = COMPONENT_CLASS_DICT[target_class.lower()]
+        target_type_id = COMPONENT_TYPE_DICT[target_type.lower()]
+        launched = client.LaunchStubService(service_name=service_name,
+                                            file_path=target_filename,
+                                            bits=bits,
+                                            target_class=target_class_id,
+                                            target_type=target_type_id,
+                                            target_version=target_version)
+        if not launched:
+            raise errors.ComponentLoadingError(
+                "Failed to launch stub service %s from file path %s" %
+                (target_type, target_filename))
+
+        # Create API spec message.
+        found_api_spec = client.ListApis()
+        if not found_api_spec:
+            raise errors.ComponentLoadingError("No API found for %s" %
+                                               service_name)
+        logging.debug("Found %d APIs for %s:\n%s", len(found_api_spec),
+                      service_name, found_api_spec)
+        if_spec_msg = IfaceSpecMsg.InterfaceSpecificationMessage()
+        text_format.Merge(found_api_spec, if_spec_msg)
+
+        # Instantiate a MirrorObject and return it.
+        hal_mirror = mirror_object.MirrorObject(client, if_spec_msg)
+        self._hal_level_mirrors[handler_name] = hal_mirror
+
+    def __getattr__(self, name):
+        return self._hal_level_mirrors[name]
diff --git a/utils/python/mirror_objects/MirrorObject.py b/utils/python/mirror/mirror_object.py
similarity index 65%
rename from utils/python/mirror_objects/MirrorObject.py
rename to utils/python/mirror/mirror_object.py
index 29a79c7..f4f26e0 100644
--- a/utils/python/mirror_objects/MirrorObject.py
+++ b/utils/python/mirror/mirror_object.py
@@ -20,21 +20,25 @@
 import random
 
 from vts.utils.python.fuzzer import FuzzerUtils
-from vts.runners.host.proto import InterfaceSpecificationMessage_pb2
+from vts.runners.host.proto import InterfaceSpecificationMessage_pb2 as IfaceSpecMsg
 from google.protobuf import text_format
 
-
 # a dict containing the IDs of the registered function pointers.
 _function_pointer_id_dict = {}
 
+class MirrorObjectError(Exception):
+    pass
 
 class MirrorObject(object):
-    """Actual mirror object.
+    """The class that mirrors objects on the native side.
 
-    Args:
+    This class exists on the host and can be used to communicate to a
+    particular HAL in the HAL agent on the target side.
+
+    Attributes:
         _client: the TCP client instance.
         _if_spec_msg: the interface specification message of a host object to
-            mirror.
+                      mirror.
         _parent_path: the name of a sub struct this object mirrors.
     """
 
@@ -45,36 +49,41 @@
 
     def GetFunctionPointerID(self, function_pointer):
         """Returns the function pointer ID for the given one."""
-        max = 0
+        max_num = 0
         for key in _function_pointer_id_dict:
             if _function_pointer_id_dict[key] == function_pointer:
                 return _function_pointer_id_dict[function_pointer]
-            if not max or key > max:
-                max = key
-        _function_pointer_id_dict[max + 1] = function_pointer
-        return str(max + 1)
+            if not max_num or key > max_num:
+                max_num = key
+        _function_pointer_id_dict[max_num + 1] = function_pointer
+        return str(max_num + 1)
 
-    def Open(self, module_name=None):
-        """Opens the target HAL component (only for conventional HAL).
+    def OpenConventionalHal(self, module_name=None):
+        """Opens the target conventional HAL component.
+
+        This is only needed for conventional HAL.
 
         Args:
             module_name: string, the name of a module to load.
         """
-        func_msg = InterfaceSpecificationMessage_pb2.FunctionSpecificationMessage()
+        func_msg = IfaceSpecMsg.FunctionSpecificationMessage()
         func_msg.name = "#Open"
-        logging.info("remote call %s", func_msg.name)
+        logging.debug("remote call %s", func_msg.name)
         if module_name:
             arg = func_msg.arg.add()
             arg.primitive_type.append("string")
             value = arg.primitive_value.add()
             value.bytes = module_name
             func_msg.return_type.primitive_type.append("int32_t")
-        logging.info("final msg %s", func_msg)
+        logging.debug("final msg %s", func_msg)
 
         result = self._client.CallApi(text_format.MessageToString(func_msg))
-        logging.info(result)
+        logging.debug(result)
         return result
 
+    def CleanUp(self):
+        self._client.Disconnect()
+
     def GetApi(self, api_name):
         """Returns the Function Specification Message.
 
@@ -116,8 +125,9 @@
             ArgumentSpecificationMessage if found, None otherwise
         """
         try:
-            for name, definition in zip(self._if_spec_msg.aggregate_type_name,
-                                        self._if_spec_msg.aggregate_type_definition):
+            for name, definition in zip(
+                    self._if_spec_msg.aggregate_type_name,
+                    self._if_spec_msg.aggregate_type_definition):
                 if name != "const" and name == type_name:
                     return copy.copy(definition)
             return None
@@ -136,8 +146,9 @@
             ArgumentSpecificationMessage if found, None otherwise
         """
         try:
-            for name, definition in zip(self._if_spec_msg.aggregate_type_name,
-                                        self._if_spec_msg.aggregate_type_definition):
+            for name, definition in zip(
+                    self._if_spec_msg.aggregate_type_name,
+                    self._if_spec_msg.aggregate_type_definition):
                 if name == "const":
                     return copy.copy(definition)
             return None
@@ -146,6 +157,7 @@
             # SpecificationMessage.
             return None
 
+    # TODO: Guard against calls to this function after self.CleanUp is called.
     def __getattr__(self, api_name, *args, **kwargs):
         """Calls a target component's API.
 
@@ -158,9 +170,9 @@
             """Dynamically calls a remote API."""
             func_msg = self.GetApi(api_name)
             if not func_msg:
-                logging.fatal("api %s unknown", func_msg)
+                raise MirrorObjectError("api %s unknown", func_msg)
 
-            logging.info("remote call %s.%s", self._parent_path, api_name)
+            logging.debug("remote call %s.%s", self._parent_path, api_name)
             logging.debug("remote call %s%s", api_name, args)
             if args:
                 for arg_msg, value_msg in zip(func_msg.arg, args):
@@ -173,12 +185,14 @@
                             pv.int32_t = value_msg
                         else:
                             # TODO: check in advance (whether it's a message)
-                            if isinstance(value_msg,
-                                          InterfaceSpecificationMessage_pb2.ArgumentSpecificationMessage):
-                              arg_msg.CopyFrom(value_msg)
-                              arg_msg.ClearField("primitive_value")
+                            if isinstance(
+                                    value_msg,
+                                    IfaceSpecMsg.ArgumentSpecificationMessage):
+                                arg_msg.CopyFrom(value_msg)
+                                arg_msg.ClearField("primitive_value")
                             else:
-                              logging.error("unknown type %s", type(value_msg))
+                                logging.error("unknown type %s",
+                                              type(value_msg))
 
                             try:
                                 for primitive_value in value_msg.primitive_value:
@@ -192,7 +206,7 @@
                             except AttributeError as e:
                                 logging.exception(e)
                                 raise
-                logging.info("final msg %s", func_msg)
+                logging.debug("final msg %s", func_msg)
             else:
                 # TODO: use kwargs
                 for arg in func_msg.arg:
@@ -203,8 +217,9 @@
                 logging.debug(func_msg)
 
             if self._parent_path:
-              func_msg.parent_path = self._parent_path
-            result = self._client.CallApi(text_format.MessageToString(func_msg))
+                func_msg.parent_path = self._parent_path
+            result = self._client.CallApi(text_format.MessageToString(
+                func_msg))
             logging.debug(result)
             return result
 
@@ -212,56 +227,57 @@
             """Dynamically generates a custom message instance."""
             arg_msg = self.GetCustomAggregateType(api_name)
             if not arg_msg:
-                logging.fatal("arg %s unknown", arg_msg)
-            logging.info("MESSAGE %s", api_name)
-            for type, name, value in zip(arg_msg.primitive_type,
-                                         arg_msg.primitive_name,
-                                         arg_msg.primitive_value):
-                logging.debug("for %s %s %s", type, name, value)
+                raise MirrorObjectError("arg %s unknown" % arg_msg)
+            logging.debug("MESSAGE %s", api_name)
+            for p_type, name, value in zip(arg_msg.primitive_type,
+                                           arg_msg.primitive_name,
+                                           arg_msg.primitive_value):
+                logging.debug("for %s %s %s", p_type, name, value)
                 for given_name, given_value in kwargs.iteritems():
-                    logging.info("check %s %s", name, given_name)
+                    logging.debug("check %s %s", name, given_name)
                     if given_name == name:
-                        logging.info("match type=%s", type)
-                        if type == "uint32_t":
+                        logging.debug("match p_type=%s", p_type)
+                        if p_type == "uint32_t":
                             value.uint32_t = given_value
-                        elif type == "int32_t":
+                        elif p_type == "int32_t":
                             value.int32_t = given_value
-                        elif type == "function_pointer":
-                            value.bytes = self.GetFunctionPointerID(given_value)
+                        elif p_type == "function_pointer":
+                            value.bytes = self.GetFunctionPointerID(
+                                given_value)
                         else:
-                            logging.fatal("support %s", type)
+                            raise MirrorObjectError("support %s" % p_type)
                         continue
-            for type, name, value, given_value in zip(arg_msg.primitive_type,
-                                                      arg_msg.primitive_name,
-                                                      arg_msg.primitive_value,
-                                                      args):
-                logging.info("arg match type=%s", type)
-                if type == "uint32_t":
+            for p_type, name, value, given_value in zip(arg_msg.primitive_type,
+                                                        arg_msg.primitive_name,
+                                                        arg_msg.primitive_value,
+                                                        args):
+                logging.debug("arg match type=%s", p_type)
+                if p_type == "uint32_t":
                     value.uint32_t = given_value
-                elif type == "int32_t":
+                elif p_type == "int32_t":
                     value.int32_t = given_value
-                elif type == "function_pointer":
+                elif p_type == "function_pointer":
                     value.bytes = self.GetFunctionPointerID(given_value)
                 else:
-                    logging.fatal("support %s", type)
+                    raise MirrorObjectError("support %s" % p_type)
                 continue
-            logging.info("generated %s", arg_msg)
+            logging.debug("generated %s", arg_msg)
             return arg_msg
 
         def MessageFuzzer(arg_msg):
             """Fuzz a custom message instance."""
             if not self.GetCustomAggregateType(api_name):
-                logging.fatal("fuzz arg %s unknown", arg_msg)
+                raise MirrorObjectError("fuzz arg %s unknown" % arg_msg)
 
             index = random.randint(0, len(arg_msg.primitive_type))
             count = 0
-            for type, name, value in zip(arg_msg.primitive_type,
-                                         arg_msg.primitive_name,
-                                         arg_msg.primitive_value):
+            for p_type, name, value in zip(arg_msg.primitive_type,
+                                           arg_msg.primitive_name,
+                                           arg_msg.primitive_value):
                 if count == index:
-                    if type == "uint32_t":
+                    if p_type == "uint32_t":
                         value.uint32_t ^= FuzzerUtils.mask_uint32_t()
-                    elif type == "int32_t":
+                    elif p_type == "int32_t":
                         mask = FuzzerUtils.mask_int32_t()
                         if mask == (1 << 31):
                             value.int32_t *= -1
@@ -269,37 +285,29 @@
                         else:
                             value.int32_t ^= mask
                     else:
-                        logging.fatal("support %s", type)
+                        raise MirrorObjectError("support %s" % p_type)
                     break
                 count += 1
-            logging.info("fuzzed %s", arg_msg)
+            logging.debug("fuzzed %s", arg_msg)
             return arg_msg
 
         def ConstGenerator():
             """Dynamically generates a const variable's value."""
             arg_msg = self.GetConstType(api_name)
             if not arg_msg:
-                logging.fatal("const %s unknown", arg_msg)
+                raise MirrorObjectError("const %s unknown" % arg_msg)
             logging.debug("check %s", api_name)
-            for type, name, value in zip(arg_msg.primitive_type,
-                                         arg_msg.primitive_name,
-                                         arg_msg.primitive_value):
-                logging.debug("for %s %s %s", type, name, value)
+            for p_type, name, value in zip(arg_msg.primitive_type,
+                                           arg_msg.primitive_name,
+                                           arg_msg.primitive_value):
+                logging.debug("for %s %s %s", p_type, name, value)
                 if api_name == name:
-                    logging.debug("match")
-                    if type == "uint32_t":
-                        logging.info("return %s", value)
-                        return value.uint32_t
-                    elif type == "int32_t":
-                        logging.info("return %s", value)
-                        return value.int32_t
-                    elif type == "bytes":
-                        logging.info("return %s", value)
-                        return value.bytes
-                    else:
-                        logging.fatal("support %s", type)
-                    continue
-            logging.fatal("const %s not found", arg_msg)
+                    logging.debug("Found match for API name %s.", name)
+                    ret_v = getattr(value, p_type, None)
+                    if ret_v is None:
+                        raise MirrorObjectError("No value found for type %s in %s." % (p_type, value))
+                    return ret_v
+            raise MirrorObjectError("const %s not found" % api_name)
 
         # handle APIs.
         func_msg = self.GetApi(api_name)
@@ -314,14 +322,13 @@
                 parent_name = "%s.%s" % (self._parent_path, api_name)
             else:
                 parent_name = api_name
-            return MirrorObjectForSubStruct(self._client, struct_msg,
-                                            parent_name)
+            return MirrorObject(self._client, struct_msg, parent_name)
 
         # handle attributes.
         fuzz = False
         if api_name.endswith("_fuzz"):
-          fuzz = True
-          api_name = api_name[:-5]
+            fuzz = True
+            api_name = api_name[:-5]
         arg_msg = self.GetCustomAggregateType(api_name)
         if arg_msg:
             logging.debug("arg %s", arg_msg)
@@ -332,16 +339,6 @@
 
         arg_msg = self.GetConstType(api_name)
         if arg_msg:
-            logging.info("const %s *\n%s", api_name, arg_msg)
+            logging.debug("const %s *\n%s", api_name, arg_msg)
             return ConstGenerator()
-        logging.fatal("unknown api name %s", api_name)
-
-
-class MirrorObjectForSubStruct(MirrorObject):
-    """Actual mirror object for sub struct.
-
-    Args:
-        _client: see MirrorObject
-        _if_spec_msg: see MirrorObject
-        _name: see MirrorObject
-    """
+        raise MirrorObjectError("unknown api name %s" % api_name)
diff --git a/utils/python/mirror_objects/MirrorBase.py b/utils/python/mirror_objects/MirrorBase.py
deleted file mode 100644
index 9471758..0000000
--- a/utils/python/mirror_objects/MirrorBase.py
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2016 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.
-#
-
-import logging
-import os
-
-from vts.runners.host import errors
-from vts.runners.host.proto import AndroidSystemControlMessage_pb2
-from vts.runners.host.proto import InterfaceSpecificationMessage_pb2
-from vts.runners.host.tcp_client import TcpClient
-from vts.utils.python.mirror_objects import MirrorObject
-
-from google.protobuf import text_format
-
-COMPONENT_CLASS_DICT = {"hal": 1,
-                        "sharedlib": 2,
-                        "hal_hidl": 3,
-                        "hal_submodule": 4,
-                        "legacy_hal": 5}
-
-COMPONENT_TYPE_DICT = {"audio": 1,
-                       "camera": 2,
-                       "gps": 3,
-                       "light": 4,
-                       "wifi": 5}
-
-
-class MirrorBase(object):
-    """Base class for all host-side mirror objects.
-
-    This creates a connection to the target-side agent and initializes the
-    child mirror class's attributes dynamically.
-
-    Attributes:
-        _target_basepath: string, the path of a base dir which contains the
-            target component files.
-    """
-
-    _target_basepath = ["/system/lib64/hw"]
-
-    def Init(self, target_class, target_type, target_version, target_basepath,
-             handler_name=None, bits=64):
-        """Initializes the connection and then calls 'Build' to init attributes.
-
-        Args:
-            target_class: string, the target class name (e.g., hal).
-            target_type: string, the target type name (e.g., light, camera).
-            target_version: float, the target component version (e.g., 1.0).
-            target_basepath: string, the base path of where a target file is
-                stored in.
-            handler_name: string, the name of the handler.
-                by default, target_type is used.
-
-        Raises:
-            ComponentLoadingError when loading fails.
-        """
-        if not target_basepath:
-            target_basepath = self._target_basepath
-        if not handler_name:
-            handler_name = target_type
-
-        logging.info("Init a Mirror for %s", target_type)
-        self._client = TcpClient.VtsTcpClient()
-
-        self._client.Connect()
-
-        logging.info("target basepath: %s", target_basepath)
-        listed_hals = self._client.ListHals(target_basepath)
-        logging.debug(listed_hals)
-
-        found_target_filename = None
-        if listed_hals:
-          for hal_filename in listed_hals:
-              if target_type in hal_filename:
-                  # TODO: check more exactly (e.g., multiple hits).
-                  found_target_filename = hal_filename
-                  break
-
-        if not found_target_filename:
-          logging.error("no target component found %s", target_type)
-          return
-
-        service_name = "vts_binder_%s" % handler_name
-        # check whether the binder service is running
-        if not self._client.CheckStubService(service_name=service_name):
-            # consider doing: raise errors.ComponentLoadingError(
-            #    "A stub for %s already exists" % handler_name)
-            target_class_id = COMPONENT_CLASS_DICT[target_class.lower()]
-            target_type_id = COMPONENT_TYPE_DICT[target_type.lower()]
-
-            launched = self._client.LaunchStubService(
-                service_name=service_name, file_path=found_target_filename,
-                bits=bits, target_class=target_class_id,
-                target_type=target_type_id, target_version=target_version)
-            if not launched:
-                raise errors.ComponentLoadingError(
-                    "Target file path %s" % found_target_filename)
-
-        found_api_spec = self._client.ListApis()
-        logging.debug("ListApis: %s", found_api_spec)
-        if found_api_spec:
-            logging.debug("len %d", len(found_api_spec))
-            self._if_spec_msg = InterfaceSpecificationMessage_pb2.InterfaceSpecificationMessage()
-            text_format.Merge(found_api_spec, self._if_spec_msg)
-            self.Build(target_type, self._if_spec_msg)
-
-    def Build(self, target_type, if_spec_msg=None):
-        """Builds the child class's attributes dynamically.
-
-        Args:
-            target_name: string, the name of the target mirror to create.
-            if_spec_msg: InterfaceSpecificationMessage_pb2 proto buf.
-        """
-        logging.info("Build a Mirror for %s", target_type)
-        if not if_spec_msg:
-            if_spec_msg = self._if_spec_msg
-
-        logging.info(if_spec_msg)
-        mirror_object = MirrorObject.MirrorObject(self._client, if_spec_msg)
-        self.__setattr__(target_type, mirror_object)
diff --git a/utils/python/mirror_objects/mirror.py b/utils/python/mirror_objects/mirror.py
deleted file mode 100644
index 792fef3..0000000
--- a/utils/python/mirror_objects/mirror.py
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2016 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.
-#
-
-from vts.utils.python.mirror_objects import MirrorBase
-
-
-class Mirror(MirrorBase.MirrorBase):
-    """HAL Mirror Object.
-
-    Attributes:
-        _target_basepath: target component's base dir path in the file system.
-    """
-
-    def __init__(self, target_basepath=None):
-        if target_basepath:
-            self._target_basepath = target_basepath
-
-    def InitHal(self, target_type, target_version, target_basepath=None,
-                handler_name=None, bits=64):
-        """Initializes a HAL.
-
-        Args:
-            target_type: string, the target type name (e.g., light, camera).
-            target_version: float, the target component version (e.g., 1.0).
-            target_basepath: string, the base path of where a target file is
-                stored in.
-            handler_name: string, the name of the handler.
-                by default, target_type is used.
-        """
-        super(Mirror, self).Init("hal", target_type, target_version,
-                                 target_basepath,
-                                 handler_name=handler_name, bits=bits)
-
-    def InitLegacyHal(self, target_type, target_version, target_basepath=None):
-        """Initializes a legacy HAL (e.g., wifi).
-
-        Args:
-          target_type: string, the target type name (e.g., light, camera).
-          target_version: float, the target component version (e.g., 1.0).
-          target_basepath: string, the base path of where a target file is stored
-              in.
-        """
-        super(Mirror, self).Init("legacy_hal", target_type, target_version,
-                                 target_basepath)