From 480a3d26f09b3781d1dd7b37025707972783438d Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Tue, 25 Oct 2022 05:31:48 -0700 Subject: [PATCH] List contents of Flipper folder on Android Summary: Design doc: https://docs.google.com/document/d/1HLCFl46RfqG0o1mSt8SWrwf_HMfOCRg_oENioc1rkvQ/edit# In later diffs, we'll start fetching the actual files as well. The list of available files itself might be useful already to see what we have in our folder on the device Reviewed By: passy Differential Revision: D40508960 fbshipit-source-id: 96193fef3fed64d509cd3397513ae3e94438ae22 --- desktop/flipper-common/src/server-types.tsx | 2 +- .../src/FlipperServerImpl.tsx | 17 ++++-- .../src/devices/DebuggableDevice.tsx | 14 +++++ .../src/devices/android/AndroidDevice.tsx | 58 ++++++++++++++++++- .../android/androidContainerUtility.tsx | 2 +- 5 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 desktop/flipper-server-core/src/devices/DebuggableDevice.tsx 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,