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