fix: repair Android startup and improve mobile web layouts
This commit is contained in:
1 parent
4c2286e70d
commit
b0cbd0f149
30 files changed
+609
-140
No files matched your search
@@ -3,6 +3,7 @@
|
||||
// - нативные библиотеки только под arm64-v8a: неполный каталог другой ABI роняет приложение при старте
|
||||
// («couldn't find DSO to load: libexpo-modules-core.so» на x86-эмуляторе);
|
||||
// - есть библиотеки, без которых приложение не запустится;
|
||||
// - .so сжаты (useLegacyPackaging): Android распакует их для SoLoader / ARM-трансляции;
|
||||
// - PT_LOAD-сегменты каждой .so выровнены по 16 КБ: иначе на устройствах с 16 КБ страницами памяти
|
||||
// (Android 15+) библиотека не загружается и приложение закрывается сразу после запуска.
|
||||
// Запуск: node scripts/check-apk.mjs app-release.apk
|
||||
@@ -29,11 +30,11 @@ export function zipEntries(buf) {
|
||||
const commentLen = buf.readUInt16LE(p + 32);
|
||||
const local = buf.readUInt32LE(p + 42);
|
||||
const name = buf.toString('utf8', p + 46, p + 46 + nameLen);
|
||||
out.set(name, () => {
|
||||
out.set(name, Object.assign(() => {
|
||||
const start = local + 30 + buf.readUInt16LE(local + 26) + buf.readUInt16LE(local + 28);
|
||||
const raw = buf.subarray(start, start + size);
|
||||
return method === 0 ? raw : inflateRawSync(raw);
|
||||
});
|
||||
}, { compressionMethod: method }));
|
||||
p += 46 + nameLen + extraLen + commentLen;
|
||||
}
|
||||
return out;
|
||||
@@ -62,6 +63,9 @@ export function checkApk(buf) {
|
||||
const mine = new Set(libs.filter((n) => n.startsWith(`lib/${ABI}/`)).map((n) => n.split('/')[2]));
|
||||
for (const lib of REQUIRED) if (!mine.has(lib)) problems.push(`нет lib/${ABI}/${lib}`);
|
||||
for (const name of libs.filter((n) => n.startsWith(`lib/${ABI}/`))) {
|
||||
if (entries.get(name).compressionMethod !== 8) {
|
||||
problems.push(`${name}: библиотека не сжата, включите useLegacyPackaging для ARM-трансляции`);
|
||||
}
|
||||
const align = loadAlignment(entries.get(name)());
|
||||
if (align < PAGE) problems.push(`${name}: сегменты выровнены по ${align} байт, нужно ${PAGE} (16 КБ страницы)`);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { test } from 'node:test';
|
||||
import { deflateRawSync } from 'node:zlib';
|
||||
|
||||
import { checkApk, loadAlignment, REQUIRED } from './check-apk.mjs';
|
||||
|
||||
@@ -16,27 +17,30 @@ function elf(align) {
|
||||
return b;
|
||||
}
|
||||
|
||||
/** ZIP без сжатия из пар [имя, содержимое]. */
|
||||
function zip(files) {
|
||||
/** ZIP из пар [имя, содержимое]. */
|
||||
function zip(files, compress = true) {
|
||||
const locals = [];
|
||||
const central = [];
|
||||
let offset = 0;
|
||||
for (const [name, data] of files) {
|
||||
const packed = compress ? deflateRawSync(data) : data;
|
||||
const n = Buffer.from(name);
|
||||
const lh = Buffer.alloc(30);
|
||||
lh.writeUInt32LE(0x04034b50, 0);
|
||||
lh.writeUInt32LE(data.length, 18);
|
||||
lh.writeUInt16LE(compress ? 8 : 0, 8);
|
||||
lh.writeUInt32LE(packed.length, 18);
|
||||
lh.writeUInt32LE(data.length, 22);
|
||||
lh.writeUInt16LE(n.length, 26);
|
||||
locals.push(lh, n, data);
|
||||
locals.push(lh, n, packed);
|
||||
const ch = Buffer.alloc(46);
|
||||
ch.writeUInt32LE(0x02014b50, 0);
|
||||
ch.writeUInt32LE(data.length, 20);
|
||||
ch.writeUInt16LE(compress ? 8 : 0, 10);
|
||||
ch.writeUInt32LE(packed.length, 20);
|
||||
ch.writeUInt32LE(data.length, 24);
|
||||
ch.writeUInt16LE(n.length, 28);
|
||||
ch.writeUInt32LE(offset, 42);
|
||||
central.push(ch, n);
|
||||
offset += 30 + n.length + data.length;
|
||||
offset += 30 + n.length + packed.length;
|
||||
}
|
||||
const cd = Buffer.concat(central);
|
||||
const end = Buffer.alloc(22);
|
||||
@@ -58,6 +62,13 @@ test('accepts an arm64-only APK aligned to 16 KB', () => {
|
||||
assert.deepEqual(checkApk(apk).problems, []);
|
||||
});
|
||||
|
||||
test('rejects direct APK library loading that breaks ARM translation', () => {
|
||||
const apk = zip(REQUIRED.map((l) => [`lib/arm64-v8a/${l}`, elf(16384)]), false);
|
||||
const { problems } = checkApk(apk);
|
||||
assert.equal(problems.length, REQUIRED.length);
|
||||
assert.ok(problems.every((p) => p.includes('useLegacyPackaging')));
|
||||
});
|
||||
|
||||
test('rejects other ABIs, missing libraries and 4 KB alignment', () => {
|
||||
const libs = REQUIRED.filter((l) => l !== 'libexpo-modules-core.so').map((l) => [
|
||||
`lib/arm64-v8a/${l}`,
|
||||
|
||||
Reference in new issue
Block a user