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_v2 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 82 client = snippet_client_v2.SnippetClientV2(package=package, ad=self._device) 83 client.initialize() 84 self._snippet_clients[name] = client 85 86 def remove_snippet_client(self, name): 87 """Removes a snippet client from management. 88 89 Args: 90 name: string, the name of the snippet client to remove. 91 92 Raises: 93 Error: if no snippet client is managed under the specified name. 94 """ 95 if name not in self._snippet_clients: 96 raise Error(self._device, MISSING_SNIPPET_CLIENT_MSG % name) 97 client = self._snippet_clients.pop(name) 98 client.stop() 99 100 def start(self): 101 """Starts all the snippet clients under management.""" 102 for client in self._snippet_clients.values(): 103 if not client.is_alive: 104 self._device.log.debug('Starting SnippetClient<%s>.', client.package) 105 client.initialize() 106 else: 107 self._device.log.debug( 108 'Not startng SnippetClient<%s> because it is already alive.', 109 client.package) 110 111 def stop(self): 112 """Stops all the snippet clients under management.""" 113 for client in self._snippet_clients.values(): 114 if client.is_alive: 115 self._device.log.debug('Stopping SnippetClient<%s>.', client.package) 116 client.stop() 117 else: 118 self._device.log.debug( 119 'Not stopping SnippetClient<%s> because it is not alive.', 120 client.package) 121 122 def pause(self): 123 """Pauses all the snippet clients under management. 124 125 This clears the host port of a client because a new port will be 126 allocated in `resume`. 127 """ 128 for client in self._snippet_clients.values(): 129 self._device.log.debug('Pausing SnippetClient<%s>.', client.package) 130 client.close_connection() 131 132 def resume(self): 133 """Resumes all paused snippet clients.""" 134 for client in self._snippet_clients.values(): 135 if not client.is_alive: 136 self._device.log.debug('Resuming SnippetClient<%s>.', client.package) 137 client.restore_server_connection() 138 else: 139 self._device.log.debug('Not resuming SnippetClient<%s>.', 140 client.package) 141 142 def __getattr__(self, name): 143 client = self.get_snippet_client(name) 144 if client: 145 return client 146 return self.__getattribute__(name) 147