1/* 2 * Copyright (C) 2024 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 {HierarchyTreeBuilderInputMethod} from 'parsers/input_method/hierarchy_tree_builder_input_method'; 18import {AddDefaults} from 'parsers/operations/add_defaults'; 19import {SetFormatters} from 'parsers/operations/set_formatters'; 20import {TranslateIntDef} from 'parsers/operations/translate_intdef'; 21import {TamperedProtoField} from 'parsers/tampered_message_type'; 22import {perfetto} from 'protos/ime/latest/static'; 23import {android} from 'protos/ime/udc/static'; 24import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node'; 25import {LazyPropertiesStrategyType} from 'trace/tree_node/properties_provider'; 26import {PropertiesProviderBuilder} from 'trace/tree_node/properties_provider_builder'; 27import {PropertyTreeBuilderFromProto} from 'trace/tree_node/property_tree_builder_from_proto'; 28import {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 29 30export class HierarchyTreeClientsFactory { 31 private static readonly ENTRY_DENYLIST_PROPERTIES = ['client']; 32 private static readonly ENTRY_EAGER_PROPERTIES = ['where']; 33 private static readonly CLIENT_EAGER_PROPERTIES = [ 34 'viewRootImpl', 35 'inputMethodManager', 36 'editorInfo', 37 ]; 38 39 private SetFormattersClient: SetFormatters; 40 private TranslateIntDefClient: TranslateIntDef; 41 private AddDefaultsClientEager: AddDefaults; 42 private AddDefaultsClientLazy: AddDefaults; 43 private SetFormattersEntry: SetFormatters; 44 private AddDefaultsEntryEager: AddDefaults; 45 private AddDefaultsEntryLazy: AddDefaults; 46 47 constructor(entryField: TamperedProtoField, clientField: TamperedProtoField) { 48 this.SetFormattersClient = new SetFormatters(clientField); 49 this.TranslateIntDefClient = new TranslateIntDef(clientField); 50 this.AddDefaultsClientEager = new AddDefaults( 51 clientField, 52 HierarchyTreeClientsFactory.CLIENT_EAGER_PROPERTIES, 53 ); 54 this.AddDefaultsClientLazy = new AddDefaults( 55 clientField, 56 undefined, 57 HierarchyTreeClientsFactory.CLIENT_EAGER_PROPERTIES, 58 ); 59 this.SetFormattersEntry = new SetFormatters(entryField); 60 this.AddDefaultsEntryEager = new AddDefaults( 61 entryField, 62 HierarchyTreeClientsFactory.ENTRY_EAGER_PROPERTIES, 63 ); 64 this.AddDefaultsEntryLazy = new AddDefaults( 65 entryField, 66 undefined, 67 HierarchyTreeClientsFactory.ENTRY_EAGER_PROPERTIES.concat( 68 HierarchyTreeClientsFactory.ENTRY_DENYLIST_PROPERTIES, 69 ), 70 ); 71 } 72 73 makeHierarchyTree( 74 entryProto: 75 | android.view.inputmethod.IInputMethodClientsTraceProto 76 | perfetto.protos.IInputMethodClientsTraceProto, 77 ): HierarchyTreeNode { 78 const entry = new PropertiesProviderBuilder() 79 .setEagerProperties(this.makeEntryEagerPropertiesTree(entryProto)) 80 .setLazyPropertiesStrategy( 81 this.makeEntryLazyPropertiesStrategy(entryProto), 82 ) 83 .setEagerOperations([this.AddDefaultsEntryEager]) 84 .setCommonOperations([this.SetFormattersEntry]) 85 .setLazyOperations([this.AddDefaultsEntryLazy]) 86 .build(); 87 88 const client = new PropertiesProviderBuilder() 89 .setEagerProperties(this.makeClientEagerPropertiesTree(entryProto.client)) 90 .setLazyPropertiesStrategy( 91 this.makeClientLazyPropertiesStrategy(entryProto.client), 92 ) 93 .setEagerOperations( 94 entryProto.client ? [this.AddDefaultsClientEager] : [], 95 ) 96 .setCommonOperations([ 97 this.SetFormattersClient, 98 this.TranslateIntDefClient, 99 ]) 100 .setLazyOperations(entryProto.client ? [this.AddDefaultsClientLazy] : []) 101 .build(); 102 103 return new HierarchyTreeBuilderInputMethod() 104 .setRoot(entry) 105 .setChildren([client]) 106 .build(); 107 } 108 109 private makeEntryEagerPropertiesTree( 110 entryProto: 111 | android.view.inputmethod.IInputMethodClientsTraceProto 112 | perfetto.protos.IInputMethodClientsTraceProto, 113 ): PropertyTreeNode { 114 const denyList: string[] = []; 115 Object.getOwnPropertyNames(entryProto).forEach((it) => { 116 if (!HierarchyTreeClientsFactory.ENTRY_EAGER_PROPERTIES.includes(it)) { 117 denyList.push(it); 118 } 119 }); 120 121 return new PropertyTreeBuilderFromProto() 122 .setData(entryProto) 123 .setRootId('InputMethodClients') 124 .setRootName('entry') 125 .setDenyList(denyList) 126 .build(); 127 } 128 129 private makeEntryLazyPropertiesStrategy( 130 entryProto: 131 | android.view.inputmethod.IInputMethodClientsTraceProto 132 | perfetto.protos.IInputMethodClientsTraceProto, 133 ): LazyPropertiesStrategyType { 134 return async () => { 135 return new PropertyTreeBuilderFromProto() 136 .setData(entryProto) 137 .setRootId('InputMethodClients') 138 .setRootName('entry') 139 .setDenyList( 140 HierarchyTreeClientsFactory.ENTRY_EAGER_PROPERTIES.concat( 141 HierarchyTreeClientsFactory.ENTRY_DENYLIST_PROPERTIES, 142 ), 143 ) 144 .build(); 145 }; 146 } 147 148 private makeClientEagerPropertiesTree( 149 clientProto: 150 | android.view.inputmethod.InputMethodClientsTraceProto.IClientSideProto 151 | perfetto.protos.InputMethodClientsTraceProto.IClientSideProto 152 | null 153 | undefined, 154 ): PropertyTreeNode { 155 const denyList: string[] = []; 156 let data: any = clientProto; 157 if (clientProto) { 158 Object.getOwnPropertyNames(clientProto).forEach((it) => { 159 if (!HierarchyTreeClientsFactory.CLIENT_EAGER_PROPERTIES.includes(it)) { 160 denyList.push(it); 161 } 162 }); 163 } else { 164 data = {client: null}; 165 } 166 167 return new PropertyTreeBuilderFromProto() 168 .setData(data) 169 .setRootId('InputMethodClients') 170 .setRootName('client') 171 .setDenyList(denyList) 172 .setVisitPrototype(false) 173 .build(); 174 } 175 176 private makeClientLazyPropertiesStrategy( 177 clientProto: 178 | android.view.inputmethod.InputMethodClientsTraceProto.IClientSideProto 179 | perfetto.protos.InputMethodClientsTraceProto.IClientSideProto 180 | null 181 | undefined, 182 ): LazyPropertiesStrategyType { 183 return async () => { 184 return new PropertyTreeBuilderFromProto() 185 .setData(clientProto ?? {client: null}) 186 .setRootId('InputMethodClients') 187 .setRootName('client') 188 .setDenyList(HierarchyTreeClientsFactory.CLIENT_EAGER_PROPERTIES) 189 .setVisitPrototype(false) 190 .build(); 191 }; 192 } 193} 194