| # Copyright 2015 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Provides a work around for various adb commands on android gce instances. |
| |
| Some adb commands don't work well when the device is a cloud vm, namely |
| 'push' and 'pull'. With gce instances, moving files through adb can be |
| painfully slow and hit timeouts, so the methods here just use scp instead. |
| """ |
| # pylint: disable=unused-argument |
| |
| import logging |
| import os |
| import subprocess |
| |
| from devil.android import device_errors |
| from devil.android.sdk import adb_wrapper |
| from devil.utils import cmd_helper |
| |
| |
| # SSH key file for accessing the instances. The keys are created at |
| # startup and removed & revoked at teardown. |
| _SSH_KEY_FILE = '/tmp/ssh_android_gce_instance' |
| |
| |
| class GceAdbWrapper(adb_wrapper.AdbWrapper): |
| |
| def __init__(self, device_serial): |
| super(GceAdbWrapper, self).__init__(device_serial) |
| self._instance_ip = self.Shell('getprop net.gce.ip_address').strip() |
| |
| # override |
| def Push(self, local, remote, **kwargs): |
| """Pushes an object from the host to the gce instance. |
| |
| Args: |
| local: Path on the host filesystem. |
| remote: Path on the instance filesystem. |
| """ |
| adb_wrapper.VerifyLocalFileExists(_SSH_KEY_FILE) |
| adb_wrapper.VerifyLocalFileExists(local) |
| if os.path.isdir(local): |
| self.Shell('mkdir -p %s' % cmd_helper.SingleQuote(remote)) |
| |
| # When the object to be pushed is a directory, adb merges the source dir |
| # with the destination dir. So if local is a dir, just scp its contents. |
| for f in os.listdir(local): |
| self._PushObject(os.path.join(local, f), os.path.join(remote, f)) |
| self.Shell('chmod 777 %s' % |
| cmd_helper.SingleQuote(os.path.join(remote, f))) |
| else: |
| parent_dir = remote[0:remote.rfind('/')] |
| if parent_dir: |
| self.Shell('mkdir -p %s' % cmd_helper.SingleQuote(parent_dir)) |
| self._PushObject(local, remote) |
| self.Shell('chmod 777 %s' % cmd_helper.SingleQuote(remote)) |
| |
| def _PushObject(self, local, remote): |
| """Copies an object from the host to the gce instance using scp. |
| |
| Args: |
| local: Path on the host filesystem. |
| remote: Path on the instance filesystem. |
| """ |
| cmd = [ |
| 'scp', |
| '-r', |
| '-i', _SSH_KEY_FILE, |
| '-o', 'UserKnownHostsFile=/dev/null', |
| '-o', 'StrictHostKeyChecking=no', |
| local, |
| 'root@%s:%s' % (self._instance_ip, remote) |
| ] |
| status, _ = cmd_helper.GetCmdStatusAndOutput(cmd) |
| if status: |
| raise device_errors.AdbCommandFailedError( |
| cmd, 'File not reachable on host: %s' % local, |
| device_serial=str(self)) |
| |
| # override |
| def Pull(self, remote, local, **kwargs): |
| """Pulls a file from the gce instance to the host. |
| |
| Args: |
| remote: Path on the instance filesystem. |
| local: Path on the host filesystem. |
| """ |
| adb_wrapper.VerifyLocalFileExists(_SSH_KEY_FILE) |
| cmd = [ |
| 'scp', |
| '-p', |
| '-r', |
| '-i', _SSH_KEY_FILE, |
| '-o', 'UserKnownHostsFile=/dev/null', |
| '-o', 'StrictHostKeyChecking=no', |
| 'root@%s:%s' % (self._instance_ip, remote), |
| local, |
| ] |
| status, _ = cmd_helper.GetCmdStatusAndOutput(cmd) |
| if status: |
| raise device_errors.AdbCommandFailedError( |
| cmd, 'File not reachable on host: %s' % local, |
| device_serial=str(self)) |
| |
| try: |
| adb_wrapper.VerifyLocalFileExists(local) |
| except (subprocess.CalledProcessError, IOError): |
| logging.exception('Error when pulling files from android instance.') |
| raise device_errors.AdbCommandFailedError( |
| cmd, 'File not reachable on host: %s' % local, |
| device_serial=str(self)) |
| |
| # override |
| def Install(self, apk_path, forward_lock=False, reinstall=False, |
| sd_card=False, **kwargs): |
| """Installs an apk on the gce instance |
| |
| Args: |
| apk_path: Host path to the APK file. |
| forward_lock: (optional) If set forward-locks the app. |
| reinstall: (optional) If set reinstalls the app, keeping its data. |
| sd_card: (optional) If set installs on the SD card. |
| """ |
| adb_wrapper.VerifyLocalFileExists(_SSH_KEY_FILE) |
| adb_wrapper.VerifyLocalFileExists(apk_path) |
| cmd = ['install'] |
| if forward_lock: |
| cmd.append('-l') |
| if reinstall: |
| cmd.append('-r') |
| if sd_card: |
| cmd.append('-s') |
| self.Push(apk_path, '/data/local/tmp/tmp.apk') |
| cmd = ['pm'] + cmd |
| cmd.append('/data/local/tmp/tmp.apk') |
| output = self.Shell(' '.join(cmd)) |
| self.Shell('rm /data/local/tmp/tmp.apk') |
| if 'Success' not in output: |
| raise device_errors.AdbCommandFailedError( |
| cmd, output, device_serial=self._device_serial) |
| |
| # override |
| @property |
| def is_emulator(self): |
| return True |