| # Copyright 2018 Google Inc. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """Module for the manager of services.""" |
| # TODO(xpconanfan: move the device errors to a more generic location so |
| # other device controllers like iOS can share it. |
| import inspect |
| |
| from mobly import expects |
| from mobly.controllers.android_device_lib import errors |
| from mobly.controllers.android_device_lib.services import base_service |
| |
| |
| class Error(errors.DeviceError): |
| """Root error type for this module.""" |
| |
| |
| class ServiceManager(object): |
| """Manager for services of AndroidDevice. |
| |
| A service is a long running process that involves an Android device, like |
| adb logcat or Snippet. |
| """ |
| |
| def __init__(self, device): |
| self._service_objects = {} |
| self._device = device |
| |
| def has_service_by_name(self, name): |
| """Checks if the manager has a service registered with a specific name. |
| |
| Args: |
| name: string, the name to look for. |
| |
| Returns: |
| True if a service is registered with the specified name, False |
| otherwise. |
| """ |
| return name in self._service_objects |
| |
| @property |
| def is_any_alive(self): |
| """True if any service is alive; False otherwise.""" |
| for service in self._service_objects.values(): |
| if service.is_alive: |
| return True |
| return False |
| |
| def register(self, alias, service_class, configs=None, start_service=True): |
| """Registers a service. |
| |
| This will create a service instance, starts the service, and adds the |
| instance to the mananger. |
| |
| Args: |
| alias: string, the alias for this instance. |
| service_class: class, the service class to instantiate. |
| configs: (optional) config object to pass to the service class's |
| constructor. |
| start_service: bool, whether to start the service instance or not. |
| Default is True. |
| """ |
| if not inspect.isclass(service_class): |
| raise Error(self._device, '"%s" is not a class!' % service_class) |
| if not issubclass(service_class, base_service.BaseService): |
| raise Error( |
| self._device, |
| 'Class %s is not a subclass of BaseService!' % service_class) |
| if alias in self._service_objects: |
| raise Error( |
| self._device, |
| 'A service is already registered with alias "%s".' % alias) |
| service_obj = service_class(self._device, configs) |
| if start_service: |
| service_obj.start() |
| self._service_objects[alias] = service_obj |
| |
| def unregister(self, alias): |
| """Unregisters a service instance. |
| |
| Stops a service and removes it from the manager. |
| |
| Args: |
| alias: string, the alias of the service instance to unregister. |
| """ |
| if alias not in self._service_objects: |
| raise Error(self._device, |
| 'No service is registered with alias "%s".' % alias) |
| service_obj = self._service_objects.pop(alias) |
| if service_obj.is_alive: |
| with expects.expect_no_raises( |
| 'Failed to stop service instance "%s".' % alias): |
| service_obj.stop() |
| |
| def unregister_all(self): |
| """Safely unregisters all active instances. |
| |
| Errors occurred here will be recorded but not raised. |
| """ |
| aliases = list(self._service_objects.keys()) |
| for alias in aliases: |
| self.unregister(alias) |
| |
| def start_all(self): |
| """Starts all inactive service instances.""" |
| for alias, service in self._service_objects.items(): |
| if not service.is_alive: |
| with expects.expect_no_raises( |
| 'Failed to start service "%s".' % alias): |
| service.start() |
| |
| def stop_all(self): |
| """Stops all active service instances.""" |
| for alias, service in self._service_objects.items(): |
| if service.is_alive: |
| with expects.expect_no_raises( |
| 'Failed to stop service "%s".' % alias): |
| service.stop() |
| |
| def pause_all(self): |
| """Pauses all service instances.""" |
| for alias, service in self._service_objects.items(): |
| with expects.expect_no_raises( |
| 'Failed to pause service "%s".' % alias): |
| service.pause() |
| |
| def resume_all(self): |
| """Resumes all service instances.""" |
| for alias, service in self._service_objects.items(): |
| with expects.expect_no_raises( |
| 'Failed to resume service "%s".' % alias): |
| service.resume() |
| |
| def __getattr__(self, name): |
| """Syntactic sugar to enable direct access of service objects by alias. |
| |
| Args: |
| name: string, the alias a service object was registered under. |
| """ |
| if self.has_service_by_name(name): |
| return self._service_objects[name] |
| return self.__getattribute__(name) |