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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
# Copyright (c) 2021 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:`int_to_cmd`
* :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 int_to_cmd(n):
"""Convert from an integer (4 bytes) to an ADB command.
Parameters
----------
n : int
The integer that will be converted to an ADB command
Returns
-------
str
The ADB command (e.g., ``'CNXN'``)
"""
return ''.join(chr((n >> (i * 8)) % 256) for i in range(4)).encode('utf-8')
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 message's data
data_checksum : int
The checksum of the message's data
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)
|