appium-android-driver 5.13.0 → 5.13.2

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 (151) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/build/index.js +43 -40
  3. package/build/lib/android-helpers.d.ts +136 -0
  4. package/build/lib/android-helpers.d.ts.map +1 -0
  5. package/build/lib/android-helpers.js +760 -679
  6. package/build/lib/android-helpers.js.map +1 -1
  7. package/build/lib/bootstrap.d.ts +29 -0
  8. package/build/lib/bootstrap.d.ts.map +1 -0
  9. package/build/lib/bootstrap.js +192 -179
  10. package/build/lib/bootstrap.js.map +1 -1
  11. package/build/lib/commands/actions.d.ts +209 -0
  12. package/build/lib/commands/actions.d.ts.map +1 -0
  13. package/build/lib/commands/actions.js +327 -265
  14. package/build/lib/commands/actions.js.map +1 -1
  15. package/build/lib/commands/alert.d.ts +10 -0
  16. package/build/lib/commands/alert.d.ts.map +1 -0
  17. package/build/lib/commands/alert.js +12 -18
  18. package/build/lib/commands/alert.js.map +1 -1
  19. package/build/lib/commands/app-management.d.ts +314 -0
  20. package/build/lib/commands/app-management.d.ts.map +1 -0
  21. package/build/lib/commands/app-management.js +278 -110
  22. package/build/lib/commands/app-management.js.map +1 -1
  23. package/build/lib/commands/context.d.ts +94 -0
  24. package/build/lib/commands/context.d.ts.map +1 -0
  25. package/build/lib/commands/context.js +412 -260
  26. package/build/lib/commands/context.js.map +1 -1
  27. package/build/lib/commands/coverage.d.ts +5 -0
  28. package/build/lib/commands/coverage.d.ts.map +1 -0
  29. package/build/lib/commands/coverage.js +14 -17
  30. package/build/lib/commands/coverage.js.map +1 -1
  31. package/build/lib/commands/element.d.ts +36 -0
  32. package/build/lib/commands/element.d.ts.map +1 -0
  33. package/build/lib/commands/element.js +97 -127
  34. package/build/lib/commands/element.js.map +1 -1
  35. package/build/lib/commands/emu-console.d.ts +49 -0
  36. package/build/lib/commands/emu-console.d.ts.map +1 -0
  37. package/build/lib/commands/emu-console.js +36 -25
  38. package/build/lib/commands/emu-console.js.map +1 -1
  39. package/build/lib/commands/execute.d.ts +6 -0
  40. package/build/lib/commands/execute.d.ts.map +1 -0
  41. package/build/lib/commands/execute.js +68 -69
  42. package/build/lib/commands/execute.js.map +1 -1
  43. package/build/lib/commands/file-actions.d.ts +129 -0
  44. package/build/lib/commands/file-actions.d.ts.map +1 -0
  45. package/build/lib/commands/file-actions.js +321 -178
  46. package/build/lib/commands/file-actions.js.map +1 -1
  47. package/build/lib/commands/find.d.ts +13 -0
  48. package/build/lib/commands/find.d.ts.map +1 -0
  49. package/build/lib/commands/find.js +69 -51
  50. package/build/lib/commands/find.js.map +1 -1
  51. package/build/lib/commands/general.d.ts +133 -0
  52. package/build/lib/commands/general.d.ts.map +1 -0
  53. package/build/lib/commands/general.js +275 -216
  54. package/build/lib/commands/general.js.map +1 -1
  55. package/build/lib/commands/ime.d.ts +11 -0
  56. package/build/lib/commands/ime.d.ts.map +1 -0
  57. package/build/lib/commands/ime.js +27 -33
  58. package/build/lib/commands/ime.js.map +1 -1
  59. package/build/lib/commands/index.d.ts +3 -0
  60. package/build/lib/commands/index.d.ts.map +1 -0
  61. package/build/lib/commands/index.js +32 -35
  62. package/build/lib/commands/index.js.map +1 -1
  63. package/build/lib/commands/intent.d.ts +418 -0
  64. package/build/lib/commands/intent.d.ts.map +1 -0
  65. package/build/lib/commands/intent.js +281 -151
  66. package/build/lib/commands/intent.js.map +1 -1
  67. package/build/lib/commands/keyboard.d.ts +6 -0
  68. package/build/lib/commands/keyboard.d.ts.map +1 -0
  69. package/build/lib/commands/keyboard.js +6 -14
  70. package/build/lib/commands/keyboard.js.map +1 -1
  71. package/build/lib/commands/log.d.ts +45 -0
  72. package/build/lib/commands/log.d.ts.map +1 -0
  73. package/build/lib/commands/log.js +117 -103
  74. package/build/lib/commands/log.js.map +1 -1
  75. package/build/lib/commands/media-projection.d.ts +144 -0
  76. package/build/lib/commands/media-projection.d.ts.map +1 -0
  77. package/build/lib/commands/media-projection.js +228 -171
  78. package/build/lib/commands/media-projection.js.map +1 -1
  79. package/build/lib/commands/network.d.ts +139 -0
  80. package/build/lib/commands/network.d.ts.map +1 -0
  81. package/build/lib/commands/network.js +249 -181
  82. package/build/lib/commands/network.js.map +1 -1
  83. package/build/lib/commands/performance.d.ts +101 -0
  84. package/build/lib/commands/performance.d.ts.map +1 -0
  85. package/build/lib/commands/performance.js +390 -236
  86. package/build/lib/commands/performance.js.map +1 -1
  87. package/build/lib/commands/permissions.d.ts +93 -0
  88. package/build/lib/commands/permissions.d.ts.map +1 -0
  89. package/build/lib/commands/permissions.js +133 -93
  90. package/build/lib/commands/permissions.js.map +1 -1
  91. package/build/lib/commands/recordscreen.d.ts +194 -0
  92. package/build/lib/commands/recordscreen.d.ts.map +1 -0
  93. package/build/lib/commands/recordscreen.js +293 -224
  94. package/build/lib/commands/recordscreen.js.map +1 -1
  95. package/build/lib/commands/shell.d.ts +8 -0
  96. package/build/lib/commands/shell.d.ts.map +1 -0
  97. package/build/lib/commands/shell.js +38 -43
  98. package/build/lib/commands/shell.js.map +1 -1
  99. package/build/lib/commands/streamscreen.d.ts +104 -0
  100. package/build/lib/commands/streamscreen.d.ts.map +1 -0
  101. package/build/lib/commands/streamscreen.js +364 -305
  102. package/build/lib/commands/streamscreen.js.map +1 -1
  103. package/build/lib/commands/system-bars.d.ts +100 -0
  104. package/build/lib/commands/system-bars.d.ts.map +1 -0
  105. package/build/lib/commands/system-bars.js +148 -90
  106. package/build/lib/commands/system-bars.js.map +1 -1
  107. package/build/lib/commands/touch.d.ts +30 -0
  108. package/build/lib/commands/touch.d.ts.map +1 -0
  109. package/build/lib/commands/touch.js +311 -287
  110. package/build/lib/commands/touch.js.map +1 -1
  111. package/build/lib/desired-caps.d.ts +353 -0
  112. package/build/lib/desired-caps.d.ts.map +1 -0
  113. package/build/lib/desired-caps.js +291 -292
  114. package/build/lib/desired-caps.js.map +1 -1
  115. package/build/lib/driver.d.ts +430 -0
  116. package/build/lib/driver.d.ts.map +1 -0
  117. package/build/lib/driver.js +449 -384
  118. package/build/lib/driver.js.map +1 -1
  119. package/build/lib/logger.d.ts +3 -0
  120. package/build/lib/logger.d.ts.map +1 -0
  121. package/build/lib/logger.js +5 -11
  122. package/build/lib/logger.js.map +1 -1
  123. package/build/lib/method-map.d.ts +389 -0
  124. package/build/lib/method-map.d.ts.map +1 -0
  125. package/build/lib/method-map.js +220 -394
  126. package/build/lib/method-map.js.map +1 -1
  127. package/build/lib/stubs.d.ts +8 -0
  128. package/build/lib/stubs.d.ts.map +1 -0
  129. package/build/lib/stubs.js +5 -0
  130. package/build/lib/stubs.js.map +1 -0
  131. package/build/lib/uiautomator.d.ts +24 -0
  132. package/build/lib/uiautomator.d.ts.map +1 -0
  133. package/build/lib/uiautomator.js +86 -82
  134. package/build/lib/uiautomator.js.map +1 -1
  135. package/build/lib/unlock-helpers.d.ts +38 -0
  136. package/build/lib/unlock-helpers.d.ts.map +1 -0
  137. package/build/lib/unlock-helpers.js +228 -204
  138. package/build/lib/unlock-helpers.js.map +1 -1
  139. package/build/lib/utils.d.ts +11 -0
  140. package/build/lib/utils.d.ts.map +1 -0
  141. package/build/lib/utils.js +23 -18
  142. package/build/lib/utils.js.map +1 -1
  143. package/build/lib/webview-helpers.d.ts +223 -0
  144. package/build/lib/webview-helpers.d.ts.map +1 -0
  145. package/build/lib/webview-helpers.js +476 -298
  146. package/build/lib/webview-helpers.js.map +1 -1
  147. package/index.js +3 -1
  148. package/lib/android-helpers.js +2 -1
  149. package/lib/stubs.ts +8 -0
  150. package/lib/unlock-helpers.js +2 -2
  151. package/package.json +23 -14
@@ -1,16 +1,14 @@
1
1
  "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
- Object.defineProperty(exports, "__esModule", {
5
- value: true
6
- });
7
- exports.default = exports.commands = void 0;
8
- require("source-map-support/register");
9
- var _lodash = _interopRequireDefault(require("lodash"));
10
- var _asyncbox = require("asyncbox");
11
- var _support = require("@appium/support");
12
- var _teen_process = require("teen_process");
13
- var _path = _interopRequireDefault(require("path"));
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.commands = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const asyncbox_1 = require("asyncbox");
9
+ const support_1 = require("@appium/support");
10
+ const teen_process_1 = require("teen_process");
11
+ const path_1 = __importDefault(require("path"));
14
12
  const commands = {};
15
13
  exports.commands = commands;
16
14
  const RETRY_PAUSE = 300;
@@ -22,232 +20,303 @@ const PROCESS_SHUTDOWN_TIMEOUT = 10 * 1000;
22
20
  const SCREENRECORD_BINARY = 'screenrecord';
23
21
  const DEFAULT_EXT = '.mp4';
24
22
  const MIN_EMULATOR_API_LEVEL = 27;
