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 HierarchyTreeManagerServiceFactory {
31  private static readonly ENTRY_DENYLIST_PROPERTIES = [
32    'inputMethodManagerService',
33  ];
34  private static readonly ENTRY_EAGER_PROPERTIES = ['where'];
35  private static readonly SERVICE_EAGER_PROPERTIES = [
36    'curMethodId',
37    'curFocusedWindowName',
38    'lastImeTargetWindowName',
39    'inputShown',
40  ];
41
42  private SetFormattersService: SetFormatters;
43  private TranslateIntDefService: TranslateIntDef;
44  private AddDefaultsServiceEager: AddDefaults;
45  private AddDefaultsServiceLazy: AddDefaults;
46  private SetFormattersEntry: SetFormatters;
47  private AddDefaultsEntryEager: AddDefaults;
48  private AddDefaultsEntryLazy: AddDefaults;
49
50  constructor(
51    entryField: TamperedProtoField,
52    managerServiceField: TamperedProtoField,
53  ) {
54    this.SetFormattersService = new SetFormatters(managerServiceField);
55    this.TranslateIntDefService = new TranslateIntDef(managerServiceField);
56    this.AddDefaultsServiceEager = new AddDefaults(
57      managerServiceField,
58      HierarchyTreeManagerServiceFactory.SERVICE_EAGER_PROPERTIES,
59    );
60    this.AddDefaultsServiceLazy = new AddDefaults(
61      managerServiceField,
62      undefined,
63      HierarchyTreeManagerServiceFactory.SERVICE_EAGER_PROPERTIES,
64    );
65    this.SetFormattersEntry = new SetFormatters(entryField);
66    this.AddDefaultsEntryEager = new AddDefaults(
67      entryField,
68      HierarchyTreeManagerServiceFactory.ENTRY_EAGER_PROPERTIES,
69    );
70    this.AddDefaultsEntryLazy = new AddDefaults(
71      entryField,
72      undefined,
73      HierarchyTreeManagerServiceFactory.ENTRY_EAGER_PROPERTIES.concat(
74        HierarchyTreeManagerServiceFactory.ENTRY_DENYLIST_PROPERTIES,
75      ),
76    );
77  }
78
79  makeHierarchyTree(
80    entryProto:
81      | android.view.inputmethod.IInputMethodManagerServiceTraceProto
82      | perfetto.protos.IInputMethodManagerServiceTraceProto,
83  ): HierarchyTreeNode {
84    const entry = new PropertiesProviderBuilder()
85      .setEagerProperties(this.makeEntryEagerPropertiesTree(entryProto))
86      .setLazyPropertiesStrategy(
87        this.makeEntryLazyPropertiesStrategy(entryProto),
88      )
89      .setEagerOperations([this.AddDefaultsEntryEager])
90      .setCommonOperations([this.SetFormattersEntry])
91      .setLazyOperations([this.AddDefaultsEntryLazy])
92      .build();
93
94    const inputMethodManagerService = entryProto.inputMethodManagerService
95      ? new PropertiesProviderBuilder()
96          .setEagerProperties(
97            this.makeServiceEagerPropertiesTree(
98              entryProto.inputMethodManagerService,
99            ),
100          )
101          .setLazyPropertiesStrategy(
102            this.makeServiceLazyPropertiesStrategy(
103              entryProto.inputMethodManagerService,
104            ),
105          )
106          .setEagerOperations([this.AddDefaultsServiceEager])
107          .setCommonOperations([
108            this.SetFormattersService,
109            this.TranslateIntDefService,
110          ])
111          .setLazyOperations([this.AddDefaultsServiceLazy])
112          .build()
113      : undefined;
114
115    return new HierarchyTreeBuilderInputMethod()
116      .setRoot(entry)
117      .setChildren(inputMethodManagerService ? [inputMethodManagerService] : [])
118      .build();
119  }
120
121  private makeEntryEagerPropertiesTree(
122    entryProto:
123      | android.view.inputmethod.IInputMethodManagerServiceTraceProto
124      | perfetto.protos.IInputMethodManagerServiceTraceProto,
125  ): PropertyTreeNode {
126    const denyList: string[] = [];
127    Object.getOwnPropertyNames(entryProto).forEach((it) => {
128      if (
129        !HierarchyTreeManagerServiceFactory.ENTRY_EAGER_PROPERTIES.includes(it)
130      ) {
131        denyList.push(it);
132      }
133    });
134
135    return new PropertyTreeBuilderFromProto()
136      .setData(entryProto)
137      .setRootId('InputMethodManagerService')
138      .setRootName('entry')
139      .setDenyList(denyList)
140      .build();
141  }
142
143  private makeEntryLazyPropertiesStrategy(
144    entryProto: perfetto.protos.IInputMethodManagerServiceTraceProto,
145  ): LazyPropertiesStrategyType {
146    return async () => {
147      return new PropertyTreeBuilderFromProto()
148        .setData(entryProto)
149        .setRootId('InputMethodManagerService')
150        .setRootName('entry')
151        .setDenyList(
152          HierarchyTreeManagerServiceFactory.ENTRY_EAGER_PROPERTIES.concat(
153            HierarchyTreeManagerServiceFactory.ENTRY_DENYLIST_PROPERTIES,
154          ),
155        )
156        .build();
157    };
158  }
159
160  private makeServiceEagerPropertiesTree(
161    serviceProto: android.server.inputmethod.IInputMethodManagerServiceProto,
162  ): PropertyTreeNode {
163    const denyList: string[] = [];
164    let data: any = serviceProto;
165    if (serviceProto) {
166      Object.getOwnPropertyNames(serviceProto).forEach((it) => {
167        if (
168          !HierarchyTreeManagerServiceFactory.SERVICE_EAGER_PROPERTIES.includes(
169            it,
170          )
171        ) {
172          denyList.push(it);
173        }
174      });
175    } else {
176      data = {inputMethodManagerService: null};
177    }
178
179    return new PropertyTreeBuilderFromProto()
180      .setData(data)
181      .setRootId('InputMethodManagerService')
182      .setRootName('inputMethodManagerService')
183      .setDenyList(denyList)
184      .setVisitPrototype(false)
185      .build();
186  }
187
188  private makeServiceLazyPropertiesStrategy(
189    serviceProto: android.server.inputmethod.IInputMethodManagerServiceProto,
190  ): LazyPropertiesStrategyType {
191    return async () => {
192      return new PropertyTreeBuilderFromProto()
193        .setData(serviceProto ?? {inputMethodManagerService: null})
194        .setRootId('InputMethodManagerService')
195        .setRootName('inputMethodManagerService')
196        .setDenyList(
197          HierarchyTreeManagerServiceFactory.SERVICE_EAGER_PROPERTIES,
198        )
199        .setVisitPrototype(false)
200        .build();
201    };
202  }
203}
204