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 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 27class 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