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 
17 package com.android.server.inputmethod;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.SystemClock;
22 import android.view.WindowManager;
23 import android.view.inputmethod.EditorInfo;
24 
25 import com.android.internal.inputmethod.InputMethodDebug;
26 import com.android.internal.inputmethod.SoftInputShowHideReason;
27 
28 import java.io.PrintWriter;
29 import java.time.Instant;
30 import java.time.ZoneId;
31 import java.time.format.DateTimeFormatter;
32 import java.util.Locale;
33 import java.util.concurrent.atomic.AtomicInteger;
34 
35 final class SoftInputShowHideHistory {
36     private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
37 
38     private final Entry[] mEntries = new Entry[16];
39     private int mNextIndex = 0;
40 
41     static final class Entry {
42         final int mSequenceNumber = sSequenceNumber.getAndIncrement();
43         @Nullable
44         final ClientState mClientState;
45         @WindowManager.LayoutParams.SoftInputModeFlags
46         final int mFocusedWindowSoftInputMode;
47         @SoftInputShowHideReason
48         final int mReason;
49         // The timing of handling showCurrentInputLocked() or hideCurrentInputLocked().
50         final long mTimestamp;
51         final long mWallTime;
52         final boolean mInFullscreenMode;
53         @NonNull
54         final String mFocusedWindowName;
55         @Nullable
56         final EditorInfo mEditorInfo;
57         @NonNull
58         final String mRequestWindowName;
59         @Nullable
60         final String mImeControlTargetName;
61         @Nullable
62         final String mImeTargetNameFromWm;
63         @Nullable
64         final String mImeSurfaceParentName;
65 
Entry(ClientState client, EditorInfo editorInfo, String focusedWindowName, @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, @SoftInputShowHideReason int reason, boolean inFullscreenMode, String requestWindowName, @Nullable String imeControlTargetName, @Nullable String imeTargetName, @Nullable String imeSurfaceParentName)66         Entry(ClientState client, EditorInfo editorInfo,
67                 String focusedWindowName,
68                 @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
69                 @SoftInputShowHideReason int reason,
70                 boolean inFullscreenMode, String requestWindowName,
71                 @Nullable String imeControlTargetName, @Nullable String imeTargetName,
72                 @Nullable String imeSurfaceParentName) {
73             mClientState = client;
74             mEditorInfo = editorInfo;
75             mFocusedWindowName = focusedWindowName;
76             mFocusedWindowSoftInputMode = softInputMode;
77             mReason = reason;
78             mTimestamp = SystemClock.uptimeMillis();
79             mWallTime = System.currentTimeMillis();
80             mInFullscreenMode = inFullscreenMode;
81             mRequestWindowName = requestWindowName;
82             mImeControlTargetName = imeControlTargetName;
83             mImeTargetNameFromWm = imeTargetName;
84             mImeSurfaceParentName = imeSurfaceParentName;
85         }
86     }
87 
addEntry(@onNull Entry entry)88     void addEntry(@NonNull Entry entry) {
89         final int index = mNextIndex;
90         mEntries[index] = entry;
91         mNextIndex = (mNextIndex + 1) % mEntries.length;
92     }
93 
dump(@onNull PrintWriter pw, @NonNull String prefix)94     void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
95         final DateTimeFormatter formatter =
96                 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
97                         .withZone(ZoneId.systemDefault());
98 
99         for (int i = 0; i < mEntries.length; ++i) {
100             final Entry entry = mEntries[(i + mNextIndex) % mEntries.length];
101             if (entry == null) {
102                 continue;
103             }
104             pw.print(prefix);
105             pw.println("SoftInputShowHide #" + entry.mSequenceNumber + ":");
106 
107             pw.print(prefix);
108             pw.println("  time=" + formatter.format(Instant.ofEpochMilli(entry.mWallTime))
109                     + " (timestamp=" + entry.mTimestamp + ")");
110 
111             pw.print(prefix);
112             pw.print("  reason=" + InputMethodDebug.softInputDisplayReasonToString(
113                     entry.mReason));
114             pw.println(" inFullscreenMode=" + entry.mInFullscreenMode);
115 
116             pw.print(prefix);
117             pw.println("  requestClient=" + entry.mClientState);
118 
119             pw.print(prefix);
120             pw.println("  focusedWindowName=" + entry.mFocusedWindowName);
121 
122             pw.print(prefix);
123             pw.println("  requestWindowName=" + entry.mRequestWindowName);
124 
125             pw.print(prefix);
126             pw.println("  imeControlTargetName=" + entry.mImeControlTargetName);
127 
128             pw.print(prefix);
129             pw.println("  imeTargetNameFromWm=" + entry.mImeTargetNameFromWm);
130 
131             pw.print(prefix);
132             pw.println("  imeSurfaceParentName=" + entry.mImeSurfaceParentName);
133 
134             pw.print(prefix);
135             pw.print("  editorInfo:");
136             if (entry.mEditorInfo != null) {
137                 pw.print(" inputType=" + entry.mEditorInfo.inputType);
138                 pw.print(" privateImeOptions=" + entry.mEditorInfo.privateImeOptions);
139                 pw.println(" fieldId (viewId)=" + entry.mEditorInfo.fieldId);
140             } else {
141                 pw.println(" null");
142             }
143 
144             pw.print(prefix);
145             pw.println("  focusedWindowSoftInputMode=" + InputMethodDebug.softInputModeToString(
146                     entry.mFocusedWindowSoftInputMode));
147         }
148     }
149 }
150