import { IPCMessages as IPC , Subscription } from '../../services/service.electron' ; import { exec , ExecException , ExecOptions } from 'child_process' ; import { IAdbProcess , IAdbDevice , IAdbSession } from '../../../../common/interfaces/interface.adb' ; import Process from './controller.process' ; import ServiceElectron from '../../services/service.electron' ; import ServiceEnv from '../../services/service.env' ; import Logger from '../../tools/env.logger' ; import * as Tools from '../../tools/index' ; export default class ControllerStreamAdb { private _logger : Logger ; private _guid : string ; private _process ? : Process ; private _subscriptions : { [ key ] : Subscription ; } = { } ; private _session : IAdbSession = { devices : [ ] , processes : [ ] , device : undefined , pid : undefined , logLevel : 'V' , recieved : 0 , } ; constructor ( guid ) { this . _guid = guid ; this . _logger = new Logger ( `ControllerStreamAdb: ${ guid }` ) ; ServiceElectron . IPC . subscribe ( IPC . AdbStartRequest , this . _ipc_AdbStartRequest . bind ( this ) as any ) . then ( ( subscription ) => { this . _subscriptions . AdbStartRequest = subscription ; } ) . catch ( ( err ) => this . _logger . error ( `Fail to subscribe to AdbStartRequest due error: ${ err . message }` ) ) ; ServiceElectron . IPC . subscribe ( IPC . AdbProcessesRequest , this . _ipc_AdbProcessesRequest . bind ( this ) as any ) . then ( ( subscription ) => { this . _subscriptions . AdbProcessesRequest = subscription ; } ) . catch ( ( err ) => this . _logger . error ( `Fail to subscribe to AdbProcessesRequest due error: ${ err . message }` ) ) ; ServiceElectron . IPC . subscribe ( IPC . AdbDevicesRequest , this . _ipc_AdbDevicesRequest . bind ( this ) as any ) . then ( ( subscription ) => { this . _subscriptions . AdbDevicesRequest = subscription ; } ) . catch ( ( err ) => this . _logger . error ( `Fail to subscribe to AdbDevicesRequest due error: ${ err . message }` ) ) ; ServiceElectron . IPC . subscribe ( IPC . AdbLoadRequest , this . _ipc_AdbLoadRequest . bind ( this ) as any ) . then ( ( subscription ) => { this . _subscriptions . AdbLoadRequest = subscription ; } ) . catch ( ( err ) => this . _logger . error ( `Fail to subscribe to AdbLoadRequest due error: ${ err . message }` ) ) ; ServiceElectron . IPC . subscribe ( IPC . AdbStopRequest , this . _ipc_AdbStopRequest . bind ( this ) as any ) . then ( ( subscription ) => { this . _subscriptions . AdbStopRequest = subscription ; } ) . catch ( ( err ) => this . _logger . error ( `Fail to subscribe to AdbStopRequest due error: ${ err . message }` ) ) ; ServiceElectron . IPC . subscribe ( IPC . AdbStartServerRequest , this . _ipc_AdbStartServerRequest . bind ( this ) as any ) . then ( ( subscription ) => { this . _subscriptions . AdbStartServerRequest = subscription ; } ) . catch ( ( err ) => this . _logger . error ( `Fail to subscribe to AdbStartServerRequest due error: ${ err . message }` ) ) ; } public destroy ( ) { return new Promise ( ( resolve ) => { Object . keys ( this . _subscriptions ) . forEach ( ( key ) => { this . _subscriptions [ key ] . destroy ( ) ; } ) ; this . _kill ( ) ; resolve ( ) ; } ) ; } private _ipc_AdbStartRequest ( request , response ) { if ( request . session !== this . _guid ) { return ; } this . _kill ( ) ; this . _session . device = request . device ; this . _session . logLevel = request . level ; this . _session . pid = request . pid ; const guid = Tools . guid ( ) ; this . _process = new Process ( this . _guid , { guid : guid , device : request . device , level : request . level , pid : request . pid , } ) ; this . _process . on ( Process . Events . destroy , ( ) => { if ( this . _process !== undefined ) { this . _process . removeAllListeners ( ) ; ServiceElectron . IPC . send ( new IPC . AdbDeviceDisconnected ( { guid : this . _guid , device : request . device , } ) ) ; } } ) ; this . _process . on ( Process . Events . recieved , ( recieved ) => { this . _session . recieved += recieved ; } ) ; this . _process . execute ( ) ; response ( new IPC . AdbStartResponse ( { guid : guid } ) ) ; } private _ipc_AdbStopRequest ( request , response ) { if ( request . session !== this . _guid ) { return ; } const error = this . _kill ( ) ; this . _session . device = undefined ; this . _session . pid = undefined ; this . _session . recieved = 0 ; response ( new IPC . AdbStopResponse ( { error : error !== undefined ? error . message : undefined } ) ) ; } private _ipc_AdbProcessesRequest ( request , response ) { if ( request . session !== this . _guid ) { return ; } exec ( `adb -s ${ request . device } shell ps -A` , this . _getExecOpts ( ) , ( error , stdout , stderr ) => { if ( error || stderr ) { return response ( new IPC . AdbDevicesResponse ( { error : error ? error . message : stderr } ) ) ; } let match ; const split = stdout . split ( /[\n\r]/g ) ; const processes = [ ] ; const reg = /(\w+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\w+)\s+\[??(.+)\]?\s*$/g ; split . forEach ( ( line ) => { match = reg . exec ( line ) ; if ( match !== null ) { processes . push ( { user : match [ 1 ] . toString ( ) , pid : this . _toNumber ( match [ 2 ] ) , ppid : this . _toNumber ( match [ 3 ] ) , vsz : this . _toNumber ( match [ 4 ] ) , rss : this . _toNumber ( match [ 5 ] ) , wchan : this . _toNumber ( match [ 6 ] ) , addr : this . _toNumber ( match [ 7 ] ) , s : match [ 8 ] , name : match [ 9 ] , } ) ; } } ) ; this . _session . processes = processes ; return response ( new IPC . AdbProcessesResponse ( { processes : processes } ) ) ; } ) ; } private _ipc_AdbDevicesRequest ( request , response ) { if ( request . session !== this . _guid ) { return ; } exec ( 'adb devices' , this . _getExecOpts ( ) , ( error , stdout , stderr ) => { if ( error || stderr ) { return response ( new IPC . AdbDevicesResponse ( { error : error ? error . message : stderr } ) ) ; } let match ; const split = stdout . split ( /[\n\r]/g ) ; const devices = [ ] ; const reg = /(.*)\t(.*)/g ; split . forEach ( ( line ) => { match = reg . exec ( line ) ; if ( match !== null ) { devices . push ( { name : match [ 1 ] , type : match [ 3 ] , } ) ; } } ) ; this . _session . devices = devices ; return response ( new IPC . AdbDevicesResponse ( { devices : devices } ) ) ; } ) ; } private _ipc_AdbLoadRequest ( request , response ) { if ( request . session !== this . _guid ) { return ; } response ( new IPC . AdbLoadResponse ( { session : this . _guid , data : this . _session , } ) ) ; } private _ipc_AdbStartServerRequest ( request , response ) { if ( request . guid !== this . _guid ) { return ; } exec ( 'adb start-server' , this . _getExecOpts ( ) , ( error , stdout , stderr ) => { if ( error !== null ) { return response ( new IPC . AdbStartServerResponse ( { guid : this . _guid , error : error . message , } ) ) ; } return response ( new IPC . AdbStartServerResponse ( { guid : this . _guid } ) ) ; } ) ; } private _toNumber ( input ) { const int = parseInt ( input , 10 ) ; return ! isNaN ( int ) && isFinite ( int ) ? int : undefined ; } private _getExecOpts ( ) { return { env : ServiceEnv . getOS ( ) , } ; } private _kill ( ) { if ( this . _process !== undefined ) { this . _process . removeAllListeners ( ) ; const error = this . _process . destroy ( ) ; this . _process = undefined ; this . _session . recieved = 0 ; if ( error ) { this . _logger . warn ( `Problems with killing process: ${ error . message }` ) ; } return error ; } else { return undefined ; } } }