diff --git a/desktop/flipper-common/src/server-types.tsx b/desktop/flipper-common/src/server-types.tsx index 82ee66d9f..64ce78541 100644 --- a/desktop/flipper-common/src/server-types.tsx +++ b/desktop/flipper-common/src/server-types.tsx @@ -165,7 +165,7 @@ export interface FSStatsLike { export interface DeviceDebugData { serial: string; appId: string; - files: {path: string; data: string}[]; + data: ({path: string; data: string} | {command: string; result: string})[]; } export type FlipperServerCommands = { diff --git a/desktop/flipper-server-core/src/FlipperServerImpl.tsx b/desktop/flipper-server-core/src/FlipperServerImpl.tsx index b308c307d..468c7ffbd 100644 --- a/desktop/flipper-server-core/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server-core/src/FlipperServerImpl.tsx @@ -53,6 +53,7 @@ import {initializeAdbClient} from './devices/android/adbClient'; import {assertNotNull} from './comms/Utilities'; import {mkdirp} from 'fs-extra'; import {flipperDataFolder, flipperSettingsFolder} from './utils/paths'; +import {DebuggableDevice} from './devices/DebuggableDevice'; const {access, copyFile, mkdir, unlink, stat, readlink, readFile, writeFile} = promises; @@ -419,10 +420,7 @@ export class FlipperServerImpl implements FlipperServer { 'device-clear-logs': async (serial) => this.getDevice(serial).clearLogs(), 'device-navigate': async (serial, loc) => this.getDevice(serial).navigateToLocation(loc), - 'fetch-debug-data': async () => { - // TODO: Implement fetching client logs from all devices - return []; - }, + 'fetch-debug-data': () => this.fetchDebugLogs(), 'metro-command': async (serial: string, command: string) => { const device = this.getDevice(serial); if (!(device instanceof MetroDevice)) { @@ -604,6 +602,17 @@ export class FlipperServerImpl implements FlipperServer { return Array.from(this.devices.values()); } + private async fetchDebugLogs() { + const debugDataForEachDevice = await Promise.all( + [...this.devices.values()] + .filter((device) => device.info.os === 'Android') + .map((device) => + (device as unknown as DebuggableDevice).readFlipperFolderForAllApps(), + ), + ); + return debugDataForEachDevice.flat(); + } + public async close() { this.server.close(); for (const device of this.devices.values()) { diff --git a/desktop/flipper-server-core/src/devices/DebuggableDevice.tsx b/desktop/flipper-server-core/src/devices/DebuggableDevice.tsx new file mode 100644 index 000000000..edd6db560 --- /dev/null +++ b/desktop/flipper-server-core/src/devices/DebuggableDevice.tsx @@ -0,0 +1,14 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {DeviceDebugData} from 'flipper-common'; + +export interface DebuggableDevice { + readFlipperFolderForAllApps(): Promise; +} diff --git a/desktop/flipper-server-core/src/devices/android/AndroidDevice.tsx b/desktop/flipper-server-core/src/devices/android/AndroidDevice.tsx index d3bc7431d..e5283dd9a 100644 --- a/desktop/flipper-server-core/src/devices/android/AndroidDevice.tsx +++ b/desktop/flipper-server-core/src/devices/android/AndroidDevice.tsx @@ -7,10 +7,10 @@ * @format */ -import adb, {Client as ADBClient, PullTransfer} from 'adbkit'; +import adb, {util, Client as ADBClient, PullTransfer} from 'adbkit'; import {Reader} from 'adbkit-logcat'; import {createWriteStream} from 'fs'; -import type {DeviceType} from 'flipper-common'; +import type {DeviceDebugData, DeviceType} from 'flipper-common'; import {spawn} from 'child_process'; import {dirname, join} from 'path'; import {DeviceSpec} from 'flipper-common'; @@ -18,10 +18,15 @@ import {ServerDevice} from '../ServerDevice'; import {FlipperServerImpl} from '../../FlipperServerImpl'; import {AndroidCrashWatcher} from './AndroidCrashUtils'; import {AndroidLogListener} from './AndroidLogListener'; +import {DebuggableDevice} from '../DebuggableDevice'; +import {executeCommandAsApp} from './androidContainerUtility'; const DEVICE_RECORDING_DIR = '/sdcard/flipper_recorder'; -export default class AndroidDevice extends ServerDevice { +export default class AndroidDevice + extends ServerDevice + implements DebuggableDevice +{ adb: ADBClient; pidAppMapping: {[key: number]: string} = {}; private recordingProcess?: Promise; @@ -279,6 +284,53 @@ export default class AndroidDevice extends ServerDevice { console.log(`Installing app with adb ${apkPath}`); await this.adb.install(this.serial, apkPath); } + + async readFlipperFolderForAllApps(): Promise { + console.debug( + 'AndroidDevice.readFlipperFolderForAllApps', + this.info.serial, + ); + const output = await this.adb + .shell(this.info.serial, 'pm list packages -3 -e') + .then(util.readAll) + .then((buffer) => buffer.toString()); + + const appIds = output + .split('\n') + // Last each appId has \n at the end. The last appId also has it. + // As a result, there is an "" (empty string) item at the end after the split. + .filter((appId) => appId !== '') + // Cut off the "package:" prefix + .map((appIdRaw) => appIdRaw.substring('package:'.length)); + console.debug( + 'AndroidDevice.readFlipperFolderForAllApps -> found apps', + this.info.serial, + appIds, + ); + + const appsCommandsResults = await Promise.all( + appIds.map(async (appId): Promise => { + const singleAppCommandResults = await Promise.all([ + executeCommandAsApp( + this.adb, + this.info.serial, + appId, + `ls -al /data/data/${appId}/files/sonar`, + ).then((output): DeviceDebugData['data'][0] => ({ + command: `ls -al /data/data/${appId}/files/sonar`, + result: output, + })), + ]); + return { + serial: this.info.serial, + appId, + data: singleAppCommandResults, + }; + }), + ); + + return appsCommandsResults; + } } export async function launchEmulator( diff --git a/desktop/flipper-server-core/src/devices/android/androidContainerUtility.tsx b/desktop/flipper-server-core/src/devices/android/androidContainerUtility.tsx index 8b2f3cc71..ae5464c10 100644 --- a/desktop/flipper-server-core/src/devices/android/androidContainerUtility.tsx +++ b/desktop/flipper-server-core/src/devices/android/androidContainerUtility.tsx @@ -120,7 +120,7 @@ function _pull( } // Keep this method private since it relies on pre-validated arguments -function executeCommandAsApp( +export function executeCommandAsApp( client: Client, deviceId: string, app: string,