blob: 9e7976a9b7b04cf56c8c2be73250eea0de6dc093 [file] [log] [blame]
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +01001#!/usr/bin/env python3
2
Franz-Xaver Geigerb28348e2018-02-19 14:41:40 +01003import os
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +01004import time
5from uiautomator import Device
6import sys
7import subprocess
8import pathlib
9
Franz-Xaver Geigerb28348e2018-02-19 14:41:40 +010010
11PREBUILTS_PATH = '../../vendor/smartviser/viser/prebuilts/apk'
12
Franz-Xaver Geigerb3f7c982018-04-12 09:29:32 +020013PREBUILT_PROXY_APKS = {
14 'gms': 'com.lunarlabs.panda.proxy.apk',
15 'sibon': 'com.lunarlabs.panda.proxy-sibon.apk',
16}
17
Franz-Xaver Geigerb28348e2018-02-19 14:41:40 +010018PREBUILT_APKS = [
19 'com.smartviser.demogame.apk',
Franz-Xaver Geigerb28348e2018-02-19 14:41:40 +010020 'com.lunarlabs.panda.apk',
21]
22
23
24def adb(*args, serial = None):
25 """Run ADB command attached to serial.
26
27 Example:
28 >>> process = adb('shell', 'getprop', 'ro.build.fingerprint', serial='cc60c021')
29 >>> process.returncode
30 0
31 >>> process.stdout.strip()
32 'Fairphone/FP2/FP2:6.0.1/FP2-gms-18.02.0/FP2-gms-18.02.0:user/release-keys'
33
34 :param *args:
35 List of options to ADB (including command).
36 :param str serial:
37 Identifier for ADB connection to device.
38 :returns subprocess.CompletedProcess:
39 Completed process.
40 """
41
42 # Make sure the adb server is started to avoid the infamous "out of date"
43 # message that pollutes stdout.
44 subprocess.run(
45 ['adb', 'start-server'], stdout=subprocess.PIPE, stderr=subprocess.PIPE,
46 universal_newlines=True)
47
48 command = ['adb']
49 if serial:
50 command += ['-s', serial]
51 if args:
52 command += list(args)
53 return subprocess.run(
54 command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
55 universal_newlines=True)
56
57
58def aapt(*args):
59 """Run an AAPT command.
60
61 :param *args:
62 The AAPT command with its options.
63 :returns subprocess.CompletedProcess:
64 Completed process.
65 """
66 command = ['aapt']
67 if args:
68 command += list(args)
69 return subprocess.run(
70 command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
71 universal_newlines=True)
72
73
Borjan Tchakaloff64ec42b2018-02-07 11:33:15 -080074def force_awake(serial, always=True):
75 """Force the device to stay awake."""
76 return adb('shell', 'svc power stayon {}'.format(
77 'true' if always else 'false'), serial=serial)
78
79
80def unlock(serial):
81 """Wake-up the device and unlock it."""
82 adb('shell', 'input keyevent KEYCODE_POWER', serial=serial)
83 time.sleep(1)
84 adb('shell', 'input keyevent KEYCODE_MENU', serial=serial)
85 time.sleep(1)
86 adb('shell', 'input keyevent KEYCODE_HOME', serial=serial)
87
88
Franz-Xaver Geigerb3f7c982018-04-12 09:29:32 +020089def getprop(serial, key):
90 """Get system property of device.
91
92 Example:
93 >>> getprop('167eb6e8', 'ro.build.id')
94 'FP2-gms-18.02.0'
95
96 :param str serial:
97 Identifier for ADB connection to device.
98 :param str key:
99 Key of property to get.
100 :returns str:
101 Value of system property.
102 """
103 process = adb('shell', 'getprop', key, serial=serial)
104 return process.stdout.strip()
105
106
107def is_gms_device(serial):
108 """Test if device runs GMS or sibon.
109
110 Example:
111 >>> is_gms_device('167eb6e8')
112 True
113
114 :param str serial:
115 Identifier for ADB connection to device.
116 :returns bool:
117 True if device runs GMS, false otherwise.
118 """
119 return getprop(serial, 'ro.build.id').startswith('FP2-gms-')
120
121
Franz-Xaver Geigerb28348e2018-02-19 14:41:40 +0100122def uninstall_apk(serial, filename, prebuilts_dir):
123 """Uninstall apk from prebuilts_dir on device."""
124 ret = aapt('dump', 'badging', '{}/{}'.format(prebuilts_dir, filename))
125 if 0 < ret.returncode:
126 print('Could retrieve app `{}` info, error was: {}'.format(
127 filename, ret.stderr), file=sys.stderr)
128 else:
129 package = None
130 for line in ret.stdout.splitlines():
131 if line.startswith('package'):
132 for token in line.split(' '):
133 if token.startswith('name='):
134 # Extract the package name out of the token
135 # (name='some.package.name')
136 package = token[6:-1]
137 break
138 if not package:
139 print('Could not find package of app `{}`'.format(filename),
140 file=sys.stderr)
141 else:
142 print('Uninstalling `{}`'.format(package))
143 ret = adb('uninstall', package, serial=serial)
144 if 0 < ret.returncode:
145 print('Could not uninstall app `{}`, error was: {}'.format(
146 filename, ret.stderr), file=sys.stderr)
147 else:
148 print('App `{}` uninstalled.'.format(filename))
149
150
151def install_apk(serial, filename, prebuilts_dir):
152 """Install apk from prebuilts_dir on device."""
153 print('Installing {}.'.format(filename))
154 path = os.path.join(prebuilts_dir, filename)
155 ret = adb('install', '-r', path, serial=serial)
156 if 0 < ret.returncode:
157 print('Could not install app `{}`, error was: {}'.format(
158 filename, ret.stderr), file=sys.stderr)
159 else:
160 print('App `{}` installed.'.format(filename))
161
162
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100163# Prepare the DUT
Franz-Xaver Geigerb28348e2018-02-19 14:41:40 +0100164def prepare_dut(serial, scenarios_dir, data_dir, prebuilts_dir):
Franz-Xaver Geigerb3f7c982018-04-12 09:29:32 +0200165 flavour = 'gms' if is_gms_device(serial) else 'sibon'
166
Franz-Xaver Geigerb28348e2018-02-19 14:41:40 +0100167 # Uninstall the smartviser apps
Franz-Xaver Geigerb3f7c982018-04-12 09:29:32 +0200168 for app in PREBUILT_APKS + [PREBUILT_PROXY_APKS[flavour]]:
Franz-Xaver Geigerb28348e2018-02-19 14:41:40 +0100169 uninstall_apk(serial, app, prebuilts_dir)
170
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100171 # Copy the scenarios
Franz-Xaver Geiger223ae752018-02-19 14:54:08 +0100172 ret = adb('push', scenarios_dir, '/sdcard/viser', serial=serial)
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100173 if 0 < ret.returncode:
174 print('Could not push the scenarios, error was: {}'.format(ret.stderr),
175 file=sys.stderr)
176 else:
177 print('Scenarios pushed.')
178
179 # Copy the scenarios data
Franz-Xaver Geiger223ae752018-02-19 14:54:08 +0100180 ret = adb('push', data_dir, '/sdcard/viser/data', serial=serial)
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100181 if 0 < ret.returncode:
182 print('Could not push the scenarios data, error was: {}'.format(ret.stderr),
183 file=sys.stderr)
184 else:
185 print('Scenarios data pushed.')
186
Franz-Xaver Geigerb3f7c982018-04-12 09:29:32 +0200187 # Install the smartviser apps (starting with the proxy app)
188 for app in [PREBUILT_PROXY_APKS[flavour]] + PREBUILT_APKS:
Franz-Xaver Geigerb28348e2018-02-19 14:41:40 +0100189 install_apk(serial, app, prebuilts_dir)
190
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100191
192# Grant the permissions through the UI
193def configure_perms(dut):
Borjan Tchakaloff6dad85d2018-04-11 13:55:25 +0200194 dut() \
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100195 .child_by_text('You need to grant access for the Permissions Group '
196 'Calendar Camera Contacts Microphone SMS Storage Telephone Location',
Borjan Tchakaloff6dad85d2018-04-11 13:55:25 +0200197 resourceId='android:id/content') \
198 .child(text='OK', className='android.widget.Button') \
199 .click()
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100200
201 # Accept any permission that is asked for
202 prompt = dut(resourceId='com.android.packageinstaller:id/permission_allow_button',
203 className='android.widget.Button')
204 while prompt.exists:
205 prompt.click()
206
207 # Input the credentials
208 dut(resourceId='android:id/content') \
209 .child(text='Username') \
210 .child(className='android.widget.EditText') \
211 .set_text('borjan@fairphone.com')
212 dut(resourceId='android:id/content') \
213 .child(text='Password') \
214 .child(className='android.widget.EditText') \
215 .set_text('Rickisalso1niceguy!')
216
217 # Sign in
218 dut(resourceId='android:id/content') \
219 .child(text='Sign in', className='android.widget.Button') \
220 .click()
221
222def configure_sms(dut):
223 # TODO wait for the connection to be established and time-out
224 prompt = dut(resourceId='android:id/content') \
225 .child(text='Viser must be your SMS app to send messages')
226 while not prompt.exists:
227 time.sleep(1)
228
229 # Make viser the default SMS app
230 dut(resourceId='android:id/content') \
231 .child_by_text('Viser must be your SMS app to send messages',
232 className='android.widget.LinearLayout') \
233 .child(text='OK', className='android.widget.Button') \
234 .click()
235
236 dut(resourceId='android:id/content') \
237 .child_by_text('Change SMS app?', className='android.widget.LinearLayout') \
238 .child(text='Yes', className='android.widget.Button') \
239 .click()
240
241def configure_settings(dut):
242 # Set the e-mail account
243 dut(text='Settings', className='android.widget.TextView') \
244 .click()
245 dut(resourceId='android:id/list') \
246 .child_by_text('User settings', className='android.widget.LinearLayout') \
247 .click()
248 dut(resourceId='android:id/list') \
249 .child_by_text('Email account', className='android.widget.LinearLayout') \
250 .click()
251 prompt = dut(resourceId='android:id/content') \
252 .child_by_text('Email account', className='android.widget.LinearLayout')
253 prompt.child(resourceId='android:id/edit') \
254 .set_text('fairphone.viser@gmail.com')
255 prompt.child(text='OK', className='android.widget.Button') \
256 .click()
257 dut(resourceId='android:id/list') \
258 .child_by_text('Email password', className='android.widget.LinearLayout') \
259 .click()
260 dut(text='Password :') \
261 .child(className='android.widget.EditText') \
262 .set_text('fairphoneviser2017')
263 dut(description='OK', className='android.widget.TextView') \
264 .click()
265 dut.press.back()
266 dut.press.back()
267
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100268
Franz-Xaver Geigerd1079e22018-02-19 14:34:15 +0100269def deploy():
270 serials = []
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100271
Franz-Xaver Geigerd1079e22018-02-19 14:34:15 +0100272 if len(sys.argv) > 1:
273 serials.append(sys.argv[1])
274 else:
275 # List devices attached to adb
276 ret = subprocess.run(
277 "adb devices | grep -E '^[a-z0-9-]+\s+device$' | awk '{{print $1}}'",
278 shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
279 universal_newlines=True)
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100280
Franz-Xaver Geigerd1079e22018-02-19 14:34:15 +0100281 if 0 < ret.returncode:
282 # TODO handle the error
283 raise Exception('Failed to list adb devices')
284 elif ret.stdout:
285 for line in ret.stdout.splitlines():
286 serial = line.strip()
287 if serial:
288 serials.append(serial)
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100289
Franz-Xaver Geigerd1079e22018-02-19 14:34:15 +0100290 for serial in serials:
291 print('Configuring device {}'.format(serial))
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100292
Franz-Xaver Geigerd1079e22018-02-19 14:34:15 +0100293 dut = Device(serial)
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100294
Borjan Tchakaloff64ec42b2018-02-07 11:33:15 -0800295 # Make sure the screen stays on - we're going to use UI automation
296 force_awake(serial)
297 unlock(serial)
298
Franz-Xaver Geigerd1079e22018-02-19 14:34:15 +0100299 # Push the scenarios, their data, and install the apps
Franz-Xaver Geigerb28348e2018-02-19 14:41:40 +0100300 prepare_dut(serial, '../scenarios', '../scenarios-data', PREBUILTS_PATH)
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100301
Franz-Xaver Geigerd1079e22018-02-19 14:34:15 +0100302 # Start the viser app
Franz-Xaver Geiger223ae752018-02-19 14:54:08 +0100303 ret = adb(
304 'shell', 'monkey', '-p', 'com.lunarlabs.panda', '-c',
305 'android.intent.category.LAUNCHER', '1', serial=serial)
Franz-Xaver Geigerd1079e22018-02-19 14:34:15 +0100306 if 0 < ret.returncode:
307 print('Could not start the viser app, error was: {}'.format(app,
308 ret.stderr), file=sys.stderr)
309 exit(-1)
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100310
Franz-Xaver Geigerd1079e22018-02-19 14:34:15 +0100311 configure_perms(dut)
Borjan Tchakaloffa4bdae12017-11-21 14:58:12 +0100312
Franz-Xaver Geigerd1079e22018-02-19 14:34:15 +0100313 # TODO DO NOT DO THE FOLLOWING IF NO SIM CARD IS IN THE DUT
314 # time.sleep(10)
315 # configure_sms(dut)
316
317 configure_settings(dut)
318
Borjan Tchakaloff64ec42b2018-02-07 11:33:15 -0800319 # Leave the device alone now
320 force_awake(serial, always=False)
321
Franz-Xaver Geigerd1079e22018-02-19 14:34:15 +0100322
323if __name__ == '__main__':
324 deploy()