@aiot-toolkit/emulator 2.0.5-beta.22 → 2.0.5-beta.24

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.
@@ -57,14 +57,14 @@ const VelaImageVersionList = exports.VelaImageVersionList = [{
57
57
  label: 'vela-miwear-watch-5.0',
58
58
  description: '适用于手表/手环的,带表盘的 vela 5.0 镜像,不可自定义模拟器尺寸',
59
59
  value: _Vvd.VelaImageType.VELA_MIWEAR_WATCH_5,
60
- time: '20250619',
60
+ time: '20250716',
61
61
  hide: false,
62
62
  icon: ''
63
63
  }, {
64
64
  label: 'vela-watch-5.0',
65
65
  description: '适用于手表/手环的,不带表盘的 vela 5.0 镜像,可自定义模拟器尺寸',
66
66
  value: _Vvd.VelaImageType.VELA_WATCH_5,
67
- time: '20250619',
67
+ time: '20250716',
68
68
  hide: false,
69
69
  icon: ''
70
70
  }, {
@@ -93,7 +93,7 @@ const EmulatorEnvVersion = exports.EmulatorEnvVersion = {
93
93
  name: '模拟器资源版本管理',
94
94
  [_Vvd.SDKParts.EMULATOR]: '0.1.0',
95
95
  [_Vvd.SDKParts.QA]: '0.0.1',
96
- [_Vvd.SDKParts.SKINS]: '0.0.9',
96
+ [_Vvd.SDKParts.SKINS]: '0.0.10',
97
97
  [_Vvd.SDKParts.SYSTEM_IMAGES]: VelaImageVersionList[0].time,
98
98
  [_Vvd.SDKParts.MODEM_SIMULATOR]: '0.0.3'
99
99
  };
package/lib/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { VvdManager } from './vvd';
2
- import { getSystemArch, tryRun } from './utils';
2
+ export { getSystemArch, tryRun } from './utils';
3
3
  export * from './instance';
4
4
  export * from './typing/Vvd';
5
5
  export * from './typing/Instance';
6
6
  export * from './emulatorutil';
7
- export { getSystemArch, tryRun };
7
+ export * from './shared';
package/lib/index.js CHANGED
@@ -75,4 +75,16 @@ Object.keys(_emulatorutil).forEach(function (key) {
75
75
  return _emulatorutil[key];
76
76
  }
77
77
  });
78
+ });
79
+ var _shared = require("./shared");
80
+ Object.keys(_shared).forEach(function (key) {
81
+ if (key === "default" || key === "__esModule") return;
82
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
83
+ if (key in exports && exports[key] === _shared[key]) return;
84
+ Object.defineProperty(exports, key, {
85
+ enumerable: true,
86
+ get: function () {
87
+ return _shared[key];
88
+ }
89
+ });
78
90
  });
