Home | History | Annotate | Download | only in sdk
      1 # Copyright 2015 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """Provides a work around for various adb commands on android gce instances.
      6 
      7 Some adb commands don't work well when the device is a cloud vm, namely
      8 'push' and 'pull'. With gce instances, moving files through adb can be
      9 painfully slow and hit timeouts, so the methods here just use scp instead.
     10 """
     11 # pylint: disable=unused-argument
     12 
     13 import logging
     14 import os
     15 import subprocess
     16 
     17 from devil.android import device_errors
     18 from devil.android.sdk import adb_wrapper
     19 from devil.utils import cmd_helper
     20 
     21 
     22 # SSH key file for accessing the instances. The keys are created at
     23 # startup and removed & revoked at teardown.
     24 _SSH_KEY_FILE = '/tmp/ssh_android_gce_instance'
     25 
     26 
     27 class GceAdbWrapper(adb_wrapper.AdbWrapper):
     28 
     29   def __init__(self, device_serial):
     30     super(GceAdbWrapper, self).__init__(device_serial)
     31     self._instance_ip = self.Shell('getprop net.gce.ip_address').strip()
     32 
     33   # override
     34   def Push(self, local, remote, **kwargs):
     35     """Pushes an object from the host to the gce instance.
     36 
     37     Args:
     38       local: Path on the host filesystem.
     39       remote: Path on the instance filesystem.
     40     """
     41     adb_wrapper.VerifyLocalFileExists(_SSH_KEY_FILE)
     42     adb_wrapper.VerifyLocalFileExists(local)
     43     if os.path.isdir(local):
     44       self.Shell('mkdir -p %s' % cmd_helper.SingleQuote(remote))
     45 
     46       # When the object to be pushed is a directory, adb merges the source dir
     47       # with the destination dir. So if local is a dir, just scp its contents.
     48       for f in os.listdir(local):
     49         self._PushObject(os.path.join(local, f), os.path.join(remote, f))
     50         self.Shell('chmod 777 %s' %
     51                    cmd_helper.SingleQuote(os.path.join(remote, f)))
     52     else:
     53       parent_dir = remote[0:remote.rfind('/')]
     54       if parent_dir:
     55         self.Shell('mkdir -p %s' % cmd_helper.SingleQuote(parent_dir))
     56       self._PushObject(local, remote)
     57       self.Shell('chmod 777 %s' % cmd_helper.SingleQuote(remote))
     58 
     59   def _PushObject(self, local, remote):
     60     """Copies an object from the host to the gce instance using scp.
     61 
     62     Args:
     63       local: Path on the host filesystem.
     64       remote: Path on the instance filesystem.
     65     """
     66     cmd = [
     67         'scp',
     68         '-r',
     69         '-i', _SSH_KEY_FILE,
     70         '-o', 'UserKnownHostsFile=/dev/null',
     71         '-o', 'StrictHostKeyChecking=no',
     72         local,
     73         'root@%s:%s' % (self._instance_ip, remote)
     74     ]
     75     status, _ = cmd_helper.GetCmdStatusAndOutput(cmd)
     76     if status:
     77       raise device_errors.AdbCommandFailedError(
     78           cmd, 'File not reachable on host: %s' % local,
     79           device_serial=str(self))
     80 
     81   # override
     82   def Pull(self, remote, local, **kwargs):
     83     """Pulls a file from the gce instance to the host.
     84 
     85     Args:
     86       remote: Path on the instance filesystem.
     87       local: Path on the host filesystem.
     88     """
     89     adb_wrapper.VerifyLocalFileExists(_SSH_KEY_FILE)
     90     cmd = [
     91         'scp',
     92         '-p',
     93         '-r',
     94         '-i', _SSH_KEY_FILE,
     95         '-o', 'UserKnownHostsFile=/dev/null',
     96         '-o', 'StrictHostKeyChecking=no',
     97         'root@%s:%s' % (self._instance_ip, remote),
     98         local,
     99     ]
    100     status, _ = cmd_helper.GetCmdStatusAndOutput(cmd)
    101     if status:
    102       raise device_errors.AdbCommandFailedError(
    103           cmd, 'File not reachable on host: %s' % local,
    104           device_serial=str(self))
    105 
    106     try:
    107       adb_wrapper.VerifyLocalFileExists(local)
    108     except (subprocess.CalledProcessError, IOError):
    109       logging.exception('Error when pulling files from android instance.')
    110       raise device_errors.AdbCommandFailedError(
    111           cmd, 'File not reachable on host: %s' % local,
    112           device_serial=str(self))
    113 
    114   # override
    115   def Install(self, apk_path, forward_lock=False, reinstall=False,
    116               sd_card=False, **kwargs):
    117     """Installs an apk on the gce instance
    118 
    119     Args:
    120       apk_path: Host path to the APK file.
    121       forward_lock: (optional) If set forward-locks the app.
    122       reinstall: (optional) If set reinstalls the app, keeping its data.
    123       sd_card: (optional) If set installs on the SD card.
    124     """
    125     adb_wrapper.VerifyLocalFileExists(_SSH_KEY_FILE)
    126     adb_wrapper.VerifyLocalFileExists(apk_path)
    127     cmd = ['install']
    128     if forward_lock:
    129       cmd.append('-l')
    130     if reinstall:
    131       cmd.append('-r')
    132     if sd_card:
    133       cmd.append('-s')
    134     self.Push(apk_path, '/data/local/tmp/tmp.apk')
    135     cmd = ['pm'] + cmd
    136     cmd.append('/data/local/tmp/tmp.apk')
    137     output = self.Shell(' '.join(cmd))
    138     self.Shell('rm /data/local/tmp/tmp.apk')
    139     if 'Success' not in output:
    140       raise device_errors.AdbCommandFailedError(
    141           cmd, output, device_serial=self._device_serial)
    142 
    143   # override
    144   @property
    145   def is_emulator(self):
    146     return True
    147