blob: ab964ef5a7facfe80601c62a9da3acf5bddc2fac [file]
// Copyright (C) 2026 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import type {Trace} from '../../../public/trace';
import type {
InputLifecycleExtension,
StageDefinition,
SqlJoinSpec,
} from './interface';
import {STR_NULL} from '../../../trace_processor/query_result';
export class PixelInputLifecycleExtension implements InputLifecycleExtension {
readonly id = 'com.google.PixelInputLifecycle';
readonly name = 'Google Pixel Touch';
readonly requiredModules = ['pixel.touch'];
async isEligible(trace: Trace): Promise<boolean> {
const result = await trace.engine.query(
`SELECT 1 FROM slice WHERE name GLOB 'algo->processFrame:*' LIMIT 1`,
);
return result.numRows() > 0;
}
getSqlJoinSpec(): SqlJoinSpec {
return {
tableName: 'pixel_touch_events',
tableAlias: 'pixel',
joinOn:
'((cast_int!(pixel.in_ts + pixel.resample_latency_offset) / 1000) * 1000) = ((cast_int!(a_evt.event_time) / 1000) * 1000)',
};
}
getStages(): StageDefinition[] {
return [
{
key: 'pixel_touch_th',
headerName: 'Pixel Touch Top Half',
sequenceNumber: 100,
idField: 'id_pixel_touch_th',
trackField: 'track_pixel_touch_th',
tsField: 'ts_pixel_touch_th',
durField: 'dur_pixel_touch_th',
},
{
key: 'pixel_touch_bh',
headerName: 'Pixel Touch Bottom Half',
sequenceNumber: 300,
idField: 'id_pixel_touch_bh',
trackField: 'track_pixel_touch_bh',
tsField: 'ts_pixel_touch_bh',
durField: 'dur_pixel_touch_bh',
},
{
key: 'pixel_touch',
headerName: 'Pixel Touch Twoshay',
sequenceNumber: 500,
idField: 'id_pixel_touch',
trackField: 'track_pixel_touch',
tsField: 'ts_pixel_touch',
durField: 'dur_pixel_touch',
},
];
}
async resolveInputId(
trace: Trace,
sliceId: number,
): Promise<string | undefined> {
const stages = this.getStages();
const whereClauses = stages
.map((s) => `p.${s.idField} = ${sliceId}`)
.join(' OR ');
const query = `
SELECT e.input_event_id AS input_id
FROM android_input_events e
JOIN pixel_touch_events p ON
((cast_int!(p.in_ts + p.resample_latency_offset) / 1000) * 1000) = ((cast_int!(e.event_time) / 1000) * 1000)
WHERE ${whereClauses}
LIMIT 1
`;
const result = await trace.engine.query(query);
const it = result.iter({input_id: STR_NULL});
return it.valid() ? (it.input_id ?? undefined) : undefined;
}
}