Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2750)

Unified Diff: build/android/pylib/android_commands.py

Issue 99713002: Factor out a system_properties interface for interacting with getprop/setprop. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Use shlex.split() Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | build/android/pylib/system_properties.py » ('j') | build/android/pylib/system_properties.py » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: build/android/pylib/android_commands.py
diff --git a/build/android/pylib/android_commands.py b/build/android/pylib/android_commands.py
index 8d8949cac74c8db343ff68b5c9a33da11d462bf8..95b193bbc231131f3c8b389cabfadfe429f06f21 100644
--- a/build/android/pylib/android_commands.py
+++ b/build/android/pylib/android_commands.py
@@ -9,6 +9,7 @@ Assumes adb binary is currently on system path.
import collections
import datetime
+import inspect
import logging
import os
import re
@@ -22,6 +23,7 @@ import time
import cmd_helper
import constants
import screenshot
+import system_properties
from utils import host_path_finder
@@ -228,14 +230,17 @@ def GetLogTimestamp(log_line, year):
class AndroidCommands(object):
- """Helper class for communicating with Android device via adb.
+ """Helper class for communicating with Android device via adb."""
- Args:
- device: If given, adb commands are only send to the device of this ID.
- Otherwise commands are sent to all attached devices.
- """
+ def __init__(self, device=None, api_strict_mode=False):
+ """Constructor.
- def __init__(self, device=None):
+ Args:
+ device: If given, adb commands are only send to the device of this ID.
+ Otherwise commands are sent to all attached devices.
+ api_strict_mode: A boolean indicating whether fatal errors should be
+ raised if this API is used improperly.
+ """
adb_dir = os.path.dirname(constants.ADB_PATH)
if adb_dir and adb_dir not in os.environ['PATH'].split(os.pathsep):
# Required by third_party/android_testrunner to call directly 'adb'.
@@ -253,6 +258,18 @@ class AndroidCommands(object):
self._actual_push_size = 0
self._external_storage = ''
self._util_wrapper = ''
+ self._api_strict_mode = api_strict_mode
+ self._system_properties = system_properties.SystemProperties(self.Adb())
+
+ if not self._api_strict_mode:
+ logging.warning(
+ 'API STRICT MODE IS DISABLED.\n'
+ 'It should be enabled as soon as possible as it will eventually '
+ 'become the default.')
+
+ @property
+ def system_properties(self):
+ return self._system_properties
def _LogShell(self, cmd):
"""Logs the adb shell command."""
@@ -264,6 +281,7 @@ class AndroidCommands(object):
def Adb(self):
"""Returns our AdbInterface to avoid us wrapping all its methods."""
+ # TODO(tonyg): Disable this method when in _api_strict_mode.
return self._adb
def GetDevice(self):
@@ -352,6 +370,7 @@ class AndroidCommands(object):
return
if full_reboot or not self.IsRootEnabled():
self._adb.SendCommand('reboot')
+ self._system_properties = system_properties.SystemProperties(self.Adb())
timeout = 300
retries = 1
# Wait for the device to disappear.
@@ -369,6 +388,7 @@ class AndroidCommands(object):
def Shutdown(self):
"""Shuts down the device."""
self._adb.SendCommand('reboot -p')
+ self._system_properties = system_properties.SystemProperties(self.Adb())
def Uninstall(self, package):
"""Uninstalls the specified package from the device.
@@ -536,8 +556,7 @@ class AndroidCommands(object):
attempts = 0
wait_period = 5
while not boot_completed and (attempts * wait_period) < wait_time:
- output = self._adb.SendShellCommand('getprop sys.boot_completed',
- retry_count=1)
+ output = self.system_properties['sys.boot_completed']
output = output.strip()
if output == '1':
boot_completed = True
@@ -571,6 +590,34 @@ class AndroidCommands(object):
raise errors.WaitForResponseTimedOutError(
'SD card not ready after %s seconds' % timeout_time)
+ def _CheckCommandIsValid(self, command):
+ """Raises a ValueError if the command is not valid."""
+
+ # A dict of commands the user should not run directly and a mapping to the
+ # API they should use instead.
+ preferred_apis = {
+ 'getprop': 'system_properties[<PROPERTY>]',
+ 'setprop': 'system_properties[<PROPERTY>]',
+ 'su': 'RunShellCommandWithSU()',
+ }
+
+ # A dict of commands to methods that may call them.
+ whitelisted_callers = {
+ 'su': 'RunShellCommandWithSU',
+ }
+
+ base_command = shlex.split(command)[0]
+ if (base_command in preferred_apis and
+ (base_command not in whitelisted_callers or
+ whitelisted_callers[base_command] not in [
+ f[3] for f in inspect.stack()])):
+ error_msg = ('%s cannot be run directly. Instead use: %s' %
+ (base_command, preferred_apis[base_command]))
+ if self._api_strict_mode:
+ raise ValueError(error_msg)
+ else:
+ logging.warning(error_msg)
+
# It is tempting to turn this function into a generator, however this is not
# possible without using a private (local) adb_shell instance (to ensure no
# other command interleaves usage of it), which would defeat the main aim of
@@ -589,6 +636,7 @@ class AndroidCommands(object):
Returns:
list containing the lines of output received from running the command
"""
+ self._CheckCommandIsValid(command)
self._LogShell(command)
if "'" in command: logging.warning(command + " contains ' quotes")
result = self._adb.SendShellCommand(
@@ -1000,9 +1048,7 @@ class AndroidCommands(object):
return self.GetExternalStorage() + '/' + base_name % i
def RunShellCommandWithSU(self, command, timeout_time=20, log_result=False):
- return self.RunShellCommand('su -c %s' % command,
- timeout_time=timeout_time,
- log_result=log_result)
+ return self.RunShellCommand('su -c %s' % command, timeout_time, log_result)
def CanAccessProtectedFileContents(self):
"""Returns True if Get/SetProtectedFileContents would work via "su".
@@ -1103,41 +1149,39 @@ class AndroidCommands(object):
# Next, check the current runtime value is what we need, and
# if not, set it and report that a reboot is required.
- was_set = 'all' in self.RunShellCommand('getprop ' + JAVA_ASSERT_PROPERTY)
+ was_set = 'all' in self.system_properties[JAVA_ASSERT_PROPERTY]
if was_set == enable:
return False
-
- self.RunShellCommand('setprop %s "%s"' % (JAVA_ASSERT_PROPERTY,
- enable and 'all' or ''))
+ self.system_properties[JAVA_ASSERT_PROPERTY] = enable and 'all' or ''
return True
def GetBuildId(self):
"""Returns the build ID of the system (e.g. JRM79C)."""
- build_id = self.RunShellCommand('getprop ro.build.id')[0]
+ build_id = self.system_properties['ro.build.id']
assert build_id
return build_id
def GetBuildType(self):
"""Returns the build type of the system (e.g. eng)."""
- build_type = self.RunShellCommand('getprop ro.build.type')[0]
+ build_type = self.system_properties['ro.build.type']
assert build_type
return build_type
def GetBuildProduct(self):
"""Returns the build product of the device (e.g. maguro)."""
- build_product = self.RunShellCommand('getprop ro.build.product')[0]
+ build_product = self.system_properties['ro.build.product']
assert build_product
return build_product
def GetProductName(self):
"""Returns the product name of the device (e.g. takju)."""
- name = self.RunShellCommand('getprop ro.product.name')[0]
+ name = self.system_properties['ro.product.name']
assert name
return name
def GetBuildFingerprint(self):
"""Returns the build fingerprint of the device."""
- build_fingerprint = self.RunShellCommand('getprop ro.build.fingerprint')[0]
+ build_fingerprint = self.system_properties['ro.build.fingerprint']
assert build_fingerprint
return build_fingerprint
@@ -1146,19 +1190,19 @@ class AndroidCommands(object):
For example, "yakju-userdebug 4.1 JRN54F 364167 dev-keys".
"""
- description = self.RunShellCommand('getprop ro.build.description')[0]
+ description = self.system_properties['ro.build.description']
assert description
return description
def GetProductModel(self):
"""Returns the name of the product model (e.g. "Galaxy Nexus") """
- model = self.RunShellCommand('getprop ro.product.model')[0]
+ model = self.system_properties['ro.product.model']
assert model
return model
def GetWifiIP(self):
"""Returns the wifi IP on the device."""
- wifi_ip = self.RunShellCommand('getprop dhcp.wlan0.ipaddress')[0]
+ wifi_ip = self.system_properties['dhcp.wlan0.ipaddress']
# Do not assert here. Devices (e.g. emulators) may not have a WifiIP.
return wifi_ip
@@ -1176,7 +1220,7 @@ class AndroidCommands(object):
def GetSetupWizardStatus(self):
"""Returns the status of the device setup wizard (e.g. DISABLED)."""
- status = self.RunShellCommand('getprop ro.setupwizard.mode')[0]
+ status = self.system_properties['ro.setupwizard.mode']
# On some devices, the status is empty if not otherwise set. In such cases
# the caller should expect an empty string to be returned.
return status
« no previous file with comments | « no previous file | build/android/pylib/system_properties.py » ('j') | build/android/pylib/system_properties.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698