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