appium-android-driver 7.8.3 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/build/lib/commands/app-management.d.ts +129 -5
  3. package/build/lib/commands/app-management.d.ts.map +1 -1
  4. package/build/lib/commands/app-management.js +433 -128
  5. package/build/lib/commands/app-management.js.map +1 -1
  6. package/build/lib/commands/appearance.d.ts +17 -4
  7. package/build/lib/commands/appearance.d.ts.map +1 -1
  8. package/build/lib/commands/appearance.js +32 -33
  9. package/build/lib/commands/appearance.js.map +1 -1
  10. package/build/lib/commands/context/cache.d.ts +19 -0
  11. package/build/lib/commands/context/cache.d.ts.map +1 -0
  12. package/build/lib/commands/context/cache.js +32 -0
  13. package/build/lib/commands/context/cache.js.map +1 -0
  14. package/build/lib/commands/context/exports.d.ts +141 -0
  15. package/build/lib/commands/context/exports.d.ts.map +1 -0
  16. package/build/lib/commands/context/exports.js +351 -0
  17. package/build/lib/commands/context/exports.js.map +1 -0
  18. package/build/lib/commands/context/helpers.d.ts +98 -0
  19. package/build/lib/commands/context/helpers.d.ts.map +1 -0
  20. package/build/lib/commands/context/helpers.js +715 -0
  21. package/build/lib/commands/context/helpers.js.map +1 -0
  22. package/build/lib/commands/device/common.d.ts +23 -0
  23. package/build/lib/commands/device/common.d.ts.map +1 -0
  24. package/build/lib/commands/device/common.js +230 -0
  25. package/build/lib/commands/device/common.js.map +1 -0
  26. package/build/lib/commands/device/emulator-actions.d.ts +114 -0
  27. package/build/lib/commands/device/emulator-actions.d.ts.map +1 -0
  28. package/build/lib/commands/device/emulator-actions.js +197 -0
  29. package/build/lib/commands/device/emulator-actions.js.map +1 -0
  30. package/build/lib/commands/device/emulator-console.d.ts +7 -0
  31. package/build/lib/commands/device/emulator-console.d.ts.map +1 -0
  32. package/build/lib/commands/device/emulator-console.js +24 -0
  33. package/build/lib/commands/device/emulator-console.js.map +1 -0
  34. package/build/lib/commands/device/utils.d.ts +50 -0
  35. package/build/lib/commands/device/utils.d.ts.map +1 -0
  36. package/build/lib/commands/device/utils.js +238 -0
  37. package/build/lib/commands/device/utils.js.map +1 -0
  38. package/build/lib/commands/deviceidle.d.ts +8 -5
  39. package/build/lib/commands/deviceidle.d.ts.map +1 -1
  40. package/build/lib/commands/deviceidle.js +31 -37
  41. package/build/lib/commands/deviceidle.js.map +1 -1
  42. package/build/lib/commands/element.d.ts +99 -5
  43. package/build/lib/commands/element.d.ts.map +1 -1
  44. package/build/lib/commands/element.js +152 -116
  45. package/build/lib/commands/element.js.map +1 -1
  46. package/build/lib/commands/execute.d.ts +12 -4
  47. package/build/lib/commands/execute.d.ts.map +1 -1
  48. package/build/lib/commands/execute.js +83 -78
  49. package/build/lib/commands/execute.js.map +1 -1
  50. package/build/lib/commands/file-actions.d.ts +42 -5
  51. package/build/lib/commands/file-actions.d.ts.map +1 -1
  52. package/build/lib/commands/file-actions.js +230 -194
  53. package/build/lib/commands/file-actions.js.map +1 -1
  54. package/build/lib/commands/find.d.ts +5 -4
  55. package/build/lib/commands/find.d.ts.map +1 -1
  56. package/build/lib/commands/find.js +7 -10
  57. package/build/lib/commands/find.js.map +1 -1
  58. package/build/lib/commands/geolocation.d.ts +45 -0
  59. package/build/lib/commands/geolocation.d.ts.map +1 -0
  60. package/build/lib/commands/geolocation.js +182 -0
  61. package/build/lib/commands/geolocation.js.map +1 -0
  62. package/build/lib/commands/ime.d.ts +25 -5
  63. package/build/lib/commands/ime.d.ts.map +1 -1
  64. package/build/lib/commands/ime.js +59 -42
  65. package/build/lib/commands/ime.js.map +1 -1
  66. package/build/lib/commands/intent.d.ts +56 -5
  67. package/build/lib/commands/intent.d.ts.map +1 -1
  68. package/build/lib/commands/intent.js +135 -83
  69. package/build/lib/commands/intent.js.map +1 -1
  70. package/build/lib/commands/keyboard.d.ts +58 -4
  71. package/build/lib/commands/keyboard.d.ts.map +1 -1
  72. package/build/lib/commands/keyboard.js +119 -17
  73. package/build/lib/commands/keyboard.js.map +1 -1
  74. package/build/lib/commands/lock/exports.d.ts +301 -0
  75. package/build/lib/commands/lock/exports.d.ts.map +1 -0
  76. package/build/lib/commands/lock/exports.js +121 -0
  77. package/build/lib/commands/lock/exports.js.map +1 -0
  78. package/build/lib/commands/lock/helpers.d.ts +349 -0
  79. package/build/lib/commands/lock/helpers.d.ts.map +1 -0
  80. package/build/lib/commands/lock/helpers.js +375 -0
  81. package/build/lib/commands/lock/helpers.js.map +1 -0
  82. package/build/lib/commands/log.d.ts +59 -5
  83. package/build/lib/commands/log.d.ts.map +1 -1
  84. package/build/lib/commands/log.js +150 -140
  85. package/build/lib/commands/log.js.map +1 -1
  86. package/build/lib/commands/media-projection.d.ts +16 -5
  87. package/build/lib/commands/media-projection.d.ts.map +1 -1
  88. package/build/lib/commands/media-projection.js +69 -58
  89. package/build/lib/commands/media-projection.js.map +1 -1
  90. package/build/lib/commands/memory.d.ts +9 -5
  91. package/build/lib/commands/memory.d.ts.map +1 -1
  92. package/build/lib/commands/memory.js +19 -24
  93. package/build/lib/commands/memory.js.map +1 -1
  94. package/build/lib/commands/misc.d.ts +42 -0
  95. package/build/lib/commands/misc.d.ts.map +1 -0
  96. package/build/lib/commands/misc.js +100 -0
  97. package/build/lib/commands/misc.js.map +1 -0
  98. package/build/lib/commands/network.d.ts +61 -5
  99. package/build/lib/commands/network.d.ts.map +1 -1
  100. package/build/lib/commands/network.js +196 -189
  101. package/build/lib/commands/network.js.map +1 -1
  102. package/build/lib/commands/performance.d.ts +67 -27
  103. package/build/lib/commands/performance.d.ts.map +1 -1
  104. package/build/lib/commands/performance.js +105 -80
  105. package/build/lib/commands/performance.js.map +1 -1
  106. package/build/lib/commands/permissions.d.ts +12 -6
  107. package/build/lib/commands/permissions.d.ts.map +1 -1
  108. package/build/lib/commands/permissions.js +65 -62
  109. package/build/lib/commands/permissions.js.map +1 -1
  110. package/build/lib/commands/recordscreen.d.ts +44 -5
  111. package/build/lib/commands/recordscreen.d.ts.map +1 -1
  112. package/build/lib/commands/recordscreen.js +131 -126
  113. package/build/lib/commands/recordscreen.js.map +1 -1
  114. package/build/lib/commands/resources.d.ts +16 -0
  115. package/build/lib/commands/resources.d.ts.map +1 -0
  116. package/build/lib/commands/resources.js +91 -0
  117. package/build/lib/commands/resources.js.map +1 -0
  118. package/build/lib/commands/shell.d.ts +8 -5
  119. package/build/lib/commands/shell.d.ts.map +1 -1
  120. package/build/lib/commands/shell.js +29 -33
  121. package/build/lib/commands/shell.js.map +1 -1
  122. package/build/lib/commands/streamscreen.d.ts +34 -6
  123. package/build/lib/commands/streamscreen.d.ts.map +1 -1
  124. package/build/lib/commands/streamscreen.js +166 -162
  125. package/build/lib/commands/streamscreen.js.map +1 -1
  126. package/build/lib/commands/system-bars.d.ts +18 -13
  127. package/build/lib/commands/system-bars.d.ts.map +1 -1
  128. package/build/lib/commands/system-bars.js +68 -64
  129. package/build/lib/commands/system-bars.js.map +1 -1
  130. package/build/lib/commands/time.d.ts +14 -0
  131. package/build/lib/commands/time.d.ts.map +1 -0
  132. package/build/lib/commands/time.js +39 -0
  133. package/build/lib/commands/time.js.map +1 -0
  134. package/build/lib/commands/touch.d.ts +99 -6
  135. package/build/lib/commands/touch.d.ts.map +1 -1
  136. package/build/lib/commands/touch.js +399 -280
  137. package/build/lib/commands/touch.js.map +1 -1
  138. package/build/lib/commands/types.d.ts +110 -2
  139. package/build/lib/commands/types.d.ts.map +1 -1
  140. package/build/lib/doctor/checks.d.ts.map +1 -1
  141. package/build/lib/doctor/checks.js +4 -4
  142. package/build/lib/doctor/checks.js.map +1 -1
  143. package/build/lib/driver.d.ts +224 -27
  144. package/build/lib/driver.d.ts.map +1 -1
  145. package/build/lib/driver.js +232 -7
  146. package/build/lib/driver.js.map +1 -1
  147. package/build/lib/index.d.ts +1 -4
  148. package/build/lib/index.d.ts.map +1 -1
  149. package/build/lib/index.js +1 -13
  150. package/build/lib/index.js.map +1 -1
  151. package/build/lib/logger.js.map +1 -1
  152. package/build/lib/method-map.d.ts +0 -23
  153. package/build/lib/method-map.d.ts.map +1 -1
  154. package/build/lib/method-map.js +0 -11
  155. package/build/lib/method-map.js.map +1 -1
  156. package/build/lib/utils.d.ts +12 -0
  157. package/build/lib/utils.d.ts.map +1 -1
  158. package/build/lib/utils.js +38 -2
  159. package/build/lib/utils.js.map +1 -1
  160. package/lib/commands/app-management.js +470 -145
  161. package/lib/commands/appearance.js +29 -36
  162. package/lib/commands/context/cache.js +29 -0
  163. package/lib/commands/context/exports.js +379 -0
  164. package/lib/commands/context/helpers.js +802 -0
  165. package/lib/commands/device/common.js +264 -0
  166. package/lib/commands/device/emulator-actions.js +194 -0
  167. package/lib/commands/device/emulator-console.js +24 -0
  168. package/lib/commands/device/utils.js +285 -0
  169. package/lib/commands/deviceidle.js +31 -44
  170. package/lib/commands/element.js +149 -142
  171. package/lib/commands/execute.js +86 -87
  172. package/lib/commands/file-actions.js +249 -222
  173. package/lib/commands/find.ts +13 -19
  174. package/lib/commands/geolocation.js +179 -0
  175. package/lib/commands/ime.js +53 -45
  176. package/lib/commands/intent.js +149 -91
  177. package/lib/commands/keyboard.js +114 -17
  178. package/lib/commands/lock/exports.js +139 -0
  179. package/lib/commands/lock/helpers.js +379 -0
  180. package/lib/commands/log.js +170 -166
  181. package/lib/commands/media-projection.js +75 -70
  182. package/lib/commands/memory.js +17 -29
  183. package/lib/commands/misc.js +94 -0
  184. package/lib/commands/network.js +209 -223
  185. package/lib/commands/performance.js +88 -73
  186. package/lib/commands/permissions.js +83 -84
  187. package/lib/commands/recordscreen.js +171 -170
  188. package/lib/commands/resources.js +96 -0
  189. package/lib/commands/shell.js +28 -42
  190. package/lib/commands/streamscreen.js +207 -206
  191. package/lib/commands/system-bars.js +76 -77
  192. package/lib/commands/time.js +36 -0
  193. package/lib/commands/touch.js +442 -346
  194. package/lib/commands/types.ts +123 -2
  195. package/lib/doctor/checks.js +24 -16
  196. package/lib/driver.ts +454 -12
  197. package/lib/index.ts +1 -13
  198. package/lib/logger.js +1 -1
  199. package/lib/method-map.js +0 -11
  200. package/lib/utils.js +40 -3
  201. package/package.json +1 -1
  202. package/build/lib/commands/actions.d.ts +0 -8
  203. package/build/lib/commands/actions.d.ts.map +0 -1
  204. package/build/lib/commands/actions.js +0 -207
  205. package/build/lib/commands/actions.js.map +0 -1
  206. package/build/lib/commands/alert.d.ts +0 -8
  207. package/build/lib/commands/alert.d.ts.map +0 -1
  208. package/build/lib/commands/alert.js +0 -29
  209. package/build/lib/commands/alert.js.map +0 -1
  210. package/build/lib/commands/context.d.ts +0 -10
  211. package/build/lib/commands/context.d.ts.map +0 -1
  212. package/build/lib/commands/context.js +0 -431
  213. package/build/lib/commands/context.js.map +0 -1
  214. package/build/lib/commands/emu-console.d.ts +0 -7
  215. package/build/lib/commands/emu-console.d.ts.map +0 -1
  216. package/build/lib/commands/emu-console.js +0 -27
  217. package/build/lib/commands/emu-console.js.map +0 -1
  218. package/build/lib/commands/general.d.ts +0 -9
  219. package/build/lib/commands/general.d.ts.map +0 -1
  220. package/build/lib/commands/general.js +0 -293
  221. package/build/lib/commands/general.js.map +0 -1
  222. package/build/lib/commands/index.d.ts +0 -28
  223. package/build/lib/commands/index.d.ts.map +0 -1
  224. package/build/lib/commands/index.js +0 -57
  225. package/build/lib/commands/index.js.map +0 -1
  226. package/build/lib/commands/mixins.d.ts +0 -747
  227. package/build/lib/commands/mixins.d.ts.map +0 -1
  228. package/build/lib/commands/mixins.js +0 -19
  229. package/build/lib/commands/mixins.js.map +0 -1
  230. package/build/lib/helpers/android.d.ts +0 -163
  231. package/build/lib/helpers/android.d.ts.map +0 -1
  232. package/build/lib/helpers/android.js +0 -818
  233. package/build/lib/helpers/android.js.map +0 -1
  234. package/build/lib/helpers/index.d.ts +0 -7
  235. package/build/lib/helpers/index.d.ts.map +0 -1
  236. package/build/lib/helpers/index.js +0 -29
  237. package/build/lib/helpers/index.js.map +0 -1
  238. package/build/lib/helpers/types.d.ts +0 -122
  239. package/build/lib/helpers/types.d.ts.map +0 -1
  240. package/build/lib/helpers/types.js +0 -3
  241. package/build/lib/helpers/types.js.map +0 -1
  242. package/build/lib/helpers/unlock.d.ts +0 -32
  243. package/build/lib/helpers/unlock.d.ts.map +0 -1
  244. package/build/lib/helpers/unlock.js +0 -273
  245. package/build/lib/helpers/unlock.js.map +0 -1
  246. package/build/lib/helpers/webview.d.ts +0 -74
  247. package/build/lib/helpers/webview.d.ts.map +0 -1
  248. package/build/lib/helpers/webview.js +0 -448
  249. package/build/lib/helpers/webview.js.map +0 -1
  250. package/lib/commands/actions.js +0 -244
  251. package/lib/commands/alert.js +0 -34
  252. package/lib/commands/context.js +0 -507
  253. package/lib/commands/emu-console.js +0 -31
  254. package/lib/commands/general.js +0 -343
  255. package/lib/commands/index.ts +0 -54
  256. package/lib/commands/mixins.ts +0 -976
  257. package/lib/helpers/android.ts +0 -1153
  258. package/lib/helpers/index.ts +0 -6
  259. package/lib/helpers/types.ts +0 -136
  260. package/lib/helpers/unlock.ts +0 -329
  261. package/lib/helpers/webview.ts +0 -610