@@ -10,7 +10,7 @@ declare abstract class CommonEmulatorInstance {
10
10
  static appInstallFailedFlag: RegExp;
11
11
  static appUninstalledFlag: RegExp;
12
12
  static appStartedFlag: RegExp;
13
- static isAppInstalled(log: string): boolean;
13
+ static isAppInstalled(...args: any[]): boolean;
14
14
  static isAppInstallFailed(log: string): boolean;
15
15
  static isAppUninstalled(log: string, packageName: string): boolean;
16
16
  static isEmulatorStarted(log: string): boolean;
@@ -26,7 +26,10 @@ declare abstract class CommonEmulatorInstance {
26
26
  logger: (log: string) => void;
27
27
  constructor(params: IEmulatorInstanceParams);
28
28
  /** 安装应用,留给子类实现 */
29
- abstract install(rpkPath: string, packageName: string): Promise<void>;
29
+ abstract install(rpkPath: string, options?: {
30
+ packageName?: string;
31
+ size?: number;
32
+ }): Promise<void>;
30
33
  /** 卸载应用,留给子类实现 */
31
34
  abstract uninstall(packageName: string): Promise<void>;
32
35
  /** 重启应用 */
@@ -11,6 +11,8 @@ var adbMiwt = _adb;
11
11
  var _utils = require("../utils");
12
12
  var _logcat = require("../vvd/logcat");
13
13
  var _emulatorutil = require("../emulatorutil");
14
+ var _shared = require("../shared");
15
+ var _fs = _interopRequireDefault(require("fs"));
14
16
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
15
17
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
16
18
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -21,14 +23,15 @@ class CommonEmulatorInstance {
21
23
  static appInstallFailedFlag = /uv_mq_read_cb: install prepare failed/;
22
24
  static appUninstalledFlag = /uninstalled app/;
23
25
  static appStartedFlag = /Start App loop/;
24
- static isAppInstalled(log) {
26
+ static isAppInstalled() {
27
+ const log = arguments.length <= 0 ? undefined : arguments[0];
25
28
  return this.appInstalledFlag.test(log);
26
29
  }
27
30
  static isAppInstallFailed(log) {
28
31
  return this.appInstallFailedFlag.test(log);
29
32
  }
30
33
  static isAppUninstalled(log, packageName) {
31
- return new RegExp(this.appUninstalledFlag + ':\\s+' + packageName).test(log);
34
+ return new RegExp(this.appUninstalledFlag.source + ':\\s+' + packageName).test(log);
32
35
  }
33
36
  static isEmulatorStarted(log) {
34
37
  return this.emulatorStartedFlag.test(log);
@@ -117,7 +120,25 @@ class CommonEmulatorInstance {
117
120
  }
118
121
  async pushAndInstall(rpkPath, appName) {
119
122
  const targetPath = await this.pushRpk(rpkPath, appName);
120
- await this.install(targetPath, appName);
123
+ try {
124
+ await this.install(targetPath, {
125
+ packageName: appName,
126
+ size: _fs.default.statSync(rpkPath).size
127
+ });
128
+ } catch (error) {
129
+ let timer;
130
+ if (error === _shared.RpkInstallFailedReason.Busy) {
131
+ this.logger('install process is busy, 3s later retry');
132
+ await new Promise((resolve, rejcet) => {
133
+ timer = setTimeout(() => {
134
+ this.pushAndInstall(rpkPath, appName).then(resolve).catch(rejcet);
135
+ }, 3000);
136
+ });
137
+ } else {
138
+ clearTimeout(timer);
139
+ throw error;
140
+ }
141
+ }
121
142
  }
122
143
 
123
144
  /** 关闭模拟器 */
@@ -9,7 +9,10 @@ declare class GoldfishInstance extends CommonEmulatorInstance {
9
9
  imageType: VelaImageType;
10
10
  static appDir: string;
11
11
  static emulatorStartedFlag: RegExp;
12
- install(rpkPath: string, appPackageName?: string): Promise<void>;
12
+ install(rpkPath: string, opt: {
13
+ packageName?: string;
14
+ size?: number;
15
+ }): Promise<void>;
13
16
  /**
14
17
  * 在模拟器中启动快应用
15
18
  * 通过vapp命令启动,调试时需额外配置--jsdebugger参数
@@ -23,10 +23,10 @@ class GoldfishInstance extends _common.default {
23
23
  imageType = (() => _Vvd.VelaImageType.DEV)();
24
24
  static appDir = '/data/app';
25
25
  static emulatorStartedFlag = /(NSH)/;
26
- async install(rpkPath, appPackageName) {
26
+ async install(rpkPath, opt) {
27
27
  // 基于 vapp 启动的应用只需要将 rpk 解压到指定的目录下
28
- rpkPath = rpkPath || `${this.appDir}/${appPackageName}.rpk`;
29
- const targetPath = `${this.appDir}/${appPackageName}`;
28
+ rpkPath = rpkPath || `${this.appDir}/${opt.packageName}.rpk`;
29
+ const targetPath = `${this.appDir}/${opt.packageName}`;
30
30
  const mkdirCmd = `adb -s ${this.sn} shell mkdir ${targetPath}`;
31
31
  this.logger(`Excuting: ${mkdirCmd}`);
32
32
  await adbMiwt.execAdbCmdAsync(mkdirCmd);
@@ -9,11 +9,15 @@ declare class MiwearInstance extends CommonEmulatorInstance {
9
9
  static appInstalledFlag: RegExp;
10
10
  imageType: VelaImageType;
11
11
  appDir: string;
12
+ static isAppInstalled(log: string, targetRpk: string): boolean;
12
13
  /**
13
14
  * 使用 pm 安装快应用
14
15
  * @param targeRpk 快应用的rpk文件路径
15
16
  */
16
- install(targeRpk: string): Promise<void>;
17
+ install(targeRpk: string, options?: {
18
+ packageName?: string;
19
+ size?: number;
20
+ }): Promise<void>;
17
21
  /**
18
22
  * 使用 pm 卸载快应用
19
23
  * @param packageName 快应用的包名
@@ -9,6 +9,7 @@ var _path = _interopRequireDefault(require("path"));
9
9
  var _utils = require("../utils");
10
10
  var _common = _interopRequireDefault(require("./common"));
11
11
  var _Vvd = require("../typing/Vvd");
12
+ var _shared = require("../shared");
12
13
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
14
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
14
15
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
@@ -21,40 +22,61 @@ class MiwearInstance extends _common.default {
21
22
  static appInstalledFlag = /InstallState_Finished|install finished/;
22
23
  imageType = (() => _Vvd.VelaImageType.REL)();
23
24
  appDir = '/data/quickapp/app';
24
-
25
+ static isAppInstalled(log, targetRpk) {
26
+ // 注意:targetRpk 可能包含特殊字符,需要转义
27
+ const escapedRpk = targetRpk.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
28
+ const reg = new RegExp(`executeInstall: rpk install: ${escapedRpk} .* success`);
29
+ return reg.test(log);
30
+ }
25
31
  /**
26
32
  * 使用 pm 安装快应用
27
33
  * @param targeRpk 快应用的rpk文件路径
28
34
  */
29
- async install(targeRpk) {
35
+ async install(targeRpk, options) {
30
36
  const installCmd = `adb -s ${this.sn} shell pm install ${targeRpk}`;
31
37
  this.logger(`Excuting: ${installCmd}`);
32
38
  adbMiwt.execAdbCmd(installCmd);
33
39
  return new Promise((resolve, reject) => {
40
+ const clearFn = () => {
41
+ clearTimeout(timer);
42
+ this.stdoutReadline.off('line', func);
43
+ };
34
44
  const func = msg => {
35
- if (MiwearInstance.isAppInstalled(msg)) {
36
- clearTimeout(timer);
45
+ if (MiwearInstance.isAppInstalled(msg, targeRpk)) {
37
46
  this.logger(`Install to ${this.vvdName} ${targeRpk} successfully`);
38
- this.stdoutReadline.off('line', func);
47
+ clearFn();
39
48
  resolve();
40
49
  }
41
50
  if (MiwearInstance.isAppInstallFailed(msg)) {
42
- clearTimeout(timer);
43
- this.stdoutReadline.off('line', func);
44
51
  this.logger(`Failed Install to ${this.vvdName} ${targeRpk}`);
45
- reject(msg);
52
+ this.logger(_shared.RpkInstallFailedReason.LowStorage);
53
+ clearFn();
54
+ reject(_shared.RpkInstallFailedReason.LowStorage);
55
+ }
56
+
57
+ // 当前安装进程被占用
58
+ if (msg.includes('installRpk: installation is running, please wait')) {
59
+ this.logger(_shared.RpkInstallFailedReason.Busy);
60
+ clearFn();
61
+ reject(_shared.RpkInstallFailedReason.Busy);
46
62
  }
47
63
  };
64
+ function getTimeout(size) {
65
+ if (!size) return 10 * 1000;
66
+ const mu = size / 1024 / 1024;
67
+ return mu < 1 ? 10 * 1000 : mu < 4 ? 60 * 1000 : 2 * 60 * 1000;
68
+ }
69
+ const timeout = getTimeout(options?.size);
48
70
  let timer = setTimeout(() => {
49
71
  this.stdoutReadline.off('line', func);
50
72
  this.logger(`Install to ${this.vvdName} ${targeRpk} timeout`);
51
- reject('Install timeout');
52
- }, 2 * 60 * 1000);
73
+ reject(_shared.RpkInstallFailedReason.Timeout);
74
+ }, timeout);
53
75
  this.stdoutReadline.on('line', func);
54
76
  this.stdoutReadline.on('close', () => {
55
- this.stdoutReadline.removeAllListeners();
77
+ this.stdoutReadline.off('line', func);
56
78
  clearTimeout(timer);
57
- reject('Device poweroff');
79
+ reject(_shared.RpkInstallFailedReason.Poweroff);
58
80
  });
59
81
  });
60
82
  }
@@ -98,9 +120,9 @@ class MiwearInstance extends _common.default {
98
120
  }
99
121
  async closeApp(appName) {
100
122
  const stopCmd = `adb -s ${this.sn} shell am stop ${appName}`;
123
+ this.logger(`Excuting: ${stopCmd}`);
101
124
  await adbMiwt.execAdbCmdAsync(stopCmd);
102
125
  this.logger(`Stop ${this.vvdName} ${appName} successfully`);
103
- // 这里是为了等am stop命令清除资源等
104
126
  }
105
127
  async reboot() {
106
128
  await super.reboot();
@@ -3,5 +3,4 @@ import MiwearInstance from './miwear';
3
3
  export declare class VelaMiwear5 extends MiwearInstance {
4
4
  imageType: VelaImageType;
5
5
  appDir: string;
6
- static emulatorStartedFlag: RegExp;
7
6
  }
@@ -10,6 +10,5 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
10
10
  class VelaMiwear5 extends _miwear.default {
11
11
  imageType = (() => _Vvd.VelaImageType.VELA_MIWEAR_WATCH_5)();
12
12
  appDir = '/data/app';
13
- static emulatorStartedFlag = /\[App Active Flag/;
14
13
  }
15
14
  exports.VelaMiwear5 = VelaMiwear5;
@@ -7,7 +7,10 @@ declare class PreInstance extends CommonEmulatorInstance {
7
7
  imageType: VelaImageType;
8
8
  appDir: string;
9
9
  static emulatorStartedFlag: RegExp;
10
- install(rpkPath: string, appPackageName?: string): Promise<void>;
10
+ install(rpkPath: string, opt: {
11
+ packageName: string;
12
+ size?: number;
13
+ }): Promise<void>;
11
14
  uninstall(packageName: string): Promise<void>;
12
15
  /**
13
16
  * 在模拟器中启动快应用
@@ -17,10 +17,10 @@ class PreInstance extends _common.default {
17
17
  imageType = (() => _Vvd.VelaImageType.PRE)();
18
18
  appDir = '/data/quickapp/app';
19
19
  static emulatorStartedFlag = /(NSH)/;
20
- async install(rpkPath, appPackageName) {
20
+ async install(rpkPath, opt) {
21
21
  // 基于 vapp 启动的应用只需要将 rpk 解压到指定的目录下
22
- rpkPath = rpkPath || `${this.appDir}/${appPackageName}.rpk`;
23
- const targetPath = `${this.appDir}/${appPackageName}`;
22
+ rpkPath = rpkPath || `${this.appDir}/${opt.packageName}.rpk`;
23
+ const targetPath = `${this.appDir}/${opt.packageName}`;
24
24
  const mkdirCmd = `adb -s ${this.sn} shell mkdir ${targetPath}`;
25
25
  this.logger(`Excuting: ${mkdirCmd}`);
26
26
  await adbMiwt.execAdbCmdAsync(mkdirCmd);
@@ -10,4 +10,9 @@ export declare class Vela5Instance extends MiwearInstance {
10
10
  * @param targeRpk 快应用的rpk文件路径
11
11
  */
12
12
  install(targeRpk: string): Promise<void>;
13
+ /** 使用 am start 启动快应用 */
14
+ startApp(packageName: string): Promise<void>;
15
+ /** 重启模拟器 */
16
+ reboot(): Promise<void>;
17
+ closeApp(appName: string): Promise<void>;
13
18
  }
@@ -4,7 +4,9 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.Vela5Instance = void 0;
7
+ var _shared = require("../shared");
7
8
  var _Vvd = require("../typing/Vvd");
9
+ var _logcat = require("../vvd/logcat");
8
10
  var _miwear = _interopRequireDefault(require("./miwear"));
9
11
  var adbMiwt = _interopRequireWildcard(require("@miwt/adb"));
10
12
  var _util = require("util");
@@ -12,7 +14,7 @@ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return
12
14
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
13
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
16
  class Vela5Instance extends _miwear.default {
15
- static emulatorStartedFlag = /adb_register_service/;
17
+ static emulatorStartedFlag = /booting completed|Boot completed/;
16
18
  static appStartedFlag = /Start main loop/;
17
19
  imageType = (() => _Vvd.VelaImageType.VELA_WATCH_5)();
18
20
  appDir = '/data/app';
@@ -35,5 +37,62 @@ class Vela5Instance extends _miwear.default {
35
37
  return Promise.reject(res);
36
38
  }
37
39
  }
40
+
41
+ /** 使用 am start 启动快应用 */
42
+ async startApp(packageName) {
43
+ const startCmd = `adb -s ${this.sn} shell am start ${packageName}`;
44
+ this.logger(`Excuting: ${startCmd}`);
45
+ adbMiwt.execAdbCmdAsync(startCmd);
46
+ return new Promise((resolve, reject) => {
47
+ const clearFn = () => {
48
+ clearTimeout(timer);
49
+ this.stdoutReadline.off('line', func);
50
+ };
51
+ const func = msg => {
52
+ if (Vela5Instance.appStartedFlag.test(msg)) {
53
+ this.logger(`Start ${packageName} for ${this.vvdName} successfully`);
54
+ clearFn();
55
+ resolve();
56
+ }
57
+ const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
58
+ const reg = new RegExp(`Failed to open: .+?/${escapedPackageName}/manifest\.json`);
59
+ if (reg.test(msg)) {
60
+ this.logger(`Failed Start ${packageName} for ${this.vvdName}`);
61
+ clearFn();
62
+ reject(_shared.RpkStartFailedReason.getManifestFailed);
63
+ }
64
+ };
65
+ let timer = setTimeout(() => {
66
+ this.stdoutReadline.off('line', func);
67
+ this.logger(`Start ${packageName} for ${this.vvdName} timeout`);
68
+ reject('Timeout');
69
+ }, 8 * 1000);
70
+ this.stdoutReadline.on('line', func);
71
+ });
72
+ }
73
+
74
+ /** 重启模拟器 */
75
+ async reboot() {
76
+ const rebootCmd = `adb -s ${this.sn} shell reboot`;
77
+ this.logger(`Excuting: ${rebootCmd}`);
78
+ await adbMiwt.execAdbCmdAsync(rebootCmd);
79
+ await this.isConnected();
80
+ if (this.logcatProcess.exitCode !== null) {
81
+ // 如果 logcat 进程被杀死,则重新创建
82
+ const r = (0, _logcat.creatLogcat)(this.sn, this.onStdout, this.onErrout);
83
+ this.logcatProcess = r.logcatProcess;
84
+ this.stdoutReadline = r.stdoutReadline;
85
+ this.stderrReadline = r.stderrReadline;
86
+ this.disposeReadlines = r.dispose;
87
+ }
88
+ }
89
+ async closeApp(appName) {
90
+ await super.closeApp(appName);
91
+ await new Promise(resolve => {
92
+ setTimeout(() => {
93
+ resolve();
94
+ }, 2000);
95
+ });
96
+ }
38
97
  }
39
98
  exports.Vela5Instance = Vela5Instance;
@@ -6,3 +6,12 @@ import { VelaImageType } from '../typing/Vvd';
6
6
  export declare function isVelaImageType(value: any): value is VelaImageType;
7
7
  export declare function isMiwearImageType(val: VelaImageType): boolean;
8
8
  export declare function getDefaultImage(): VelaImageType;
9
+ export declare enum RpkStartFailedReason {
10
+ getManifestFailed = "Get app manifest.json failed"
11
+ }
12
+ export declare enum RpkInstallFailedReason {
13
+ Busy = "Installation process is busy",
14
+ Timeout = "Install timeout",
15
+ Poweroff = "Device poweroff",
16
+ LowStorage = "Application exceeds maximum quantity or remaining space is low"
17
+ }
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.RpkStartFailedReason = exports.RpkInstallFailedReason = void 0;
6
7
  exports.getDefaultImage = getDefaultImage;
7
8
  exports.isMiwearImageType = isMiwearImageType;
8
9
  exports.isVelaImageType = isVelaImageType;
@@ -20,4 +21,15 @@ function isMiwearImageType(val) {
20
21
  }
21
22
  function getDefaultImage() {
22
23
  return _Vvd.VelaImageType.VELA_MIWEAR_WATCH_5;
23
- }
24
+ }
25
+ let RpkStartFailedReason = exports.RpkStartFailedReason = /*#__PURE__*/function (RpkStartFailedReason) {
26
+ RpkStartFailedReason["getManifestFailed"] = "Get app manifest.json failed";
27
+ return RpkStartFailedReason;
28
+ }({});
29
+ let RpkInstallFailedReason = exports.RpkInstallFailedReason = /*#__PURE__*/function (RpkInstallFailedReason) {
30
+ RpkInstallFailedReason["Busy"] = "Installation process is busy";
31
+ RpkInstallFailedReason["Timeout"] = "Install timeout";
32
+ RpkInstallFailedReason["Poweroff"] = "Device poweroff";
33
+ RpkInstallFailedReason["LowStorage"] = "Application exceeds maximum quantity or remaining space is low";
34
+ return RpkInstallFailedReason;
35
+ }({});
@@ -23,10 +23,9 @@
23
23
  "hw.lcd.height": 466,
24
24
  "hw.lcd.width": 466,
25
25
  "hw.mainKeys": "no",
26
- "hw.ramSize": 512,
26
+ "hw.ramSize": 1024,
27
27
  "hw.sdCard": "no",
28
28
  "hw.sensors.orientation": "yes",
29
- "hw.sensors.proximity": "yes",
30
29
  "hw.trackBall": "no",
31
30
  "image.sysdir.2": "",
32
31
  "runtime.network.latency": "none",
@@ -36,11 +35,11 @@
36
35
  "skin.name": "",
37
36
  "skin.path": "",
38
37
  "hw.accelerometer": "yes",
39
- "hw.gyroscope ": "yes",
40
- "hw.sensors.magnetic_field ": "yes",
41
- "hw.sensors.temperature ": "yes",
42
- "hw.sensors.proximity ": "yes",
43
- "hw.sensors.light ": "yes",
44
- "hw.sensors.humidity ": "yes",
45
- "hw.sensors.heart_rate ": "yes"
38
+ "hw.gyroscope": "yes",
39
+ "hw.sensors.proximity": "yes",
40
+ "hw.sensors.magnetic_field": "yes",
41
+ "hw.sensors.temperature": "yes",
42
+ "hw.sensors.light": "yes",
43
+ "hw.sensors.humidity": "yes",
44
+ "hw.sensors.heart_rate": "yes"
46
45
  }
@@ -4,8 +4,7 @@ export interface IVvdResourcePaths {
4
4
  imageHome?: string;
5
5
  }
6
6
  export declare enum IVvdArchType {
7
- arm = "arm",
8
- arm64 = "arm64"
7
+ arm = "arm"
9
8
  }
10
9
  export declare enum VelaImageType {
11
10
  VELA_MIWEAR_MINISOUND_5 = "vela-miwear-minisound-5.0",
@@ -37,6 +36,7 @@ export interface EmulatorPart {
37
36
  height: string;
38
37
  x: string;
39
38
  y: string;
39
+ corner_radius: string;
40
40
  shape?: string;
41
41
  flavor?: string;
42
42
  density?: string;
package/lib/typing/Vvd.js CHANGED
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.VelaImageType = exports.VELAHOME = exports.SDKParts = exports.IVvdArchType = void 0;
7
7
  let IVvdArchType = exports.IVvdArchType = /*#__PURE__*/function (IVvdArchType) {
8
8
  IVvdArchType["arm"] = "arm";
9
- IVvdArchType["arm64"] = "arm64";
10
9
  return IVvdArchType;
11
10
  }({});
12
11
  let VelaImageType = exports.VelaImageType = /*#__PURE__*/function (VelaImageType) {
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @param mode Optional modifiers that specify the behavior of the copy operation. It is possible to create a mask consisting of the bitwise OR of two or more values (e.g. fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE)
3
+ */
4
+ export declare function copyDir(source: string, target: string, mode?: number): Promise<void>;
5
+ /**
6
+ * @param mode Optional modifiers that specify the behavior of the copy operation. It is possible to create a mask consisting of the bitwise OR of two or more values (e.g. fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE)
7
+ */
8
+ export declare function copyDirSync(source: string, target: string, mode?: number): void;
package/lib/utils/file.js CHANGED
@@ -1 +1,48 @@
1
- "use strict";
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.copyDir = copyDir;
7
+ exports.copyDirSync = copyDirSync;
8
+ var _path = _interopRequireDefault(require("path"));
9
+ var _promises = _interopRequireDefault(require("fs/promises"));
10
+ var _fs = _interopRequireDefault(require("fs"));
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
+ /**
13
+ * @param mode Optional modifiers that specify the behavior of the copy operation. It is possible to create a mask consisting of the bitwise OR of two or more values (e.g. fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE)
14
+ */
15
+ async function copyDir(source, target, mode) {
16
+ const entries = await _promises.default.readdir(source, {
17
+ withFileTypes: true
18
+ });
19
+ await _promises.default.mkdir(target, {
20
+ recursive: true
21
+ });
22
+ await Promise.all(entries.map(entry => {
23
+ const srcPath = _path.default.join(source, entry.name);
24
+ const destPath = _path.default.join(target, entry.name);
25
+ return entry.isDirectory() ? copyDir(srcPath, destPath, mode) : _promises.default.copyFile(srcPath, destPath, mode);
26
+ }));
27
+ }
28
+
29
+ /**
30
+ * @param mode Optional modifiers that specify the behavior of the copy operation. It is possible to create a mask consisting of the bitwise OR of two or more values (e.g. fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE)
31
+ */
32
+ function copyDirSync(source, target, mode) {
33
+ _fs.default.mkdirSync(target, {
34
+ recursive: true
35
+ });
36
+ const entries = _fs.default.readdirSync(source, {
37
+ withFileTypes: true
38
+ });
39
+ for (const entry of entries) {
40
+ const srcPath = _path.default.join(source, entry.name);
41
+ const destPath = _path.default.join(target, entry.name);
42
+ if (entry.isDirectory()) {
43
+ copyDirSync(srcPath, destPath, mode);
44
+ } else {
45
+ _fs.default.copyFileSync(srcPath, destPath, mode);
46
+ }
47
+ }
48
+ }
@@ -4,6 +4,7 @@ import { EmulatorConfig } from '../emulatorutil';
4
4
  import { findInstance } from '../instance';
5
5
  import type { DownloadFileOptions } from 'ipull';
6
6
  import GrpcEmulator from './grpc';
7
+ export declare const isHeadlessEnvironment: () => boolean;
7
8
  export declare class VvdManager {
8
9
  private vvdHome;
9
10
  private sdkHome;
@@ -19,7 +20,7 @@ export declare class VvdManager {
19
20
  * @returns
20
21
  */
21
22
  createVvd(vvdParams: IVvdParams): boolean;
22
- getVvdDir(vvdName: string): string;
23
+ getVvdDir(vvdName: string): any;
23
24
  /** 根据AVD名字获取模拟器的详细信息 */
24
25
  getVvdInfo(vvdName: string): IVvdParams;
25
26
  /** 根据名字删除AVD */
@@ -50,6 +51,7 @@ export declare class VvdManager {
50
51
  /** 重置自定义的镜像目录 */
51
52
  resetImageDir(vvdName: string): void;
52
53
  getEmulatorBinPath(sdkHome: string): string;
54
+ oldEmulatorMigrate(vvdName: string): Promise<void>;
53
55
  getVvdStartCmd(options: IStartOptions | IStartWithSerialPort): Promise<string>;
54
56
  startVvd(options: IStartOptions | IStartWithSerialPort): Promise<{
55
57
  coldBoot: boolean;
package/lib/vvd/index.js CHANGED
@@ -3,8 +3,9 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.VvdManager = void 0;
6
+ exports.isHeadlessEnvironment = exports.VvdManager = void 0;
7
7
  var _ColorConsole = _interopRequireDefault(require("@aiot-toolkit/shared-utils/lib/ColorConsole"));
8
+ var _promises = _interopRequireDefault(require("fs/promises"));
8
9
  var _fs = _interopRequireDefault(require("fs"));
9
10
  var _os = _interopRequireDefault(require("os"));
10
11
  var _path = _interopRequireDefault(require("path"));
@@ -26,6 +27,7 @@ var _logcat = require("./logcat");
26
27
  var _sharedUtils = require("@aiot-toolkit/shared-utils");
27
28
  var _ILog = require("@aiot-toolkit/shared-utils/lib/interface/ILog");
28
29
  var _grpc = require("./grpc");
30
+ var _file = require("../utils/file");
29
31
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
30
32
  // TODO: 升级构建工具支持 esm @xujunjie
31
33
  const getPort = (async () => {
@@ -42,6 +44,21 @@ const EAvdParamsToIni = {
42
44
  abiType: 'arm64-v8a'
43
45
  }
44
46
  };
47
+ const isHeadlessEnvironment = () => {
48
+ // 检查是否在 Docker 容器中
49
+ if (_fs.default.existsSync('/.dockerenv')) return true;
50
+
51
+ // 检查是否在 WSL 环境中
52
+ if (process.env.WSL_DISTRO_NAME) return true;
53
+
54
+ // 检查是否设置了 DISPLAY 环境变量(Linux)
55
+ if (process.platform === 'linux' && !process.env.DISPLAY) return true;
56
+
57
+ // 检查是否在 CI 环境中
58
+ if (process.env.CI) return true;
59
+ return false;
60
+ };
61
+ exports.isHeadlessEnvironment = isHeadlessEnvironment;
45
62
  class VvdManager {
46
63
  // 需要复制的文件
47
64
  binFiles = ['system.img', 'data.img', 'coredump.core', 'vela_data.bin', 'vela_resource.bin', 'vela_system.bin'];
@@ -144,10 +161,13 @@ class VvdManager {
144
161
  }
145
162
  }
146
163
  getVvdDir(vvdName) {
147
- // 模拟器有可能在 .vvd .avd 目录下
148
- let res = _path.default.resolve(this.vvdHome, `${vvdName}.vvd`);
149
- if (!_fs.default.existsSync(res)) res = _path.default.resolve(this.vvdHome, `${vvdName}.avd`);
150
- return res;
164
+ const maybe = [_path.default.join(this.vvdHome, `${vvdName}.ini`), _path.default.join(_os.default.homedir(), '.android', 'avd', `${vvdName}.ini`), _path.default.join(_os.default.homedir(), '.android', 'vvd', `${vvdName}.ini`)];
165
+ for (const file of maybe) {
166
+ if (_fs.default.existsSync(file)) {
167
+ return (0, _ini.parse)(_fs.default.readFileSync(file, 'utf-8')).path;
168
+ }
169
+ }
170
+ throw new Error(`VVD directory for ${vvdName} not found`);
151
171
  }
152
172
 
153
173
  /** 根据AVD名字获取模拟器的详细信息 */
@@ -333,6 +353,54 @@ class VvdManager {
333
353
  const emulatorHome = _path.default.resolve(sdkHome, 'emulator');
334
354
  return _path.default.resolve(emulatorHome, `${platform}-${arch}`, 'emulator');
335
355
  }
356
+
357
+ // 旧的模拟器迁移
358
+ async oldEmulatorMigrate(vvdName) {
359
+ const configIni = _path.default.join(this.getVvdDir(vvdName), 'config.ini');
360
+ const contents = await _promises.default.readFile(configIni, 'utf-8');
361
+ const config = (0, _ini.parse)(contents);
362
+ let needUpdate = false;
363
+
364
+ // 检查 ramsize 调整为 1024
365
+ if (config['hw.ramSize'] < 1024) {
366
+ needUpdate = true;
367
+ config['hw.ramSize'] = 1024;
368
+ }
369
+ // 检查皮肤路径
370
+ if (config['skin.path']?.includes('.export_dev/skins')) {
371
+ if (config['skin.path'].includes('.export_dev/skins/user')) {
372
+ // 自定义的皮肤,将自定义皮肤迁移到新的 sdk 目录中
373
+ const oldUserSkinDir = _path.default.join(_os.default.homedir(), '.export_dev/skins/user');
374
+ const newUserSkinDir = _path.default.join(this.sdkHome, 'skins', 'user');
375
+ const userSkins = await _promises.default.readdir(oldUserSkinDir, {
376
+ withFileTypes: true
377
+ });
378
+ for (const skin of userSkins) {
379
+ if (!skin.isDirectory()) continue;
380
+ const oldSkinPath = _path.default.join(oldUserSkinDir, skin.name);
381
+ const newSkinPath = _path.default.join(newUserSkinDir, skin.name);
382
+ if (!_fs.default.existsSync(newSkinPath)) {
383
+ await _promises.default.mkdir(newSkinPath, {
384
+ recursive: true
385
+ });
386
+ }
387
+ await (0, _file.copyDir)(oldSkinPath, newSkinPath);
388
+ }
389
+ }
390
+ needUpdate = true;
391
+ config['skin.path'] = config['skin.path'].replace('.export_dev/skins', '.vela/sdk/skins');
392
+ }
393
+
394
+ // 检查镜像路径
395
+ if (config['image.sysdir.2']?.includes('.export_dev/system-images')) {
396
+ needUpdate = true;
397
+ config['image.sysdir.2'] = config['image.sysdir.2'].replace('.export_dev/system-images', '.vela/sdk/system-images');
398
+ }
399
+ if (needUpdate) {
400
+ await _promises.default.writeFile(configIni, (0, _ini.stringify)(config));
401
+ _ColorConsole.default.log(`update ${vvdName} config.ini`);
402
+ }
403
+ }
336
404
  async getVvdStartCmd(options) {
337
405
  const vvdName = options.vvdName;
338
406
 
@@ -365,7 +433,11 @@ class VvdManager {
365
433
  const qemuOption = `-device virtio-snd,bus=virtio-mmio-bus.2 -allow-host-audio -semihosting`;
366
434
 
367
435
  // qt windows 配置
368
- const windowOption = options.qtHideWindow ? `-no-window` : '';
436
+ // docker,wls,等无界面平台上用 -no-window ,否则用 -qt-hide-window
437
+ // 使用 -no-window 时 extended control 不可用
438
+ // 在无界面平台上必须使用 -no-window 否则会报错 qt `Could not load the Qt platform plugin \"xcb\"
439
+ const noWindowOption = isHeadlessEnvironment() ? `-no-window` : '-qt-hide-window';
440
+ const windowOption = options.qtHideWindow ? noWindowOption : '';
369
441
  let grpcStr = options.grpcPort ? `-grpc ${options.grpcPort}` : '';
370
442
  let serialStr = ``;
371
443
  if (options.serialPort) {
@@ -379,6 +451,7 @@ class VvdManager {
379
451
  // 启动模拟器的命令和参数
380
452
  const cmd = `${emulatorBin} -vela -avd ${options.vvdName} ${serialStr} -show-kernel ${portMappingStr} ${windowOption} ${grpcStr} ${verboseOption} -qemu ${qemuOption}`;
381
453
  const vvdInfo = this.getVvdInfo(vvdName);
454
+ await this.oldEmulatorMigrate(vvdName);
382
455
  if (!vvdInfo.imageDir) {
383
456
  const errMsg = `${vvdName} is not supported`;
384
457
  _ColorConsole.default.throw(errMsg);
@@ -394,12 +467,14 @@ class VvdManager {
394
467
  } else continue;
395
468
  if (!_fs.default.existsSync(pOfVvd)) {
396
469
  // 文件不存在则直接复制
470
+ _ColorConsole.default.log(`${file} not found, copy from ${pOfImageDir}`);
397
471
  _fs.default.copyFileSync(pOfImageDir, pOfVvd);
398
472
  } else {
399
473
  // 文件存在但过时
400
474
  const statsInAvd = _fs.default.statSync(pOfVvd);
401
475
  const stats = _fs.default.statSync(pOfImageDir);
402
476
  if ((0, _dayjs.default)(stats.mtime).isAfter(statsInAvd.mtime)) {
477
+ _ColorConsole.default.log(`${file} file is outdate, update from ${pOfImageDir}`);
403
478
  _fs.default.copyFileSync(pOfImageDir, pOfVvd);
404
479
  }
405
480
  }
@@ -745,6 +820,10 @@ class VvdManager {
745
820
  zip.extractAllTo(targetDir, true, true);
746
821
  await _fs.default.promises.rm(zipFile, {
747
822
  force: true
823
+ }).catch(e => {
824
+ _ColorConsole.default.info(`remove ${zipFile} failed: ${e}`);
825
+ // my be failed on windows as resource busy or locked, don't care
826
+ return Promise.resolve();
748
827
  });
749
828
  }
750
829
  const verFile = this.getSDKVersionPath();
@@ -774,6 +853,10 @@ class VvdManager {
774
853
  zip.extractAllTo(_path.default.resolve(this.sdkHome, _Vvd.SDKParts.SYSTEM_IMAGES, imageId), true, true);
775
854
  await _fs.default.promises.rm(zipFile, {
776
855
  force: true
856
+ }).catch(e => {
857
+ _ColorConsole.default.info(`remove ${zipFile} failed: ${e}`);
858
+ // my be failed on windows as resource busy or locked, don't care
859
+ return Promise.resolve();
777
860
  });
778
861
  });
779
862
  return Object.assign(downloader, {
package/lib/vvd/logcat.js CHANGED
@@ -27,7 +27,7 @@ function attachReadline(p, onStdout, onErrout) {
27
27
  let stderrReadline;
28
28
 
29
29
  // 利用 readline 接口可解决子进程日志换行的问题
30
- if (p.stdout) {
30
+ if (p?.stdout) {
31
31
  stdoutReadline = _readline.default.createInterface({
32
32
  input: p.stdout
33
33
  });
@@ -39,7 +39,7 @@ function attachReadline(p, onStdout, onErrout) {
39
39
  stdoutReadline = undefined;
40
40
  });
41
41
  }
42
- if (p.stderr) {
42
+ if (p?.stderr) {
43
43
  stderrReadline = _readline.default.createInterface({
44
44
  input: p.stderr
45
45
  });
@@ -59,7 +59,7 @@ function attachReadline(p, onStdout, onErrout) {
59
59
  stdoutReadline = undefined;
60
60
  stderrReadline = undefined;
61
61
  }
62
- p.on('exit', () => {
62
+ p?.on('exit', () => {
63
63
  dispose();
64
64
  });
65
65
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiot-toolkit/emulator",
3
- "version": "2.0.5-beta.22",
3
+ "version": "2.0.5-beta.24",
4
4
  "description": "vela emulator tool.",
5
5
  "homepage": "",
6
6
  "license": "ISC",
@@ -36,7 +36,7 @@
36
36
  "emulator"
37
37
  ],
38
38
  "dependencies": {
39
- "@aiot-toolkit/shared-utils": "2.0.5-beta.22",
39
+ "@aiot-toolkit/shared-utils": "2.0.5-beta.24",
40
40
  "@grpc/grpc-js": "^1.13.3",
41
41
  "@grpc/proto-loader": "^0.7.13",
42
42
  "@miwt/adb": "0.10.1",
@@ -53,5 +53,5 @@
53
53
  "@types/adm-zip": "^0.5.5",
54
54
  "@types/ini": "^4.1.1"
55
55
  },
56
- "gitHead": "cb8b82107dd1d8e977a529fcd334095627bd5d7c"
56
+ "gitHead": "9db5595088ce88b2530a20f9edae156e9026718e"
57
57
  }