1#!/usr/bin/env python
2#
3# Copyright (C) 2020 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#
17
18import logging
19import subprocess
20import time
21
22from threading import Timer
23
24class AdbError(Exception):
25    """Raised when there is an error in adb operations."""
26
27    def __init__(self, cmd, stdout, stderr, ret_code):
28        self.cmd = cmd
29        self.stdout = stdout
30        self.stderr = stderr
31        self.ret_code = ret_code
32
33    def __str__(self):
34        return ("Error executing adb cmd '%s'. ret: %d, stdout: %s, stderr: %s"
35                ) % (self.cmd, self.ret_code, self.stdout, self.stderr)
36
37
38class ADB(object):
39    """This class to wrap adb command."""
40
41    # Default adb timeout 5 minutes
42    DEFAULT_ADB_TIMEOUT = 300
43
44    def __init__(self, serial_number):
45        self._serial_number = serial_number
46
47    def Execute(self, cmd_list, timeout=DEFAULT_ADB_TIMEOUT):
48        """Executes a command.
49
50        Args:
51            args: Strings, the arguments.
52
53        Returns:
54            Stdout as a string, stderr as a string, and return code as an
55            integer.
56        """
57        cmd = ["adb", "-s", self._serial_number]
58        cmd.extend(cmd_list)
59        return RunCommand(cmd, timeout)
60
61
62def RunCommand(cmd, timeout=None):
63    kill = lambda process:process.kill()
64    proc = subprocess.Popen(args=cmd,
65                            stderr=subprocess.PIPE,
66                            stdout=subprocess.PIPE)
67    _timer = Timer(timeout, kill, [proc])
68    try:
69        _timer.start()
70        (out, err) = proc.communicate()
71    finally:
72        _timer.cancel()
73
74    out = out.decode("utf-8")
75    err = err.decode("utf-8")
76
77    if proc.returncode != 0:
78        raise AdbError(cmd, out, err, proc.returncode)
79    return out, err, proc.returncode
80
81