init
This commit is contained in:
commit
451223816b
338 changed files with 9938 additions and 0 deletions
184
src/lib/core/adb/adb.ts
Normal file
184
src/lib/core/adb/adb.ts
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
import { Adb, AdbDaemonTransport, encodeUtf8 } from '@yume-chan/adb';
|
||||
import AdbWebCredentialStore from '@yume-chan/adb-credential-web';
|
||||
import {
|
||||
AdbDaemonWebUsbDeviceManager,
|
||||
AdbDaemonWebUsbDeviceObserver,
|
||||
type AdbDaemonWebUsbDevice
|
||||
} from '@yume-chan/adb-daemon-webusb';
|
||||
import { AdbInstance } from '../../../routes/state.svelte';
|
||||
import { deviceCredentialManager } from './deviceCredManager';
|
||||
import { Consumable, MaybeConsumable, ReadableStream } from '@yume-chan/stream-extra';
|
||||
import { AdbScrcpyClient } from '@yume-chan/adb-scrcpy';
|
||||
|
||||
export async function connnectViaWebUSB() {
|
||||
const device = await AdbDaemonWebUsbDeviceManager.BROWSER?.requestDevice();
|
||||
console.log('usb ok', globalThis.navigator.usb);
|
||||
if (device) {
|
||||
console.log('connect ', device.name);
|
||||
|
||||
try {
|
||||
const credentialStore = new AdbWebCredentialStore();
|
||||
const connection = await device.connect();
|
||||
|
||||
const transport = await AdbDaemonTransport.authenticate({
|
||||
connection: connection,
|
||||
serial: device.serial,
|
||||
credentialStore: credentialStore
|
||||
});
|
||||
|
||||
const adb = new Adb(transport);
|
||||
saveAdbInstance(adb);
|
||||
|
||||
// save device info
|
||||
await deviceCredentialManager.saveDeviceInfo(device);
|
||||
} catch (e: any) {
|
||||
console.error('error on connect', e);
|
||||
throw new Error(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function connectDeviceByCred(
|
||||
device: AdbDaemonWebUsbDevice,
|
||||
credStore: AdbWebCredentialStore
|
||||
) {
|
||||
try {
|
||||
const connection = await device.connect();
|
||||
const transport = await AdbDaemonTransport.authenticate({
|
||||
connection: connection,
|
||||
serial: device.serial,
|
||||
credentialStore: credStore
|
||||
});
|
||||
|
||||
const adb = new Adb(transport);
|
||||
saveAdbInstance(adb);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function saveAdbInstance(adb: Adb | undefined) {
|
||||
AdbInstance.instance = adb;
|
||||
}
|
||||
|
||||
export function getAdbInstance() {
|
||||
return AdbInstance.instance;
|
||||
}
|
||||
|
||||
export async function executeCmd(command: string) {
|
||||
let instance = getAdbInstance();
|
||||
|
||||
if (!instance) {
|
||||
console.error('instance not found');
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
if (instance?.subprocess.shellProtocol?.isSupported) {
|
||||
const result = await instance.subprocess.shellProtocol.spawnWaitText(command);
|
||||
return {
|
||||
output: result.stdout,
|
||||
error: result.stderr,
|
||||
exitCode: result.exitCode
|
||||
};
|
||||
} else {
|
||||
const process = await instance.subprocess.noneProtocol.spawn(command);
|
||||
const reader = process.output.getReader();
|
||||
const chunks = [];
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
chunks.push(decoder.decode(value, { stream: true }));
|
||||
}
|
||||
|
||||
return {
|
||||
output: chunks.join('')
|
||||
};
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.log(e.message);
|
||||
//ExactReadable ended
|
||||
if (e.message.includes('ExactReadable ended')) {
|
||||
console.error('connection cut off');
|
||||
return {
|
||||
error: 'ExactReadableEndedError'
|
||||
};
|
||||
}
|
||||
|
||||
console.error('error while execute command', e);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export async function disconnect() {
|
||||
let instance = getAdbInstance();
|
||||
if (instance) {
|
||||
await instance.close();
|
||||
console.log('close instance');
|
||||
saveAdbInstance(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
export async function pull(filename: string) {
|
||||
let instance = getAdbInstance();
|
||||
if (instance) {
|
||||
let chunkList: Uint8Array<ArrayBufferLike>[] = [];
|
||||
let sync = await instance.sync();
|
||||
const content = sync.read(filename);
|
||||
let result = content.values();
|
||||
let res;
|
||||
|
||||
let result_string = '';
|
||||
|
||||
while ((res = await result.next()) != null) {
|
||||
// console.log(res.value);
|
||||
if (res.value != undefined) {
|
||||
result_string += new TextDecoder().decode(res.value);
|
||||
}
|
||||
if (res.done) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result_string;
|
||||
}
|
||||
}
|
||||
|
||||
export async function push(path: string, obj: string) {
|
||||
let instance = getAdbInstance();
|
||||
if (instance) {
|
||||
let sync = await instance.sync();
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
const file: ReadableStream<MaybeConsumable<Uint8Array>> = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(new Uint8Array(encoder.encode(obj)));
|
||||
controller.close();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
console.log('support push v2', sync.supportsSendReceiveV2);
|
||||
|
||||
await sync.write({
|
||||
filename: path,
|
||||
file
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('error while trying to write to machine', error);
|
||||
} finally {
|
||||
await sync.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// logcat stream
|
||||
|
||||
// TODO: screen mirror
|
||||
export function getScrcpyBinaryFromSource() {
|
||||
//https://github.com/Genymobile/scrcpy/releases
|
||||
}
|
||||
106
src/lib/core/adb/deviceCredManager.ts
Normal file
106
src/lib/core/adb/deviceCredManager.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import AdbWebCredentialStore from '@yume-chan/adb-credential-web';
|
||||
|
||||
export class DeviceCredentialManager {
|
||||
#credentialStore;
|
||||
constructor() {
|
||||
this.#credentialStore = new AdbWebCredentialStore();
|
||||
}
|
||||
|
||||
async saveDeviceInfo(device: any) {
|
||||
try {
|
||||
const deviceInfo = {
|
||||
name: device.name,
|
||||
serial: device.serial,
|
||||
vendorId: device.vendorId,
|
||||
productId: device.productId,
|
||||
lastConnected: new Date().toISOString()
|
||||
};
|
||||
|
||||
const storedDevices = this.getStoredDeviceInfos();
|
||||
storedDevices[device.serial] = deviceInfo;
|
||||
|
||||
localStorage.setItem('adb_device_infos', JSON.stringify(storedDevices));
|
||||
console.log('save device info', deviceInfo);
|
||||
} catch (error) {
|
||||
console.error('save device info error', error);
|
||||
}
|
||||
}
|
||||
|
||||
getStoredDeviceInfos() {
|
||||
try {
|
||||
const stored = localStorage.getItem('adb_device_infos');
|
||||
return stored ? JSON.parse(stored) : {};
|
||||
} catch (error) {
|
||||
console.error('unable to get stored device info', error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
async hasStoredKeys() {
|
||||
try {
|
||||
for await (const key of this.#credentialStore.iterateKeys()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.error('check stored keys fail', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async getStoredKeyCount() {
|
||||
try {
|
||||
let count = 0;
|
||||
for await (const key of this.#credentialStore.iterateKeys()) {
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
} catch (error) {
|
||||
console.error('get key stored count error', error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
async clearAllCredentials() {
|
||||
try {
|
||||
let clearedCount = 0;
|
||||
const keys = [];
|
||||
for await (const key of this.#credentialStore.iterateKeys()) {
|
||||
keys.push(key);
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
try {
|
||||
clearedCount++;
|
||||
} catch (error) {
|
||||
console.error('clear error', error);
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.removeItem('adb_device_infos');
|
||||
|
||||
try {
|
||||
const dbName = 'webadb-credentials';
|
||||
const request = indexedDB.deleteDatabase(dbName);
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
request.onsuccess = () => resolve(null);
|
||||
request.onerror = () => reject(request.error);
|
||||
request.onblocked = () => {
|
||||
console.warn('request delete got blocked');
|
||||
resolve(null);
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
return clearedCount;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const deviceCredentialManager = new DeviceCredentialManager();
|
||||
Loading…
Add table
Add a link
Reference in a new issue