• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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