| /* |
| * Copyright (C) 2024 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. |
| */ |
| |
| package com.android.internal.inputmethod; |
| |
| import static android.tracing.perfetto.DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.internal.perfetto.protos.Inputmethodeditor.InputMethodClientsTraceProto; |
| import android.internal.perfetto.protos.Inputmethodeditor.InputMethodManagerServiceTraceProto; |
| import android.internal.perfetto.protos.Inputmethodeditor.InputMethodServiceTraceProto; |
| import android.internal.perfetto.protos.TracePacketOuterClass.TracePacket; |
| import android.internal.perfetto.protos.WinscopeExtensionsImplOuterClass.WinscopeExtensionsImpl; |
| import android.os.SystemClock; |
| import android.os.Trace; |
| import android.tracing.inputmethod.InputMethodDataSource; |
| import android.tracing.perfetto.DataSourceParams; |
| import android.tracing.perfetto.InitArguments; |
| import android.tracing.perfetto.Producer; |
| import android.util.proto.ProtoOutputStream; |
| import android.view.inputmethod.InputMethodManager; |
| |
| import java.io.PrintWriter; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| /** |
| * An implementation of {@link ImeTracing} for perfetto tracing. |
| */ |
| final class ImeTracingPerfettoImpl extends ImeTracing { |
| private final AtomicInteger mTracingSessionsCount = new AtomicInteger(0); |
| private final AtomicBoolean mIsClientDumpInProgress = new AtomicBoolean(false); |
| private final AtomicBoolean mIsServiceDumpInProgress = new AtomicBoolean(false); |
| private final AtomicBoolean mIsManagerServiceDumpInProgress = new AtomicBoolean(false); |
| private final InputMethodDataSource mDataSource = new InputMethodDataSource( |
| mTracingSessionsCount::incrementAndGet, |
| mTracingSessionsCount::decrementAndGet); |
| |
| ImeTracingPerfettoImpl() { |
| Producer.init(InitArguments.DEFAULTS); |
| DataSourceParams params = |
| new DataSourceParams.Builder() |
| .setBufferExhaustedPolicy( |
| PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT) |
| .setNoFlush(true) |
| .setWillNotifyOnStop(false) |
| .build(); |
| mDataSource.register(params); |
| } |
| |
| |
| @Override |
| public void triggerClientDump(String where, InputMethodManager immInstance, |
| @Nullable byte[] icProto) { |
| if (!isEnabled() || !isAvailable()) { |
| return; |
| } |
| |
| if (!mIsClientDumpInProgress.compareAndSet(false, true)) { |
| return; |
| } |
| |
| if (immInstance == null) { |
| return; |
| } |
| |
| try { |
| Trace.beginSection("inputmethod_client_dump"); |
| mDataSource.trace((ctx) -> { |
| final ProtoOutputStream os = ctx.newTracePacket(); |
| os.write(TracePacket.TIMESTAMP, SystemClock.elapsedRealtimeNanos()); |
| final long tokenWinscopeExtensions = |
| os.start(TracePacket.WINSCOPE_EXTENSIONS); |
| final long tokenExtensionsField = |
| os.start(WinscopeExtensionsImpl.INPUTMETHOD_CLIENTS); |
| os.write(InputMethodClientsTraceProto.WHERE, where); |
| final long tokenClient = |
| os.start(InputMethodClientsTraceProto.CLIENT); |
| immInstance.dumpDebug(os, icProto); |
| os.end(tokenClient); |
| os.end(tokenExtensionsField); |
| os.end(tokenWinscopeExtensions); |
| }); |
| } finally { |
| mIsClientDumpInProgress.set(false); |
| Trace.endSection(); |
| } |
| } |
| |
| @Override |
| public void triggerServiceDump(String where, |
| @NonNull ServiceDumper dumper, @Nullable byte[] icProto) { |
| if (!isEnabled() || !isAvailable()) { |
| return; |
| } |
| |
| if (!mIsServiceDumpInProgress.compareAndSet(false, true)) { |
| return; |
| } |
| |
| try { |
| Trace.beginSection("inputmethod_service_dump"); |
| mDataSource.trace((ctx) -> { |
| final ProtoOutputStream os = ctx.newTracePacket(); |
| os.write(TracePacket.TIMESTAMP, SystemClock.elapsedRealtimeNanos()); |
| final long tokenWinscopeExtensions = |
| os.start(TracePacket.WINSCOPE_EXTENSIONS); |
| final long tokenExtensionsField = |
| os.start(WinscopeExtensionsImpl.INPUTMETHOD_SERVICE); |
| os.write(InputMethodServiceTraceProto.WHERE, where); |
| dumper.dumpToProto(os, icProto); |
| os.end(tokenExtensionsField); |
| os.end(tokenWinscopeExtensions); |
| }); |
| } finally { |
| mIsServiceDumpInProgress.set(false); |
| Trace.endSection(); |
| } |
| } |
| |
| @Override |
| public void triggerManagerServiceDump(@NonNull String where, @NonNull ServiceDumper dumper) { |
| if (!isEnabled() || !isAvailable()) { |
| return; |
| } |
| |
| if (!mIsManagerServiceDumpInProgress.compareAndSet(false, true)) { |
| return; |
| } |
| |
| try { |
| Trace.beginSection("inputmethod_manager_service_dump"); |
| mDataSource.trace((ctx) -> { |
| final ProtoOutputStream os = ctx.newTracePacket(); |
| os.write(TracePacket.TIMESTAMP, SystemClock.elapsedRealtimeNanos()); |
| final long tokenWinscopeExtensions = |
| os.start(TracePacket.WINSCOPE_EXTENSIONS); |
| final long tokenExtensionsField = |
| os.start(WinscopeExtensionsImpl.INPUTMETHOD_MANAGER_SERVICE); |
| os.write(InputMethodManagerServiceTraceProto.WHERE, where); |
| dumper.dumpToProto(os, null); |
| os.end(tokenExtensionsField); |
| os.end(tokenWinscopeExtensions); |
| }); |
| } finally { |
| mIsManagerServiceDumpInProgress.set(false); |
| Trace.endSection(); |
| } |
| } |
| |
| @Override |
| public boolean isEnabled() { |
| return mTracingSessionsCount.get() > 0; |
| } |
| |
| @Override |
| public void startTrace(@Nullable PrintWriter pw) { |
| // Intentionally left empty. Tracing start/stop is managed through Perfetto. |
| } |
| |
| @Override |
| public void stopTrace(@Nullable PrintWriter pw) { |
| // Intentionally left empty. Tracing start/stop is managed through Perfetto. |
| } |
| |
| @Override |
| public void addToBuffer(ProtoOutputStream proto, int source) { |
| // Intentionally left empty. Only used for legacy tracing. |
| } |
| } |