From ddd2e29b87fa5daa12d88b9c6f7ea3b6e7c8ca9c Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Thu, 6 Jul 2017 17:30:12 +0100 Subject: [PATCH 1/6] AndroidTarget: Adds methods to get/set screen brightness --- devlib/target.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/devlib/target.py b/devlib/target.py index 27b1c4b..af49989 100644 --- a/devlib/target.py +++ b/devlib/target.py @@ -1186,6 +1186,26 @@ class AndroidTarget(Target): if self.is_screen_on(): self.execute('input keyevent 26') + def set_auto_brightness(self, auto_brightness): + cmd = 'settings put system screen_brightness_mode {}' + self.execute(cmd.format(int(boolean(auto_brightness)))) + + def get_auto_brightness(self): + cmd = 'settings get system screen_brightness_mode' + return boolean(self.execute(cmd).strip()) + + def set_brightness(self, value): + if not 0 <= value <= 255: + msg = 'Invalid brightness "{}"; Must be between 0 and 255' + raise ValueError(msg.format(value)) + self.set_auto_brightness(False) + cmd = 'settings put system screen_brightness {}' + self.execute(cmd.format(int(value))) + + def get_brightness(self): + cmd = 'settings get system screen_brightness' + return integer(self.execute(cmd).strip()) + def homescreen(self): self.execute('am start -a android.intent.action.MAIN -c android.intent.category.HOME') From 3e751746d6c07a1643cdfb7f383dbc4a17765d88 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Thu, 6 Jul 2017 17:31:02 +0100 Subject: [PATCH 2/6] AndroidTarget: Adds methods to get/set airplane mode In order to change the state of airplane mode, the setting needs to be configured before broadcasting an intent to update the system. As of Android N, root is required to send the broadcast, therefore this method will raise an error if the requirements are not satisfied. --- devlib/target.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/devlib/target.py b/devlib/target.py index af49989..925f770 100644 --- a/devlib/target.py +++ b/devlib/target.py @@ -1206,6 +1206,18 @@ class AndroidTarget(Target): cmd = 'settings get system screen_brightness' return integer(self.execute(cmd).strip()) + def get_airplane_mode(self): + cmd = 'settings get global airplane_mode_on' + return boolean(self.execute(cmd).strip()) + + def set_airplane_mode(self, mode): + root_required = self.get_sdk_version() > 23 + if root_required and not self.is_rooted: + raise TargetError('Root is required to toggle airplane mode on Android 7+') + cmd = 'settings put global airplane_mode_on {}' + self.execute(cmd.format(int(boolean(mode)))) + self.execute('am broadcast -a android.intent.action.AIRPLANE_MODE', as_root=root_required) + def homescreen(self): self.execute('am start -a android.intent.action.MAIN -c android.intent.category.HOME') From 003785dde127c4d60a1e4ce4f558226c91298817 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Mon, 24 Jul 2017 17:44:33 +0100 Subject: [PATCH 3/6] AndroidTarget: Adds methods to get/set screen rotation --- devlib/target.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/devlib/target.py b/devlib/target.py index 925f770..c8b15d8 100644 --- a/devlib/target.py +++ b/devlib/target.py @@ -1218,6 +1218,37 @@ class AndroidTarget(Target): self.execute(cmd.format(int(boolean(mode)))) self.execute('am broadcast -a android.intent.action.AIRPLANE_MODE', as_root=root_required) + def get_auto_rotation(self): + cmd = 'settings get system accelerometer_rotation' + return boolean(self.execute(cmd).strip()) + + def set_auto_rotation(self, autorotate): + cmd = 'settings put system accelerometer_rotation {}' + self.execute(cmd.format(int(boolean(autorotate)))) + + def set_natural_rotation(self): + self.set_rotation(0) + + def set_left_rotation(self): + self.set_rotation(1) + + def set_inverted_rotation(self): + self.set_rotation(2) + + def set_right_rotation(self): + self.set_rotation(3) + + def get_rotation(self): + cmd = 'settings get system user_rotation' + return self.execute(cmd).strip() + + def set_rotation(self, rotation): + if not 0 <= rotation <= 3: + raise ValueError('Rotation value must be between 0 and 3') + self.set_auto_rotation(False) + cmd = 'settings put system user_rotation {}' + self.execute(cmd.format(rotation)) + def homescreen(self): self.execute('am start -a android.intent.action.MAIN -c android.intent.category.HOME') From 1229af089509d099fcb71009c90b87969814a70a Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Wed, 26 Jul 2017 11:29:10 +0100 Subject: [PATCH 4/6] AndroidTarget: Fixes 'swipe_to_unlock' method. --- devlib/target.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/devlib/target.py b/devlib/target.py index c8b15d8..4892951 100644 --- a/devlib/target.py +++ b/devlib/target.py @@ -1065,16 +1065,16 @@ class AndroidTarget(Target): width, height = self.screen_resolution command = 'input swipe {} {} {} {}' if direction == "horizontal": - swipe_heigh = height * 2 // 3 + swipe_height = height * 2 // 3 start = 100 stop = width - start - self.execute(command.format(start, swipe_heigh, stop, swipe_heigh)) - if direction == "vertical": - swipe_middle = height / 2 - swipe_heigh = height * 2 // 3 - self.execute(command.format(swipe_middle, swipe_heigh, swipe_middle, 0)) + self.execute(command.format(start, swipe_height, stop, swipe_height)) + elif direction == "vertical": + swipe_middle = width / 2 + swipe_height = height * 2 // 3 + self.execute(command.format(swipe_middle, swipe_height, swipe_middle, 0)) else: - raise DeviceError("Invalid swipe direction: {}".format(self.swipe_to_unlock)) + raise TargetError("Invalid swipe direction: {}".format(direction)) def getprop(self, prop=None): props = AndroidProperties(self.execute('getprop')) From 8839ed01bad79fbd110bcd7d47c620c211627d60 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Thu, 10 Aug 2017 17:19:13 +0100 Subject: [PATCH 5/6] AndroidTarget: Changes default unlocking method Instead of using a default 'horizontal' unlocking method, now will try a diagonal swipe up as this should work most modern devices with vertical or horizontal unlocking methods. --- devlib/target.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/devlib/target.py b/devlib/target.py index 4892951..b96bbdc 100644 --- a/devlib/target.py +++ b/devlib/target.py @@ -1061,10 +1061,15 @@ class AndroidTarget(Target): # Android-specific - def swipe_to_unlock(self, direction="horizontal"): + def swipe_to_unlock(self, direction="diagonal"): width, height = self.screen_resolution command = 'input swipe {} {} {} {}' - if direction == "horizontal": + if direction == "diagonal": + start = 100 + stop = width - start + swipe_height = height * 2 // 3 + self.execute(command.format(start, swipe_height, stop, 0)) + elif direction == "horizontal": swipe_height = height * 2 // 3 start = 100 stop = width - start From 5b99c1613b1f040d1f2b8fdeb3fe0fd680a05b81 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Wed, 9 Aug 2017 10:56:01 +0100 Subject: [PATCH 6/6] Documentation: Adds documentation for Android Target Adds documentation for the `AndroidTarget` class including the new android methods. --- doc/target.rst | 138 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 124 insertions(+), 14 deletions(-) diff --git a/doc/target.rst b/doc/target.rst index b64246a..08472e2 100644 --- a/doc/target.rst +++ b/doc/target.rst @@ -2,18 +2,18 @@ Target ====== -.. class:: Target(connection_settings=None, platform=None, working_directory=None, executables_directory=None, connect=True, modules=None, load_default_modules=True, shell_prompt=DEFAULT_SHELL_PROMPT) - +.. class:: Target(connection_settings=None, platform=None, working_directory=None, executables_directory=None, connect=True, modules=None, load_default_modules=True, shell_prompt=DEFAULT_SHELL_PROMPT, conn_cls=None) + :class:`Target` is the primary interface to the remote device. All interactions with the device are performed via a :class:`Target` instance, either directly, or via its modules or a wrapper interface (such as an :class:`Instrument`). - :param connection_settings: A ``dict`` that specifies how to connect to the remote + :param connection_settings: A ``dict`` that specifies how to connect to the remote device. Its contents depend on the specific :class:`Target` type (used see :ref:`connection-types`\ ). - :param platform: A :class:`Target` defines interactions at Operating System level. A + :param platform: A :class:`Target` defines interactions at Operating System level. A :class:`Platform` describes the underlying hardware (such as CPUs available). If a :class:`Platform` instance is not specified on :class:`Target` creation, one will be created automatically and it will @@ -22,8 +22,8 @@ Target :param working_directory: This is primary location for on-target file system interactions performed by ``devlib``. This location *must* be readable and - writable directly (i.e. without sudo) by the connection's user account. - It may or may not allow execution. This location will be created, + writable directly (i.e. without sudo) by the connection's user account. + It may or may not allow execution. This location will be created, if necessary, during ``setup()``. If not explicitly specified, this will be set to a default value @@ -35,7 +35,7 @@ Target (obviously). It should also be possible to write to this location, possibly with elevated privileges (i.e. on a rooted Linux target, it should be possible to write here with sudo, but not necessarily directly - by the connection's account). This location will be created, + by the connection's account). This location will be created, if necessary, during ``setup()``. This location does *not* need to be same as the system's executables @@ -52,7 +52,7 @@ Target :param modules: a list of additional modules to be installed. Some modules will try to install by default (if supported by the underlying target). - Current default modules are ``hotplug``, ``cpufreq``, ``cpuidle``, + Current default modules are ``hotplug``, ``cpufreq``, ``cpuidle``, ``cgroups``, and ``hwmon`` (See :ref:`modules`\ ). See modules documentation for more detail. @@ -68,6 +68,9 @@ Target prompted on the target. This may be used by some modules that establish auxiliary connections to a target over UART. + :param conn_cls: This is the type of connection that will be used to communicate + with the device. + .. attribute:: Target.core_names This is a list containing names of CPU cores on the target, in the order in @@ -94,7 +97,7 @@ Target .. attribute:: Target.is_connected A boolean value that indicates whether an active connection exists to the - target device. + target device. .. attribute:: Target.connected_as_root @@ -146,7 +149,7 @@ Target thread. .. method:: Target.connect([timeout]) - + Establish a connection to the target. It is usually not necessary to call this explicitly, as a connection gets automatically established on instantiation. @@ -225,7 +228,7 @@ Target :param timeout: Timeout (in seconds) for the execution of the command. If specified, an exception will be raised if execution does not complete with the specified period. - :param check_exit_code: If ``True`` (the default) the exit code (on target) + :param check_exit_code: If ``True`` (the default) the exit code (on target) from execution of the command will be checked, and an exception will be raised if it is not ``0``. :param as_root: The command will be executed as root. This will fail on @@ -262,7 +265,7 @@ Target will be interpreted as a comma-separated list of cpu ranges, e.g. ``"0,4-7"``. :param as_root: Specify whether the command should be run as root - :param timeout: If this is specified and invocation does not terminate within this number + :param timeout: If this is specified and invocation does not terminate within this number of seconds, an exception will be raised. .. method:: Target.background_invoke(binary [, args [, in_directory [, on_cpus [, as_root ]]]]) @@ -314,13 +317,13 @@ Target .. method:: Target.write_value(path, value [, verify]) - Write the value to the specified path on the target. This is primarily + Write the value to the specified path on the target. This is primarily intended for sysfs/procfs/debugfs etc. :param path: file to write into :param value: value to be written :param verify: If ``True`` (the default) the value will be read back after - it is written to make sure it has been written successfully. This due to + it is written to make sure it has been written successfully. This due to some sysfs entries silently failing to set the written value without returning an error code. @@ -450,3 +453,110 @@ Target Returns the path to the extracted contents. In case of files (gzip and bzip2), the path to the decompressed file is returned; for archives, the path to the directory with the archive's contents is returned. + + +Android Target +--------------- + +.. class:: AndroidTarget(connection_settings=None, platform=None, working_directory=None, executables_directory=None, connect=True, modules=None, load_default_modules=True, shell_prompt=DEFAULT_SHELL_PROMPT, conn_cls=AdbConnection, package_data_directory="/data/data") + + :class:`AndroidTarget` is a subclass of :class:`Target` with additional features specific to a device running Android. + + :param package_data_directory: This is the location of the data stored + for installed Android packages on the device. + +.. method:: AndroidTarget.set_rotation(rotation) + + Specify an integer representing the desired screen rotation with the + following mappings: Natural: ``0``, Rotated Left: ``1``, Inverted : ``2`` + and Rotated Right : ``3``. + +.. method:: AndroidTarget.get_rotation(rotation) + + Returns an integer value representing the orientation of the devices + screen. ``0`` : Natural, ``1`` : Rotated Left, ``2`` : Inverted + and ``3`` : Rotated Right. + +.. method:: AndroidTarget.set_natural_rotation() + + Sets the screen orientation of the device to its natural (0 degrees) + orientation. + +.. method:: AndroidTarget.set_left_rotation() + + Sets the screen orientation of the device to 90 degrees. + +.. method:: AndroidTarget.set_inverted_rotation() + + Sets the screen orientation of the device to its inverted (180 degrees) + orientation. + +.. method:: AndroidTarget.set_right_rotation() + + Sets the screen orientation of the device to 270 degrees. + +.. method:: AndroidTarget.set_auto_rotation(autorotate) + + Specify a boolean value for whether the devices auto-rotation should + be enabled. + +.. method:: AndroidTarget.get_auto_rotation() + + Returns ``True`` if the targets auto rotation is currently enabled and + ``False`` otherwise. + +.. method:: AndroidTarget.set_airplane_mode(mode) + + Specify a boolean value for whether the device should be in airplane mode. + + .. note:: Requires the device to be rooted if the device is running Android 7+. + +.. method:: AndroidTarget.get_airplane_mode() + + Returns ``True`` if the target is currently in airplane mode and + ``False`` otherwise. + +.. method:: AndroidTarget.set_brightness(value) + + Sets the devices screen brightness to a specified integer between ``0`` and + ``255``. + +.. method:: AndroidTarget.get_brightness() + + Returns an integer between ``0`` and ``255`` representing the devices + current screen brightness. + +.. method:: AndroidTarget.set_auto_brightness(auto_brightness) + + Specify a boolean value for whether the devices auto brightness + should be enabled. + +.. method:: AndroidTarget.get_auto_brightness() + + Returns ``True`` if the targets auto brightness is currently + enabled and ``False`` otherwise. + +.. method:: AndroidTarget.ensure_screen_is_off() + + Checks if the devices screen is on and if so turns it off. + +.. method:: AndroidTarget.ensure_screen_is_on() + + Checks if the devices screen is off and if so turns it on. + +.. method:: AndroidTarget.is_screen_on() + + Returns ``True`` if the targets screen is currently on and ``False`` + otherwise. + +.. method:: AndroidTarget.homescreen() + + Returns the device to its home screen. + +.. method:: AndroidTarget.swipe_to_unlock(direction="diagonal") + + Performs a swipe input on the device to try and unlock the device. + A direction of ``"horizontal"``, ``"vertical"`` or ``"diagonal"`` + can be supplied to specify in which direction the swipe should be + performed. By default ``"diagonal"`` will be used to try and + support the majority of newer devices.