Skip to content

Commit 4756580

Browse files
committed
Added adb connection resilience.
- Will now attempt to reconnect when the adb process dies
1 parent add9f7d commit 4756580

File tree

1 file changed

+71
-43
lines changed

1 file changed

+71
-43
lines changed

Diff for: handler/adb.py

+71-43
Original file line numberDiff line numberDiff line change
@@ -75,59 +75,26 @@ def __init__(
7575
}
7676
self._callbacks = {STATE_TYPE_HDR: None, STATE_TYPE_POWER: None}
7777

78-
# Check for existence of ADB on the path
79-
try:
80-
self._adb_path = subprocess.check_output(
81-
("/usr/bin/which", "adb"), stderr=subprocess.DEVNULL
82-
).strip()
83-
except subprocess.CalledProcessError:
84-
print("Error: adb util not found on path!", file=sys.stderr)
85-
sys.exit(1)
78+
self._ps = None
79+
self._pl_thread = None
8680

87-
# Connect ADB to the SHIELD TV
88-
try:
89-
subprocess.check_output(
90-
(self._adb_path, "connect", f"{hostname}:{port}"),
91-
stderr=subprocess.DEVNULL,
92-
)
93-
except subprocess.CalledProcessError:
94-
print(
95-
"Unable to connect to adb port on SHIELD TV device "
96-
f"at {hostname}:{port}.",
97-
file=sys.stderr,
98-
)
99-
sys.exit(2)
100-
finally:
101-
self._connected = True
102-
103-
# Flush the adb logs
104-
try:
105-
subprocess.check_output(
106-
(self._adb_path, "logcat", "-c"),
107-
stderr=subprocess.DEVNULL,
108-
)
109-
except subprocess.CalledProcessError:
110-
print("Unable to flush adb logs", file=sys.stderr)
111-
sys.exit(3)
81+
self._queue = Queue()
11282

11383
# Prepare common log parsing regex
114-
self.regex_log_parse = re.compile(
84+
self._regex_log_parse = re.compile(
11585
r"^(?P<month>\d{2})-(?P<date>\d{2})\s+"
11686
r"(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})\.(?P<millisecond>\d{3})\s+"
11787
r"\d+\s+\d+\s+\w\s+(?P<process>\S+)\s*:\s+(?P<message>.+)$"
11888
)
89+
self._regex_logcat_fails = [
90+
re.compile("logcat: Unexpected EOF!")
91+
]
11992

120-
# Start processing logs
121-
self._ps = subprocess.Popen((self._adb_path, "logcat"), stdout=subprocess.PIPE)
122-
123-
self._queue = Queue()
124-
t = Thread(target=self._process_log, args=(self._ps.stdout, self._queue))
125-
t.daemon = True
126-
t.start()
93+
self._adb_start()
12794

128-
def _process_log(self, output, queue: Queue):
95+
def _process_log(self, output, err, queue: Queue):
12996
for line in iter(output.readline, b""):
130-
matched_line = self.regex_log_parse.match(line.decode("utf-8"))
97+
matched_line = self._regex_log_parse.match(line.decode("utf-8"))
13198

13299
if matched_line:
133100
process = matched_line.group("process")
@@ -156,8 +123,68 @@ def _process_log(self, output, queue: Queue):
156123
}
157124
)
158125

126+
for line in iter(err.readline, b""):
127+
for fail in self._regex_logcat_fails:
128+
matched_line = fail.match(line.decode("utf-8"))
129+
130+
if matched_line:
131+
# Terminate now, cleanup later
132+
self._ps.terminate()
133+
159134
output.close()
160135

136+
def _adb_start(self):
137+
# Check for existence of ADB on the path
138+
try:
139+
self._adb_path = subprocess.check_output(
140+
("/usr/bin/which", "adb"), stderr=subprocess.DEVNULL
141+
).strip()
142+
except subprocess.CalledProcessError:
143+
print("Error: adb util not found on path!", file=sys.stderr)
144+
sys.exit(1)
145+
146+
# Connect ADB to the SHIELD TV
147+
while not self._connected:
148+
try:
149+
subprocess.check_output(
150+
(self._adb_path, "connect", f"{self._hostname}:{self._port}"),
151+
stderr=subprocess.DEVNULL,
152+
)
153+
except subprocess.CalledProcessError:
154+
print(
155+
"Unable to connect to adb port on SHIELD TV device "
156+
f"at {self._hostname}:{self._port}.",
157+
file=sys.stderr,
158+
)
159+
time.sleep(5)
160+
finally:
161+
self._connected = True
162+
163+
# Flush the adb logs
164+
try:
165+
subprocess.check_output(
166+
(self._adb_path, "logcat", "-c"),
167+
stderr=subprocess.DEVNULL,
168+
)
169+
except subprocess.CalledProcessError:
170+
print("Unable to flush adb logs", file=sys.stderr)
171+
sys.exit(3)
172+
173+
# Start processing logs
174+
self._ps = subprocess.Popen(
175+
(self._adb_path, "logcat"), stdout=subprocess.PIPE, stderr=subprocess.PIPE
176+
)
177+
178+
self._pl_thread = Thread(target=self._process_log, args=(self._ps.stdout, self._ps.stderr, self._queue))
179+
self._pl_thread.daemon = True
180+
self._pl_thread.start()
181+
182+
def _check_adb_state(self):
183+
if self._ps.poll() is not None:
184+
self._connected = False
185+
print("ADB process has died. Restarting...")
186+
self._adb_start()
187+
161188
def _update_states_from_queue(self):
162189
new_state = copy.deepcopy(self._current_state)
163190

@@ -185,4 +212,5 @@ def set_power_callback(self, callback):
185212
self._callbacks[STATE_TYPE_POWER] = callback
186213

187214
def loop(self):
215+
self._check_adb_state()
188216
self._update_states_from_queue()

0 commit comments

Comments
 (0)