25
- const FFMPEG_BINARY = `ffmpeg${_support.system.isWindows() ? '.exe' : ''}`;
23
+ const FFMPEG_BINARY = `ffmpeg${support_1.system.isWindows() ? '.exe' : ''}`;
26
24
  async function uploadRecordedMedia(localFile, remotePath = null, uploadOptions = {}) {
27
- if (_lodash.default.isEmpty(remotePath)) {
28
- return (await _support.util.toInMemoryBase64(localFile)).toString();
29
- }
30
- const {
31
- user,
32
- pass,
33
- method,
34
- headers,
35
- fileFieldName,
36
- formFields
37
- } = uploadOptions;
38
- const options = {
39
- method: method || 'PUT',
40
- headers,
41
- fileFieldName,
42
- formFields
43
- };
44
- if (user && pass) {
45
- options.auth = {
46
- user,
47
- pass
25
+ if (lodash_1.default.isEmpty(remotePath)) {
26
+ return (await support_1.util.toInMemoryBase64(localFile)).toString();
27
+ }
28
+ const { user, pass, method, headers, fileFieldName, formFields } = uploadOptions;
29
+ const options = {
30
+ method: method || 'PUT',
31
+ headers,
32
+ fileFieldName,
33
+ formFields,
48
34
  };
49
- }
50
- await _support.net.uploadFile(localFile, remotePath, options);
51
- return '';
35
+ if (user && pass) {
36
+ options.auth = { user, pass };
37
+ }
38
+ await support_1.net.uploadFile(localFile, remotePath, options);
39
+ return '';
52
40
  }
53
41
  async function verifyScreenRecordIsSupported(adb, isEmulator) {
54
- const apiLevel = await adb.getApiLevel();
55
- if (isEmulator && apiLevel < MIN_EMULATOR_API_LEVEL) {
56
- throw new Error(`Screen recording does not work on emulators running Android API level less than ${MIN_EMULATOR_API_LEVEL}`);
57
- }
58
- if (apiLevel < 19) {
59
- throw new Error(`Screen recording not available on API Level ${apiLevel}. Minimum API Level is 19.`);
60
- }
42
+ const apiLevel = await adb.getApiLevel();
43
+ if (isEmulator && apiLevel < MIN_EMULATOR_API_LEVEL) {
44
+ throw new Error(`Screen recording does not work on emulators running Android API level less than ${MIN_EMULATOR_API_LEVEL}`);
45
+ }
46
+ if (apiLevel < 19) {
47
+ throw new Error(`Screen recording not available on API Level ${apiLevel}. Minimum API Level is 19.`);
48
+ }
61
49
  }
62
50
  async function scheduleScreenRecord(adb, recordingProperties, log = null) {
63
- if (recordingProperties.stopped) {
64
- return;
65
- }
66
- const {
67
- timer,
68
- videoSize,
69
- bitRate,
70
- timeLimit,
71
- bugReport
72
- } = recordingProperties;
73
- let currentTimeLimit = MAX_RECORDING_TIME_SEC;
74
- if (_support.util.hasValue(recordingProperties.currentTimeLimit)) {
75
- const currentTimeLimitInt = parseInt(recordingProperties.currentTimeLimit, 10);
76
- if (!isNaN(currentTimeLimitInt) && currentTimeLimitInt < MAX_RECORDING_TIME_SEC) {
77
- currentTimeLimit = currentTimeLimitInt;
78
- }
79
- }
80
- const pathOnDevice = `/sdcard/${_support.util.uuidV4().substring(0, 8)}${DEFAULT_EXT}`;
81
- const recordingProc = adb.screenrecord(pathOnDevice, {
82
- videoSize,
83
- bitRate,
84
- timeLimit: currentTimeLimit,
85
- bugReport
86
- });
87
- recordingProc.on('end', () => {
88
- if (recordingProperties.stopped || !_support.util.hasValue(timeLimit)) {
89
- return;
90
- }
91
- const currentDuration = timer.getDuration().asSeconds.toFixed(0);
92
- log === null || log === void 0 ? void 0 : log.debug(`The overall screen recording duration is ${currentDuration}s so far`);
93
- const timeLimitInt = parseInt(timeLimit, 10);
94
- if (isNaN(timeLimitInt) || currentDuration >= timeLimitInt) {
95
- log === null || log === void 0 ? void 0 : log.debug('There is no need to start the next recording chunk');
96
- return;
97
- }
98
- recordingProperties.currentTimeLimit = timeLimitInt - currentDuration;
99
- const chunkDuration = recordingProperties.currentTimeLimit < MAX_RECORDING_TIME_SEC ? recordingProperties.currentTimeLimit : MAX_RECORDING_TIME_SEC;
100
- log === null || log === void 0 ? void 0 : log.debug(`Starting the next ${chunkDuration}s-chunk ` + `of screen recording in order to achieve ${timeLimitInt}s total duration`);
101
- (async () => {
102
- try {
103
- await scheduleScreenRecord(adb, recordingProperties, log);
104
- } catch (e) {
105
- log === null || log === void 0 ? void 0 : log.error(e.stack);
106
- recordingProperties.stopped = true;
107
- }
108
- })();
109
- });
110
- await recordingProc.start(0);
111
- try {
112
- await (0, _asyncbox.waitForCondition)(async () => await adb.fileExists(pathOnDevice), {
113
- waitMs: RETRY_TIMEOUT,
114
- intervalMs: RETRY_PAUSE
51
+ if (recordingProperties.stopped) {
52
+ return;
53
+ }
54
+ const { timer, videoSize, bitRate, timeLimit, bugReport, } = recordingProperties;
55
+ let currentTimeLimit = MAX_RECORDING_TIME_SEC;
56
+ if (support_1.util.hasValue(recordingProperties.currentTimeLimit)) {
57
+ const currentTimeLimitInt = parseInt(recordingProperties.currentTimeLimit, 10);
58
+ if (!isNaN(currentTimeLimitInt) && currentTimeLimitInt < MAX_RECORDING_TIME_SEC) {
59
+ currentTimeLimit = currentTimeLimitInt;
60
+ }
61
+ }
62
+ const pathOnDevice = `/sdcard/${support_1.util.uuidV4().substring(0, 8)}${DEFAULT_EXT}`;
63
+ const recordingProc = adb.screenrecord(pathOnDevice, {
64
+ videoSize,
65
+ bitRate,
66
+ timeLimit: currentTimeLimit,
67
+ bugReport,
68
+ });
69
+ recordingProc.on('end', () => {
70
+ if (recordingProperties.stopped || !support_1.util.hasValue(timeLimit)) {
71
+ return;
72
+ }
73
+ const currentDuration = timer.getDuration().asSeconds.toFixed(0);
74
+ log?.debug(`The overall screen recording duration is ${currentDuration}s so far`);
75
+ const timeLimitInt = parseInt(timeLimit, 10);
76
+ if (isNaN(timeLimitInt) || currentDuration >= timeLimitInt) {
77
+ log?.debug('There is no need to start the next recording chunk');
78
+ return;
79
+ }
80
+ recordingProperties.currentTimeLimit = timeLimitInt - currentDuration;
81
+ const chunkDuration = recordingProperties.currentTimeLimit < MAX_RECORDING_TIME_SEC
82
+ ? recordingProperties.currentTimeLimit
83
+ : MAX_RECORDING_TIME_SEC;
84
+ log?.debug(`Starting the next ${chunkDuration}s-chunk ` +
85
+ `of screen recording in order to achieve ${timeLimitInt}s total duration`);
86
+ (async () => {
87
+ try {
88
+ await scheduleScreenRecord(adb, recordingProperties, log);
89
+ }
90
+ catch (e) {
91
+ log?.error(e.stack);
92
+ recordingProperties.stopped = true;
93
+ }
94
+ })();
115
95
  });
116
- } catch (e) {
117
- throw new Error(`The expected screen record file '${pathOnDevice}' does not exist after ${RETRY_TIMEOUT}ms. ` + `Is ${SCREENRECORD_BINARY} utility available and operational on the device under test?`);
118
- }
119
- recordingProperties.records.push(pathOnDevice);
120
- recordingProperties.recordingProcess = recordingProc;
96
+ await recordingProc.start(0);
97
+ try {
98
+ await (0, asyncbox_1.waitForCondition)(async () => await adb.fileExists(pathOnDevice), { waitMs: RETRY_TIMEOUT, intervalMs: RETRY_PAUSE });
99
+ }
100
+ catch (e) {
101
+ throw new Error(`The expected screen record file '${pathOnDevice}' does not exist after ${RETRY_TIMEOUT}ms. ` +
102
+ `Is ${SCREENRECORD_BINARY} utility available and operational on the device under test?`);
103
+ }
104
+ recordingProperties.records.push(pathOnDevice);
105
+ recordingProperties.recordingProcess = recordingProc;
121
106
  }
122
107
  async function mergeScreenRecords(mediaFiles, log = null) {
123
- try {
124
- await _support.fs.which(FFMPEG_BINARY);
125
- } catch (e) {
126
- throw new Error(`${FFMPEG_BINARY} utility is not available in PATH. Please install it from https://www.ffmpeg.org/`);
127
- }
128
- const configContent = mediaFiles.map(x => `file '${x}'`).join('\n');
129
- const configFile = _path.default.resolve(_path.default.dirname(mediaFiles[0]), 'config.txt');
130
- await _support.fs.writeFile(configFile, configContent, 'utf8');
131
- log === null || log === void 0 ? void 0 : log.debug(`Generated ffmpeg merging config '${configFile}' with items:\n${configContent}`);
132
- const result = _path.default.resolve(_path.default.dirname(mediaFiles[0]), `merge_${Math.floor(new Date())}${DEFAULT_EXT}`);
133
- const args = ['-safe', '0', '-f', 'concat', '-i', configFile, '-c', 'copy', result];
134
- log === null || log === void 0 ? void 0 : log.info(`Initiating screen records merging using the command '${FFMPEG_BINARY} ${args.join(' ')}'`);
135
- await (0, _teen_process.exec)(FFMPEG_BINARY, args);
136
- return result;
108
+ try {
109
+ await support_1.fs.which(FFMPEG_BINARY);
110
+ }
111
+ catch (e) {
112
+ throw new Error(`${FFMPEG_BINARY} utility is not available in PATH. Please install it from https://www.ffmpeg.org/`);
113
+ }
114
+ const configContent = mediaFiles
115
+ .map((x) => `file '${x}'`)
116
+ .join('\n');
117
+ const configFile = path_1.default.resolve(path_1.default.dirname(mediaFiles[0]), 'config.txt');
118
+ await support_1.fs.writeFile(configFile, configContent, 'utf8');
119
+ log?.debug(`Generated ffmpeg merging config '${configFile}' with items:\n${configContent}`);
120
+ const result = path_1.default.resolve(path_1.default.dirname(mediaFiles[0]), `merge_${Math.floor(new Date())}${DEFAULT_EXT}`);
121
+ const args = ['-safe', '0', '-f', 'concat', '-i', configFile, '-c', 'copy', result];
122
+ log?.info(`Initiating screen records merging using the command '${FFMPEG_BINARY} ${args.join(' ')}'`);
123
+ await (0, teen_process_1.exec)(FFMPEG_BINARY, args);
124
+ return result;
137
125
  }
138
126
  async function terminateBackgroundScreenRecording(adb, force = true) {
139
- const pids = (await adb.getPIDsByName(SCREENRECORD_BINARY)).map(p => `${p}`);
140
- if (_lodash.default.isEmpty(pids)) {
141
- return false;
142
- }
143
- try {
144
- await adb.shell(['kill', force ? '-15' : '-2', ...pids]);
145
- await (0, _asyncbox.waitForCondition)(async () => _lodash.default.isEmpty(await adb.getPIDsByName(SCREENRECORD_BINARY)), {
146
- waitMs: PROCESS_SHUTDOWN_TIMEOUT,
147
- intervalMs: 500
148
- });
149
- return true;
150
- } catch (err) {
151
- throw new Error(`Unable to stop the background screen recording: ${err.message}`);
152
- }
127
+ const pids = (await adb.getPIDsByName(SCREENRECORD_BINARY))
128
+ .map((p) => `${p}`);
129
+ if (lodash_1.default.isEmpty(pids)) {
130
+ return false;
131
+ }
132
+ try {
133
+ await adb.shell(['kill', force ? '-15' : '-2', ...pids]);
134
+ await (0, asyncbox_1.waitForCondition)(async () => lodash_1.default.isEmpty(await adb.getPIDsByName(SCREENRECORD_BINARY)), {
135
+ waitMs: PROCESS_SHUTDOWN_TIMEOUT,
136
+ intervalMs: 500,
137
+ });
138
+ return true;
139
+ }
140
+ catch (err) {
141
+ throw new Error(`Unable to stop the background screen recording: ${err.message}`);
142
+ }
153
143
  }
144
+ /**
145
+ * @typedef {Object} StartRecordingOptions
146
+ *
147
+ * @property {?string} remotePath - The path to the remote location, where the captured video should be uploaded.
148
+ * The following protocols are supported: http/https, ftp.
149
+ * Null or empty string value (the default setting) means the content of resulting
150
+ * file should be encoded as Base64 and passed as the endpount response value.
151
+ * An exception will be thrown if the generated media file is too big to
152
+ * fit into the available process memory.
153
+ * This option only has an effect if there is screen recording process in progreess
154
+ * and `forceRestart` parameter is not set to `true`.
155
+ * @property {?string} user - The name of the user for the remote authentication. Only works if `remotePath` is provided.
156
+ * @property {?string} pass - The password for the remote authentication. Only works if `remotePath` is provided.
157
+ * @property {?string} method [PUT] - The http multipart upload method name. Only works if `remotePath` is provided.
158
+ * @property {?Object} headers - Additional headers mapping for multipart http(s) uploads
159
+ * @property {?string} fileFieldName [file] - The name of the form field, where the file content BLOB should be stored for
160
+ * http(s) uploads
161
+ * @property {?Object|Array<Pair>} formFields - Additional form fields for multipart http(s) uploads
162
+ * @property {?string} videoSize - The format is widthxheight.
163
+ * The default value is the device's native display resolution (if supported),
164
+ * 1280x720 if not. For best results,
165
+ * use a size supported by your device's Advanced Video Coding (AVC) encoder.
166
+ * For example, "1280x720"
167
+ * @property {?boolean} bugReport - Set it to `true` in order to display additional information on the video overlay,
168
+ * such as a timestamp, that is helpful in videos captured to illustrate bugs.
169
+ * This option is only supported since API level 27 (Android P).
170
+ * @property {?string|number} timeLimit - The maximum recording time, in seconds. The default value is 180 (3 minutes).
171
+ * The maximum value is 1800 (30 minutes). If the passed value is greater than 180 then
172
+ * the algorithm will try to schedule multiple screen recording chunks and merge the
173
+ * resulting videos into a single media file using `ffmpeg` utility.
174
+ * If the utility is not available in PATH then the most recent screen recording chunk is
175
+ * going to be returned.
176
+ * @property {?string|number} bitRate - The video bit rate for the video, in bits per second.
177
+ * The default value is 4000000 (4 Mbit/s). You can increase the bit rate to improve video quality,
178
+ * but doing so results in larger movie files.
179
+ * @property {?boolean} forceRestart - Whether to try to catch and upload/return the currently running screen recording
180
+ * (`false`, the default setting) or ignore the result of it and start a new recording
181
+ * immediately (`true`).
182
+ */
183
+ /**
184
+ * Record the display of a real devices running Android 4.4 (API level 19) and higher.
185
+ * Emulators are supported since API level 27 (Android P).
186
+ * It records screen activity to an MPEG-4 file. Audio is not recorded with the video file.
187
+ * If screen recording has been already started then the command will stop it forcefully and start a new one.
188
+ * The previously recorded video file will be deleted.
189
+ *
190
+ * @param {?StartRecordingOptions} options - The available options.
191
+ * @returns {string} Base64-encoded content of the recorded media file if
192
+ * any screen recording is currently running or an empty string.
193
+ * @throws {Error} If screen recording has failed to start or is not supported on the device under test.
194
+ */
154
195
  commands.startRecordingScreen = async function startRecordingScreen(options = {}) {
155
- await verifyScreenRecordIsSupported(this.adb, this.isEmulator());
156
- let result = '';
157
- const {
158
- videoSize,
159
- timeLimit = DEFAULT_RECORDING_TIME_SEC,
160
- bugReport,
161
- bitRate,
162
- forceRestart
163
- } = options;
164
- if (!forceRestart) {
165
- result = await this.stopRecordingScreen(options);
166
- }
167
- if (await terminateBackgroundScreenRecording(this.adb, true)) {
168
- this.log.warn(`There were some ${SCREENRECORD_BINARY} process leftovers running ` + `in the background. Make sure you stop screen recording each time after it is started, ` + `otherwise the recorded media might quickly exceed all the free space on the device under test.`);
169
- }
170
- if (!_lodash.default.isEmpty(this._screenRecordingProperties)) {
171
- for (const record of this._screenRecordingProperties.records || []) {
172
- await this.adb.rimraf(record);
173
- }
174
- this._screenRecordingProperties = null;
175
- }
176
- const timeout = parseFloat(timeLimit);
177
- if (isNaN(timeout) || timeout > MAX_TIME_SEC || timeout <= 0) {
178
- throw new Error(`The timeLimit value must be in range [1, ${MAX_TIME_SEC}] seconds. ` + `The value of '${timeLimit}' has been passed instead.`);
179
- }
180
- this._screenRecordingProperties = {
181
- timer: new _support.timing.Timer().start(),
182
- videoSize,
183
- timeLimit,
184
- currentTimeLimit: timeLimit,
185
- bitRate,
186
- bugReport,
187
- records: [],
188
- recordingProcess: null,
189
- stopped: false
190
- };
191
- await scheduleScreenRecord(this.adb, this._screenRecordingProperties, this.log);
192
- return result;
196
+ await verifyScreenRecordIsSupported(this.adb, this.isEmulator());
197
+ let result = '';
198
+ const { videoSize, timeLimit = DEFAULT_RECORDING_TIME_SEC, bugReport, bitRate, forceRestart } = options;
199
+ if (!forceRestart) {
200
+ result = await this.stopRecordingScreen(options);
201
+ }
202
+ if (await terminateBackgroundScreenRecording(this.adb, true)) {
203
+ this.log.warn(`There were some ${SCREENRECORD_BINARY} process leftovers running ` +
204
+ `in the background. Make sure you stop screen recording each time after it is started, ` +
205
+ `otherwise the recorded media might quickly exceed all the free space on the device under test.`);
206
+ }
207
+ if (!lodash_1.default.isEmpty(this._screenRecordingProperties)) {
208
+ for (const record of (this._screenRecordingProperties.records || [])) {
209
+ await this.adb.rimraf(record);
210
+ }
211
+ this._screenRecordingProperties = null;
212
+ }
213
+ const timeout = parseFloat(timeLimit);
214
+ if (isNaN(timeout) || timeout > MAX_TIME_SEC || timeout <= 0) {
215
+ throw new Error(`The timeLimit value must be in range [1, ${MAX_TIME_SEC}] seconds. ` +
216
+ `The value of '${timeLimit}' has been passed instead.`);
217
+ }
218
+ this._screenRecordingProperties = {
219
+ timer: new support_1.timing.Timer().start(),
220
+ videoSize,
221
+ timeLimit,
222
+ currentTimeLimit: timeLimit,
223
+ bitRate,
224
+ bugReport,
225
+ records: [],
226
+ recordingProcess: null,
227
+ stopped: false,
228
+ };
229
+ await scheduleScreenRecord(this.adb, this._screenRecordingProperties, this.log);
230
+ return result;
193
231
  };
232
+ /**
233
+ * @typedef {Object} StopRecordingOptions
234
+ *
235
+ * @property {?string} remotePath - The path to the remote location, where the resulting video should be uploaded.
236
+ * The following protocols are supported: http/https, ftp.
237
+ * Null or empty string value (the default setting) means the content of resulting
238
+ * file should be encoded as Base64 and passed as the endpount response value.
239
+ * An exception will be thrown if the generated media file is too big to
240
+ * fit into the available process memory.
241
+ * @property {?string} user - The name of the user for the remote authentication.
242
+ * @property {?string} pass - The password for the remote authentication.
243
+ * @property {?string} method - The http multipart upload method name. The 'PUT' one is used by default.
244
+ * @property {?Object} headers - Additional headers mapping for multipart http(s) uploads
245
+ * @property {?string} fileFieldName [file] - The name of the form field, where the file content BLOB should be stored for
246
+ * http(s) uploads
247
+ * @property {?Object|Array<Pair>} formFields - Additional form fields for multipart http(s) uploads
248
+ */
249
+ /**
250
+ * Stop recording the screen.
251
+ * If no screen recording has been started before then the method returns an empty string.
252
+ *
253
+ * @param {?StopRecordingOptions} options - The available options.
254
+ * @returns {string} Base64-encoded content of the recorded media file if 'remotePath'
255
+ * parameter is falsy or an empty string.
256
+ * @throws {Error} If there was an error while getting the name of a media file
257
+ * or the file content cannot be uploaded to the remote location
258
+ * or screen recording is not supported on the device under test.
259
+ */
194
260
  commands.stopRecordingScreen = async function stopRecordingScreen(options = {}) {
195
- await verifyScreenRecordIsSupported(this.adb, this.isEmulator());
196
- if (!_lodash.default.isEmpty(this._screenRecordingProperties)) {
197
- this._screenRecordingProperties.stopped = true;
198
- }
199
- try {
200
- await terminateBackgroundScreenRecording(this.adb, false);
201
- } catch (err) {
202
- this.log.warn(err.message);
203
- if (!_lodash.default.isEmpty(this._screenRecordingProperties)) {
204
- this.log.warn('The resulting video might be corrupted');
205
- }
206
- }
207
- if (_lodash.default.isEmpty(this._screenRecordingProperties)) {
208
- this.log.info(`Screen recording has not been previously started by Appium. There is nothing to stop`);
209
- return '';
210
- }
211
- if (this._screenRecordingProperties.recordingProcess && this._screenRecordingProperties.recordingProcess.isRunning) {
261
+ await verifyScreenRecordIsSupported(this.adb, this.isEmulator());
262
+ if (!lodash_1.default.isEmpty(this._screenRecordingProperties)) {
263
+ this._screenRecordingProperties.stopped = true;
264
+ }
212
265
  try {
213
- await this._screenRecordingProperties.recordingProcess.stop('SIGINT', PROCESS_SHUTDOWN_TIMEOUT);
214
- } catch (e) {
215
- this.log.errorAndThrow(`Unable to stop screen recording within ${PROCESS_SHUTDOWN_TIMEOUT}ms`);
216
- }
217
- this._screenRecordingProperties.recordingProcess = null;
218
- }
219
- if (_lodash.default.isEmpty(this._screenRecordingProperties.records)) {
220
- this.log.errorAndThrow(`No screen recordings have been stored on the device so far. ` + `Are you sure the ${SCREENRECORD_BINARY} utility works as expected?`);
221
- }
222
- const tmpRoot = await _support.tempDir.openDir();
223
- try {
224
- const localRecords = [];
225
- for (const pathOnDevice of this._screenRecordingProperties.records) {
226
- localRecords.push(_path.default.resolve(tmpRoot, _path.default.posix.basename(pathOnDevice)));
227
- await this.adb.pull(pathOnDevice, _lodash.default.last(localRecords));
228
- await this.adb.rimraf(pathOnDevice);
229
- }
230
- let resultFilePath = _lodash.default.last(localRecords);
231
- if (localRecords.length > 1) {
232
- this.log.info(`Got ${localRecords.length} screen recordings. Trying to merge them`);
233
- try {
234
- resultFilePath = await mergeScreenRecords(localRecords, this.log);
235
- } catch (e) {
236
- this.log.warn(`Cannot merge the recorded files. The most recent screen recording is going to be returned as the result. ` + `Original error: ${e.message}`);
237
- }
238
- }
239
- if (_lodash.default.isEmpty(options.remotePath)) {
240
- const {
241
- size
242
- } = await _support.fs.stat(resultFilePath);
243
- this.log.debug(`The size of the resulting screen recording is ${_support.util.toReadableSizeString(size)}`);
244
- }
245
- return await uploadRecordedMedia(resultFilePath, options.remotePath, options);
246
- } finally {
247
- await _support.fs.rimraf(tmpRoot);
248
- this._screenRecordingProperties = null;
249
- }
266
+ await terminateBackgroundScreenRecording(this.adb, false);
267
+ }
268
+ catch (err) {
269
+ this.log.warn(err.message);
270
+ if (!lodash_1.default.isEmpty(this._screenRecordingProperties)) {
271
+ this.log.warn('The resulting video might be corrupted');
272
+ }
273
+ }
274
+ if (lodash_1.default.isEmpty(this._screenRecordingProperties)) {
275
+ this.log.info(`Screen recording has not been previously started by Appium. There is nothing to stop`);
276
+ return '';
277
+ }
278
+ if (this._screenRecordingProperties.recordingProcess && this._screenRecordingProperties.recordingProcess.isRunning) {
279
+ try {
280
+ await this._screenRecordingProperties.recordingProcess.stop('SIGINT', PROCESS_SHUTDOWN_TIMEOUT);
281
+ }
282
+ catch (e) {
283
+ this.log.errorAndThrow(`Unable to stop screen recording within ${PROCESS_SHUTDOWN_TIMEOUT}ms`);
284
+ }
285
+ this._screenRecordingProperties.recordingProcess = null;
286
+ }
287
+ if (lodash_1.default.isEmpty(this._screenRecordingProperties.records)) {
288
+ this.log.errorAndThrow(`No screen recordings have been stored on the device so far. ` +
289
+ `Are you sure the ${SCREENRECORD_BINARY} utility works as expected?`);
290
+ }
291
+ const tmpRoot = await support_1.tempDir.openDir();
292
+ try {
293
+ const localRecords = [];
294
+ for (const pathOnDevice of this._screenRecordingProperties.records) {
295
+ localRecords.push(path_1.default.resolve(tmpRoot, path_1.default.posix.basename(pathOnDevice)));
296
+ await this.adb.pull(pathOnDevice, lodash_1.default.last(localRecords));
297
+ await this.adb.rimraf(pathOnDevice);
298
+ }
299
+ let resultFilePath = lodash_1.default.last(localRecords);
300
+ if (localRecords.length > 1) {
301
+ this.log.info(`Got ${localRecords.length} screen recordings. Trying to merge them`);
302
+ try {
303
+ resultFilePath = await mergeScreenRecords(localRecords, this.log);
304
+ }
305
+ catch (e) {
306
+ this.log.warn(`Cannot merge the recorded files. The most recent screen recording is going to be returned as the result. ` +
307
+ `Original error: ${e.message}`);
308
+ }
309
+ }
310
+ if (lodash_1.default.isEmpty(options.remotePath)) {
311
+ const { size } = await support_1.fs.stat(resultFilePath);
312
+ this.log.debug(`The size of the resulting screen recording is ${support_1.util.toReadableSizeString(size)}`);
313
+ }
314
+ return await uploadRecordedMedia(resultFilePath, options.remotePath, options);
315
+ }
316
+ finally {
317
+ await support_1.fs.rimraf(tmpRoot);
318
+ this._screenRecordingProperties = null;
319
+ }
250
320
  };
251
- var _default = commands;
252
- exports.default = _default;
253
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbG9kYXNoIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfYXN5bmNib3giLCJfc3VwcG9ydCIsIl90ZWVuX3Byb2Nlc3MiLCJfcGF0aCIsImNvbW1hbmRzIiwiZXhwb3J0cyIsIlJFVFJZX1BBVVNFIiwiUkVUUllfVElNRU9VVCIsIk1BWF9SRUNPUkRJTkdfVElNRV9TRUMiLCJNQVhfVElNRV9TRUMiLCJERUZBVUxUX1JFQ09SRElOR19USU1FX1NFQyIsIlBST0NFU1NfU0hVVERPV05fVElNRU9VVCIsIlNDUkVFTlJFQ09SRF9CSU5BUlkiLCJERUZBVUxUX0VYVCIsIk1JTl9FTVVMQVRPUl9BUElfTEVWRUwiLCJGRk1QRUdfQklOQVJZIiwic3lzdGVtIiwiaXNXaW5kb3dzIiwidXBsb2FkUmVjb3JkZWRNZWRpYSIsImxvY2FsRmlsZSIsInJlbW90ZVBhdGgiLCJ1cGxvYWRPcHRpb25zIiwiXyIsImlzRW1wdHkiLCJ1dGlsIiwidG9Jbk1lbW9yeUJhc2U2NCIsInRvU3RyaW5nIiwidXNlciIsInBhc3MiLCJtZXRob2QiLCJoZWFkZXJzIiwiZmlsZUZpZWxkTmFtZSIsImZvcm1GaWVsZHMiLCJvcHRpb25zIiwiYXV0aCIsIm5ldCIsInVwbG9hZEZpbGUiLCJ2ZXJpZnlTY3JlZW5SZWNvcmRJc1N1cHBvcnRlZCIsImFkYiIsImlzRW11bGF0b3IiLCJhcGlMZXZlbCIsImdldEFwaUxldmVsIiwiRXJyb3IiLCJzY2hlZHVsZVNjcmVlblJlY29yZCIsInJlY29yZGluZ1Byb3BlcnRpZXMiLCJsb2ciLCJzdG9wcGVkIiwidGltZXIiLCJ2aWRlb1NpemUiLCJiaXRSYXRlIiwidGltZUxpbWl0IiwiYnVnUmVwb3J0IiwiY3VycmVudFRpbWVMaW1pdCIsImhhc1ZhbHVlIiwiY3VycmVudFRpbWVMaW1pdEludCIsInBhcnNlSW50IiwiaXNOYU4iLCJwYXRoT25EZXZpY2UiLCJ1dWlkVjQiLCJzdWJzdHJpbmciLCJyZWNvcmRpbmdQcm9jIiwic2NyZWVucmVjb3JkIiwib24iLCJjdXJyZW50RHVyYXRpb24iLCJnZXREdXJhdGlvbiIsImFzU2Vjb25kcyIsInRvRml4ZWQiLCJkZWJ1ZyIsInRpbWVMaW1pdEludCIsImNodW5rRHVyYXRpb24iLCJlIiwiZXJyb3IiLCJzdGFjayIsInN0YXJ0Iiwid2FpdEZvckNvbmRpdGlvbiIsImZpbGVFeGlzdHMiLCJ3YWl0TXMiLCJpbnRlcnZhbE1zIiwicmVjb3JkcyIsInB1c2giLCJyZWNvcmRpbmdQcm9jZXNzIiwibWVyZ2VTY3JlZW5SZWNvcmRzIiwibWVkaWFGaWxlcyIsImZzIiwid2hpY2giLCJjb25maWdDb250ZW50IiwibWFwIiwieCIsImpvaW4iLCJjb25maWdGaWxlIiwicGF0aCIsInJlc29sdmUiLCJkaXJuYW1lIiwid3JpdGVGaWxlIiwicmVzdWx0IiwiTWF0aCIsImZsb29yIiwiRGF0ZSIsImFyZ3MiLCJpbmZvIiwiZXhlYyIsInRlcm1pbmF0ZUJhY2tncm91bmRTY3JlZW5SZWNvcmRpbmciLCJmb3JjZSIsInBpZHMiLCJnZXRQSURzQnlOYW1lIiwicCIsInNoZWxsIiwiZXJyIiwibWVzc2FnZSIsInN0YXJ0UmVjb3JkaW5nU2NyZWVuIiwiZm9yY2VSZXN0YXJ0Iiwic3RvcFJlY29yZGluZ1NjcmVlbiIsIndhcm4iLCJfc2NyZWVuUmVjb3JkaW5nUHJvcGVydGllcyIsInJlY29yZCIsInJpbXJhZiIsInRpbWVvdXQiLCJwYXJzZUZsb2F0IiwidGltaW5nIiwiVGltZXIiLCJpc1J1bm5pbmciLCJzdG9wIiwiZXJyb3JBbmRUaHJvdyIsInRtcFJvb3QiLCJ0ZW1wRGlyIiwib3BlbkRpciIsImxvY2FsUmVjb3JkcyIsInBvc2l4IiwiYmFzZW5hbWUiLCJwdWxsIiwibGFzdCIsInJlc3VsdEZpbGVQYXRoIiwibGVuZ3RoIiwic2l6ZSIsInN0YXQiLCJ0b1JlYWRhYmxlU2l6ZVN0cmluZyIsIl9kZWZhdWx0IiwiZGVmYXVsdCJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL2xpYi9jb21tYW5kcy9yZWNvcmRzY3JlZW4uanMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCB7IHdhaXRGb3JDb25kaXRpb24gfSBmcm9tICdhc3luY2JveCc7XG5pbXBvcnQgeyB1dGlsLCBmcywgbmV0LCB0ZW1wRGlyLCBzeXN0ZW0sIHRpbWluZyB9IGZyb20gJ0BhcHBpdW0vc3VwcG9ydCc7XG5pbXBvcnQgeyBleGVjIH0gZnJvbSAndGVlbl9wcm9jZXNzJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuXG5cbmNvbnN0IGNvbW1hbmRzID0ge307XG5cbmNvbnN0IFJFVFJZX1BBVVNFID0gMzAwO1xuY29uc3QgUkVUUllfVElNRU9VVCA9IDUwMDA7XG5jb25zdCBNQVhfUkVDT1JESU5HX1RJTUVfU0VDID0gNjAgKiAzO1xuY29uc3QgTUFYX1RJTUVfU0VDID0gNjAgKiAzMDtcbmNvbnN0IERFRkFVTFRfUkVDT1JESU5HX1RJTUVfU0VDID0gTUFYX1JFQ09SRElOR19USU1FX1NFQztcbmNvbnN0IFBST0NFU1NfU0hVVERPV05fVElNRU9VVCA9IDEwICogMTAwMDtcbmNvbnN0IFNDUkVFTlJFQ09SRF9CSU5BUlkgPSAnc2NyZWVucmVjb3JkJztcbmNvbnN0IERFRkFVTFRfRVhUID0gJy5tcDQnO1xuY29uc3QgTUlOX0VNVUxBVE9SX0FQSV9MRVZFTCA9IDI3O1xuY29uc3QgRkZNUEVHX0JJTkFSWSA9IGBmZm1wZWcke3N5c3RlbS5pc1dpbmRvd3MoKSA/ICcuZXhlJyA6ICcnfWA7XG5cbmFzeW5jIGZ1bmN0aW9uIHVwbG9hZFJlY29yZGVkTWVkaWEgKGxvY2FsRmlsZSwgcmVtb3RlUGF0aCA9IG51bGwsIHVwbG9hZE9wdGlvbnMgPSB7fSkge1xuICBpZiAoXy5pc0VtcHR5KHJlbW90ZVBhdGgpKSB7XG4gICAgcmV0dXJuIChhd2FpdCB1dGlsLnRvSW5NZW1vcnlCYXNlNjQobG9jYWxGaWxlKSkudG9TdHJpbmcoKTtcbiAgfVxuXG4gIGNvbnN0IHt1c2VyLCBwYXNzLCBtZXRob2QsIGhlYWRlcnMsIGZpbGVGaWVsZE5hbWUsIGZvcm1GaWVsZHN9ID0gdXBsb2FkT3B0aW9ucztcbiAgY29uc3Qgb3B0aW9ucyA9IHtcbiAgICBtZXRob2Q6IG1ldGhvZCB8fCAnUFVUJyxcbiAgICBoZWFkZXJzLFxuICAgIGZpbGVGaWVsZE5hbWUsXG4gICAgZm9ybUZpZWxkcyxcbiAgfTtcbiAgaWYgKHVzZXIgJiYgcGFzcykge1xuICAgIG9wdGlvbnMuYXV0aCA9IHt1c2VyLCBwYXNzfTtcbiAgfVxuICBhd2FpdCBuZXQudXBsb2FkRmlsZShsb2NhbEZpbGUsIHJlbW90ZVBhdGgsIG9wdGlvbnMpO1xuICByZXR1cm4gJyc7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHZlcmlmeVNjcmVlblJlY29yZElzU3VwcG9ydGVkIChhZGIsIGlzRW11bGF0b3IpIHtcbiAgY29uc3QgYXBpTGV2ZWwgPSBhd2FpdCBhZGIuZ2V0QXBpTGV2ZWwoKTtcbiAgaWYgKGlzRW11bGF0b3IgJiYgYXBpTGV2ZWwgPCBNSU5fRU1VTEFUT1JfQVBJX0xFVkVMKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBTY3JlZW4gcmVjb3JkaW5nIGRvZXMgbm90IHdvcmsgb24gZW11bGF0b3JzIHJ1bm5pbmcgQW5kcm9pZCBBUEkgbGV2ZWwgbGVzcyB0aGFuICR7TUlOX0VNVUxBVE9SX0FQSV9MRVZFTH1gKTtcbiAgfVxuICBpZiAoYXBpTGV2ZWwgPCAxOSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgU2NyZWVuIHJlY29yZGluZyBub3QgYXZhaWxhYmxlIG9uIEFQSSBMZXZlbCAke2FwaUxldmVsfS4gTWluaW11bSBBUEkgTGV2ZWwgaXMgMTkuYCk7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gc2NoZWR1bGVTY3JlZW5SZWNvcmQgKGFkYiwgcmVjb3JkaW5nUHJvcGVydGllcywgbG9nID0gbnVsbCkge1xuICBpZiAocmVjb3JkaW5nUHJvcGVydGllcy5zdG9wcGVkKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3Qge1xuICAgIHRpbWVyLFxuICAgIHZpZGVvU2l6ZSxcbiAgICBiaXRSYXRlLFxuICAgIHRpbWVMaW1pdCxcbiAgICBidWdSZXBvcnQsXG4gIH0gPSByZWNvcmRpbmdQcm9wZXJ0aWVzO1xuXG4gIGxldCBjdXJyZW50VGltZUxpbWl0ID0gTUFYX1JFQ09SRElOR19USU1FX1NFQztcbiAgaWYgKHV0aWwuaGFzVmFsdWUocmVjb3JkaW5nUHJvcGVydGllcy5jdXJyZW50VGltZUxpbWl0KSkge1xuICAgIGNvbnN0IGN1cnJlbnRUaW1lTGltaXRJbnQgPSBwYXJzZUludChyZWNvcmRpbmdQcm9wZXJ0aWVzLmN1cnJlbnRUaW1lTGltaXQsIDEwKTtcbiAgICBpZiAoIWlzTmFOKGN1cnJlbnRUaW1lTGltaXRJbnQpICYmIGN1cnJlbnRUaW1lTGltaXRJbnQgPCBNQVhfUkVDT1JESU5HX1RJTUVfU0VDKSB7XG4gICAgICBjdXJyZW50VGltZUxpbWl0ID0gY3VycmVudFRpbWVMaW1pdEludDtcbiAgICB9XG4gIH1cbiAgY29uc3QgcGF0aE9uRGV2aWNlID0gYC9zZGNhcmQvJHt1dGlsLnV1aWRWNCgpLnN1YnN0cmluZygwLCA4KX0ke0RFRkFVTFRfRVhUfWA7XG4gIGNvbnN0IHJlY29yZGluZ1Byb2MgPSBhZGIuc2NyZWVucmVjb3JkKHBhdGhPbkRldmljZSwge1xuICAgIHZpZGVvU2l6ZSxcbiAgICBiaXRSYXRlLFxuICAgIHRpbWVMaW1pdDogY3VycmVudFRpbWVMaW1pdCxcbiAgICBidWdSZXBvcnQsXG4gIH0pO1xuXG4gIHJlY29yZGluZ1Byb2Mub24oJ2VuZCcsICgpID0+IHtcbiAgICBpZiAocmVjb3JkaW5nUHJvcGVydGllcy5zdG9wcGVkIHx8ICF1dGlsLmhhc1ZhbHVlKHRpbWVMaW1pdCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgY3VycmVudER1cmF0aW9uID0gdGltZXIuZ2V0RHVyYXRpb24oKS5hc1NlY29uZHMudG9GaXhlZCgwKTtcbiAgICBsb2c/LmRlYnVnKGBUaGUgb3ZlcmFsbCBzY3JlZW4gcmVjb3JkaW5nIGR1cmF0aW9uIGlzICR7Y3VycmVudER1cmF0aW9ufXMgc28gZmFyYCk7XG4gICAgY29uc3QgdGltZUxpbWl0SW50ID0gcGFyc2VJbnQodGltZUxpbWl0LCAxMCk7XG4gICAgaWYgKGlzTmFOKHRpbWVMaW1pdEludCkgfHwgY3VycmVudER1cmF0aW9uID49IHRpbWVMaW1pdEludCkge1xuICAgICAgbG9nPy5kZWJ1ZygnVGhlcmUgaXMgbm8gbmVlZCB0byBzdGFydCB0aGUgbmV4dCByZWNvcmRpbmcgY2h1bmsnKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICByZWNvcmRpbmdQcm9wZXJ0aWVzLmN1cnJlbnRUaW1lTGltaXQgPSB0aW1lTGltaXRJbnQgLSBjdXJyZW50RHVyYXRpb247XG4gICAgY29uc3QgY2h1bmtEdXJhdGlvbiA9IHJlY29yZGluZ1Byb3BlcnRpZXMuY3VycmVudFRpbWVMaW1pdCA8IE1BWF9SRUNPUkRJTkdfVElNRV9TRUNcbiAgICAgID8gcmVjb3JkaW5nUHJvcGVydGllcy5jdXJyZW50VGltZUxpbWl0XG4gICAgICA6IE1BWF9SRUNPUkRJTkdfVElNRV9TRUM7XG4gICAgbG9nPy5kZWJ1ZyhgU3RhcnRpbmcgdGhlIG5leHQgJHtjaHVua0R1cmF0aW9ufXMtY2h1bmsgYCArXG4gICAgICBgb2Ygc2NyZWVuIHJlY29yZGluZyBpbiBvcmRlciB0byBhY2hpZXZlICR7dGltZUxpbWl0SW50fXMgdG90YWwgZHVyYXRpb25gKTtcbiAgICAoYXN5bmMgKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgc2NoZWR1bGVTY3JlZW5SZWNvcmQoYWRiLCByZWNvcmRpbmdQcm9wZXJ0aWVzLCBsb2cpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBsb2c/LmVycm9yKGUuc3RhY2spO1xuICAgICAgICByZWNvcmRpbmdQcm9wZXJ0aWVzLnN0b3BwZWQgPSB0cnVlO1xuICAgICAgfVxuICAgIH0pKCk7XG4gIH0pO1xuXG4gIGF3YWl0IHJlY29yZGluZ1Byb2Muc3RhcnQoMCk7XG4gIHRyeSB7XG4gICAgYXdhaXQgd2FpdEZvckNvbmRpdGlvbihhc3luYyAoKSA9PiBhd2FpdCBhZGIuZmlsZUV4aXN0cyhwYXRoT25EZXZpY2UpLFxuICAgICAge3dhaXRNczogUkVUUllfVElNRU9VVCwgaW50ZXJ2YWxNczogUkVUUllfUEFVU0V9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgVGhlIGV4cGVjdGVkIHNjcmVlbiByZWNvcmQgZmlsZSAnJHtwYXRoT25EZXZpY2V9JyBkb2VzIG5vdCBleGlzdCBhZnRlciAke1JFVFJZX1RJTUVPVVR9bXMuIGAgK1xuICAgICAgYElzICR7U0NSRUVOUkVDT1JEX0JJTkFSWX0gdXRpbGl0eSBhdmFpbGFibGUgYW5kIG9wZXJhdGlvbmFsIG9uIHRoZSBkZXZpY2UgdW5kZXIgdGVzdD9gKTtcbiAgfVxuXG4gIHJlY29yZGluZ1Byb3BlcnRpZXMucmVjb3Jkcy5wdXNoKHBhdGhPbkRldmljZSk7XG4gIHJlY29yZGluZ1Byb3BlcnRpZXMucmVjb3JkaW5nUHJvY2VzcyA9IHJlY29yZGluZ1Byb2M7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIG1lcmdlU2NyZWVuUmVjb3JkcyAobWVkaWFGaWxlcywgbG9nID0gbnVsbCkge1xuICB0cnkge1xuICAgIGF3YWl0IGZzLndoaWNoKEZGTVBFR19CSU5BUlkpO1xuICB9IGNhdGNoIChlKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGAke0ZGTVBFR19CSU5BUll9IHV0aWxpdHkgaXMgbm90IGF2YWlsYWJsZSBpbiBQQVRILiBQbGVhc2UgaW5zdGFsbCBpdCBmcm9tIGh0dHBzOi8vd3d3LmZmbXBlZy5vcmcvYCk7XG4gIH1cbiAgY29uc3QgY29uZmlnQ29udGVudCA9IG1lZGlhRmlsZXNcbiAgICAubWFwKCh4KSA9PiBgZmlsZSAnJHt4fSdgKVxuICAgIC5qb2luKCdcXG4nKTtcbiAgY29uc3QgY29uZmlnRmlsZSA9IHBhdGgucmVzb2x2ZShwYXRoLmRpcm5hbWUobWVkaWFGaWxlc1swXSksICdjb25maWcudHh0Jyk7XG4gIGF3YWl0IGZzLndyaXRlRmlsZShjb25maWdGaWxlLCBjb25maWdDb250ZW50LCAndXRmOCcpO1xuICBsb2c/LmRlYnVnKGBHZW5lcmF0ZWQgZmZtcGVnIG1lcmdpbmcgY29uZmlnICcke2NvbmZpZ0ZpbGV9JyB3aXRoIGl0ZW1zOlxcbiR7Y29uZmlnQ29udGVudH1gKTtcbiAgY29uc3QgcmVzdWx0ID0gcGF0aC5yZXNvbHZlKHBhdGguZGlybmFtZShtZWRpYUZpbGVzWzBdKSwgYG1lcmdlXyR7TWF0aC5mbG9vcihuZXcgRGF0ZSgpKX0ke0RFRkFVTFRfRVhUfWApO1xuICBjb25zdCBhcmdzID0gWyctc2FmZScsICcwJywgJy1mJywgJ2NvbmNhdCcsICctaScsIGNvbmZpZ0ZpbGUsICctYycsICdjb3B5JywgcmVzdWx0XTtcbiAgbG9nPy5pbmZvKGBJbml0aWF0aW5nIHNjcmVlbiByZWNvcmRzIG1lcmdpbmcgdXNpbmcgdGhlIGNvbW1hbmQgJyR7RkZNUEVHX0JJTkFSWX0gJHthcmdzLmpvaW4oJyAnKX0nYCk7XG4gIGF3YWl0IGV4ZWMoRkZNUEVHX0JJTkFSWSwgYXJncyk7XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHRlcm1pbmF0ZUJhY2tncm91bmRTY3JlZW5SZWNvcmRpbmcgKGFkYiwgZm9yY2UgPSB0cnVlKSB7XG4gIGNvbnN0IHBpZHMgPSAoYXdhaXQgYWRiLmdldFBJRHNCeU5hbWUoU0NSRUVOUkVDT1JEX0JJTkFSWSkpXG4gICAgLm1hcCgocCkgPT4gYCR7cH1gKTtcbiAgaWYgKF8uaXNFbXB0eShwaWRzKSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIHRyeSB7XG4gICAgYXdhaXQgYWRiLnNoZWxsKFsna2lsbCcsIGZvcmNlID8gJy0xNScgOiAnLTInLCAuLi5waWRzXSk7XG4gICAgYXdhaXQgd2FpdEZvckNvbmRpdGlvbihhc3luYyAoKSA9PiBfLmlzRW1wdHkoYXdhaXQgYWRiLmdldFBJRHNCeU5hbWUoU0NSRUVOUkVDT1JEX0JJTkFSWSkpLCB7XG4gICAgICB3YWl0TXM6IFBST0NFU1NfU0hVVERPV05fVElNRU9VVCxcbiAgICAgIGludGVydmFsTXM6IDUwMCxcbiAgICB9KTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBVbmFibGUgdG8gc3RvcCB0aGUgYmFja2dyb3VuZCBzY3JlZW4gcmVjb3JkaW5nOiAke2Vyci5tZXNzYWdlfWApO1xuICB9XG59XG5cblxuLyoqXG4gKiBAdHlwZWRlZiB7T2JqZWN0fSBTdGFydFJlY29yZGluZ09wdGlvbnNcbiAqXG4gKiBAcHJvcGVydHkgez9zdHJpbmd9IHJlbW90ZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgcmVtb3RlIGxvY2F0aW9uLCB3aGVyZSB0aGUgY2FwdHVyZWQgdmlkZW8gc2hvdWxkIGJlIHVwbG9hZGVkLlxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhlIGZvbGxvd2luZyBwcm90b2NvbHMgYXJlIHN1cHBvcnRlZDogaHR0cC9odHRwcywgZnRwLlxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVsbCBvciBlbXB0eSBzdHJpbmcgdmFsdWUgKHRoZSBkZWZhdWx0IHNldHRpbmcpIG1lYW5zIHRoZSBjb250ZW50IG9mIHJlc3VsdGluZ1xuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZSBzaG91bGQgYmUgZW5jb2RlZCBhcyBCYXNlNjQgYW5kIHBhc3NlZCBhcyB0aGUgZW5kcG91bnQgcmVzcG9uc2UgdmFsdWUuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbiBleGNlcHRpb24gd2lsbCBiZSB0aHJvd24gaWYgdGhlIGdlbmVyYXRlZCBtZWRpYSBmaWxlIGlzIHRvbyBiaWcgdG9cbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpdCBpbnRvIHRoZSBhdmFpbGFibGUgcHJvY2VzcyBtZW1vcnkuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaGlzIG9wdGlvbiBvbmx5IGhhcyBhbiBlZmZlY3QgaWYgdGhlcmUgaXMgc2NyZWVuIHJlY29yZGluZyBwcm9jZXNzIGluIHByb2dyZWVzc1xuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5kIGBmb3JjZVJlc3RhcnRgIHBhcmFtZXRlciBpcyBub3Qgc2V0IHRvIGB0cnVlYC5cbiAqIEBwcm9wZXJ0eSB7P3N0cmluZ30gdXNlciAtIFRoZSBuYW1lIG9mIHRoZSB1c2VyIGZvciB0aGUgcmVtb3RlIGF1dGhlbnRpY2F0aW9uLiBPbmx5IHdvcmtzIGlmIGByZW1vdGVQYXRoYCBpcyBwcm92aWRlZC5cbiAqIEBwcm9wZXJ0eSB7P3N0cmluZ30gcGFzcyAtIFRoZSBwYXNzd29yZCBmb3IgdGhlIHJlbW90ZSBhdXRoZW50aWNhdGlvbi4gT25seSB3b3JrcyBpZiBgcmVtb3RlUGF0aGAgaXMgcHJvdmlkZWQuXG4gKiBAcHJvcGVydHkgez9zdHJpbmd9IG1ldGhvZCBbUFVUXSAtIFRoZSBodHRwIG11bHRpcGFydCB1cGxvYWQgbWV0aG9kIG5hbWUuIE9ubHkgd29ya3MgaWYgYHJlbW90ZVBhdGhgIGlzIHByb3ZpZGVkLlxuICogQHByb3BlcnR5IHs/T2JqZWN0fSBoZWFkZXJzIC0gQWRkaXRpb25hbCBoZWFkZXJzIG1hcHBpbmcgZm9yIG11bHRpcGFydCBodHRwKHMpIHVwbG9hZHNcbiAqIEBwcm9wZXJ0eSB7P3N0cmluZ30gZmlsZUZpZWxkTmFtZSBbZmlsZV0gLSBUaGUgbmFtZSBvZiB0aGUgZm9ybSBmaWVsZCwgd2hlcmUgdGhlIGZpbGUgY29udGVudCBCTE9CIHNob3VsZCBiZSBzdG9yZWQgZm9yXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHR0cChzKSB1cGxvYWRzXG4gKiBAcHJvcGVydHkgez9PYmplY3R8QXJyYXk8UGFpcj59IGZvcm1GaWVsZHMgLSBBZGRpdGlvbmFsIGZvcm0gZmllbGRzIGZvciBtdWx0aXBhcnQgaHR0cChzKSB1cGxvYWRzXG4gKiBAcHJvcGVydHkgez9zdHJpbmd9IHZpZGVvU2l6ZSAtIFRoZSBmb3JtYXQgaXMgd2lkdGh4aGVpZ2h0LlxuICogICAgICAgICAgICAgICAgICBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0aGUgZGV2aWNlJ3MgbmF0aXZlIGRpc3BsYXkgcmVzb2x1dGlvbiAoaWYgc3VwcG9ydGVkKSxcbiAqICAgICAgICAgICAgICAgICAgMTI4MHg3MjAgaWYgbm90LiBGb3IgYmVzdCByZXN1bHRzLFxuICogICAgICAgICAgICAgICAgICB1c2UgYSBzaXplIHN1cHBvcnRlZCBieSB5b3VyIGRldmljZSdzIEFkdmFuY2VkIFZpZGVvIENvZGluZyAoQVZDKSBlbmNvZGVyLlxuICogICAgICAgICAgICAgICAgICBGb3IgZXhhbXBsZSwgXCIxMjgweDcyMFwiXG4gKiBAcHJvcGVydHkgez9ib29sZWFufSBidWdSZXBvcnQgLSBTZXQgaXQgdG8gYHRydWVgIGluIG9yZGVyIHRvIGRpc3BsYXkgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBvbiB0aGUgdmlkZW8gb3ZlcmxheSxcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2ggYXMgYSB0aW1lc3RhbXAsIHRoYXQgaXMgaGVscGZ1bCBpbiB2aWRlb3MgY2FwdHVyZWQgdG8gaWxsdXN0cmF0ZSBidWdzLlxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhpcyBvcHRpb24gaXMgb25seSBzdXBwb3J0ZWQgc2luY2UgQVBJIGxldmVsIDI3IChBbmRyb2lkIFApLlxuICogQHByb3BlcnR5IHs/c3RyaW5nfG51bWJlcn0gdGltZUxpbWl0IC0gVGhlIG1heGltdW0gcmVjb3JkaW5nIHRpbWUsIGluIHNlY29uZHMuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIDE4MCAoMyBtaW51dGVzKS5cbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRoZSBtYXhpbXVtIHZhbHVlIGlzIDE4MDAgKDMwIG1pbnV0ZXMpLiBJZiB0aGUgcGFzc2VkIHZhbHVlIGlzIGdyZWF0ZXIgdGhhbiAxODAgdGhlblxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlIGFsZ29yaXRobSB3aWxsIHRyeSB0byBzY2hlZHVsZSBtdWx0aXBsZSBzY3JlZW4gcmVjb3JkaW5nIGNodW5rcyBhbmQgbWVyZ2UgdGhlXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHRpbmcgdmlkZW9zIGludG8gYSBzaW5nbGUgbWVkaWEgZmlsZSB1c2luZyBgZmZtcGVnYCB1dGlsaXR5LlxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSWYgdGhlIHV0aWxpdHkgaXMgbm90IGF2YWlsYWJsZSBpbiBQQVRIIHRoZW4gdGhlIG1vc3QgcmVjZW50IHNjcmVlbiByZWNvcmRpbmcgY2h1bmsgaXNcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdvaW5nIHRvIGJlIHJldHVybmVkLlxuICogQHByb3BlcnR5IHs/c3RyaW5nfG51bWJlcn0gYml0UmF0ZSAtIFRoZSB2aWRlbyBiaXQgcmF0ZSBmb3IgdGhlIHZpZGVvLCBpbiBiaXRzIHBlciBzZWNvbmQuXG4gKiAgICAgICAgICAgICAgICBUaGUgZGVmYXVsdCB2YWx1ZSBpcyA0MDAwMDAwICg0IE1iaXQvcykuIFlvdSBjYW4gaW5jcmVhc2UgdGhlIGJpdCByYXRlIHRvIGltcHJvdmUgdmlkZW8gcXVhbGl0eSxcbiAqICAgICAgICAgICAgICAgIGJ1dCBkb2luZyBzbyByZXN1bHRzIGluIGxhcmdlciBtb3ZpZSBmaWxlcy5cbiAqIEBwcm9wZXJ0eSB7P2Jvb2xlYW59IGZvcmNlUmVzdGFydCAtIFdoZXRoZXIgdG8gdHJ5IHRvIGNhdGNoIGFuZCB1cGxvYWQvcmV0dXJuIHRoZSBjdXJyZW50bHkgcnVubmluZyBzY3JlZW4gcmVjb3JkaW5nXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoYGZhbHNlYCwgdGhlIGRlZmF1bHQgc2V0dGluZykgb3IgaWdub3JlIHRoZSByZXN1bHQgb2YgaXQgYW5kIHN0YXJ0IGEgbmV3IHJlY29yZGluZ1xuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW1tZWRpYXRlbHkgKGB0cnVlYCkuXG4gKi9cblxuLyoqXG4gKiBSZWNvcmQgdGhlIGRpc3BsYXkgb2YgYSByZWFsIGRldmljZXMgcnVubmluZyBBbmRyb2lkIDQuNCAoQVBJIGxldmVsIDE5KSBhbmQgaGlnaGVyLlxuICogRW11bGF0b3JzIGFyZSBzdXBwb3J0ZWQgc2luY2UgQVBJIGxldmVsIDI3IChBbmRyb2lkIFApLlxuICogSXQgcmVjb3JkcyBzY3JlZW4gYWN0aXZpdHkgdG8gYW4gTVBFRy00IGZpbGUuIEF1ZGlvIGlzIG5vdCByZWNvcmRlZCB3aXRoIHRoZSB2aWRlbyBmaWxlLlxuICogSWYgc2NyZWVuIHJlY29yZGluZyBoYXMgYmVlbiBhbHJlYWR5IHN0YXJ0ZWQgdGhlbiB0aGUgY29tbWFuZCB3aWxsIHN0b3AgaXQgZm9yY2VmdWxseSBhbmQgc3RhcnQgYSBuZXcgb25lLlxuICogVGhlIHByZXZpb3VzbHkgcmVjb3JkZWQgdmlkZW8gZmlsZSB3aWxsIGJlIGRlbGV0ZWQuXG4gKlxuICogQHBhcmFtIHs/U3RhcnRSZWNvcmRpbmdPcHRpb25zfSBvcHRpb25zIC0gVGhlIGF2YWlsYWJsZSBvcHRpb25zLlxuICogQHJldHVybnMge3N0cmluZ30gQmFzZTY0LWVuY29kZWQgY29udGVudCBvZiB0aGUgcmVjb3JkZWQgbWVkaWEgZmlsZSBpZlxuICogICAgICAgICAgICAgICAgICAgYW55IHNjcmVlbiByZWNvcmRpbmcgaXMgY3VycmVudGx5IHJ1bm5pbmcgb3IgYW4gZW1wdHkgc3RyaW5nLlxuICogQHRocm93cyB7RXJyb3J9IElmIHNjcmVlbiByZWNvcmRpbmcgaGFzIGZhaWxlZCB0byBzdGFydCBvciBpcyBub3Qgc3VwcG9ydGVkIG9uIHRoZSBkZXZpY2UgdW5kZXIgdGVzdC5cbiAqL1xuY29tbWFuZHMuc3RhcnRSZWNvcmRpbmdTY3JlZW4gPSBhc3luYyBmdW5jdGlvbiBzdGFydFJlY29yZGluZ1NjcmVlbiAob3B0aW9ucyA9IHt9KSB7XG4gIGF3YWl0IHZlcmlmeVNjcmVlblJlY29yZElzU3VwcG9ydGVkKHRoaXMuYWRiLCB0aGlzLmlzRW11bGF0b3IoKSk7XG5cbiAgbGV0IHJlc3VsdCA9ICcnO1xuICBjb25zdCB7dmlkZW9TaXplLCB0aW1lTGltaXQgPSBERUZBVUxUX1JFQ09SRElOR19USU1FX1NFQywgYnVnUmVwb3J0LCBiaXRSYXRlLCBmb3JjZVJlc3RhcnR9ID0gb3B0aW9ucztcbiAgaWYgKCFmb3JjZVJlc3RhcnQpIHtcbiAgICByZXN1bHQgPSBhd2FpdCB0aGlzLnN0b3BSZWNvcmRpbmdTY3JlZW4ob3B0aW9ucyk7XG4gIH1cblxuICBpZiAoYXdhaXQgdGVybWluYXRlQmFja2dyb3VuZFNjcmVlblJlY29yZGluZyh0aGlzLmFkYiwgdHJ1ZSkpIHtcbiAgICB0aGlzLmxvZy53YXJuKGBUaGVyZSB3ZXJlIHNvbWUgJHtTQ1JFRU5SRUNPUkRfQklOQVJZfSBwcm9jZXNzIGxlZnRvdmVycyBydW5uaW5nIGAgK1xuICAgICAgYGluIHRoZSBiYWNrZ3JvdW5kLiBNYWtlIHN1cmUgeW91IHN0b3Agc2NyZWVuIHJlY29yZGluZyBlYWNoIHRpbWUgYWZ0ZXIgaXQgaXMgc3RhcnRlZCwgYCArXG4gICAgICBgb3RoZXJ3aXNlIHRoZSByZWNvcmRlZCBtZWRpYSBtaWdodCBxdWlja2x5IGV4Y2VlZCBhbGwgdGhlIGZyZWUgc3BhY2Ugb24gdGhlIGRldmljZSB1bmRlciB0ZXN0LmApO1xuICB9XG5cbiAgaWYgKCFfLmlzRW1wdHkodGhpcy5fc2NyZWVuUmVjb3JkaW5nUHJvcGVydGllcykpIHtcbiAgICBmb3IgKGNvbnN0IHJlY29yZCBvZiAodGhpcy5fc2NyZWVuUmVjb3JkaW5nUHJvcGVydGllcy5yZWNvcmRzIHx8IFtdKSkge1xuICAgICAgYXdhaXQgdGhpcy5hZGIucmltcmFmKHJlY29yZCk7XG4gICAgfVxuICAgIHRoaXMuX3NjcmVlblJlY29yZGluZ1Byb3BlcnRpZXMgPSBudWxsO1xuICB9XG5cbiAgY29uc3QgdGltZW91dCA9IHBhcnNlRmxvYXQodGltZUxpbWl0KTtcbiAgaWYgKGlzTmFOKHRpbWVvdXQpIHx8IHRpbWVvdXQgPiBNQVhfVElNRV9TRUMgfHwgdGltZW91dCA8PSAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBUaGUgdGltZUxpbWl0IHZhbHVlIG11c3QgYmUgaW4gcmFuZ2UgWzEsICR7TUFYX1RJTUVfU0VDfV0gc2Vjb25kcy4gYCArXG4gICAgICBgVGhlIHZhbHVlIG9mICcke3RpbWVMaW1pdH0nIGhhcyBiZWVuIHBhc3NlZCBpbnN0ZWFkLmApO1xuICB9XG5cbiAgdGhpcy5fc2NyZWVuUmVjb3JkaW5nUHJvcGVydGllcyA9IHtcbiAgICB0aW1lcjogbmV3IHRpbWluZy5UaW1lcigpLnN0YXJ0KCksXG4gICAgdmlkZW9TaXplLFxuICAgIHRpbWVMaW1pdCxcbiAgICBjdXJyZW50VGltZUxpbWl0OiB0aW1lTGltaXQsXG4gICAgYml0UmF0ZSxcbiAgICBidWdSZXBvcnQsXG4gICAgcmVjb3JkczogW10sXG4gICAgcmVjb3JkaW5nUHJvY2VzczogbnVsbCxcbiAgICBzdG9wcGVkOiBmYWxzZSxcbiAgfTtcbiAgYXdhaXQgc2NoZWR1bGVTY3JlZW5SZWNvcmQodGhpcy5hZGIsIHRoaXMuX3NjcmVlblJlY29yZGluZ1Byb3BlcnRpZXMsIHRoaXMubG9nKTtcbiAgcmV0dXJuIHJlc3VsdDtcbn07XG5cbi8qKlxuICogQHR5cGVkZWYge09iamVjdH0gU3RvcFJlY29yZGluZ09wdGlvbnNcbiAqXG4gKiBAcHJvcGVydHkgez9zdHJpbmd9IHJlbW90ZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgcmVtb3RlIGxvY2F0aW9uLCB3aGVyZSB0aGUgcmVzdWx0aW5nIHZpZGVvIHNob3VsZCBiZSB1cGxvYWRlZC5cbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRoZSBmb2xsb3dpbmcgcHJvdG9jb2xzIGFyZSBzdXBwb3J0ZWQ6IGh0dHAvaHR0cHMsIGZ0cC5cbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bGwgb3IgZW1wdHkgc3RyaW5nIHZhbHVlICh0aGUgZGVmYXVsdCBzZXR0aW5nKSBtZWFucyB0aGUgY29udGVudCBvZiByZXN1bHRpbmdcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUgc2hvdWxkIGJlIGVuY29kZWQgYXMgQmFzZTY0IGFuZCBwYXNzZWQgYXMgdGhlIGVuZHBvdW50IHJlc3BvbnNlIHZhbHVlLlxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQW4gZXhjZXB0aW9uIHdpbGwgYmUgdGhyb3duIGlmIHRoZSBnZW5lcmF0ZWQgbWVkaWEgZmlsZSBpcyB0b28gYmlnIHRvXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXQgaW50byB0aGUgYXZhaWxhYmxlIHByb2Nlc3MgbWVtb3J5LlxuICogQHByb3BlcnR5IHs/c3RyaW5nfSB1c2VyIC0gVGhlIG5hbWUgb2YgdGhlIHVzZXIgZm9yIHRoZSByZW1vdGUgYXV0aGVudGljYXRpb24uXG4gKiBAcHJvcGVydHkgez9zdHJpbmd9IHBhc3MgLSBUaGUgcGFzc3dvcmQgZm9yIHRoZSByZW1vdGUgYXV0aGVudGljYXRpb24uXG4gKiBAcHJvcGVydHkgez9zdHJpbmd9IG1ldGhvZCAtIFRoZSBodHRwIG11bHRpcGFydCB1cGxvYWQgbWV0aG9kIG5hbWUuIFRoZSAnUFVUJyBvbmUgaXMgdXNlZCBieSBkZWZhdWx0LlxuICogQHByb3BlcnR5IHs/T2JqZWN0fSBoZWFkZXJzIC0gQWRkaXRpb25hbCBoZWFkZXJzIG1hcHBpbmcgZm9yIG11bHRpcGFydCBodHRwKHMpIHVwbG9hZHNcbiAqIEBwcm9wZXJ0eSB7P3N0cmluZ30gZmlsZUZpZWxkTmFtZSBbZmlsZV0gLSBUaGUgbmFtZSBvZiB0aGUgZm9ybSBmaWVsZCwgd2hlcmUgdGhlIGZpbGUgY29udGVudCBCTE9CIHNob3VsZCBiZSBzdG9yZWQgZm9yXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHR0cChzKSB1cGxvYWRzXG4gKiBAcHJvcGVydHkgez9PYmplY3R8QXJyYXk8UGFpcj59IGZvcm1GaWVsZHMgLSBBZGRpdGlvbmFsIGZvcm0gZmllbGRzIGZvciBtdWx0aXBhcnQgaHR0cChzKSB1cGxvYWRzXG4gKi9cblxuLyoqXG4gKiBTdG9wIHJlY29yZGluZyB0aGUgc2NyZWVuLlxuICogSWYgbm8gc2NyZWVuIHJlY29yZGluZyBoYXMgYmVlbiBzdGFydGVkIGJlZm9yZSB0aGVuIHRoZSBtZXRob2QgcmV0dXJucyBhbiBlbXB0eSBzdHJpbmcuXG4gKlxuICogQHBhcmFtIHs/U3RvcFJlY29yZGluZ09wdGlvbnN9IG9wdGlvbnMgLSBUaGUgYXZhaWxhYmxlIG9wdGlvbnMuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBCYXNlNjQtZW5jb2RlZCBjb250ZW50IG9mIHRoZSByZWNvcmRlZCBtZWRpYSBmaWxlIGlmICdyZW1vdGVQYXRoJ1xuICogICAgICAgICAgICAgICAgICAgcGFyYW1ldGVyIGlzIGZhbHN5IG9yIGFuIGVtcHR5IHN0cmluZy5cbiAqIEB0aHJvd3Mge0Vycm9yfSBJZiB0aGVyZSB3YXMgYW4gZXJyb3Igd2hpbGUgZ2V0dGluZyB0aGUgbmFtZSBvZiBhIG1lZGlhIGZpbGVcbiAqICAgICAgICAgICAgICAgICBvciB0aGUgZmlsZSBjb250ZW50IGNhbm5vdCBiZSB1cGxvYWRlZCB0byB0aGUgcmVtb3RlIGxvY2F0aW9uXG4gKiAgICAgICAgICAgICAgICAgb3Igc2NyZWVuIHJlY29yZGluZyBpcyBub3Qgc3VwcG9ydGVkIG9uIHRoZSBkZXZpY2UgdW5kZXIgdGVzdC5cbiAqL1xuY29tbWFuZHMuc3RvcFJlY29yZGluZ1NjcmVlbiA9IGFzeW5jIGZ1bmN0aW9uIHN0b3BSZWNvcmRpbmdTY3JlZW4gKG9wdGlvbnMgPSB7fSkge1xuICBhd2FpdCB2ZXJpZnlTY3JlZW5SZWNvcmRJc1N1cHBvcnRlZCh0aGlzLmFkYiwgdGhpcy5pc0VtdWxhdG9yKCkpO1xuXG4gIGlmICghXy5pc0VtcHR5KHRoaXMuX3NjcmVlblJlY29yZGluZ1Byb3BlcnRpZXMpKSB7XG4gICAgdGhpcy5fc2NyZWVuUmVjb3JkaW5nUHJvcGVydGllcy5zdG9wcGVkID0gdHJ1ZTtcbiAgfVxuXG4gIHRyeSB7XG4gICAgYXdhaXQgdGVybWluYXRlQmFja2dyb3VuZFNjcmVlblJlY29yZGluZyh0aGlzLmFkYiwgZmFsc2UpO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICB0aGlzLmxvZy53YXJuKGVyci5tZXNzYWdlKTtcbiAgICBpZiAoIV8uaXNFbXB0eSh0aGlzLl9zY3JlZW5SZWNvcmRpbmdQcm9wZXJ0aWVzKSkge1xuICAgICAgdGhpcy5sb2cud2FybignVGhlIHJlc3VsdGluZyB2aWRlbyBtaWdodCBiZSBjb3JydXB0ZWQnKTtcbiAgICB9XG4gIH1cblxuICBpZiAoXy5pc0VtcHR5KHRoaXMuX3NjcmVlblJlY29yZGluZ1Byb3BlcnRpZXMpKSB7XG4gICAgdGhpcy5sb2cuaW5mbyhgU2NyZWVuIHJlY29yZGluZyBoYXMgbm90IGJlZW4gcHJldmlvdXNseSBzdGFydGVkIGJ5IEFwcGl1bS4gVGhlcmUgaXMgbm90aGluZyB0byBzdG9wYCk7XG4gICAgcmV0dXJuICcnO1xuICB9XG5cbiAgaWYgKHRoaXMuX3NjcmVlblJlY29yZGluZ1Byb3BlcnRpZXMucmVjb3JkaW5nUHJvY2VzcyAmJiB0aGlzLl9zY3JlZW5SZWNvcmRpbmdQcm9wZXJ0aWVzLnJlY29yZGluZ1Byb2Nlc3MuaXNSdW5uaW5nKSB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHRoaXMuX3NjcmVlblJlY29yZGluZ1Byb3BlcnRpZXMucmVjb3JkaW5nUHJvY2Vzcy5zdG9wKCdTSUdJTlQnLCBQUk9DRVNTX1NIVVRET1dOX1RJTUVPVVQpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRoaXMubG9nLmVycm9yQW5kVGhyb3coYFVuYWJsZSB0byBzdG9wIHNjcmVlbiByZWNvcmRpbmcgd2l0aGluICR7UFJPQ0VTU19TSFVURE9XTl9USU1FT1VUfW1zYCk7XG4gICAgfVxuICAgIHRoaXMuX3NjcmVlblJlY29yZGluZ1Byb3BlcnRpZXMucmVjb3JkaW5nUHJvY2VzcyA9IG51bGw7XG4gIH1cblxuICBpZiAoXy5pc0VtcHR5KHRoaXMuX3NjcmVlblJlY29yZGluZ1Byb3BlcnRpZXMucmVjb3JkcykpIHtcbiAgICB0aGlzLmxvZy5lcnJvckFuZFRocm93KGBObyBzY3JlZW4gcmVjb3JkaW5ncyBoYXZlIGJlZW4gc3RvcmVkIG9uIHRoZSBkZXZpY2Ugc28gZmFyLiBgICtcbiAgICAgIGBBcmUgeW91IHN1cmUgdGhlICR7U0NSRUVOUkVDT1JEX0JJTkFSWX0gdXRpbGl0eSB3b3JrcyBhcyBleHBlY3RlZD9gKTtcbiAgfVxuXG4gIGNvbnN0IHRtcFJvb3QgPSBhd2FpdCB0ZW1wRGlyLm9wZW5EaXIoKTtcbiAgdHJ5IHtcbiAgICBjb25zdCBsb2NhbFJlY29yZHMgPSBbXTtcbiAgICBmb3IgKGNvbnN0IHBhdGhPbkRldmljZSBvZiB0aGlzLl9zY3JlZW5SZWNvcmRpbmdQcm9wZXJ0aWVzLnJlY29yZHMpIHtcbiAgICAgIGxvY2FsUmVjb3Jkcy5wdXNoKHBhdGgucmVzb2x2ZSh0bXBSb290LCBwYXRoLnBvc2l4LmJhc2VuYW1lKHBhdGhPbkRldmljZSkpKTtcbiAgICAgIGF3YWl0IHRoaXMuYWRiLnB1bGwocGF0aE9uRGV2aWNlLCBfLmxhc3QobG9jYWxSZWNvcmRzKSk7XG4gICAgICBhd2FpdCB0aGlzLmFkYi5yaW1yYWYocGF0aE9uRGV2aWNlKTtcbiAgICB9XG4gICAgbGV0IHJlc3VsdEZpbGVQYXRoID0gXy5sYXN0KGxvY2FsUmVjb3Jkcyk7XG4gICAgaWYgKGxvY2FsUmVjb3Jkcy5sZW5ndGggPiAxKSB7XG4gICAgICB0aGlzLmxvZy5pbmZvKGBHb3QgJHtsb2NhbFJlY29yZHMubGVuZ3RofSBzY3JlZW4gcmVjb3JkaW5ncy4gVHJ5aW5nIHRvIG1lcmdlIHRoZW1gKTtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJlc3VsdEZpbGVQYXRoID0gYXdhaXQgbWVyZ2VTY3JlZW5SZWNvcmRzKGxvY2FsUmVjb3JkcywgdGhpcy5sb2cpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aGlzLmxvZy53YXJuKGBDYW5ub3QgbWVyZ2UgdGhlIHJlY29yZGVkIGZpbGVzLiBUaGUgbW9zdCByZWNlbnQgc2NyZWVuIHJlY29yZGluZyBpcyBnb2luZyB0byBiZSByZXR1cm5lZCBhcyB0aGUgcmVzdWx0LiBgICtcbiAgICAgICAgICBgT3JpZ2luYWwgZXJyb3I6ICR7ZS5tZXNzYWdlfWApO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoXy5pc0VtcHR5KG9wdGlvbnMucmVtb3RlUGF0aCkpIHtcbiAgICAgIGNvbnN0IHtzaXplfSA9IGF3YWl0IGZzLnN0YXQocmVzdWx0RmlsZVBhdGgpO1xuICAgICAgdGhpcy5sb2cuZGVidWcoYFRoZSBzaXplIG9mIHRoZSByZXN1bHRpbmcgc2NyZWVuIHJlY29yZGluZyBpcyAke3V0aWwudG9SZWFkYWJsZVNpemVTdHJpbmcoc2l6ZSl9YCk7XG4gICAgfVxuICAgIHJldHVybiBhd2FpdCB1cGxvYWRSZWNvcmRlZE1lZGlhKHJlc3VsdEZpbGVQYXRoLCBvcHRpb25zLnJlbW90ZVBhdGgsIG9wdGlvbnMpO1xuICB9IGZpbmFsbHkge1xuICAgIGF3YWl0IGZzLnJpbXJhZih0bXBSb290KTtcbiAgICB0aGlzLl9zY3JlZW5SZWNvcmRpbmdQcm9wZXJ0aWVzID0gbnVsbDtcbiAgfVxufTtcblxuXG5leHBvcnQgeyBjb21tYW5kcyB9O1xuZXhwb3J0IGRlZmF1bHQgY29tbWFuZHM7XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBQUEsSUFBQUEsT0FBQSxHQUFB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
321
+ exports.default = commands;
322
+ //# sourceMappingURL=recordscreen.js.map