@@ -1,1153 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
- import {fs, tempDir, util} from '@appium/support';
3
- import type {AppiumServer, StringRecord} from '@appium/types';
4
- import {ADB} from 'appium-adb';
5
- import {retryInterval, waitForCondition} from 'asyncbox';
6
- import B from 'bluebird';
7
- import {
8
- path as SETTINGS_APK_PATH,
9
- SettingsApp,
10
- SETTINGS_HELPER_ID,
11
- UNICODE_IME,
12
- EMPTY_IME,
13
- } from 'io.appium.settings';
14
- import _ from 'lodash';
15
- import {EOL} from 'node:os';
16
- import path from 'node:path';
17
- import semver, {type SemVer} from 'semver';
18
- import type {SetRequired, ValueOf} from 'type-fest';
19
- import type {UnlockType} from '../commands/types';
20
- import type {AndroidDriver, AndroidDriverCaps, AndroidDriverOpts} from '../driver';
21
- import logger from '../logger';
22
- import type {ADBDeviceInfo, ADBLaunchInfo} from './types';
23
- import Unlocker, {
24
- FINGERPRINT_UNLOCK,
25
- PASSWORD_UNLOCK,
26
- PATTERN_UNLOCK,
27
- PIN_UNLOCK,
28
- PIN_UNLOCK_KEY_EVENT,
29
- } from './unlock';
30
-
31
- const MOCK_APP_IDS_STORE = '/data/local/tmp/mock_apps.json';
32
- const PACKAGE_INSTALL_TIMEOUT_MS = 90000;
33
- const HELPER_APP_INSTALL_RETRIES = 3;
34
- const HELPER_APP_INSTALL_RETRY_DELAY_MS = 5000;
35
- // https://cs.chromium.org/chromium/src/chrome/browser/devtools/device/android_device_info_query.cc
36
- const CHROME_BROWSER_PACKAGE_ACTIVITY = {
37
- chrome: {
38
- pkg: 'com.android.chrome',
39
- activity: 'com.google.android.apps.chrome.Main',
40
- },
41
- chromium: {
42
- pkg: 'org.chromium.chrome.shell',
43
- activity: '.ChromeShellActivity',
44
- },
45
- chromebeta: {
46
- pkg: 'com.chrome.beta',
47
- activity: 'com.google.android.apps.chrome.Main',
48
- },
49
- browser: {
50
- pkg: 'com.android.browser',
51
- activity: 'com.android.browser.BrowserActivity',
52
- },
53
- 'chromium-browser': {
54
- pkg: 'org.chromium.chrome',
55
- activity: 'com.google.android.apps.chrome.Main',
56
- },
57
- 'chromium-webview': {
58
- pkg: 'org.chromium.webview_shell',
59
- activity: 'org.chromium.webview_shell.WebViewBrowserActivity',
60
- },
61
- default: {
62
- pkg: 'com.android.chrome',
63
- activity: 'com.google.android.apps.chrome.Main',
64
- },
65
- } as const;
66
- const EMULATOR_PATTERN = /\bemulator\b/i;
67
- // These constants are in sync with
68
- // https://developer.apple.com/documentation/xctest/xcuiapplicationstate/xcuiapplicationstaterunningbackground?language=objc
69
- const APP_STATE = {
70
- NOT_INSTALLED: 0,
71
- NOT_RUNNING: 1,
72
- RUNNING_IN_BACKGROUND: 3,
73
- RUNNING_IN_FOREGROUND: 4,
74
- } as const;
75
-
76
- function ensureNetworkSpeed(adb: ADB, networkSpeed: string) {
77
- if (networkSpeed.toUpperCase() in adb.NETWORK_SPEED) {
78
- return networkSpeed;
79
- }
80
- logger.warn(
81
- `Wrong network speed param '${networkSpeed}', using default: ${adb.NETWORK_SPEED.FULL}. ` +
82
- `Supported values: ${_.values(adb.NETWORK_SPEED)}`
83
- );
84
- return adb.NETWORK_SPEED.FULL;
85
- }
86
-
87
- function prepareAvdArgs(adb: ADB, opts: AndroidDriverOpts): string[] {
88
- const {networkSpeed, isHeadless, avdArgs} = opts;
89
- const result: string[] = [];
90
- if (avdArgs) {
91
- if (_.isArray(avdArgs)) {
92
- result.push(...avdArgs);
93
- } else {
94
- result.push(...(util.shellParse(`${avdArgs}`) as string[]));
95
- }
96
- }
97
- if (networkSpeed) {
98
- result.push('-netspeed', ensureNetworkSpeed(adb, networkSpeed));
99
- }
100
- if (isHeadless) {
101
- result.push('-no-window');
102
- }
103
- return result;
104
- }
105
-
106
- function toCredentialType(unlockType: UnlockType) {
107
- const result = {
108
- [PIN_UNLOCK]: 'pin',
109
- [PIN_UNLOCK_KEY_EVENT]: 'pin',
110
- [PASSWORD_UNLOCK]: 'password',
111
- [PATTERN_UNLOCK]: 'pattern',
112
- }[unlockType];
113
- if (result) {
114
- return result;
115
- }
116
- throw new Error(`Unlock type '${unlockType}' is not known`);
117
- }
118
-
119
- interface AndroidHelpers {
120
- createBaseADB(opts?: AndroidDriverOpts): Promise<ADB>;
121
-
122
- prepareEmulator(adb: ADB, opts?: any): Promise<void>;
123
-
124
- /**
125
- * Set and ensure the locale name of the device under test.
126
- *
127
- * @param adb - The adb module instance.
128
- * @param language - Language. The language field is case insensitive, but Locale always canonicalizes to lower case.
129
- * format: [a-zA-Z]{2,8}. e.g. en, ja : https://developer.android.com/reference/java/util/Locale.html
130
- * @param country - Country. The country (region) field is case insensitive, but Locale always canonicalizes to upper case.
131
- * format: [a-zA-Z]{2} | [0-9]{3}. e.g. US, JP : https://developer.android.com/reference/java/util/Locale.html
132
- * @param script - Script. The script field is case insensitive but Locale always canonicalizes to title case.
133
- * format: [a-zA-Z]{4}. e.g. Hans in zh-Hans-CN : https://developer.android.com/reference/java/util/Locale.html
134
- * @throws {Error} If it failed to set locale properly
135
- */
136
- ensureDeviceLocale(adb: ADB, language?: string, country?: string, script?: string): Promise<void>;
137
-
138
- getDeviceInfoFromCaps<Opts extends AndroidDriverOpts>(opts?: Opts): Promise<ADBDeviceInfo>;
139
-
140
- createADB<Opts extends AndroidDriverOpts>(opts?: Opts): Promise<ADB>;
141
-
142
- validatePackageActivityNames<Opts extends AndroidDriverOpts>(opts: Opts): void;
143
- getLaunchInfo<Opts extends AndroidDriverOpts>(
144
- adb: ADB,
145
- opts: Opts
146
- ): Promise<ADBLaunchInfo | undefined>;
147
- resetApp<Opts extends AndroidDriverOpts>(
148
- adb: ADB,
149
- opts: SetRequired<Opts, 'appPackage' | 'app'>
150
- ): Promise<void>;
151
- installApk<Opts extends AndroidDriverOpts>(
152
- adb: ADB,
153
- opts: SetRequired<Opts, 'appPackage' | 'app'>
154
- ): Promise<void>;
155
-
156
- /**
157
- * Installs an array of apks
158
- * @param adb Instance of Appium ADB object
159
- * @param opts Opts defined in driver.js
160
- */
161
- installOtherApks<Opts extends AndroidDriverOpts>(
162
- apks: string[],
163
- adb: ADB,
164
- opts: SetRequired<Opts, 'appPackage' | 'app'>
165
- ): Promise<void>;
166
-
167
- /**
168
- * Uninstall an array of packages
169
- * @param adb Instance of Appium ADB object
170
- * @param appPackages An array of package names to uninstall. If this includes `'*'`, uninstall all of 3rd party apps
171
- * @param filterPackages An array of packages does not uninstall when `*` is provided as `appPackages`
172
- */
173
- uninstallOtherPackages(adb: ADB, appPackages: string[], filterPackages?: string[]): Promise<void>;
174
-
175
- /**
176
- * Get third party packages filtered with `filterPackages`
177
- * @param adb Instance of Appium ADB object
178
- * @param filterPackages An array of packages does not uninstall when `*` is provided as `appPackages`
179
- * @returns An array of installed third pary packages
180
- */
181
- getThirdPartyPackages(adb: ADB, filterPackages?: string[]): Promise<string[]>;
182
- /**
183
- * @deprecated Use hideKeyboard instead
184
- */
185
- initUnicodeKeyboard(adb: ADB): Promise<any>;
186
- hideKeyboard(adb: ADB): Promise<void>;
187
- setMockLocationApp(adb: ADB, app: string): Promise<void>;
188
- resetMockLocation(adb: ADB): Promise<void>;
189
- installHelperApp(adb: ADB, apkPath: string, packageId: string): Promise<void>;
190
-
191
- /**
192
- * Pushes and installs io.appium.settings app.
193
- * Throws an error if the setting app is required
194
- *
195
- * @param adb - The adb module instance.
196
- * @param throwError - Whether throw an error if Settings app fails to start
197
- * @param opts - Driver options dictionary.
198
- * @throws If throwError is true and something happens in installation step
199
- */
200
- pushSettingsApp(adb: ADB, throwError: boolean, opts: AndroidDriverOpts): Promise<void>;
201
-
202
- /**
203
- * Extracts string.xml and converts it to string.json and pushes
204
- * it to /data/local/tmp/string.json on for use of bootstrap
205
- * If app is not present to extract string.xml it deletes remote strings.json
206
- * If app does not have strings.xml we push an empty json object to remote
207
- *
208
- * @param language - Language abbreviation, for example 'fr'. The default language
209
- * is used if this argument is not defined.
210
- * @param adb - The adb module instance.
211
- * @param opts - Driver options dictionary.
212
- * @returns The dictionary, where string resource identifiers are keys
213
- * along with their corresponding values for the given language or an empty object
214
- * if no matching resources were extracted.
215
- */
216
- pushStrings(
217
- language: string | undefined,
218
- adb: ADB,
219
- opts: AndroidDriverOpts
220
- ): Promise<StringRecord>;
221
- unlock<D extends AndroidDriver, Caps extends AndroidDriverCaps>(
222
- driver: D,
223
- adb: ADB,
224
- capabilities: Caps
225
- ): Promise<void>;
226
- verifyUnlock(adb: ADB, timeoutMs?: number | null): Promise<void>;
227
- initDevice(adb: ADB, opts: AndroidDriverOpts): Promise<string | void>;
228
- removeNullProperties(obj: any): void;
229
- truncateDecimals(number: number, digits: number): number;
230
- isChromeBrowser(browser?: string): boolean;
231
- getChromePkg(browser: string): ValueOf<typeof CHROME_BROWSER_PACKAGE_ACTIVITY>;
232
- removeAllSessionWebSocketHandlers(
233
- server?: AppiumServer,
234
- sessionId?: string | null
235
- ): Promise<void>;
236
- parseArray(cap: string | string[]): string[];
237
-
238
- /**
239
- * Validate desired capabilities. Returns true if capabilities are valid
240
- *
241
- * @param caps Capabilities
242
- * @return Returns true if the capabilites are valid
243
- * @throws {Error} If the caps has invalid capability
244
- */
245
- validateDesiredCaps(caps: AndroidDriverCaps): boolean;
246
-
247
- /**
248
- * Adjust the capabilities for a browser session
249
- *
250
- * @param caps - Current capabilities object
251
- * !!! The object is mutated by this method call !!!
252
- * @returns The same possibly mutated `opts` instance.
253
- * No mutation is happening is the current session if
254
- * appPackage/appActivity caps have already been provided.
255
- * @privateRemarks In practice, this fn is only ever provided a `AndroidDriverOpts` object
256
- */
257
- adjustBrowserSessionCaps(caps: AndroidDriverCaps): AndroidDriverCaps;
258
-
259
- /**
260
- * Checks whether the current device under test is an emulator
261
- *
262
- * @param adb - appium-adb instance
263
- * @param opts - driver options mapping
264
- * @returns `true` if the device is an Android emulator
265
- */
266
- isEmulator(adb?: ADB, opts?: AndroidDriverOpts): boolean;
267
- unlocker: typeof Unlocker;
268
- }
269
-
270
- const AndroidHelpers: AndroidHelpers = {
271
- async createBaseADB(opts) {
272
- // filter out any unwanted options sent in
273
- // this list should be updated as ADB takes more arguments
274
- const {
275
- adbPort,
276
- suppressKillServer,
277
- remoteAdbHost,
278
- clearDeviceLogsOnStart,
279
- adbExecTimeout,
280
- useKeystore,
281
- keystorePath,
282
- keystorePassword,
283
- keyAlias,
284
- keyPassword,
285
- remoteAppsCacheLimit,
286
- buildToolsVersion,
287
- allowOfflineDevices,
288
- allowDelayAdb,
289
- } = opts ?? {};
290
- return await ADB.createADB({
291
- adbPort,
292
- suppressKillServer,
293
- remoteAdbHost,
294
- clearDeviceLogsOnStart,
295
- adbExecTimeout,
296
- useKeystore,
297
- keystorePath,
298
- keystorePassword,
299
- keyAlias,
300
- keyPassword,
301
- remoteAppsCacheLimit,
302
- buildToolsVersion,
303
- allowOfflineDevices,
304
- allowDelayAdb,
305
- });
306
- },
307
-
308
- async prepareEmulator(adb, opts) {
309
- const {
310
- avd,
311
- avdEnv: env,
312
- language,
313
- locale: country,
314
- avdLaunchTimeout: launchTimeout,
315
- avdReadyTimeout: readyTimeout,
316
- } = opts;
317
- if (!avd) {
318
- throw new Error('Cannot launch AVD without AVD name');
319
- }
320
-
321
- const avdName = avd.replace('@', '');
322
- let isEmulatorRunning = true;
323
- try {
324
- await adb.getRunningAVDWithRetry(avdName, 5000);
325
- } catch (e) {
326
- logger.debug(`Emulator '${avdName}' is not running: ${(e as Error).message}`);
327
- isEmulatorRunning = false;
328
- }
329
- const args = prepareAvdArgs(adb, opts);
330
- if (isEmulatorRunning) {
331
- if (args.includes('-wipe-data')) {
332
- logger.debug(`Killing '${avdName}' because it needs to be wiped at start.`);
333
- await adb.killEmulator(avdName);
334
- } else {
335
- logger.debug('Not launching AVD because it is already running.');
336
- return;
337
- }
338
- }
339
- await adb.launchAVD(avd, {
340
- args,
341
- env,
342
- language,
343
- country,
344
- launchTimeout,
345
- readyTimeout,
346
- });
347
- },
348
-
349
- async ensureDeviceLocale(adb, language, country, script) {
350
- const settingsApp = new SettingsApp({adb});
351
- await settingsApp.setDeviceLocale(language!, country!, script);
352
-
353
- if (!(await adb.ensureCurrentLocale(language, country, script))) {
354
- const message = script
355
- ? `language: ${language}, country: ${country} and script: ${script}`
356
- : `language: ${language} and country: ${country}`;
357
- throw new Error(`Failed to set ${message}`);
358
- }
359
- },
360
-
361
- async getDeviceInfoFromCaps(opts) {
362
- // we can create a throwaway ADB instance here, so there is no dependency
363
- // on instantiating on earlier (at this point, we have no udid)
364
- // we can only use this ADB object for commands that would not be confused
365
- // if multiple devices are connected
366
- const adb = await AndroidHelpers.createBaseADB(opts);
367
- let udid: string | undefined = opts?.udid;
368
- let emPort: number | false | undefined;
369
-
370
- // a specific avd name was given. try to initialize with that
371
- if (opts?.avd) {
372
- await AndroidHelpers.prepareEmulator(adb, opts);
373
- udid = adb.curDeviceId;
374
- emPort = adb.emulatorPort;
375
- } else {
376
- // no avd given. lets try whatever's plugged in devices/emulators
377
- logger.info('Retrieving device list');
378
- const devices = await adb.getDevicesWithRetry();
379
-
380
- // udid was given, lets try to init with that device
381
- if (udid) {
382
- if (!_.includes(_.map(devices, 'udid'), udid)) {
383
- logger.errorAndThrow(`Device ${udid} was not in the list of connected devices`);
384
- }
385
- emPort = adb.getPortFromEmulatorString(udid);
386
- } else if (opts?.platformVersion) {
387
- opts.platformVersion = `${opts.platformVersion}`.trim();
388
-
389
- // a platform version was given. lets try to find a device with the same os
390
- const platformVersion = semver.coerce(opts.platformVersion) || opts.platformVersion;
391
- logger.info(`Looking for a device with Android '${platformVersion}'`);
392
-
393
- // in case we fail to find something, give the user a useful log that has
394
- // the device udids and os versions so they know what's available
395
- const availDevices: string[] = [];
396
- let partialMatchCandidate: StringRecord<string> | undefined;
397
- // first try started devices/emulators
398
- for (const device of devices) {
399
- // direct adb calls to the specific device
400
- adb.setDeviceId(device.udid);
401
- const rawDeviceOS = await adb.getPlatformVersion();
402
- // The device OS could either be a number, like `6.0`
403
- // or an abbreviation, like `R`
404
- availDevices.push(`${device.udid} (${rawDeviceOS})`);
405
- const deviceOS = semver.coerce(rawDeviceOS) || rawDeviceOS;
406
- if (!deviceOS) {
407
- continue;
408
- }
409
-
410
- const semverPV = platformVersion as SemVer;
411
- const semverDO = deviceOS as SemVer;
412
-
413
- const bothVersionsCanBeCoerced = semver.valid(deviceOS) && semver.valid(platformVersion);
414
- const bothVersionsAreStrings = _.isString(deviceOS) && _.isString(platformVersion);
415
- if (
416
- (bothVersionsCanBeCoerced && semverDO.version === semverPV.version) ||
417
- (bothVersionsAreStrings && _.toLower(deviceOS) === _.toLower(platformVersion))
418
- ) {
419
- // Got an exact match - proceed immediately
420
- udid = device.udid;
421
- break;
422
- } else if (!bothVersionsCanBeCoerced) {
423
- // There is no point to check for partial match if either of version numbers is not coercible
424
- continue;
425
- }
426
-
427
- if (
428
- ((!_.includes(opts.platformVersion, '.') && semverPV.major === semverDO.major) ||
429
- (semverPV.major === semverDO.major && semverPV.minor === semverDO.minor)) &&
430
- // Got a partial match - make sure we consider the most recent
431
- // device version available on the host system
432
- ((partialMatchCandidate && semver.gt(deviceOS, _.values(partialMatchCandidate)[0])) ||
433
- !partialMatchCandidate)
434
- ) {
435
- partialMatchCandidate = {[device.udid]: deviceOS as string};
436
- }
437
- }
438
- if (!udid && partialMatchCandidate) {
439
- udid = _.keys(partialMatchCandidate)[0];
440
- adb.setDeviceId(udid);
441
- }
442
-
443
- if (!udid) {
444
- // we couldn't find anything! quit
445
- logger.errorAndThrow(
446
- `Unable to find an active device or emulator ` +
447
- `with OS ${opts.platformVersion}. The following are available: ` +
448
- availDevices.join(', ')
449
- );
450
- throw new Error(); // unreachable; for TS
451
- }
452
-
453
- emPort = adb.getPortFromEmulatorString(udid);
454
- } else {
455
- // a udid was not given, grab the first device we see
456
- udid = devices[0].udid;
457
- emPort = adb.getPortFromEmulatorString(udid);
458
- }
459
- }
460
-
461
- logger.info(`Using device: ${udid}`);
462
- return {udid: udid as string, emPort: emPort as number | false};
463
- },
464
-
465
- async createADB(opts) {
466
- // @ts-expect-error do not put arbitrary properties on opts
467
- const {udid, emPort} = opts ?? {};
468
- const adb = await AndroidHelpers.createBaseADB(opts);
469
- adb.setDeviceId(udid ?? '');
470
- if (emPort) {
471
- adb.setEmulatorPort(emPort);
472
- }
473
-
474
- return adb;
475
- },
476
-
477
- validatePackageActivityNames(opts) {
478
- for (const key of ['appPackage', 'appActivity', 'appWaitPackage', 'appWaitActivity']) {
479
- const name = opts[key as keyof typeof opts];
480
- if (!name) {
481
- continue;
482
- }
483
-
484
- const match = /([^\w.*,])+/.exec(String(name));
485
- if (!match) {
486
- continue;
487
- }
488
-
489
- logger.warn(
490
- `Capability '${key}' is expected to only include latin letters, digits, underscore, dot, comma and asterisk characters.`
491
- );
492
- logger.warn(
493
- `Current value '${name}' has non-matching character at index ${match.index}: '${String(
494
- name
495
- ).substring(0, match.index + 1)}'`
496
- );
497
- }
498
- },
499
-
500
- async getLaunchInfo(adb, opts) {
501
- if (!opts.app) {
502
- logger.warn('No app sent in, not parsing package/activity');
503
- return;
504
- }
505
- let {appPackage, appActivity, appWaitPackage, appWaitActivity} = opts;
506
- const {app} = opts;
507
-
508
- AndroidHelpers.validatePackageActivityNames(opts);
509
-
510
- if (appPackage && appActivity) {
511
- return;
512
- }
513
-
514
- logger.debug('Parsing package and activity from app manifest');
515
- const {apkPackage, apkActivity} = await adb.packageAndLaunchActivityFromManifest(app);
516
- if (apkPackage && !appPackage) {
517
- appPackage = apkPackage;
518
- }
519
- if (!appWaitPackage) {
520
- appWaitPackage = appPackage;
521
- }
522
- if (apkActivity && !appActivity) {
523
- appActivity = apkActivity;
524
- }
525
- if (!appWaitActivity) {
526
- appWaitActivity = appActivity;
527
- }
528
- logger.debug(`Parsed package and activity are: ${apkPackage}/${apkActivity}`);
529
- return {appPackage, appWaitPackage, appActivity, appWaitActivity};
530
- },
531
-
532
- async resetApp(adb, opts) {
533
- const {
534
- app,
535
- appPackage,
536
- fastReset,
537
- fullReset,
538
- androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS,
539
- autoGrantPermissions,
540
- allowTestPackages,
541
- } = opts ?? {};
542
-
543
- if (!appPackage) {
544
- throw new Error("'appPackage' option is required");
545
- }
546
-
547
- const isInstalled = await adb.isAppInstalled(appPackage);
548
-
549
- if (isInstalled) {
550
- try {
551
- await adb.forceStop(appPackage);
552
- } catch (ign) {}
553
- // fullReset has priority over fastReset
554
- if (!fullReset && fastReset) {
555
- const output = await adb.clear(appPackage);
556
- if (_.isString(output) && output.toLowerCase().includes('failed')) {
557
- throw new Error(
558
- `Cannot clear the application data of '${appPackage}'. Original error: ${output}`
559
- );
560
- }
561
- // executing `shell pm clear` resets previously assigned application permissions as well
562
- if (autoGrantPermissions) {
563
- try {
564
- await adb.grantAllPermissions(appPackage);
565
- } catch (error) {
566
- logger.error(
567
- `Unable to grant permissions requested. Original error: ${(error as Error).message}`
568
- );
569
- }
570
- }
571
- logger.debug(
572
- `Performed fast reset on the installed '${appPackage}' application (stop and clear)`
573
- );
574
- return;
575
- }
576
- }
577
-
578
- if (!app) {
579
- throw new Error(
580
- `Either provide 'app' option to install '${appPackage}' or ` +
581
- `consider setting 'noReset' to 'true' if '${appPackage}' is supposed to be preinstalled.`
582
- );
583
- }
584
-
585
- logger.debug(`Running full reset on '${appPackage}' (reinstall)`);
586
- if (isInstalled) {
587
- await adb.uninstallApk(appPackage);
588
- }
589
- await adb.install(app, {
590
- grantPermissions: autoGrantPermissions,
591
- timeout: androidInstallTimeout,
592
- allowTestPackages,
593
- });
594
- },
595
-
596
- async installApk(adb, opts) {
597
- const {
598
- app,
599
- appPackage,
600
- fastReset,
601
- fullReset,
602
- androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS,
603
- autoGrantPermissions,
604
- allowTestPackages,
605
- enforceAppInstall,
606
- } = opts ?? {};
607
-
608
- if (!app || !appPackage) {
609
- throw new Error("'app' and 'appPackage' options are required");
610
- }
611
-
612
- if (fullReset) {
613
- await AndroidHelpers.resetApp(adb, opts);
614
- return;
615
- }
616
-
617
- const {appState, wasUninstalled} = await adb.installOrUpgrade(app, appPackage, {
618
- grantPermissions: autoGrantPermissions,
619
- timeout: androidInstallTimeout,
620
- allowTestPackages,
621
- enforceCurrentBuild: enforceAppInstall,
622
- });
623
-
624
- // There is no need to reset the newly installed app
625
- const isInstalledOverExistingApp =
626
- !wasUninstalled && appState !== adb.APP_INSTALL_STATE.NOT_INSTALLED;
627
- if (fastReset && isInstalledOverExistingApp) {
628
- logger.info(`Performing fast reset on '${appPackage}'`);
629
- await AndroidHelpers.resetApp(adb, opts);
630
- }
631
- },
632
-
633
- async installOtherApks(otherApps, adb, opts) {
634
- const {
635
- androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS,
636
- autoGrantPermissions,
637
- allowTestPackages,
638
- } = opts;
639
-
640
- // Install all of the APK's asynchronously
641
- await B.all(
642
- otherApps.map((otherApp) => {
643
- logger.debug(`Installing app: ${otherApp}`);
644
- return adb.installOrUpgrade(otherApp, undefined, {
645
- grantPermissions: autoGrantPermissions,
646
- timeout: androidInstallTimeout,
647
- allowTestPackages,
648
- });
649
- })
650
- );
651
- },
652
-
653
- async uninstallOtherPackages(adb, appPackages, filterPackages = []) {
654
- if (appPackages.includes('*')) {
655
- logger.debug('Uninstall third party packages');
656
- appPackages = await AndroidHelpers.getThirdPartyPackages(adb, filterPackages);
657
- }
658
-
659
- logger.debug(`Uninstalling packages: ${appPackages}`);
660
- await B.all(appPackages.map((appPackage) => adb.uninstallApk(appPackage)));
661
- },
662
-
663
- async getThirdPartyPackages(adb, filterPackages = []) {
664
- try {
665
- const packagesString = await adb.shell(['pm', 'list', 'packages', '-3']);
666
- const appPackagesArray = packagesString
667
- .trim()
668
- .replace(/package:/g, '')
669
- .split(EOL);
670
- logger.debug(`'${appPackagesArray}' filtered with '${filterPackages}'`);
671
- return _.difference(appPackagesArray, filterPackages);
672
- } catch (err) {
673
- logger.warn(
674
- `Unable to get packages with 'adb shell pm list packages -3': ${(err as Error).message}`
675
- );
676
- return [];
677
- }
678
- },
679
-
680
- async initUnicodeKeyboard(adb) {
681
- logger.debug('Enabling Unicode keyboard support');
682
-
683
- // get the default IME so we can return back to it later if we want
684
- const defaultIME = await adb.defaultIME();
685
-
686
- logger.debug(`Unsetting previous IME ${defaultIME}`);
687
- logger.debug(`Setting IME to '${UNICODE_IME}'`);
688
- await adb.enableIME(UNICODE_IME);
689
- await adb.setIME(UNICODE_IME);
690
- return defaultIME;
691
- },
692
-
693
- async hideKeyboard(adb) {
694
- logger.debug(`Hiding the on-screen keyboard by setting IME to '${EMPTY_IME}'`);
695
- await adb.enableIME(EMPTY_IME);
696
- await adb.setIME(EMPTY_IME);
697
- },
698
-
699
- async setMockLocationApp(adb, app) {
700
- try {
701
- if ((await adb.getApiLevel()) < 23) {
702
- await adb.shell(['settings', 'put', 'secure', 'mock_location', '1']);
703
- } else {
704
- await adb.shell(['appops', 'set', app, 'android:mock_location', 'allow']);
705
- }
706
- } catch (err) {
707
- logger.warn(`Unable to set mock location for app '${app}': ${(err as Error).message}`);
708
- return;
709
- }
710
- try {
711
- let pkgIds: string[] = [];
712
- if (await adb.fileExists(MOCK_APP_IDS_STORE)) {
713
- try {
714
- pkgIds = JSON.parse(await adb.shell(['cat', MOCK_APP_IDS_STORE]));
715
- } catch (ign) {}
716
- }
717
- if (pkgIds.includes(app)) {
718
- return;
719
- }
720
- pkgIds.push(app);
721
- const tmpRoot = await tempDir.openDir();
722
- const srcPath = path.posix.join(tmpRoot, path.posix.basename(MOCK_APP_IDS_STORE));
723
- try {
724
- await fs.writeFile(srcPath, JSON.stringify(pkgIds), 'utf8');
725
- await adb.push(srcPath, MOCK_APP_IDS_STORE);
726
- } finally {
727
- await fs.rimraf(tmpRoot);
728
- }
729
- } catch (e) {
730
- logger.warn(`Unable to persist mock location app id '${app}': ${(e as Error).message}`);
731
- }
732
- },
733
-
734
- async resetMockLocation(adb) {
735
- try {
736
- if ((await adb.getApiLevel()) < 23) {
737
- await adb.shell(['settings', 'put', 'secure', 'mock_location', '0']);
738
- return;
739
- }
740
-
741
- const thirdPartyPkgIdsPromise = AndroidHelpers.getThirdPartyPackages(adb);
742
- let pkgIds = [];
743
- if (await adb.fileExists(MOCK_APP_IDS_STORE)) {
744
- try {
745
- pkgIds = JSON.parse(await adb.shell(['cat', MOCK_APP_IDS_STORE]));
746
- } catch (ign) {}
747
- }
748
- const thirdPartyPkgIds = await thirdPartyPkgIdsPromise;
749
- // Only include currently installed packages
750
- const resultPkgs = _.intersection(pkgIds, thirdPartyPkgIds);
751
- if (_.size(resultPkgs) <= 1) {
752
- await adb.shell([
753
- 'appops',
754
- 'set',
755
- resultPkgs[0] ?? SETTINGS_HELPER_ID,
756
- 'android:mock_location',
757
- 'deny',
758
- ]);
759
- return;
760
- }
761
-
762
- logger.debug(`Resetting mock_location permission for the following apps: ${resultPkgs}`);
763
- await B.all(
764
- resultPkgs.map((pkgId) =>
765
- (async () => {
766
- try {
767
- await adb.shell(['appops', 'set', pkgId, 'android:mock_location', 'deny']);
768
- } catch (ign) {}
769
- })()
770
- )
771
- );
772
- } catch (err) {
773
- logger.warn(`Unable to reset mock location: ${(err as Error).message}`);
774
- }
775
- },
776
-
777
- async installHelperApp(adb, apkPath, packageId) {
778
- // Sometimes adb push or adb instal take more time than expected to install an app
779
- // e.g. https://github.com/appium/io.appium.settings/issues/40#issuecomment-476593174
780
- await retryInterval(
781
- HELPER_APP_INSTALL_RETRIES,
782
- HELPER_APP_INSTALL_RETRY_DELAY_MS,
783
- async function retryInstallHelperApp() {
784
- await adb.installOrUpgrade(apkPath, packageId, {grantPermissions: true});
785
- }
786
- );
787
- },
788
-
789
- async pushSettingsApp(adb, throwError, opts) {
790
- logger.debug('Pushing settings apk to device...');
791
-
792
- try {
793
- await AndroidHelpers.installHelperApp(adb, SETTINGS_APK_PATH, SETTINGS_HELPER_ID);
794
- } catch (err) {
795
- if (throwError) {
796
- throw err;
797
- }
798
-
799
- logger.warn(
800
- `Ignored error while installing '${SETTINGS_APK_PATH}': ` +
801
- `'${(err as Error).message}'. Features that rely on this helper ` +
802
- 'require the apk such as toggle WiFi and getting location ' +
803
- 'will raise an error if you try to use them.'
804
- );
805
- }
806
-
807
- const settingsApp = new SettingsApp({adb});
808
- // Reinstall would stop the settings helper process anyway, so
809
- // there is no need to continue if the application is still running
810
- if (await settingsApp.isRunningInForeground()) {
811
- logger.debug(
812
- `${SETTINGS_HELPER_ID} is already running. ` +
813
- `There is no need to reset its permissions.`
814
- );
815
- return;
816
- }
817
-
818
- const fixSettingsAppPermissionsForLegacyApis = async () => {
819
- if (await adb.getApiLevel() > 23) {
820
- return;
821
- }
822
-
823
- // Android 6- devices should have granted permissions
824
- // https://github.com/appium/appium/pull/11640#issuecomment-438260477
825
- const perms = ['SET_ANIMATION_SCALE', 'CHANGE_CONFIGURATION', 'ACCESS_FINE_LOCATION'];
826
- logger.info(`Granting permissions ${perms} to '${SETTINGS_HELPER_ID}'`);
827
- await adb.grantPermissions(
828
- SETTINGS_HELPER_ID,
829
- perms.map((x) => `android.permission.${x}`)
830
- );
831
- };
832
-
833
- try {
834
- await B.all([
835
- settingsApp.adjustNotificationsPermissions(),
836
- settingsApp.adjustMediaProjectionServicePermissions(),
837
- fixSettingsAppPermissionsForLegacyApis(),
838
- ]);
839
- } catch (e) {
840
- logger.debug(e.stack);
841
- }
842
-
843
- // launch io.appium.settings app due to settings failing to be set
844
- // if the app is not launched prior to start the session on android 7+
845
- // see https://github.com/appium/appium/issues/8957
846
- try {
847
- await settingsApp.requireRunning({
848
- timeout: AndroidHelpers.isEmulator(adb, opts) ? 30000 : 5000,
849
- });
850
- } catch (err) {
851
- logger.debug(err);
852
- if (throwError) {
853
- throw err;
854
- }
855
- }
856
- },
857
-
858
- async pushStrings(language, adb, opts) {
859
- const remoteDir = '/data/local/tmp';
860
- const stringsJson = 'strings.json';
861
- const remoteFile = path.posix.resolve(remoteDir, stringsJson);
862
-
863
- // clean up remote string.json if present
864
- await adb.rimraf(remoteFile);
865
-
866
- let app: string;
867
- try {
868
- app = opts.app || (await adb.pullApk(opts.appPackage!, opts.tmpDir!));
869
- } catch (err) {
870
- logger.info(
871
- `Failed to pull an apk from '${opts.appPackage}' to '${opts.tmpDir}'. Original error: ${
872
- (err as Error).message
873
- }`
874
- );
875
- }
876
-
877
- if (_.isEmpty(opts.appPackage) || !(await fs.exists(app!))) {
878
- logger.debug(`No app or package specified. Returning empty strings`);
879
- return {};
880
- }
881
-
882
- const stringsTmpDir = path.resolve(opts.tmpDir!, opts.appPackage!);
883
- try {
884
- logger.debug('Extracting strings from apk', app!, language, stringsTmpDir);
885
- const {apkStrings, localPath} = await adb.extractStringsFromApk(
886
- app!,
887
- language ?? null,
888
- stringsTmpDir
889
- );
890
- await adb.push(localPath, remoteDir);
891
- return apkStrings;
892
- } catch (err) {
893
- logger.warn(
894
- `Could not get strings, continuing anyway. Original error: ${(err as Error).message}`
895
- );
896
- await adb.shell(['echo', `'{}' > ${remoteFile}`]);
897
- } finally {
898
- await fs.rimraf(stringsTmpDir);
899
- }
900
- return {};
901
- },
902
-
903
- async unlock(driver, adb, capabilities) {
904
- if (!(await adb.isScreenLocked())) {
905
- logger.info('Screen already unlocked, doing nothing');
906
- return;
907
- }
908
-
909
- logger.debug('Screen is locked, trying to unlock');
910
- if (!capabilities.unlockType && !capabilities.unlockKey) {
911
- logger.info(
912
- `Neither 'unlockType' nor 'unlockKey' capability is provided. ` +
913
- `Assuming the device is locked with a simple lock screen.`
914
- );
915
- await adb.dismissKeyguard();
916
- return;
917
- }
918
-
919
- const {unlockType, unlockKey, unlockStrategy, unlockSuccessTimeout} =
920
- Unlocker.validateUnlockCapabilities(capabilities);
921
- if (
922
- unlockKey &&
923
- unlockType !== FINGERPRINT_UNLOCK &&
924
- (_.isNil(unlockStrategy) || _.toLower(unlockStrategy) === 'locksettings') &&
925
- (await adb.isLockManagementSupported())
926
- ) {
927
- await Unlocker.fastUnlock(adb, {
928
- credential: unlockKey,
929
- credentialType: toCredentialType(unlockType as UnlockType),
930
- });
931
- } else {
932
- const unlockMethod = {
933
- [PIN_UNLOCK]: Unlocker.pinUnlock,
934
- [PIN_UNLOCK_KEY_EVENT]: Unlocker.pinUnlockWithKeyEvent,
935
- [PASSWORD_UNLOCK]: Unlocker.passwordUnlock,
936
- [PATTERN_UNLOCK]: Unlocker.patternUnlock,
937
- [FINGERPRINT_UNLOCK]: Unlocker.fingerprintUnlock,
938
- }[unlockType!];
939
- await unlockMethod!(adb, driver, capabilities);
940
- }
941
- await AndroidHelpers.verifyUnlock(adb, unlockSuccessTimeout);
942
- },
943
-
944
- async verifyUnlock(adb, timeoutMs = null) {
945
- try {
946
- await waitForCondition(async () => !(await adb.isScreenLocked()), {
947
- waitMs: timeoutMs ?? 2000,
948
- intervalMs: 500,
949
- });
950
- } catch (ign) {
951
- throw new Error('The device has failed to be unlocked');
952
- }
953
- logger.info('The device has been successfully unlocked');
954
- },
955
-
956
- async initDevice(adb, opts) {
957
- const {
958
- skipDeviceInitialization,
959
- locale,
960
- language,
961
- localeScript,
962
- unicodeKeyboard,
963
- hideKeyboard,
964
- disableWindowAnimation,
965
- skipUnlock,
966
- mockLocationApp,
967
- skipLogcatCapture,
968
- logcatFormat,
969
- logcatFilterSpecs,
970
- } = opts;
971
-
972
- if (skipDeviceInitialization) {
973
- logger.info(`'skipDeviceInitialization' is set. Skipping device initialization.`);
974
- } else {
975
- if (AndroidHelpers.isEmulator(adb, opts)) {
976
- // Check if the device wake up only for an emulator.
977
- // It takes 1 second or so even when the device is already awake in a real device.
978
- await adb.waitForDevice();
979
- }
980
- // pushSettingsApp required before calling ensureDeviceLocale for API Level 24+
981
-
982
- // Some feature such as location/wifi are not necessary for all users,
983
- // but they require the settings app. So, try to configure it while Appium
984
- // does not throw error even if they fail.
985
- const shouldThrowError = Boolean(
986
- language ||
987
- locale ||
988
- localeScript ||
989
- unicodeKeyboard ||
990
- hideKeyboard ||
991
- disableWindowAnimation ||
992
- !skipUnlock
993
- );
994
- await AndroidHelpers.pushSettingsApp(adb, shouldThrowError, opts);
995
- }
996
-
997
- if (!AndroidHelpers.isEmulator(adb, opts)) {
998
- if (mockLocationApp || _.isUndefined(mockLocationApp)) {
999
- await AndroidHelpers.setMockLocationApp(adb, mockLocationApp || SETTINGS_HELPER_ID);
1000
- } else {
1001
- await AndroidHelpers.resetMockLocation(adb);
1002
- }
1003
- }
1004
-
1005
- if (language || locale) {
1006
- await AndroidHelpers.ensureDeviceLocale(adb, language, locale, localeScript);
1007
- }
1008
-
1009
- if (skipLogcatCapture) {
1010
- logger.info(`'skipLogcatCapture' is set. Skipping starting logcat capture.`);
1011
- } else {
1012
- await adb.startLogcat({
1013
- format: logcatFormat,
1014
- filterSpecs: logcatFilterSpecs,
1015
- });
1016
- }
1017
-
1018
- if (hideKeyboard) {
1019
- await AndroidHelpers.hideKeyboard(adb);
1020
- } else if (hideKeyboard === false) {
1021
- await adb.shell(['ime', 'reset']);
1022
- }
1023
-
1024
- if (unicodeKeyboard) {
1025
- logger.warn(
1026
- `The 'unicodeKeyboard' capability has been deprecated and will be removed. ` +
1027
- `Set the 'hideKeyboard' capability to 'true' in order to make the on-screen keyboard invisible.`
1028
- );
1029
- return await AndroidHelpers.initUnicodeKeyboard(adb);
1030
- }
1031
- },
1032
-
1033
- removeNullProperties(obj) {
1034
- for (const key of _.keys(obj)) {
1035
- if (_.isNull(obj[key]) || _.isUndefined(obj[key])) {
1036
- delete obj[key];
1037
- }
1038
- }
1039
- },
1040
-
1041
- truncateDecimals(number, digits) {
1042
- const multiplier = Math.pow(10, digits),
1043
- adjustedNum = number * multiplier,
1044
- truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);
1045
-
1046
- return truncatedNum / multiplier;
1047
- },
1048
-
1049
- isChromeBrowser(browser) {
1050
- return _.includes(Object.keys(CHROME_BROWSER_PACKAGE_ACTIVITY), (browser || '').toLowerCase());
1051
- },
1052
-
1053
- getChromePkg(browser) {
1054
- return (
1055
- CHROME_BROWSER_PACKAGE_ACTIVITY[
1056
- browser.toLowerCase() as keyof typeof CHROME_BROWSER_PACKAGE_ACTIVITY
1057
- ] || CHROME_BROWSER_PACKAGE_ACTIVITY.default
1058
- );
1059
- },
1060
-
1061
- async removeAllSessionWebSocketHandlers(server, sessionId) {
1062
- if (!server || !_.isFunction(server.getWebSocketHandlers)) {
1063
- return;
1064
- }
1065
-
1066
- const activeHandlers = await server.getWebSocketHandlers(sessionId);
1067
- for (const pathname of _.keys(activeHandlers)) {
1068
- await server.removeWebSocketHandler(pathname);
1069
- }
1070
- },
1071
-
1072
- parseArray(cap) {
1073
- let parsedCaps: string | string[] | undefined;
1074
- try {
1075
- parsedCaps = JSON.parse(cap as string);
1076
- } catch (ign) {}
1077
-
1078
- if (_.isArray(parsedCaps)) {
1079
- return parsedCaps;
1080
- } else if (_.isString(cap)) {
1081
- return [cap];
1082
- }
1083
-
1084
- throw new Error(`must provide a string or JSON Array; received ${cap}`);
1085
- },
1086
-
1087
- validateDesiredCaps(caps) {
1088
- if (caps.browserName) {
1089
- if (caps.app) {
1090
- // warn if the capabilities have both `app` and `browser, although this is common with selenium grid
1091
- logger.warn(
1092
- `The desired capabilities should generally not include both an 'app' and a 'browserName'`
1093
- );
1094
- }
1095
- if (caps.appPackage) {
1096
- logger.errorAndThrow(
1097
- `The desired should not include both of an 'appPackage' and a 'browserName'`
1098
- );
1099
- }
1100
- }
1101
-
1102
- if (caps.uninstallOtherPackages) {
1103
- try {
1104
- AndroidHelpers.parseArray(caps.uninstallOtherPackages);
1105
- } catch (e) {
1106
- logger.errorAndThrow(
1107
- `Could not parse "uninstallOtherPackages" capability: ${(e as Error).message}`
1108
- );
1109
- }
1110
- }
1111
-
1112
- return true;
1113
- },
1114
-
1115
- adjustBrowserSessionCaps(caps) {
1116
- const {browserName} = caps;
1117
- logger.info(`The current session is considered browser-based`);
1118
- logger.info(
1119
- `Supported browser names: ${JSON.stringify(_.keys(CHROME_BROWSER_PACKAGE_ACTIVITY))}`
1120
- );
1121
- if (caps.appPackage || caps.appActivity) {
1122
- logger.info(
1123
- `Not overriding appPackage/appActivity capability values for '${browserName}' ` +
1124
- 'because some of them have been already provided'
1125
- );
1126
- return caps;
1127
- }
1128
-
1129
- const {pkg, activity} = AndroidHelpers.getChromePkg(String(browserName));
1130
- caps.appPackage = pkg;
1131
- caps.appActivity = activity;
1132
- logger.info(
1133
- `appPackage/appActivity capabilities have been automatically set to ${pkg}/${activity} ` +
1134
- `for '${browserName}'`
1135
- );
1136
- logger.info(
1137
- `Consider changing the browserName to the one from the list of supported browser names ` +
1138
- `or provide custom appPackage/appActivity capability values if the automatically assigned ones do ` +
1139
- `not make sense`
1140
- );
1141
- return caps;
1142
- },
1143
-
1144
- isEmulator(adb, opts) {
1145
- const possibleNames = [opts?.udid, adb?.curDeviceId];
1146
- return !!opts?.avd || possibleNames.some((x) => EMULATOR_PATTERN.test(String(x)));
1147
- },
1148
- unlocker: Unlocker,
1149
- };
1150
-
1151
- export const helpers = AndroidHelpers;
1152
- export {APP_STATE, SETTINGS_HELPER_ID, ensureNetworkSpeed, prepareAvdArgs};
1153
- export default AndroidHelpers;