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 class GceAdbWrapper(adb_wrapper.AdbWrapper):
     23 
     24   def __init__(self, device_serial):
     25     super(GceAdbWrapper, self).__init__(device_serial)
     26     self._Connect()
     27     self.Root()
     28     self._instance_ip = self.Shell('getprop net.gce.ip').strip()
     29 
     30   def _Connect(self, timeout=adb_wrapper.DEFAULT_TIMEOUT,
     31                retries=adb_wrapper.DEFAULT_RETRIES):
     32     """Connects ADB to the android gce instance."""
     33     cmd = ['connect', self._device_serial]
     34     output = self._RunAdbCmd(cmd, timeout=timeout, retries=retries)
     35     if 'unable to connect' in output:
     36       raise device_errors.AdbCommandFailedError(cmd, output)
     37     self.WaitForDevice()
     38 
     39   # override
     40   def Root(self, **kwargs):
     41     super(GceAdbWrapper, self).Root()
     42     self._Connect()
     43 
     44   # override
     45   def Push(self, local, remote, **kwargs):
     46     """Pushes an object from the host to the gce instance.
     47 
     48     Args:
     49       local: Path on the host filesystem.
     50       remote: Path on the instance filesystem.
     51     """
     52     adb_wrapper.VerifyLocalFileExists(local)
     53     if os.path.isdir(local):
     54       self.Shell('mkdir -p %s' % cmd_helper.SingleQuote(remote))
     55 
     56       # When the object to be pushed is a directory, adb merges the source dir
     57       # with the destination dir. So if local is a dir, just scp its contents.
     58       for f in os.listdir(local):
     59         self._PushObject(os.path.join(local, f), os.path.join(remote, f))
     60         self.Shell('chmod 777 %s' %
     61                    cmd_helper.SingleQuote(os.path.join(remote, f)))
     62     else:
     63       parent_dir = remote[0:remote.rfind('/')]
     64       if parent_dir:
     65         self.Shell('mkdir -p %s' % cmd_helper.SingleQuote(parent_dir))
     66       self._PushObject(local, remote)
     67       self.Shell('chmod 777 %s' % cmd_helper.SingleQuote(remote))
     68 
     69   def _PushObject(self, local, remote):
     70     """Copies an object from the host to the gce instance using scp.
     71 
     72     Args:
     73       local: Path on the host filesystem.
     74       remote: Path on the instance filesystem.
     75     """
     76     cmd = [
     77         'scp',
     78         '-r',
     79         '-o', 'UserKnownHostsFile=/dev/null',
     80         '-o', 'StrictHostKeyChecking=no',
     81         local,
     82         'root@%s:%s' % (self._instance_ip, remote)
     83     ]
     84     status, _ = cmd_helper.GetCmdStatusAndOutput(cmd)
     85     if status:
     86       raise device_errors.AdbCommandFailedError(
     87           cmd, 'File not reachable on host: %s' % local,
     88           device_serial=str(self))
     89 
     90   # override
     91   def Pull(self, remote, local, **kwargs):
     92     """Pulls a file from the gce instance to the host.
     93 
     94     Args:
     95       remote: Path on the instance filesystem.
     96       local: Path on the host filesystem.
     97     """
     98     cmd = [
     99         'scp',
    100         '-p',
    101         '-r',
    102         '-o', 'UserKnownHostsFile=/dev/null',
    103         '-o', 'StrictHostKeyChecking=no',
    104         'root@%s:%s' % (self._instance_ip, remote),
    105         local,
    106     ]
    107     status, _ = cmd_helper.GetCmdStatusAndOutput(cmd)
    108     if status:
    109       raise device_errors.AdbCommandFailedError(
    110           cmd, 'File not reachable on host: %s' % local,
    111           device_serial=str(self))
    112 
    113     try:
    114       adb_wrapper.VerifyLocalFileExists(local)
    115     except (subprocess.CalledProcessError, IOError):
    116       logging.exception('Error when pulling files from android instance.')
    117       raise device_errors.AdbCommandFailedError(
    118           cmd, 'File not reachable on host: %s' % local,
    119           device_serial=str(self))
    120 
    121   # override
    122   def Install(self, apk_path, forward_lock=False, reinstall=False,
    123               sd_card=False, **kwargs):
    124     """Installs an apk on the gce instance
    125 
    126     Args:
    127       apk_path: Host path to the APK file.
    128       forward_lock: (optional) If set forward-locks the app.
    129       reinstall: (optional) If set reinstalls the app, keeping its data.
    130       sd_card: (optional) If set installs on the SD card.
    131     """
    132     adb_wrapper.VerifyLocalFileExists(apk_path)
    133     cmd = ['install']
    134     if forward_lock:
    135       cmd.append('-l')
    136     if reinstall:
    137       cmd.append('-r')
    138     if sd_card:
    139       cmd.append('-s')
    140     self.Push(apk_path, '/data/local/tmp/tmp.apk')
    141     cmd = ['pm'] + cmd
    142     cmd.append('/data/local/tmp/tmp.apk')
    143     output = self.Shell(' '.join(cmd))
    144     self.Shell('rm /data/local/tmp/tmp.apk')
    145     if 'Success' not in output:
    146       raise device_errors.AdbCommandFailedError(
    147           cmd, output, device_serial=self._device_serial)
    148 
    149   # override
    150   @property
    151   def is_emulator(self):
    152     return True
    153