Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
S2-group
GitHub Repository: S2-group/android-runner
Path: blob/master/AndroidRunner/Adb.py
629 views
1
import logging
2
import os.path as op
3
import os, glob
4
import zipfile
5
from time import sleep
6
7
from .pyand import ADB
8
from AndroidRunner.util import ConfigError
9
10
logger = logging.getLogger(__name__)
11
12
13
class AdbError(Exception):
14
"""Raised when there's an error with ADB"""
15
pass
16
17
18
class ConnectionError(Exception):
19
"""Raised when there's an connection error"""
20
pass
21
22
23
adb = None
24
25
settings_options = {"location_high_accuracy": ("settings put secure location_providers_allowed -gps,network","settings put secure location_providers_allowed +gps,network"),
26
"location_gps_only": ("settings put secure location_providers_allowed -gps","settings put secure location_providers_allowed +gps")
27
}
28
29
def configure_settings(device_id, setting, enable):
30
cmd = settings_options[setting][enable]
31
return shell(device_id, cmd)
32
33
# noinspection PyProtectedMember
34
def setup(path='adb'):
35
global adb
36
adb = ADB(adb_path=path)
37
# Accessing class private variables to avoid another print of the same error message
38
# https://stackoverflow.com/a/1301369
39
if adb._ADB__error:
40
raise AdbError('adb path is incorrect')
41
42
43
def connect(device_id):
44
device_list = adb.get_devices()
45
if not device_list:
46
raise ConnectionError('No devices are connected')
47
logger.debug('Device list:\n%s' % device_list)
48
if device_id not in list(device_list.values()):
49
raise ConnectionError('%s: Device not recognized' % device_id)
50
51
52
def shell_su(device_id, cmd):
53
adb.set_target_by_name(device_id)
54
result = adb.shell_command("su -c \'%s\'" % cmd)
55
result = result.decode('utf-8') if (isinstance(result, bytes) == True) else result
56
logger.debug('%s: "su -c \'%s\'" returned: \n%s' % (device_id, cmd, result))
57
if 'error' in result:
58
raise AdbError(result)
59
return result.rstrip()
60
61
62
def shell(device_id, cmd):
63
adb.set_target_by_name(device_id)
64
result = adb.shell_command(cmd)
65
result = result.decode('utf-8') if (isinstance(result, bytes) == True) else result
66
logger.debug('%s: "%s" returned: \n%s' % (device_id, cmd, result))
67
if 'error' in result:
68
raise AdbError(result)
69
return result.rstrip()
70
71
72
def list_apps(device_id):
73
return shell(device_id, 'pm list packages').replace('package:', '').split()
74
75
76
def install(device_id, apk, replace=True, all_permissions=True):
77
filename = op.basename(apk)
78
logger.debug('%s: Installing "%s"' % (device_id, filename))
79
adb.set_target_by_name(device_id)
80
81
# get extension filename
82
extension = op.splitext(apk)[-1].lower()
83
84
if extension == '.xapk':
85
cmd = ['install-multiple']
86
android_runner_dir = os.getcwd()
87
# get path of directory apks will be unzipped into.
88
path_apks_to_be_installed = op.splitext(apk)[0].lower()
89
with zipfile.ZipFile(apk, 'r') as zip_ref:
90
if not op.exists(path_apks_to_be_installed):
91
os.makedirs(path_apks_to_be_installed)
92
zip_ref.extractall(path_apks_to_be_installed)
93
94
os.chdir(path_apks_to_be_installed)
95
96
current_working_directory = os.getcwd()
97
98
apk_files = glob.glob('*.apk')
99
100
if not apk_files:
101
raise ConfigError('No apks found in xapk')
102
103
apk_files_paths = [op.join(current_working_directory, apk_file) for apk_file in apk_files]
104
105
# arguments of install-multiple
106
apk = ' '.join(apk_files_paths)
107
logger.info('installing APKs %s' % apk)
108
os.chdir(android_runner_dir) # go back to android runner dir
109
else:
110
cmd = ['install']
111
112
if replace:
113
cmd += ['-r']
114
if all_permissions:
115
cmd += ['-g']
116
cmd += ['-t', apk]
117
adb.run_cmd(cmd)
118
# WARNING: Accessing class private variables
119
output = adb._ADB__output
120
logger.debug('install returned: %s' % output)
121
return output
122
123
124
def uninstall(device_id, name, keep_data=False):
125
logger.debug('%s: Uninstalling "%s"' % (device_id, name))
126
adb.set_target_by_name(device_id)
127
# Flips the keep_data flag as it is incorrectly implemented in the pyand library
128
keep_data = not keep_data
129
result = adb.uninstall(package=name, keepdata=keep_data)
130
success_or_exception(result,
131
'%s: "%s" uninstalled' % (device_id, name),
132
'%s: Failed to uninstall "%s"' % (device_id, name)
133
)
134
135
136
def clear_app_data(device_id, name):
137
adb.set_target_by_name(device_id)
138
success_or_exception(adb.shell_command('pm clear %s' % name),
139
'%s: Data of "%s" cleared' % (device_id, name),
140
'%s: Failed to clear data for "%s"' % (device_id, name)
141
)
142
143
144
def success_or_exception(result, success_msg, fail_msg):
145
result = result.decode('utf-8') if (isinstance(result, bytes) == True) else result
146
if 'Success' in result:
147
logger.info(success_msg)
148
else:
149
logger.info(fail_msg + '\nMessage returned:\n%s' % result)
150
raise AdbError(result)
151
152
153
# Same with push_local_file(), but with the quotes removed
154
# adb doesn't want quotes for some reason
155
# noinspection PyProtectedMember
156
def push(device_id, local, remote):
157
adb.set_target_by_name(device_id)
158
adb.run_cmd('push %s %s' % (local, remote))
159
# WARNING: Accessing class private variables
160
return adb._ADB__output
161
162
163
# Same with get_remote_file(), but with the quotes removed
164
# adb doesn't want quotes for some reason
165
# noinspection PyProtectedMember
166
def pull(device_id, remote, local):
167
adb.set_target_by_name(device_id)
168
adb.run_cmd('pull %s %s' % (remote, local))
169
# WARNING: Accessing class private variables
170
if adb._ADB__error and "bytes in" in adb._ADB__error:
171
adb._ADB__output = adb._ADB__error
172
adb._ADB__error = None
173
return adb._ADB__output
174
175
def logcat(device_id, regex=None):
176
"""Returns the logcat log for the given device.
177
178
When regex is provided, only return log entries that match the regex.
179
180
Grep is used to handle regular expressions. While the logcat command itself supports regular expressions using
181
the -e flag it is not available on all devices.
182
Using grep circumvents this problem provided that the host OS has grep installed.
183
184
Parameters
185
----------
186
device_id : string
187
ID of the device we want to see the logcat log of.
188
regex : string, optional, default=None
189
The regular expression
190
191
Returns
192
-------
193
string
194
The full logcat log or the logcat entries that match the regular expression.
195
"""
196
# -d prints to screen and exits
197
params = 'logcat -d'
198
if regex is not None:
199
params += f' | grep "{regex}"'
200
res = shell(device_id, params)
201
return res
202
203
def reset(cmd):
204
if cmd:
205
logger.info('Shutting down adb...')
206
sleep(1)
207
adb.kill_server()
208
sleep(2)
209
logger.info('Restarting adb...')
210
adb.get_devices()
211
sleep(10)
212
213