1#!/usr/bin/env python3.4
2#
3#   Copyright 2017 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import time
18
19SL4A_SERVICE_SETUP_TIME = 5
20
21
22class OtaError(Exception):
23    """Raised when an error in the OTA Update process occurs."""
24
25
26class OtaRunner(object):
27    """The base class for all OTA Update Runners."""
28
29    def __init__(self, ota_tool, android_device):
30        self.ota_tool = ota_tool
31        self.android_device = android_device
32        self.serial = self.android_device.serial
33
34    def _update(self):
35        log = self.android_device.log
36        old_info = self.android_device.adb.getprop('ro.build.fingerprint')
37        log.info('Starting Update. Beginning build info: %s', old_info)
38        log.info('Stopping services.')
39        self.android_device.stop_services()
40        log.info('Beginning tool.')
41        self.ota_tool.update(self)
42        log.info('Tool finished. Waiting for boot completion.')
43        self.android_device.wait_for_boot_completion()
44        new_info = self.android_device.adb.getprop('ro.build.fingerprint')
45        if not old_info or old_info == new_info:
46            raise OtaError('The device was not updated to a new build. '
47                           'Previous build: %s. New build: %s' % (old_info,
48                                                                  new_info))
49        log.info('Boot completed. Rooting adb.')
50        self.android_device.root_adb()
51        log.info('Root complete.')
52        if self.android_device.skip_sl4a:
53            self.android_device.log.info('Skipping SL4A install.')
54        else:
55            for _ in range(3):
56                self.android_device.log.info('Re-installing SL4A from "%s".',
57                                             self.get_sl4a_apk())
58                self.android_device.adb.install(
59                    '-r -g %s' % self.get_sl4a_apk(), ignore_status=True)
60                time.sleep(SL4A_SERVICE_SETUP_TIME)
61                if self.android_device.is_sl4a_installed():
62                    break
63        log.info('Starting services.')
64        self.android_device.start_services()
65        log.info('Services started. Running ota tool cleanup.')
66        self.ota_tool.cleanup(self)
67        log.info('Cleanup complete.')
68
69    def can_update(self):
70        """Whether or not an update package is available for the device."""
71        return NotImplementedError()
72
73    def get_ota_package(self):
74        raise NotImplementedError()
75
76    def get_sl4a_apk(self):
77        raise NotImplementedError()
78
79
80class SingleUseOtaRunner(OtaRunner):
81    """A single use OtaRunner.
82
83    SingleUseOtaRunners can only be ran once. If a user attempts to run it more
84    than once, an error will be thrown. Users can avoid the error by checking
85    can_update() before calling update().
86    """
87
88    def __init__(self, ota_tool, android_device, ota_package, sl4a_apk):
89        super(SingleUseOtaRunner, self).__init__(ota_tool, android_device)
90        self._ota_package = ota_package
91        self._sl4a_apk = sl4a_apk
92        self._called = False
93
94    def can_update(self):
95        return not self._called
96
97    def update(self):
98        """Starts the update process."""
99        if not self.can_update():
100            raise OtaError('A SingleUseOtaTool instance cannot update a phone '
101                           'multiple times.')
102        self._called = True
103        self._update()
104
105    def get_ota_package(self):
106        return self._ota_package
107
108    def get_sl4a_apk(self):
109        return self._sl4a_apk
110
111
112class MultiUseOtaRunner(OtaRunner):
113    """A multiple use OtaRunner.
114
115    MultiUseOtaRunner can only be ran for as many times as there have been
116    packages provided to them. If a user attempts to run it more than the number
117    of provided packages, an error will be thrown. Users can avoid the error by
118    checking can_update() before calling update().
119    """
120
121    def __init__(self, ota_tool, android_device, ota_packages, sl4a_apks):
122        super(MultiUseOtaRunner, self).__init__(ota_tool, android_device)
123        self._ota_packages = ota_packages
124        self._sl4a_apks = sl4a_apks
125        self.current_update_number = 0
126
127    def can_update(self):
128        return not self.current_update_number == len(self._ota_packages)
129
130    def update(self):
131        """Starts the update process."""
132        if not self.can_update():
133            raise OtaError('This MultiUseOtaRunner has already updated all '
134                           'given packages onto the phone.')
135        self._update()
136        self.current_update_number += 1
137
138    def get_ota_package(self):
139        return self._ota_packages[self.current_update_number]
140
141    def get_sl4a_apk(self):
142        return self._sl4a_apks[self.current_update_number]
143