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)