-
Notifications
You must be signed in to change notification settings - Fork 59
/
adb_message.py
165 lines (130 loc) · 5.26 KB
/
adb_message.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# Copyright (c) 2020 Jeff Irion and contributors
#
# This file is part of the adb-shell package. It incorporates work
# covered by the following license notice:
#
#
# Copyright 2014 Google Inc. All rights reserved.
#
# 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.
"""Functions and an :class:`AdbMessage` class for packing and unpacking ADB messages.
.. rubric:: Contents
* :class:`AdbMessage`
* :attr:`AdbMessage.checksum`
* :meth:`AdbMessage.pack`
* :func:`checksum`
* :func:`unpack`
"""
import struct
from . import constants
def checksum(data):
"""Calculate the checksum of the provided data.
Parameters
----------
data : bytearray, bytes, str
The data
Returns
-------
int
The checksum
"""
# The checksum is just a sum of all the bytes. I swear.
if isinstance(data, bytearray):
total = sum(data)
elif isinstance(data, bytes):
if data and isinstance(data[0], bytes):
# Python 2 bytes (str) index as single-character strings.
total = sum((ord(d) for d in data)) # pragma: no cover
else:
# Python 3 bytes index as numbers (and PY2 empty strings sum() to 0)
total = sum(data)
else:
# Unicode strings (should never see?)
total = sum((ord(d) for d in data))
return total & 0xFFFFFFFF
def unpack(message):
"""Unpack a received ADB message.
Parameters
----------
message : bytes
The received message
Returns
-------
cmd : int
The ADB command
arg0 : int
TODO
arg1 : int
TODO
data_length : int
The length of the data sent by the device (used by :meth:`adb_shell.adb_device.AdbDevice._read` and :meth:`adb_shell.adb_device_async.AdbDeviceAsync._read`)
data_checksum : int
The checksum of the data sent by the device
Raises
------
ValueError
Unable to unpack the ADB command.
"""
try:
cmd, arg0, arg1, data_length, data_checksum, _ = struct.unpack(constants.MESSAGE_FORMAT, message)
except struct.error as e:
raise ValueError('Unable to unpack ADB command. (length={})'.format(len(message)), constants.MESSAGE_FORMAT, message, e)
return cmd, arg0, arg1, data_length, data_checksum
class AdbMessage(object):
"""A helper class for packing ADB messages.
Parameters
----------
command : bytes
A command; examples used in this package include :const:`adb_shell.constants.AUTH`, :const:`adb_shell.constants.CNXN`, :const:`adb_shell.constants.CLSE`, :const:`adb_shell.constants.OPEN`, and :const:`adb_shell.constants.OKAY`
arg0 : int
Usually the local ID, but :meth:`~adb_shell.adb_device.AdbDevice.connect` and :meth:`~adb_shell.adb_device_async.AdbDeviceAsync.connect` provide :const:`adb_shell.constants.VERSION`, :const:`adb_shell.constants.AUTH_SIGNATURE`, and :const:`adb_shell.constants.AUTH_RSAPUBLICKEY`
arg1 : int
Usually the remote ID, but :meth:`~adb_shell.adb_device.AdbDevice.connect` and :meth:`~adb_shell.adb_device_async.AdbDeviceAsync.connect` provide :const:`adb_shell.constants.MAX_ADB_DATA`
data : bytes
The data that will be sent
Attributes
----------
arg0 : int
Usually the local ID, but :meth:`~adb_shell.adb_device.AdbDevice.connect` and :meth:`~adb_shell.adb_device_async.AdbDeviceAsync.connect` provide :const:`adb_shell.constants.VERSION`, :const:`adb_shell.constants.AUTH_SIGNATURE`, and :const:`adb_shell.constants.AUTH_RSAPUBLICKEY`
arg1 : int
Usually the remote ID, but :meth:`~adb_shell.adb_device.AdbDevice.connect` and :meth:`~adb_shell.adb_device_async.AdbDeviceAsync.connect` provide :const:`adb_shell.constants.MAX_ADB_DATA`
command : int
The input parameter ``command`` converted to an integer via :const:`adb_shell.constants.ID_TO_WIRE`
data : bytes
The data that will be sent
magic : int
``self.command`` with its bits flipped; in other words, ``self.command + self.magic == 2**32 - 1``
"""
def __init__(self, command, arg0, arg1, data=b''):
self.command = constants.ID_TO_WIRE[command]
self.magic = self.command ^ 0xFFFFFFFF
self.arg0 = arg0
self.arg1 = arg1
self.data = data
def pack(self):
"""Returns this message in an over-the-wire format.
Returns
-------
bytes
The message packed into the format required by ADB
"""
return struct.pack(constants.MESSAGE_FORMAT, self.command, self.arg0, self.arg1, len(self.data), self.checksum, self.magic)
@property
def checksum(self):
"""Return ``checksum(self.data)``
Returns
-------
int
The checksum of ``self.data``
"""
return checksum(self.data)