1# Copyright 2016 Google Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Shared library for frontends to jsonrpc servers."""
15
16import code
17import os
18import pprint
19import sys
20
21from mobly.controllers import android_device
22
23
24class Error(Exception):
25  pass
26
27
28class JsonRpcShellBase:
29
30  def _start_services(self, console_env):
31    """Starts the services needed by this client and adds them to console_env.
32
33    Must be implemented by subclasses.
34    """
35    raise NotImplementedError()
36
37  def _get_banner(self, serial):
38    """Returns the user-friendly banner message to print before the console.
39
40    Must be implemented by subclasses.
41    """
42    raise NotImplementedError()
43
44  def load_device(self, serial=None):
45    """Creates an AndroidDevice for the given serial number.
46
47    If no serial is given, it will read from the ANDROID_SERIAL
48    environmental variable. If the environmental variable is not set, then
49    it will read from 'adb devices' if there is only one.
50    """
51    serials = android_device.list_adb_devices()
52    if not serials:
53      raise Error('No adb device found!')
54    # No serial provided, try to pick up the device automatically.
55    if not serial:
56      env_serial = os.environ.get('ANDROID_SERIAL', None)
57      if env_serial is not None:
58        serial = env_serial
59      elif len(serials) == 1:
60        serial = serials[0]
61      else:
62        raise Error(
63            'Expected one phone, but %d found. Use the -s flag or '
64            'specify ANDROID_SERIAL.'
65            % len(serials)
66        )
67    if serial not in serials:
68      raise Error('Device "%s" is not found by adb.' % serial)
69    ads = android_device.get_instances([serial])
70    assert len(ads) == 1
71    self._ad = ads[0]
72
73  def start_console(self):
74    # Set up initial console environment
75    console_env = {
76        'ad': self._ad,
77        'pprint': pprint.pprint,
78    }
79
80    # Start the services
81    self._start_services(console_env)
82
83    # Start the console
84    console_banner = self._get_banner(self._ad.serial)
85    code.interact(banner=console_banner, local=console_env)
86
87    # Tear everything down
88    self._ad.services.stop_all()
89
90  def main(self, serial=None):
91    try:
92      self.load_device(serial)
93    except Error as e:
94      print('ERROR: %s' % e, file=sys.stderr)
95      sys.exit(1)
96    self.start_console()
97