1/* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17import {TimeUtils} from 'common/time_utils'; 18import {Timestamp, TimestampType} from 'trace/timestamp'; 19import {TraceFile} from 'trace/trace_file'; 20import {TraceTreeNode} from 'trace/trace_tree_node'; 21import {TraceType} from 'trace/trace_type'; 22import {ImeUtils} from 'viewers/common/ime_utils'; 23import {AbstractParser} from './abstract_parser'; 24import {InputMethodClientsTraceFileProto} from './proto_types'; 25 26class ParserInputMethodClients extends AbstractParser { 27 constructor(trace: TraceFile) { 28 super(trace); 29 this.realToElapsedTimeOffsetNs = undefined; 30 } 31 32 getTraceType(): TraceType { 33 return TraceType.INPUT_METHOD_CLIENTS; 34 } 35 36 override getMagicNumber(): number[] { 37 return ParserInputMethodClients.MAGIC_NUMBER; 38 } 39 40 override decodeTrace(buffer: Uint8Array): any[] { 41 const decoded = InputMethodClientsTraceFileProto.decode(buffer) as any; 42 if (Object.prototype.hasOwnProperty.call(decoded, 'realToElapsedTimeOffsetNanos')) { 43 this.realToElapsedTimeOffsetNs = BigInt(decoded.realToElapsedTimeOffsetNanos); 44 } else { 45 this.realToElapsedTimeOffsetNs = undefined; 46 } 47 48 return (InputMethodClientsTraceFileProto.decode(buffer) as any).entry; 49 } 50 51 override getTimestamp(type: TimestampType, entryProto: any): undefined | Timestamp { 52 if (type === TimestampType.ELAPSED) { 53 return new Timestamp(type, BigInt(entryProto.elapsedRealtimeNanos)); 54 } else if (type === TimestampType.REAL && this.realToElapsedTimeOffsetNs !== undefined) { 55 return new Timestamp( 56 type, 57 BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs 58 ); 59 } 60 return undefined; 61 } 62 63 override processDecodedEntry( 64 index: number, 65 timestampType: TimestampType, 66 entryProto: TraceTreeNode 67 ): TraceTreeNode { 68 if (entryProto.elapsedRealtimeNanos === undefined) { 69 throw Error('Missing elapsedRealtimeNanos on entry'); 70 } 71 72 let clockTimeNanos: bigint | undefined = undefined; 73 if ( 74 this.realToElapsedTimeOffsetNs !== undefined && 75 entryProto.elapsedRealtimeNanos !== undefined 76 ) { 77 clockTimeNanos = BigInt(entryProto.elapsedRealtimeNanos) + this.realToElapsedTimeOffsetNs; 78 } 79 80 const timestamp = Timestamp.from( 81 timestampType, 82 BigInt(entryProto.elapsedRealtimeNanos), 83 this.realToElapsedTimeOffsetNs 84 ); 85 86 return { 87 name: TimeUtils.format(timestamp) + ' - ' + entryProto.where, 88 kind: 'InputMethodClient entry', 89 children: [ 90 { 91 obj: ImeUtils.transformInputConnectionCall(entryProto.client), 92 kind: 'Client', 93 name: entryProto.client?.viewRootImpl?.view ?? '', 94 children: [], 95 stableId: 'client', 96 id: 'client', 97 }, 98 ], 99 obj: entryProto, 100 stableId: 'entry', 101 id: 'entry', 102 elapsedRealtimeNanos: entryProto.elapsedRealtimeNanos, 103 clockTimeNanos, 104 }; 105 } 106 107 private realToElapsedTimeOffsetNs: undefined | bigint; 108 private static readonly MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMCTRACE 109} 110 111export {ParserInputMethodClients}; 112