1# Copyright 2018 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"""Module for the snippet management service.""" 15from mobly.controllers.android_device_lib import errors 16from mobly.controllers.android_device_lib import snippet_client 17from mobly.controllers.android_device_lib.services import base_service 18 19MISSING_SNIPPET_CLIENT_MSG = 'No snippet client is registered with name "%s".' 20 21 22class Error(errors.ServiceError): 23 """Root error type for snippet management service.""" 24 SERVICE_TYPE = 'SnippetManagementService' 25 26 27class SnippetManagementService(base_service.BaseService): 28 """Management service of snippet clients. 29 30 This service manages all the snippet clients associated with an Android 31 device. 32 """ 33 34 def __init__(self, device, configs=None): 35 del configs # Unused param. 36 self._device = device 37 self._is_alive = False 38 self._snippet_clients = {} 39 super().__init__(device) 40 41 @property 42 def is_alive(self): 43 """True if any client is running, False otherwise.""" 44 return any([client.is_alive for client in self._snippet_clients.values()]) 45 46 def get_snippet_client(self, name): 47 """Gets the snippet client managed under a given name. 48 49 Args: 50 name: string, the name of the snippet client under management. 51 52 Returns: 53 SnippetClient. 54 """ 55 if name in self._snippet_clients: 56 return self._snippet_clients[name] 57 58 def add_snippet_client(self, name, package): 59 """Adds a snippet client to the management. 60 61 Args: 62 name: string, the attribute name to which to attach the snippet 63 client. E.g. `name='maps'` attaches the snippet client to 64 `ad.maps`. 65 package: string, the package name of the snippet apk to connect to. 66 67 Raises: 68 Error, if a duplicated name or package is passed in. 69 """ 70 # Should not load snippet with the same name more than once. 71 if name in self._snippet_clients: 72 raise Error( 73 self, 'Name "%s" is already registered with package "%s", it cannot ' 74 'be used again.' % (name, self._snippet_clients[name].client.package)) 75 # Should not load the same snippet package more than once. 76 for snippet_name, client in self._snippet_clients.items(): 77 if package == client.package: 78 raise Error( 79 self, 'Snippet package "%s" has already been loaded under name' 80 ' "%s".' % (package, snippet_name)) 81 client = snippet_client.SnippetClient(package=package, ad=self._device) 82 client.start_app_and_connect() 83 self._snippet_clients[name] = client 84 85 def remove_snippet_client(self, name): 86 """Removes a snippet client from management. 87 88 Args: 89 name: string, the name of the snippet client to remove. 90 91 Raises: 92 Error: if no snippet client is managed under the specified name. 93 """ 94 if name not in self._snippet_clients: 95 raise Error(self._device, MISSING_SNIPPET_CLIENT_MSG % name) 96 client = self._snippet_clients.pop(name) 97 client.stop_app() 98 99 def start(self): 100 """Starts all the snippet clients under management.""" 101 for client in self._snippet_clients.values(): 102 if not client.is_alive: 103 self._device.log.debug('Starting SnippetClient<%s>.', client.package) 104 client.start_app_and_connect() 105 else: 106 self._device.log.debug( 107 'Not startng SnippetClient<%s> because it is already alive.', 108 client.package) 109 110 def stop(self): 111 """Stops all the snippet clients under management.""" 112 for client in self._snippet_clients.values(): 113 if client.is_alive: 114 self._device.log.debug('Stopping SnippetClient<%s>.', client.package) 115 client.stop_app() 116 else: 117 self._device.log.debug( 118 'Not stopping SnippetClient<%s> because it is not alive.', 119 client.package) 120 121 def pause(self): 122 """Pauses all the snippet clients under management. 123 124 This clears the host port of a client because a new port will be 125 allocated in `resume`. 126 """ 127 for client in self._snippet_clients.values(): 128 self._device.log.debug('Pausing SnippetClient<%s>.', client.package) 129 client.disconnect() 130 131 def resume(self): 132 """Resumes all paused snippet clients.""" 133 for client in self._snippet_clients.values(): 134 if not client.is_alive: 135 self._device.log.debug('Resuming SnippetClient<%s>.', client.package) 136 client.restore_app_connection() 137 else: 138 self._device.log.debug('Not resuming SnippetClient<%s>.', 139 client.package) 140 141 def __getattr__(self, name): 142 client = self.get_snippet_client(name) 143 if client: 144 return client 145 return self.__getattribute__(name) 146