Chromium Code Reviews| Index: build/android/pylib/device/device_utils.py |
| diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py |
| index b75a41a2f1a2401e98982ad044386ec517984fff..4c6a81fdb55b01b4a4829b25ae50617a3268be27 100644 |
| --- a/build/android/pylib/device/device_utils.py |
| +++ b/build/android/pylib/device/device_utils.py |
| @@ -24,6 +24,7 @@ from pylib.device import decorators |
| from pylib.device import device_errors |
| from pylib.device.commands import install_commands |
| from pylib.utils import apk_helper |
| +from pylib.utils import device_temp_file |
| from pylib.utils import host_utils |
| from pylib.utils import parallelizer |
| from pylib.utils import timeout_retry |
| @@ -866,16 +867,19 @@ class DeviceUtils(object): |
| return self.old_interface.GetFileContents(device_path) |
| @decorators.WithTimeoutAndRetriesFromInstance() |
| - def WriteFile(self, device_path, contents, as_root=False, timeout=None, |
| - retries=None): |
| + def WriteFile(self, device_path, contents, as_root=False, force_push=False, |
| + timeout=None, retries=None): |
| """Writes |contents| to a file on the device. |
| Args: |
| device_path: A string containing the absolute path to the file to write |
| - on the device. |
| + on the device. |
| contents: A string containing the data to write to the device. |
| as_root: A boolean indicating whether the write should be executed with |
| - root privileges. |
| + root privileges (if available). |
| + force_push: A boolean indicating whether to force the operation to be |
|
Sami
2014/11/12 14:14:14
Is force_push needed by anything other than tests?
perezju
2014/11/12 17:37:03
Well, yes, certainly it's useful for testing, but
|
| + performed by pushing a file to the device. The default is, when the |
| + contents are short, to pass the contents using a shell script instead. |
| timeout: timeout in seconds |
| retries: number of retries |
| @@ -884,39 +888,31 @@ class DeviceUtils(object): |
| CommandTimeoutError on timeout. |
| DeviceUnreachableError on missing device. |
| """ |
| - if as_root: |
| - if not self.old_interface.CanAccessProtectedFileContents(): |
| - raise device_errors.CommandFailedError( |
| - 'Cannot write to %s with root privileges.' % device_path) |
| - self.old_interface.SetProtectedFileContents(device_path, contents) |
| + if len(contents) < 512 and not force_push: |
| + # When as_root=True and su is needed, trying to execute |
| + # su -c echo -n {contents} > {device_path} |
| + # won't work, because echo is interpreted by the shell and not an actual |
|
Sami
2014/11/12 14:14:14
nit: I presume it's not the echo that gets execute
perezju
2014/11/12 17:37:02
No. "su -c cmd > /some/file" works fine, because t
|
| + # command on the device. So instead we must make sure that in those cases |
| + # the command to execute becomes: |
| + # su -c sh -c 'echo -n {contents}' > {device_path} |
| + cmd = 'echo -n %s' % cmd_helper.SingleQuote(contents) |
| + sh_cmd = 'sh -c %s > %s' % (cmd_helper.SingleQuote(cmd), |
| + cmd_helper.SingleQuote(device_path)) |
| + self.RunShellCommand(sh_cmd, as_root=as_root, check_return=True) |
|
perezju
2014/11/12 01:31:24
Not sure why this never hit us before! Probably Wr
jbudorick
2014/11/12 01:42:37
Wow, nice catch. This is a nasty little corner cas
perezju
2014/11/12 17:37:03
Indeed, echo (and sometimes with as_root=True!) is
|
| else: |
| - self.old_interface.SetFileContents(device_path, contents) |
| - |
| - @decorators.WithTimeoutAndRetriesFromInstance() |
| - def WriteTextFile(self, device_path, text, as_root=False, timeout=None, |
| - retries=None): |
| - """Writes |text| to a file on the device. |
| - |
| - Assuming that |text| is a small string, this is typically more efficient |
| - than |WriteFile|, as no files are pushed into the device. |
| - |
| - Args: |
| - device_path: A string containing the absolute path to the file to write |
| - on the device. |
| - text: A short string of text to write to the file on the device. |
| - as_root: A boolean indicating whether the write should be executed with |
| - root privileges. |
| - timeout: timeout in seconds |
| - retries: number of retries |
| - |
| - Raises: |
| - CommandFailedError if the file could not be written on the device. |
| - CommandTimeoutError on timeout. |
| - DeviceUnreachableError on missing device. |
| - """ |
| - cmd = 'echo %s > %s' % (cmd_helper.SingleQuote(text), |
| - cmd_helper.SingleQuote(device_path)) |
| - self.RunShellCommand(cmd, as_root=as_root, check_return=True) |
| + with tempfile.NamedTemporaryFile() as host_temp: |
| + host_temp.write(contents) |
| + host_temp.flush() |
| + if as_root and self.NeedsSU(): |
| + with device_temp_file.DeviceTempFile(self) as device_temp: |
| + self.adb.Push(host_temp.name, device_temp.name) |
| + # Here we need 'cp' rather than 'mv' because the temp and |
| + # destination files might be on different file systems (e.g. |
| + # on internal storage and an external sd card) |
| + self.RunShellCommand(['cp', device_temp.name, device_path], |
| + as_root=True, check_return=True) |
|
perezju
2014/11/12 01:31:24
So, I had to resort to 'cp' rather than 'mv' in th
jbudorick
2014/11/12 01:42:37
While I think we should try to move away from usin
|
| + else: |
| + self.adb.Push(host_temp.name, device_path) |
| @decorators.WithTimeoutAndRetriesFromInstance() |
| def Ls(self, device_path, timeout=None, retries=None): |