Add some protologs for window organizers & shell
- Refactor out base protolog impl so that shell can have it's own
implementation which takes a different viewer config (from the resources
directly instead of a separate file in /system/etc)
- Setup the protolog transform for shell classes
- Add some basic protologs for displayarea/task organizer controller
& shell task org (for now only text logging)
Bug: 161980327
Test: adb shell wm logging enable-text WM_DEBUG_WINDOW_ORGANIZER
Test: adb shell dumpsys activity service SystemUIService WMShell enable-text-logging WM_SHELL_TASK_ORG
Test: atest ProtoLogImplTest \
ProtoLogViewerConfigReaderTest \
WmTests:ProtoLogIntegrationTest \
LogDataTypeTest \
protologtool-tests
Change-Id: I4c3fd6aaea04987c26ac56e9baf9fc8ab75a3f34
Signed-off-by: Winson Chung <winsonc@google.com>
This commit is contained in:
397
core/java/com/android/internal/protolog/BaseProtoLogImpl.java
Normal file
397
core/java/com/android/internal/protolog/BaseProtoLogImpl.java
Normal file
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.protolog;
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogFileProto.LOG;
|
||||
import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER;
|
||||
import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_H;
|
||||
import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_L;
|
||||
import static com.android.internal.protolog.ProtoLogFileProto.REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS;
|
||||
import static com.android.internal.protolog.ProtoLogFileProto.VERSION;
|
||||
import static com.android.internal.protolog.ProtoLogMessage.BOOLEAN_PARAMS;
|
||||
import static com.android.internal.protolog.ProtoLogMessage.DOUBLE_PARAMS;
|
||||
import static com.android.internal.protolog.ProtoLogMessage.ELAPSED_REALTIME_NANOS;
|
||||
import static com.android.internal.protolog.ProtoLogMessage.MESSAGE_HASH;
|
||||
import static com.android.internal.protolog.ProtoLogMessage.SINT64_PARAMS;
|
||||
import static com.android.internal.protolog.ProtoLogMessage.STR_PARAMS;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.os.ShellCommand;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Slog;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.protolog.common.IProtoLogGroup;
|
||||
import com.android.internal.protolog.common.LogDataType;
|
||||
import com.android.internal.util.TraceBuffer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.IllegalFormatConversionException;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* A service for the ProtoLog logging system.
|
||||
*/
|
||||
public class BaseProtoLogImpl {
|
||||
protected static final TreeMap<String, IProtoLogGroup> LOG_GROUPS = new TreeMap<>();
|
||||
|
||||
/**
|
||||
* A runnable to update the cached output of {@link #isEnabled}.
|
||||
*
|
||||
* Must be invoked after every action that could change the result of {@link #isEnabled}, eg.
|
||||
* starting / stopping proto log, or enabling / disabling log groups.
|
||||
*/
|
||||
public static Runnable sCacheUpdater = () -> { };
|
||||
|
||||
protected static void addLogGroupEnum(IProtoLogGroup[] config) {
|
||||
for (IProtoLogGroup group : config) {
|
||||
LOG_GROUPS.put(group.name(), group);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TAG = "ProtoLog";
|
||||
private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
|
||||
static final String PROTOLOG_VERSION = "1.0.0";
|
||||
|
||||
private final File mLogFile;
|
||||
private final String mViewerConfigFilename;
|
||||
private final TraceBuffer mBuffer;
|
||||
protected final ProtoLogViewerConfigReader mViewerConfig;
|
||||
|
||||
private boolean mProtoLogEnabled;
|
||||
private boolean mProtoLogEnabledLockFree;
|
||||
private final Object mProtoLogEnabledLock = new Object();
|
||||
|
||||
@VisibleForTesting
|
||||
public enum LogLevel {
|
||||
DEBUG, VERBOSE, INFO, WARN, ERROR, WTF
|
||||
}
|
||||
|
||||
/**
|
||||
* Main log method, do not call directly.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void log(LogLevel level, IProtoLogGroup group, int messageHash, int paramsMask,
|
||||
@Nullable String messageString, Object[] args) {
|
||||
if (group.isLogToProto()) {
|
||||
logToProto(messageHash, paramsMask, args);
|
||||
}
|
||||
if (group.isLogToLogcat()) {
|
||||
logToLogcat(group.getTag(), level, messageHash, messageString, args);
|
||||
}
|
||||
}
|
||||
|
||||
private void logToLogcat(String tag, LogLevel level, int messageHash,
|
||||
@Nullable String messageString, Object[] args) {
|
||||
String message = null;
|
||||
if (messageString == null) {
|
||||
messageString = mViewerConfig.getViewerString(messageHash);
|
||||
}
|
||||
if (messageString != null) {
|
||||
try {
|
||||
message = String.format(messageString, args);
|
||||
} catch (IllegalFormatConversionException ex) {
|
||||
Slog.w(TAG, "Invalid ProtoLog format string.", ex);
|
||||
}
|
||||
}
|
||||
if (message == null) {
|
||||
StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE (" + messageHash + ")");
|
||||
for (Object o : args) {
|
||||
builder.append(" ").append(o);
|
||||
}
|
||||
message = builder.toString();
|
||||
}
|
||||
passToLogcat(tag, level, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* SLog wrapper.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void passToLogcat(String tag, LogLevel level, String message) {
|
||||
switch (level) {
|
||||
case DEBUG:
|
||||
Slog.d(tag, message);
|
||||
break;
|
||||
case VERBOSE:
|
||||
Slog.v(tag, message);
|
||||
break;
|
||||
case INFO:
|
||||
Slog.i(tag, message);
|
||||
break;
|
||||
case WARN:
|
||||
Slog.w(tag, message);
|
||||
break;
|
||||
case ERROR:
|
||||
Slog.e(tag, message);
|
||||
break;
|
||||
case WTF:
|
||||
Slog.wtf(tag, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void logToProto(int messageHash, int paramsMask, Object[] args) {
|
||||
if (!isProtoEnabled()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ProtoOutputStream os = new ProtoOutputStream();
|
||||
long token = os.start(LOG);
|
||||
os.write(MESSAGE_HASH, messageHash);
|
||||
os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
|
||||
|
||||
if (args != null) {
|
||||
int argIndex = 0;
|
||||
ArrayList<Long> longParams = new ArrayList<>();
|
||||
ArrayList<Double> doubleParams = new ArrayList<>();
|
||||
ArrayList<Boolean> booleanParams = new ArrayList<>();
|
||||
for (Object o : args) {
|
||||
int type = LogDataType.bitmaskToLogDataType(paramsMask, argIndex);
|
||||
try {
|
||||
switch (type) {
|
||||
case LogDataType.STRING:
|
||||
os.write(STR_PARAMS, o.toString());
|
||||
break;
|
||||
case LogDataType.LONG:
|
||||
longParams.add(((Number) o).longValue());
|
||||
break;
|
||||
case LogDataType.DOUBLE:
|
||||
doubleParams.add(((Number) o).doubleValue());
|
||||
break;
|
||||
case LogDataType.BOOLEAN:
|
||||
booleanParams.add((boolean) o);
|
||||
break;
|
||||
}
|
||||
} catch (ClassCastException ex) {
|
||||
// Should not happen unless there is an error in the ProtoLogTool.
|
||||
os.write(STR_PARAMS, "(INVALID PARAMS_MASK) " + o.toString());
|
||||
Slog.e(TAG, "Invalid ProtoLog paramsMask", ex);
|
||||
}
|
||||
argIndex++;
|
||||
}
|
||||
if (longParams.size() > 0) {
|
||||
os.writePackedSInt64(SINT64_PARAMS,
|
||||
longParams.stream().mapToLong(i -> i).toArray());
|
||||
}
|
||||
if (doubleParams.size() > 0) {
|
||||
os.writePackedDouble(DOUBLE_PARAMS,
|
||||
doubleParams.stream().mapToDouble(i -> i).toArray());
|
||||
}
|
||||
if (booleanParams.size() > 0) {
|
||||
boolean[] arr = new boolean[booleanParams.size()];
|
||||
for (int i = 0; i < booleanParams.size(); i++) {
|
||||
arr[i] = booleanParams.get(i);
|
||||
}
|
||||
os.writePackedBool(BOOLEAN_PARAMS, arr);
|
||||
}
|
||||
}
|
||||
os.end(token);
|
||||
mBuffer.add(os);
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Exception while logging to proto", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BaseProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity,
|
||||
ProtoLogViewerConfigReader viewerConfig) {
|
||||
mLogFile = file;
|
||||
mBuffer = new TraceBuffer(bufferCapacity);
|
||||
mViewerConfigFilename = viewerConfigFilename;
|
||||
mViewerConfig = viewerConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the logging a circular proto buffer.
|
||||
*
|
||||
* @param pw Print writer
|
||||
*/
|
||||
public void startProtoLog(@Nullable PrintWriter pw) {
|
||||
if (isProtoEnabled()) {
|
||||
return;
|
||||
}
|
||||
synchronized (mProtoLogEnabledLock) {
|
||||
logAndPrintln(pw, "Start logging to " + mLogFile + ".");
|
||||
mBuffer.resetBuffer();
|
||||
mProtoLogEnabled = true;
|
||||
mProtoLogEnabledLockFree = true;
|
||||
}
|
||||
sCacheUpdater.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops logging to proto.
|
||||
*
|
||||
* @param pw Print writer
|
||||
* @param writeToFile If the current buffer should be written to disk or not
|
||||
*/
|
||||
public void stopProtoLog(@Nullable PrintWriter pw, boolean writeToFile) {
|
||||
if (!isProtoEnabled()) {
|
||||
return;
|
||||
}
|
||||
synchronized (mProtoLogEnabledLock) {
|
||||
logAndPrintln(pw, "Stop logging to " + mLogFile + ". Waiting for log to flush.");
|
||||
mProtoLogEnabled = mProtoLogEnabledLockFree = false;
|
||||
if (writeToFile) {
|
||||
writeProtoLogToFileLocked();
|
||||
logAndPrintln(pw, "Log written to " + mLogFile + ".");
|
||||
}
|
||||
if (mProtoLogEnabled) {
|
||||
logAndPrintln(pw, "ERROR: logging was re-enabled while waiting for flush.");
|
||||
throw new IllegalStateException("logging enabled while waiting for flush.");
|
||||
}
|
||||
}
|
||||
sCacheUpdater.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} iff logging to proto is enabled.
|
||||
*/
|
||||
public boolean isProtoEnabled() {
|
||||
return mProtoLogEnabledLockFree;
|
||||
}
|
||||
|
||||
protected int setLogging(boolean setTextLogging, boolean value, PrintWriter pw,
|
||||
String... groups) {
|
||||
for (int i = 0; i < groups.length; i++) {
|
||||
String group = groups[i];
|
||||
IProtoLogGroup g = LOG_GROUPS.get(group);
|
||||
if (g != null) {
|
||||
System.out.println("G: "+ g);
|
||||
if (setTextLogging) {
|
||||
g.setLogToLogcat(value);
|
||||
} else {
|
||||
g.setLogToProto(value);
|
||||
}
|
||||
} else {
|
||||
logAndPrintln(pw, "No IProtoLogGroup named " + group);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
sCacheUpdater.run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int unknownCommand(PrintWriter pw) {
|
||||
pw.println("Unknown command");
|
||||
pw.println("Window manager logging options:");
|
||||
pw.println(" start: Start proto logging");
|
||||
pw.println(" stop: Stop proto logging");
|
||||
pw.println(" enable [group...]: Enable proto logging for given groups");
|
||||
pw.println(" disable [group...]: Disable proto logging for given groups");
|
||||
pw.println(" enable-text [group...]: Enable logcat logging for given groups");
|
||||
pw.println(" disable-text [group...]: Disable logcat logging for given groups");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to a shell command.
|
||||
*/
|
||||
public int onShellCommand(ShellCommand shell) {
|
||||
PrintWriter pw = shell.getOutPrintWriter();
|
||||
String cmd = shell.getNextArg();
|
||||
if (cmd == null) {
|
||||
return unknownCommand(pw);
|
||||
}
|
||||
ArrayList<String> args = new ArrayList<>();
|
||||
String arg;
|
||||
while ((arg = shell.getNextArg()) != null) {
|
||||
args.add(arg);
|
||||
}
|
||||
String[] groups = args.toArray(new String[args.size()]);
|
||||
switch (cmd) {
|
||||
case "start":
|
||||
startProtoLog(pw);
|
||||
return 0;
|
||||
case "stop":
|
||||
stopProtoLog(pw, true);
|
||||
return 0;
|
||||
case "status":
|
||||
logAndPrintln(pw, getStatus());
|
||||
return 0;
|
||||
case "enable":
|
||||
return setLogging(false, true, pw, groups);
|
||||
case "enable-text":
|
||||
mViewerConfig.loadViewerConfig(pw, mViewerConfigFilename);
|
||||
return setLogging(true, true, pw, groups);
|
||||
case "disable":
|
||||
return setLogging(false, false, pw, groups);
|
||||
case "disable-text":
|
||||
return setLogging(true, false, pw, groups);
|
||||
default:
|
||||
return unknownCommand(pw);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human-readable ProtoLog status text.
|
||||
*/
|
||||
public String getStatus() {
|
||||
return "ProtoLog status: "
|
||||
+ ((isProtoEnabled()) ? "Enabled" : "Disabled")
|
||||
+ "\nEnabled log groups: \n Proto: "
|
||||
+ LOG_GROUPS.values().stream().filter(
|
||||
it -> it.isEnabled() && it.isLogToProto())
|
||||
.map(IProtoLogGroup::name).collect(Collectors.joining(" "))
|
||||
+ "\n Logcat: "
|
||||
+ LOG_GROUPS.values().stream().filter(
|
||||
it -> it.isEnabled() && it.isLogToLogcat())
|
||||
.map(IProtoLogGroup::name).collect(Collectors.joining(" "))
|
||||
+ "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the log buffer to a new file for the bugreport.
|
||||
*
|
||||
* This method is synchronized with {@code #startProtoLog(PrintWriter)} and
|
||||
* {@link #stopProtoLog(PrintWriter, boolean)}.
|
||||
*/
|
||||
public void writeProtoLogToFile() {
|
||||
synchronized (mProtoLogEnabledLock) {
|
||||
writeProtoLogToFileLocked();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeProtoLogToFileLocked() {
|
||||
try {
|
||||
long offset =
|
||||
(System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000));
|
||||
ProtoOutputStream proto = new ProtoOutputStream();
|
||||
proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
|
||||
proto.write(VERSION, PROTOLOG_VERSION);
|
||||
proto.write(REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS, offset);
|
||||
mBuffer.writeTraceToFile(mLogFile, proto);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to write buffer to file", e);
|
||||
}
|
||||
}
|
||||
|
||||
static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
|
||||
Slog.i(TAG, msg);
|
||||
if (pw != null) {
|
||||
pw.println(msg);
|
||||
pw.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ public enum ProtoLogGroup implements IProtoLogGroup {
|
||||
Consts.TAG_WM),
|
||||
WM_DEBUG_IME(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||
Consts.TAG_WM),
|
||||
WM_DEBUG_WINDOW_ORGANIZER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||
Consts.TAG_WM),
|
||||
TEST_GROUP(true, true, false, "WindowManagetProtoLogTest");
|
||||
|
||||
private final boolean mEnabled;
|
||||
|
||||
@@ -16,58 +16,22 @@
|
||||
|
||||
package com.android.internal.protolog;
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogFileProto.LOG;
|
||||
import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER;
|
||||
import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_H;
|
||||
import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_L;
|
||||
import static com.android.internal.protolog.ProtoLogFileProto.REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS;
|
||||
import static com.android.internal.protolog.ProtoLogFileProto.VERSION;
|
||||
import static com.android.internal.protolog.ProtoLogMessage.BOOLEAN_PARAMS;
|
||||
import static com.android.internal.protolog.ProtoLogMessage.DOUBLE_PARAMS;
|
||||
import static com.android.internal.protolog.ProtoLogMessage.ELAPSED_REALTIME_NANOS;
|
||||
import static com.android.internal.protolog.ProtoLogMessage.MESSAGE_HASH;
|
||||
import static com.android.internal.protolog.ProtoLogMessage.SINT64_PARAMS;
|
||||
import static com.android.internal.protolog.ProtoLogMessage.STR_PARAMS;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.os.ShellCommand;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Slog;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.protolog.common.IProtoLogGroup;
|
||||
import com.android.internal.protolog.common.LogDataType;
|
||||
import com.android.internal.util.TraceBuffer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.IllegalFormatConversionException;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* A service for the ProtoLog logging system.
|
||||
*/
|
||||
public class ProtoLogImpl {
|
||||
private static final TreeMap<String, IProtoLogGroup> LOG_GROUPS = new TreeMap<>();
|
||||
public class ProtoLogImpl extends BaseProtoLogImpl {
|
||||
private static final int BUFFER_CAPACITY = 1024 * 1024;
|
||||
private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb";
|
||||
private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz";
|
||||
|
||||
/**
|
||||
* A runnable to update the cached output of {@link #isEnabled}.
|
||||
*
|
||||
* Must be invoked after every action that could change the result of {@link #isEnabled}, eg.
|
||||
* starting / stopping proto log, or enabling / disabling log groups.
|
||||
*/
|
||||
public static Runnable sCacheUpdater = () -> { };
|
||||
|
||||
private static void addLogGroupEnum(IProtoLogGroup[] config) {
|
||||
for (IProtoLogGroup group : config) {
|
||||
LOG_GROUPS.put(group.name(), group);
|
||||
}
|
||||
}
|
||||
private static ProtoLogImpl sServiceInstance = null;
|
||||
|
||||
static {
|
||||
addLogGroupEnum(ProtoLogGroup.values());
|
||||
@@ -124,30 +88,13 @@ public class ProtoLogImpl {
|
||||
|| (group.isLogToProto() && getSingleInstance().isProtoEnabled());
|
||||
}
|
||||
|
||||
private static final int BUFFER_CAPACITY = 1024 * 1024;
|
||||
private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb";
|
||||
private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz";
|
||||
private static final String TAG = "ProtoLog";
|
||||
private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
|
||||
static final String PROTOLOG_VERSION = "1.0.0";
|
||||
|
||||
private final File mLogFile;
|
||||
private final TraceBuffer mBuffer;
|
||||
private final ProtoLogViewerConfigReader mViewerConfig;
|
||||
|
||||
private boolean mProtoLogEnabled;
|
||||
private boolean mProtoLogEnabledLockFree;
|
||||
private final Object mProtoLogEnabledLock = new Object();
|
||||
|
||||
private static ProtoLogImpl sServiceInstance = null;
|
||||
|
||||
/**
|
||||
* Returns the single instance of the ProtoLogImpl singleton class.
|
||||
*/
|
||||
public static synchronized ProtoLogImpl getSingleInstance() {
|
||||
if (sServiceInstance == null) {
|
||||
sServiceInstance = new ProtoLogImpl(new File(LOG_FILENAME), BUFFER_CAPACITY,
|
||||
new ProtoLogViewerConfigReader());
|
||||
sServiceInstance = new ProtoLogImpl(
|
||||
new File(LOG_FILENAME), BUFFER_CAPACITY, new ProtoLogViewerConfigReader());
|
||||
}
|
||||
return sServiceInstance;
|
||||
}
|
||||
@@ -157,307 +104,9 @@ public class ProtoLogImpl {
|
||||
sServiceInstance = instance;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public enum LogLevel {
|
||||
DEBUG, VERBOSE, INFO, WARN, ERROR, WTF
|
||||
}
|
||||
|
||||
/**
|
||||
* Main log method, do not call directly.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void log(LogLevel level, IProtoLogGroup group, int messageHash, int paramsMask,
|
||||
@Nullable String messageString, Object[] args) {
|
||||
if (group.isLogToProto()) {
|
||||
logToProto(messageHash, paramsMask, args);
|
||||
}
|
||||
if (group.isLogToLogcat()) {
|
||||
logToLogcat(group.getTag(), level, messageHash, messageString, args);
|
||||
}
|
||||
}
|
||||
|
||||
private void logToLogcat(String tag, LogLevel level, int messageHash,
|
||||
@Nullable String messageString, Object[] args) {
|
||||
String message = null;
|
||||
if (messageString == null) {
|
||||
messageString = mViewerConfig.getViewerString(messageHash);
|
||||
}
|
||||
if (messageString != null) {
|
||||
try {
|
||||
message = String.format(messageString, args);
|
||||
} catch (IllegalFormatConversionException ex) {
|
||||
Slog.w(TAG, "Invalid ProtoLog format string.", ex);
|
||||
}
|
||||
}
|
||||
if (message == null) {
|
||||
StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE (" + messageHash + ")");
|
||||
for (Object o : args) {
|
||||
builder.append(" ").append(o);
|
||||
}
|
||||
message = builder.toString();
|
||||
}
|
||||
passToLogcat(tag, level, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* SLog wrapper.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void passToLogcat(String tag, LogLevel level, String message) {
|
||||
switch (level) {
|
||||
case DEBUG:
|
||||
Slog.d(tag, message);
|
||||
break;
|
||||
case VERBOSE:
|
||||
Slog.v(tag, message);
|
||||
break;
|
||||
case INFO:
|
||||
Slog.i(tag, message);
|
||||
break;
|
||||
case WARN:
|
||||
Slog.w(tag, message);
|
||||
break;
|
||||
case ERROR:
|
||||
Slog.e(tag, message);
|
||||
break;
|
||||
case WTF:
|
||||
Slog.wtf(tag, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void logToProto(int messageHash, int paramsMask, Object[] args) {
|
||||
if (!isProtoEnabled()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ProtoOutputStream os = new ProtoOutputStream();
|
||||
long token = os.start(LOG);
|
||||
os.write(MESSAGE_HASH, messageHash);
|
||||
os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
|
||||
|
||||
if (args != null) {
|
||||
int argIndex = 0;
|
||||
ArrayList<Long> longParams = new ArrayList<>();
|
||||
ArrayList<Double> doubleParams = new ArrayList<>();
|
||||
ArrayList<Boolean> booleanParams = new ArrayList<>();
|
||||
for (Object o : args) {
|
||||
int type = LogDataType.bitmaskToLogDataType(paramsMask, argIndex);
|
||||
try {
|
||||
switch (type) {
|
||||
case LogDataType.STRING:
|
||||
os.write(STR_PARAMS, o.toString());
|
||||
break;
|
||||
case LogDataType.LONG:
|
||||
longParams.add(((Number) o).longValue());
|
||||
break;
|
||||
case LogDataType.DOUBLE:
|
||||
doubleParams.add(((Number) o).doubleValue());
|
||||
break;
|
||||
case LogDataType.BOOLEAN:
|
||||
booleanParams.add((boolean) o);
|
||||
break;
|
||||
}
|
||||
} catch (ClassCastException ex) {
|
||||
// Should not happen unless there is an error in the ProtoLogTool.
|
||||
os.write(STR_PARAMS, "(INVALID PARAMS_MASK) " + o.toString());
|
||||
Slog.e(TAG, "Invalid ProtoLog paramsMask", ex);
|
||||
}
|
||||
argIndex++;
|
||||
}
|
||||
if (longParams.size() > 0) {
|
||||
os.writePackedSInt64(SINT64_PARAMS,
|
||||
longParams.stream().mapToLong(i -> i).toArray());
|
||||
}
|
||||
if (doubleParams.size() > 0) {
|
||||
os.writePackedDouble(DOUBLE_PARAMS,
|
||||
doubleParams.stream().mapToDouble(i -> i).toArray());
|
||||
}
|
||||
if (booleanParams.size() > 0) {
|
||||
boolean[] arr = new boolean[booleanParams.size()];
|
||||
for (int i = 0; i < booleanParams.size(); i++) {
|
||||
arr[i] = booleanParams.get(i);
|
||||
}
|
||||
os.writePackedBool(BOOLEAN_PARAMS, arr);
|
||||
}
|
||||
}
|
||||
os.end(token);
|
||||
mBuffer.add(os);
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Exception while logging to proto", e);
|
||||
}
|
||||
}
|
||||
|
||||
public ProtoLogImpl(File file, int bufferCapacity, ProtoLogViewerConfigReader viewerConfig) {
|
||||
mLogFile = file;
|
||||
mBuffer = new TraceBuffer(bufferCapacity);
|
||||
mViewerConfig = viewerConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the logging a circular proto buffer.
|
||||
*
|
||||
* @param pw Print writer
|
||||
*/
|
||||
public void startProtoLog(@Nullable PrintWriter pw) {
|
||||
if (isProtoEnabled()) {
|
||||
return;
|
||||
}
|
||||
synchronized (mProtoLogEnabledLock) {
|
||||
logAndPrintln(pw, "Start logging to " + mLogFile + ".");
|
||||
mBuffer.resetBuffer();
|
||||
mProtoLogEnabled = true;
|
||||
mProtoLogEnabledLockFree = true;
|
||||
}
|
||||
sCacheUpdater.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops logging to proto.
|
||||
*
|
||||
* @param pw Print writer
|
||||
* @param writeToFile If the current buffer should be written to disk or not
|
||||
*/
|
||||
public void stopProtoLog(@Nullable PrintWriter pw, boolean writeToFile) {
|
||||
if (!isProtoEnabled()) {
|
||||
return;
|
||||
}
|
||||
synchronized (mProtoLogEnabledLock) {
|
||||
logAndPrintln(pw, "Stop logging to " + mLogFile + ". Waiting for log to flush.");
|
||||
mProtoLogEnabled = mProtoLogEnabledLockFree = false;
|
||||
if (writeToFile) {
|
||||
writeProtoLogToFileLocked();
|
||||
logAndPrintln(pw, "Log written to " + mLogFile + ".");
|
||||
}
|
||||
if (mProtoLogEnabled) {
|
||||
logAndPrintln(pw, "ERROR: logging was re-enabled while waiting for flush.");
|
||||
throw new IllegalStateException("logging enabled while waiting for flush.");
|
||||
}
|
||||
}
|
||||
sCacheUpdater.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} iff logging to proto is enabled.
|
||||
*/
|
||||
public boolean isProtoEnabled() {
|
||||
return mProtoLogEnabledLockFree;
|
||||
}
|
||||
|
||||
private int setLogging(ShellCommand shell, boolean setTextLogging, boolean value) {
|
||||
String group;
|
||||
while ((group = shell.getNextArg()) != null) {
|
||||
IProtoLogGroup g = LOG_GROUPS.get(group);
|
||||
if (g != null) {
|
||||
if (setTextLogging) {
|
||||
g.setLogToLogcat(value);
|
||||
} else {
|
||||
g.setLogToProto(value);
|
||||
}
|
||||
} else {
|
||||
logAndPrintln(shell.getOutPrintWriter(), "No IProtoLogGroup named " + group);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
sCacheUpdater.run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int unknownCommand(PrintWriter pw) {
|
||||
pw.println("Unknown command");
|
||||
pw.println("Window manager logging options:");
|
||||
pw.println(" start: Start proto logging");
|
||||
pw.println(" stop: Stop proto logging");
|
||||
pw.println(" enable [group...]: Enable proto logging for given groups");
|
||||
pw.println(" disable [group...]: Disable proto logging for given groups");
|
||||
pw.println(" enable-text [group...]: Enable logcat logging for given groups");
|
||||
pw.println(" disable-text [group...]: Disable logcat logging for given groups");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to a shell command.
|
||||
*/
|
||||
public int onShellCommand(ShellCommand shell) {
|
||||
PrintWriter pw = shell.getOutPrintWriter();
|
||||
String cmd = shell.getNextArg();
|
||||
if (cmd == null) {
|
||||
return unknownCommand(pw);
|
||||
}
|
||||
switch (cmd) {
|
||||
case "start":
|
||||
startProtoLog(pw);
|
||||
return 0;
|
||||
case "stop":
|
||||
stopProtoLog(pw, true);
|
||||
return 0;
|
||||
case "status":
|
||||
logAndPrintln(pw, getStatus());
|
||||
return 0;
|
||||
case "enable":
|
||||
return setLogging(shell, false, true);
|
||||
case "enable-text":
|
||||
mViewerConfig.loadViewerConfig(pw, VIEWER_CONFIG_FILENAME);
|
||||
return setLogging(shell, true, true);
|
||||
case "disable":
|
||||
return setLogging(shell, false, false);
|
||||
case "disable-text":
|
||||
return setLogging(shell, true, false);
|
||||
default:
|
||||
return unknownCommand(pw);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human-readable ProtoLog status text.
|
||||
*/
|
||||
public String getStatus() {
|
||||
return "ProtoLog status: "
|
||||
+ ((isProtoEnabled()) ? "Enabled" : "Disabled")
|
||||
+ "\nEnabled log groups: \n Proto: "
|
||||
+ LOG_GROUPS.values().stream().filter(
|
||||
it -> it.isEnabled() && it.isLogToProto())
|
||||
.map(IProtoLogGroup::name).collect(Collectors.joining(" "))
|
||||
+ "\n Logcat: "
|
||||
+ LOG_GROUPS.values().stream().filter(
|
||||
it -> it.isEnabled() && it.isLogToLogcat())
|
||||
.map(IProtoLogGroup::name).collect(Collectors.joining(" "))
|
||||
+ "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the log buffer to a new file for the bugreport.
|
||||
*
|
||||
* This method is synchronized with {@code #startProtoLog(PrintWriter)} and
|
||||
* {@link #stopProtoLog(PrintWriter, boolean)}.
|
||||
*/
|
||||
public void writeProtoLogToFile() {
|
||||
synchronized (mProtoLogEnabledLock) {
|
||||
writeProtoLogToFileLocked();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeProtoLogToFileLocked() {
|
||||
try {
|
||||
long offset =
|
||||
(System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000));
|
||||
ProtoOutputStream proto = new ProtoOutputStream();
|
||||
proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
|
||||
proto.write(VERSION, PROTOLOG_VERSION);
|
||||
proto.write(REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS, offset);
|
||||
mBuffer.writeTraceToFile(mLogFile, proto);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to write buffer to file", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
|
||||
Slog.i(TAG, msg);
|
||||
if (pw != null) {
|
||||
pw.println(msg);
|
||||
pw.flush();
|
||||
}
|
||||
public ProtoLogImpl(File logFile, int bufferCapacity,
|
||||
ProtoLogViewerConfigReader viewConfigReader) {
|
||||
super(logFile, VIEWER_CONFIG_FILENAME, bufferCapacity, viewConfigReader);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.internal.protolog;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.util.Slog;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -23,6 +26,7 @@ import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Iterator;
|
||||
@@ -34,6 +38,7 @@ import java.util.zip.GZIPInputStream;
|
||||
* Handles loading and parsing of ProtoLog viewer configuration.
|
||||
*/
|
||||
public class ProtoLogViewerConfigReader {
|
||||
private static final String TAG = "ProtoLogViewerConfigReader";
|
||||
private Map<Integer, String> mLogMessageMap = null;
|
||||
|
||||
/** Returns message format string for its hash or null if unavailable. */
|
||||
@@ -49,48 +54,54 @@ public class ProtoLogViewerConfigReader {
|
||||
* Reads the specified viewer configuration file. Does nothing if the config is already loaded.
|
||||
*/
|
||||
public synchronized void loadViewerConfig(PrintWriter pw, String viewerConfigFilename) {
|
||||
try {
|
||||
loadViewerConfig(new GZIPInputStream(new FileInputStream(viewerConfigFilename)));
|
||||
logAndPrintln(pw, "Loaded " + mLogMessageMap.size()
|
||||
+ " log definitions from " + viewerConfigFilename);
|
||||
} catch (FileNotFoundException e) {
|
||||
logAndPrintln(pw, "Unable to load log definitions: File "
|
||||
+ viewerConfigFilename + " not found." + e);
|
||||
} catch (IOException e) {
|
||||
logAndPrintln(pw, "Unable to load log definitions: IOException while reading "
|
||||
+ viewerConfigFilename + ". " + e);
|
||||
} catch (JSONException e) {
|
||||
logAndPrintln(pw, "Unable to load log definitions: JSON parsing exception while reading "
|
||||
+ viewerConfigFilename + ". " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the specified viewer configuration input stream.
|
||||
* Does nothing if the config is already loaded.
|
||||
*/
|
||||
public synchronized void loadViewerConfig(InputStream viewerConfigInputStream)
|
||||
throws IOException, JSONException {
|
||||
if (mLogMessageMap != null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
InputStreamReader config = new InputStreamReader(
|
||||
new GZIPInputStream(new FileInputStream(viewerConfigFilename)));
|
||||
BufferedReader reader = new BufferedReader(config);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
builder.append(line).append('\n');
|
||||
}
|
||||
reader.close();
|
||||
JSONObject json = new JSONObject(builder.toString());
|
||||
JSONObject messages = json.getJSONObject("messages");
|
||||
InputStreamReader config = new InputStreamReader(viewerConfigInputStream);
|
||||
BufferedReader reader = new BufferedReader(config);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
builder.append(line).append('\n');
|
||||
}
|
||||
reader.close();
|
||||
JSONObject json = new JSONObject(builder.toString());
|
||||
JSONObject messages = json.getJSONObject("messages");
|
||||
|
||||
mLogMessageMap = new TreeMap<>();
|
||||
Iterator it = messages.keys();
|
||||
while (it.hasNext()) {
|
||||
String key = (String) it.next();
|
||||
try {
|
||||
int hash = Integer.parseInt(key);
|
||||
JSONObject val = messages.getJSONObject(key);
|
||||
String msg = val.getString("message");
|
||||
mLogMessageMap.put(hash, msg);
|
||||
} catch (NumberFormatException expected) {
|
||||
// Not a messageHash - skip it
|
||||
}
|
||||
mLogMessageMap = new TreeMap<>();
|
||||
Iterator it = messages.keys();
|
||||
while (it.hasNext()) {
|
||||
String key = (String) it.next();
|
||||
try {
|
||||
int hash = Integer.parseInt(key);
|
||||
JSONObject val = messages.getJSONObject(key);
|
||||
String msg = val.getString("message");
|
||||
mLogMessageMap.put(hash, msg);
|
||||
} catch (NumberFormatException expected) {
|
||||
// Not a messageHash - skip it
|
||||
}
|
||||
ProtoLogImpl.logAndPrintln(pw, "Loaded " + mLogMessageMap.size()
|
||||
+ " log definitions from " + viewerConfigFilename);
|
||||
} catch (FileNotFoundException e) {
|
||||
ProtoLogImpl.logAndPrintln(pw, "Unable to load log definitions: File "
|
||||
+ viewerConfigFilename + " not found." + e);
|
||||
} catch (IOException e) {
|
||||
ProtoLogImpl.logAndPrintln(pw,
|
||||
"Unable to load log definitions: IOException while reading "
|
||||
+ viewerConfigFilename + ". " + e);
|
||||
} catch (JSONException e) {
|
||||
ProtoLogImpl.logAndPrintln(pw,
|
||||
"Unable to load log definitions: JSON parsing exception while reading "
|
||||
+ viewerConfigFilename + ". " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,4 +114,12 @@ public class ProtoLogViewerConfigReader {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
|
||||
Slog.i(TAG, msg);
|
||||
if (pw != null) {
|
||||
pw.println(msg);
|
||||
pw.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"-2049725903": {
|
||||
"message": "Task back pressed on root taskId=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
|
||||
},
|
||||
"-2039580386": {
|
||||
"message": "Attempted to add input method window with unknown token %s. Aborting.",
|
||||
"level": "WARN",
|
||||
@@ -85,6 +91,12 @@
|
||||
"group": "WM_DEBUG_STARTING_WINDOW",
|
||||
"at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
|
||||
},
|
||||
"-1980468143": {
|
||||
"message": "DisplayArea appeared name=%s",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
|
||||
},
|
||||
"-1976930686": {
|
||||
"message": "Attempted to add Accessibility overlay window with bad token %s. Aborting.",
|
||||
"level": "WARN",
|
||||
@@ -103,6 +115,12 @@
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"-1939861963": {
|
||||
"message": "Create root task displayId=%d winMode=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
|
||||
},
|
||||
"-1939358269": {
|
||||
"message": "mRecentScreenshotAnimator finish",
|
||||
"level": "DEBUG",
|
||||
@@ -127,6 +145,12 @@
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"-1895337367": {
|
||||
"message": "Delete root task display=%d winMode=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
|
||||
},
|
||||
"-1884933373": {
|
||||
"message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
|
||||
"level": "INFO",
|
||||
@@ -187,6 +211,12 @@
|
||||
"group": "WM_DEBUG_CONFIGURATION",
|
||||
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
|
||||
},
|
||||
"-1792633344": {
|
||||
"message": "Register task organizer=%s uid=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
|
||||
},
|
||||
"-1791031393": {
|
||||
"message": "Ensuring correct configuration: %s",
|
||||
"level": "VERBOSE",
|
||||
@@ -421,6 +451,12 @@
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"-1364754753": {
|
||||
"message": "Task vanished taskId=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
|
||||
},
|
||||
"-1352076759": {
|
||||
"message": "Removing app token: %s",
|
||||
"level": "VERBOSE",
|
||||
@@ -643,6 +679,18 @@
|
||||
"group": "WM_DEBUG_STARTING_WINDOW",
|
||||
"at": "com\/android\/server\/wm\/WindowState.java"
|
||||
},
|
||||
"-951939129": {
|
||||
"message": "Unregister task organizer=%s uid=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
|
||||
},
|
||||
"-930893991": {
|
||||
"message": "Set sync ready, syncId=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/WindowOrganizerController.java"
|
||||
},
|
||||
"-929676529": {
|
||||
"message": "Configuration changes for %s, allChanges=%s",
|
||||
"level": "VERBOSE",
|
||||
@@ -793,12 +841,6 @@
|
||||
"group": "WM_DEBUG_FOCUS",
|
||||
"at": "com\/android\/server\/wm\/ActivityRecord.java"
|
||||
},
|
||||
"-714291355": {
|
||||
"message": "Losing delayed focus: %s",
|
||||
"level": "INFO",
|
||||
"group": "WM_DEBUG_FOCUS_LIGHT",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"-694710814": {
|
||||
"message": "Pausing rotation during drag",
|
||||
"level": "DEBUG",
|
||||
@@ -919,6 +961,12 @@
|
||||
"group": "WM_DEBUG_CONFIGURATION",
|
||||
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
|
||||
},
|
||||
"-497620140": {
|
||||
"message": "Transaction ready, syncId=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/WindowOrganizerController.java"
|
||||
},
|
||||
"-496681057": {
|
||||
"message": "Attempted to get remove mode of a display that does not exist: %d",
|
||||
"level": "WARN",
|
||||
@@ -1345,6 +1393,12 @@
|
||||
"group": "WM_SHOW_SURFACE_ALLOC",
|
||||
"at": "com\/android\/server\/wm\/BlackFrame.java"
|
||||
},
|
||||
"174572959": {
|
||||
"message": "DisplayArea info changed name=%s",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
|
||||
},
|
||||
"184362060": {
|
||||
"message": "screenshotTask(%d): mCanceled=%b",
|
||||
"level": "DEBUG",
|
||||
@@ -1387,6 +1441,12 @@
|
||||
"group": "WM_DEBUG_KEEP_SCREEN_ON",
|
||||
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
|
||||
},
|
||||
"232317536": {
|
||||
"message": "Set intercept back pressed on root=%b",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
|
||||
},
|
||||
"241961619": {
|
||||
"message": "Adding %s to %s",
|
||||
"level": "VERBOSE",
|
||||
@@ -1405,6 +1465,12 @@
|
||||
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
|
||||
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
|
||||
},
|
||||
"251812577": {
|
||||
"message": "Register display organizer=%s uid=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
|
||||
},
|
||||
"254883724": {
|
||||
"message": "addWindowToken: Attempted to add binder token: %s for already created window token: %s displayId=%d",
|
||||
"level": "WARN",
|
||||
@@ -1453,6 +1519,12 @@
|
||||
"group": "WM_DEBUG_ORIENTATION",
|
||||
"at": "com\/android\/server\/wm\/DisplayRotation.java"
|
||||
},
|
||||
"302969511": {
|
||||
"message": "Task info changed taskId=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
|
||||
},
|
||||
"302992539": {
|
||||
"message": "addAnimation(%s)",
|
||||
"level": "DEBUG",
|
||||
@@ -1519,12 +1591,6 @@
|
||||
"group": "WM_DEBUG_BOOT",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"385096046": {
|
||||
"message": "Delaying loss of focus...",
|
||||
"level": "INFO",
|
||||
"group": "WM_DEBUG_FOCUS_LIGHT",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"399841913": {
|
||||
"message": "SURFACE RECOVER DESTROY: %s",
|
||||
"level": "INFO",
|
||||
@@ -1573,6 +1639,12 @@
|
||||
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
|
||||
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
|
||||
},
|
||||
"487621047": {
|
||||
"message": "DisplayArea vanished name=%s",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
|
||||
},
|
||||
"490877640": {
|
||||
"message": "onStackOrderChanged(): stack=%s",
|
||||
"level": "DEBUG",
|
||||
@@ -1849,6 +1921,12 @@
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"906215061": {
|
||||
"message": "Apply window transaction, syncId=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/WindowOrganizerController.java"
|
||||
},
|
||||
"913494177": {
|
||||
"message": "removeAllWindowsIfPossible: removing win=%s",
|
||||
"level": "WARN",
|
||||
@@ -1957,6 +2035,12 @@
|
||||
"group": "WM_DEBUG_SCREEN_ON",
|
||||
"at": "com\/android\/server\/wm\/DisplayPolicy.java"
|
||||
},
|
||||
"1149424314": {
|
||||
"message": "Unregister display organizer=%s uid=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
|
||||
},
|
||||
"1160771501": {
|
||||
"message": "Resize reasons for w=%s: %s surfaceResized=%b configChanged=%b dragResizingChanged=%b reportOrientationChanged=%b",
|
||||
"level": "VERBOSE",
|
||||
@@ -2443,6 +2527,12 @@
|
||||
"group": "WM_DEBUG_ADD_REMOVE",
|
||||
"at": "com\/android\/server\/wm\/ActivityRecord.java"
|
||||
},
|
||||
"1918448345": {
|
||||
"message": "Task appeared taskId=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
|
||||
},
|
||||
"1921821199": {
|
||||
"message": "Preserving %s until the new one is added",
|
||||
"level": "VERBOSE",
|
||||
@@ -2676,6 +2766,9 @@
|
||||
"WM_DEBUG_WINDOW_MOVEMENT": {
|
||||
"tag": "WindowManager"
|
||||
},
|
||||
"WM_DEBUG_WINDOW_ORGANIZER": {
|
||||
"tag": "WindowManager"
|
||||
},
|
||||
"WM_ERROR": {
|
||||
"tag": "WindowManager"
|
||||
},
|
||||
|
||||
@@ -12,14 +12,88 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Begin ProtoLog
|
||||
java_library {
|
||||
name: "wm_shell_protolog-groups",
|
||||
srcs: [
|
||||
"src/com/android/wm/shell/protolog/ShellProtoLogGroup.java",
|
||||
":protolog-common-src",
|
||||
],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "wm_shell-sources",
|
||||
srcs: ["src/**/*.java"],
|
||||
path: "src",
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "wm_shell_protolog_src",
|
||||
srcs: [
|
||||
":wm_shell_protolog-groups",
|
||||
":wm_shell-sources",
|
||||
],
|
||||
tools: ["protologtool"],
|
||||
cmd: "$(location protologtool) transform-protolog-calls " +
|
||||
"--protolog-class com.android.internal.protolog.common.ProtoLog " +
|
||||
"--protolog-impl-class com.android.wm.shell.protolog.ShellProtoLogImpl " +
|
||||
"--protolog-cache-class com.android.wm.shell.protolog.ShellProtoLogCache " +
|
||||
"--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
|
||||
"--loggroups-jar $(location :wm_shell_protolog-groups) " +
|
||||
"--output-srcjar $(out) " +
|
||||
"$(locations :wm_shell-sources)",
|
||||
out: ["wm_shell_protolog.srcjar"],
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "generate-wm_shell_protolog.json",
|
||||
srcs: [
|
||||
":wm_shell_protolog-groups",
|
||||
":wm_shell-sources",
|
||||
],
|
||||
tools: ["protologtool"],
|
||||
cmd: "$(location protologtool) generate-viewer-config " +
|
||||
"--protolog-class com.android.internal.protolog.common.ProtoLog " +
|
||||
"--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
|
||||
"--loggroups-jar $(location :wm_shell_protolog-groups) " +
|
||||
"--viewer-conf $(out) " +
|
||||
"$(locations :wm_shell-sources)",
|
||||
out: ["wm_shell_protolog.json"],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "wm_shell_protolog.json",
|
||||
srcs: ["res/raw/wm_shell_protolog.json"],
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "checked-wm_shell_protolog.json",
|
||||
srcs: [
|
||||
":generate-wm_shell_protolog.json",
|
||||
":wm_shell_protolog.json",
|
||||
],
|
||||
cmd: "cp $(location :generate-wm_shell_protolog.json) $(out) && " +
|
||||
"{ ! (diff $(out) $(location :wm_shell_protolog.json) | grep -q '^<') || " +
|
||||
"{ echo -e '\\n\\n################################################################\\n#\\n" +
|
||||
"# ERROR: ProtoLog viewer config is stale. To update it, run:\\n#\\n" +
|
||||
"# cp $(location :generate-wm_shell_protolog.json) " +
|
||||
"$(location :wm_shell_protolog.json)\\n#\\n" +
|
||||
"################################################################\\n\\n' >&2 && false; } }",
|
||||
out: ["wm_shell_protolog.json"],
|
||||
}
|
||||
// End ProtoLog
|
||||
|
||||
android_library {
|
||||
name: "WindowManager-Shell",
|
||||
srcs: [
|
||||
"src/**/*.java",
|
||||
":wm_shell_protolog_src",
|
||||
"src/**/I*.aidl",
|
||||
],
|
||||
resource_dirs: [
|
||||
"res",
|
||||
],
|
||||
static_libs: [
|
||||
"protolog-lib",
|
||||
],
|
||||
manifest: "AndroidManifest.xml",
|
||||
}
|
||||
}
|
||||
46
libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
Normal file
46
libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"messages": {
|
||||
"-1340279385": {
|
||||
"message": "Remove listener=%s",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_SHELL_TASK_ORG",
|
||||
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
|
||||
},
|
||||
"-880817403": {
|
||||
"message": "Task vanished taskId=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_SHELL_TASK_ORG",
|
||||
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
|
||||
},
|
||||
"-460572385": {
|
||||
"message": "Task appeared taskId=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_SHELL_TASK_ORG",
|
||||
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
|
||||
},
|
||||
"-242812822": {
|
||||
"message": "Add listener for modes=%s listener=%s",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_SHELL_TASK_ORG",
|
||||
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
|
||||
},
|
||||
"157713005": {
|
||||
"message": "Task info changed taskId=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_SHELL_TASK_ORG",
|
||||
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
|
||||
},
|
||||
"980952660": {
|
||||
"message": "Task root back pressed taskId=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_SHELL_TASK_ORG",
|
||||
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
|
||||
}
|
||||
},
|
||||
"groups": {
|
||||
"WM_SHELL_TASK_ORG": {
|
||||
"tag": "WindowManagerShell"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,17 +17,20 @@
|
||||
package com.android.wm.shell;
|
||||
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.TaskOrganizer;
|
||||
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogImpl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Unified task organizer for all components in the shell.
|
||||
@@ -57,6 +60,8 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
* Adds a listener for tasks in a specific windowing mode.
|
||||
*/
|
||||
public void addListener(TaskListener listener, int... windowingModes) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Add listener for modes=%s listener=%s",
|
||||
Arrays.toString(windowingModes), listener);
|
||||
for (int winMode : windowingModes) {
|
||||
ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(winMode);
|
||||
if (listeners == null) {
|
||||
@@ -84,6 +89,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
* Removes a registered listener.
|
||||
*/
|
||||
public void removeListener(TaskListener listener) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
|
||||
for (int i = 0; i < mListenersByWindowingMode.size(); i++) {
|
||||
mListenersByWindowingMode.valueAt(i).remove(listener);
|
||||
}
|
||||
@@ -91,6 +97,8 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
|
||||
@Override
|
||||
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task appeared taskId=%d",
|
||||
taskInfo.taskId);
|
||||
mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, leash));
|
||||
ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(
|
||||
getWindowingMode(taskInfo));
|
||||
@@ -103,6 +111,8 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
|
||||
@Override
|
||||
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task info changed taskId=%d",
|
||||
taskInfo.taskId);
|
||||
Pair<RunningTaskInfo, SurfaceControl> data = mTasks.get(taskInfo.taskId);
|
||||
int winMode = getWindowingMode(taskInfo);
|
||||
int prevWinMode = getWindowingMode(data.first);
|
||||
@@ -134,6 +144,8 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
|
||||
@Override
|
||||
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d",
|
||||
taskInfo.taskId);
|
||||
ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(
|
||||
getWindowingMode(taskInfo));
|
||||
if (listeners != null) {
|
||||
@@ -145,6 +157,8 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
|
||||
@Override
|
||||
public void onTaskVanished(RunningTaskInfo taskInfo) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task vanished taskId=%d",
|
||||
taskInfo.taskId);
|
||||
int prevWinMode = getWindowingMode(mTasks.get(taskInfo.taskId).first);
|
||||
mTasks.remove(taskInfo.taskId);
|
||||
ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(prevWinMode);
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.wm.shell.protolog;
|
||||
|
||||
import com.android.internal.protolog.common.IProtoLogGroup;
|
||||
|
||||
/**
|
||||
* Defines logging groups for ProtoLog.
|
||||
*
|
||||
* This file is used by the ProtoLogTool to generate optimized logging code.
|
||||
*/
|
||||
public enum ShellProtoLogGroup implements IProtoLogGroup {
|
||||
WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||
Consts.TAG_WM_SHELL),
|
||||
TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
|
||||
|
||||
private final boolean mEnabled;
|
||||
private volatile boolean mLogToProto;
|
||||
private volatile boolean mLogToLogcat;
|
||||
private final String mTag;
|
||||
|
||||
/**
|
||||
* @param enabled set to false to exclude all log statements for this group from
|
||||
* compilation,
|
||||
* they will not be available in runtime.
|
||||
* @param logToProto enable binary logging for the group
|
||||
* @param logToLogcat enable text logging for the group
|
||||
* @param tag name of the source of the logged message
|
||||
*/
|
||||
ShellProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) {
|
||||
this.mEnabled = enabled;
|
||||
this.mLogToProto = logToProto;
|
||||
this.mLogToLogcat = logToLogcat;
|
||||
this.mTag = tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogToProto() {
|
||||
return mLogToProto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogToLogcat() {
|
||||
return mLogToLogcat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogToAny() {
|
||||
return mLogToLogcat || mLogToProto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return mTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogToProto(boolean logToProto) {
|
||||
this.mLogToProto = logToProto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogToLogcat(boolean logToLogcat) {
|
||||
this.mLogToLogcat = logToLogcat;
|
||||
}
|
||||
|
||||
private static class Consts {
|
||||
private static final String TAG_WM_SHELL = "WindowManagerShell";
|
||||
|
||||
private static final boolean ENABLE_DEBUG = true;
|
||||
private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.wm.shell.protolog;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.protolog.BaseProtoLogImpl;
|
||||
import com.android.internal.protolog.ProtoLogViewerConfigReader;
|
||||
import com.android.internal.protolog.common.IProtoLogGroup;
|
||||
import com.android.wm.shell.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
|
||||
/**
|
||||
* A service for the ProtoLog logging system.
|
||||
*/
|
||||
public class ShellProtoLogImpl extends BaseProtoLogImpl {
|
||||
private static final String TAG = "ProtoLogImpl";
|
||||
private static final int BUFFER_CAPACITY = 1024 * 1024;
|
||||
// TODO: Get the right path for the proto log file when we initialize the shell components
|
||||
private static final String LOG_FILENAME = new File("wm_shell_log.pb").getAbsolutePath();
|
||||
|
||||
private static ShellProtoLogImpl sServiceInstance = null;
|
||||
|
||||
private final PrintWriter mSystemOutWriter;
|
||||
|
||||
static {
|
||||
addLogGroupEnum(ShellProtoLogGroup.values());
|
||||
}
|
||||
|
||||
/** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
|
||||
public static void d(IProtoLogGroup group, int messageHash, int paramsMask,
|
||||
@Nullable String messageString,
|
||||
Object... args) {
|
||||
getSingleInstance()
|
||||
.log(LogLevel.DEBUG, group, messageHash, paramsMask, messageString, args);
|
||||
}
|
||||
|
||||
/** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
|
||||
public static void v(IProtoLogGroup group, int messageHash, int paramsMask,
|
||||
@Nullable String messageString,
|
||||
Object... args) {
|
||||
getSingleInstance().log(LogLevel.VERBOSE, group, messageHash, paramsMask, messageString,
|
||||
args);
|
||||
}
|
||||
|
||||
/** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
|
||||
public static void i(IProtoLogGroup group, int messageHash, int paramsMask,
|
||||
@Nullable String messageString,
|
||||
Object... args) {
|
||||
getSingleInstance().log(LogLevel.INFO, group, messageHash, paramsMask, messageString, args);
|
||||
}
|
||||
|
||||
/** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
|
||||
public static void w(IProtoLogGroup group, int messageHash, int paramsMask,
|
||||
@Nullable String messageString,
|
||||
Object... args) {
|
||||
getSingleInstance().log(LogLevel.WARN, group, messageHash, paramsMask, messageString, args);
|
||||
}
|
||||
|
||||
/** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
|
||||
public static void e(IProtoLogGroup group, int messageHash, int paramsMask,
|
||||
@Nullable String messageString,
|
||||
Object... args) {
|
||||
getSingleInstance()
|
||||
.log(LogLevel.ERROR, group, messageHash, paramsMask, messageString, args);
|
||||
}
|
||||
|
||||
/** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
|
||||
public static void wtf(IProtoLogGroup group, int messageHash, int paramsMask,
|
||||
@Nullable String messageString,
|
||||
Object... args) {
|
||||
getSingleInstance().log(LogLevel.WTF, group, messageHash, paramsMask, messageString, args);
|
||||
}
|
||||
|
||||
/** Returns true iff logging is enabled for the given {@code IProtoLogGroup}. */
|
||||
public static boolean isEnabled(IProtoLogGroup group) {
|
||||
return group.isLogToLogcat()
|
||||
|| (group.isLogToProto() && getSingleInstance().isProtoEnabled());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the single instance of the ProtoLogImpl singleton class.
|
||||
*/
|
||||
public static synchronized ShellProtoLogImpl getSingleInstance() {
|
||||
if (sServiceInstance == null) {
|
||||
sServiceInstance = new ShellProtoLogImpl();
|
||||
}
|
||||
return sServiceInstance;
|
||||
}
|
||||
|
||||
public void startTextLogging(Context context, String... groups) {
|
||||
try {
|
||||
mViewerConfig.loadViewerConfig(
|
||||
context.getResources().openRawResource(R.raw.wm_shell_protolog));
|
||||
setLogging(true /* setTextLogging */, true, mSystemOutWriter, groups);
|
||||
} catch (IOException e) {
|
||||
Log.i(TAG, "Unable to load log definitions: IOException while reading "
|
||||
+ "wm_shell_protolog. " + e);
|
||||
} catch (JSONException e) {
|
||||
Log.i(TAG, "Unable to load log definitions: JSON parsing exception while reading "
|
||||
+ "wm_shell_protolog. " + e);
|
||||
}
|
||||
}
|
||||
|
||||
public void stopTextLogging(String... groups) {
|
||||
setLogging(true /* setTextLogging */, false, mSystemOutWriter, groups);
|
||||
}
|
||||
|
||||
private ShellProtoLogImpl() {
|
||||
super(new File(LOG_FILENAME), null, BUFFER_CAPACITY,
|
||||
new ProtoLogViewerConfigReader());
|
||||
mSystemOutWriter = new PrintWriter(System.out, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +174,9 @@ android_app {
|
||||
kotlincflags: ["-Xjvm-default=enable"],
|
||||
|
||||
dxflags: ["--multi-dex"],
|
||||
required: ["privapp_whitelist_com.android.systemui"],
|
||||
required: [
|
||||
"privapp_whitelist_com.android.systemui",
|
||||
"checked-wm_shell_protolog.json",
|
||||
],
|
||||
|
||||
}
|
||||
|
||||
@@ -41,7 +41,12 @@
|
||||
public <init>(android.content.Context);
|
||||
}
|
||||
|
||||
# Keep the wm shell lib
|
||||
-keep class com.android.wm.shell.*
|
||||
# Keep the protolog group methods that are called by the generated code
|
||||
-keepclassmembers class com.android.wm.shell.protolog.ShellProtoLogGroup {
|
||||
*;
|
||||
}
|
||||
|
||||
-keep class com.android.systemui.dagger.GlobalRootComponent { *; }
|
||||
-keep class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { *; }
|
||||
|
||||
@@ -29,7 +29,11 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.stackdivider.SplitScreen;
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogImpl;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -108,4 +112,35 @@ public final class WMShell extends SystemUI {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
// Handle commands if provided
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
switch (args[i]) {
|
||||
case "enable-text-logging": {
|
||||
String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
|
||||
startTextLogging(groups);
|
||||
pw.println("Starting logging on groups: " + Arrays.toString(groups));
|
||||
return;
|
||||
}
|
||||
case "disable-text-logging": {
|
||||
String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
|
||||
stopTextLogging(groups);
|
||||
pw.println("Stopping logging on groups: " + Arrays.toString(groups));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dump WMShell stuff here if no commands were handled
|
||||
}
|
||||
|
||||
private void startTextLogging(String... groups) {
|
||||
ShellProtoLogImpl.getSingleInstance().startTextLogging(mContext, groups);
|
||||
}
|
||||
|
||||
private void stopTextLogging(String... groups) {
|
||||
ShellProtoLogImpl.getSingleInstance().stopTextLogging(groups);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ package com.android.server.wm;
|
||||
|
||||
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
|
||||
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
|
||||
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
@@ -25,6 +28,8 @@ import android.view.SurfaceControl;
|
||||
import android.window.IDisplayAreaOrganizer;
|
||||
import android.window.IDisplayAreaOrganizerController;
|
||||
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub {
|
||||
@@ -67,9 +72,12 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
|
||||
@Override
|
||||
public void registerOrganizer(IDisplayAreaOrganizer organizer, int feature) {
|
||||
enforceStackPermission("registerOrganizer()");
|
||||
final long uid = Binder.getCallingUid();
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register display organizer=%s uid=%d",
|
||||
organizer.asBinder(), uid);
|
||||
if (mOrganizersByFeatureIds.get(feature) != null) {
|
||||
throw new IllegalStateException(
|
||||
"Replacing existing organizer currently unsupported");
|
||||
@@ -96,9 +104,12 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
|
||||
@Override
|
||||
public void unregisterOrganizer(IDisplayAreaOrganizer organizer) {
|
||||
enforceStackPermission("unregisterTaskOrganizer()");
|
||||
final long uid = Binder.getCallingUid();
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Unregister display organizer=%s uid=%d",
|
||||
organizer.asBinder(), uid);
|
||||
mOrganizersByFeatureIds.entrySet().removeIf(
|
||||
entry -> entry.getValue().asBinder() == organizer.asBinder());
|
||||
|
||||
@@ -113,6 +124,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
|
||||
}
|
||||
|
||||
void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea appeared name=%s", da.getName());
|
||||
try {
|
||||
SurfaceControl outSurfaceControl = new SurfaceControl(da.getSurfaceControl(),
|
||||
"DisplayAreaOrganizerController.onDisplayAreaAppeared");
|
||||
@@ -123,6 +135,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
|
||||
}
|
||||
|
||||
void onDisplayAreaVanished(IDisplayAreaOrganizer organizer, DisplayArea da) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea vanished name=%s", da.getName());
|
||||
try {
|
||||
organizer.onDisplayAreaVanished(da.getDisplayAreaInfo());
|
||||
} catch (RemoteException e) {
|
||||
@@ -131,6 +144,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
|
||||
}
|
||||
|
||||
void onDisplayAreaInfoChanged(IDisplayAreaOrganizer organizer, DisplayArea da) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea info changed name=%s", da.getName());
|
||||
try {
|
||||
organizer.onDisplayAreaInfoChanged(da.getDisplayAreaInfo());
|
||||
} catch (RemoteException e) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
|
||||
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
|
||||
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
|
||||
|
||||
@@ -43,13 +44,12 @@ import android.window.ITaskOrganizerController;
|
||||
import android.window.WindowContainerToken;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -126,6 +126,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
}
|
||||
|
||||
void onTaskAppeared(Task task) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId);
|
||||
final boolean visible = task.isVisible();
|
||||
final RunningTaskInfo taskInfo = task.getTaskInfo();
|
||||
mDeferTaskOrgCallbacksConsumer.accept(() -> {
|
||||
@@ -147,6 +148,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
|
||||
|
||||
void onTaskVanished(Task task) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task vanished taskId=%d", task.mTaskId);
|
||||
final RunningTaskInfo taskInfo = task.getTaskInfo();
|
||||
mDeferTaskOrgCallbacksConsumer.accept(() -> {
|
||||
try {
|
||||
@@ -163,6 +165,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
// by the organizer that don't receive that signal
|
||||
return;
|
||||
}
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId);
|
||||
mDeferTaskOrgCallbacksConsumer.accept(() -> {
|
||||
if (!task.isOrganized()) {
|
||||
// This is safe to ignore if the task is no longer organized
|
||||
@@ -177,6 +180,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
}
|
||||
|
||||
void onBackPressedOnTaskRoot(Task task) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task back pressed on root taskId=%d",
|
||||
task.mTaskId);
|
||||
if (!task.mCreatedByOrganizer && !task.mTaskAppearedSent) {
|
||||
// Skip if the task has not yet received taskAppeared(), except for tasks created
|
||||
// by the organizer that don't receive that signal
|
||||
@@ -306,6 +311,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d",
|
||||
organizer.asBinder(), uid);
|
||||
for (int winMode : SUPPORTED_WINDOWING_MODES) {
|
||||
if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
|
||||
mTaskOrganizers.add(organizer);
|
||||
@@ -327,6 +334,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
@Override
|
||||
public void unregisterTaskOrganizer(ITaskOrganizer organizer) {
|
||||
enforceStackPermission("unregisterTaskOrganizer()");
|
||||
final int uid = Binder.getCallingUid();
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
@@ -334,6 +342,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Unregister task organizer=%s uid=%d",
|
||||
organizer.asBinder(), uid);
|
||||
state.unlinkDeath();
|
||||
state.dispose();
|
||||
}
|
||||
@@ -383,6 +393,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
return null;
|
||||
}
|
||||
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create root task displayId=%d winMode=%d",
|
||||
displayId, windowingMode);
|
||||
final Task task = display.getDefaultTaskDisplayArea().createStack(windowingMode,
|
||||
ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(),
|
||||
true /* createdByOrganizer */);
|
||||
@@ -407,6 +419,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
throw new IllegalArgumentException(
|
||||
"Attempt to delete task not created by organizer task=" + task);
|
||||
}
|
||||
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete root task display=%d winMode=%d",
|
||||
task.getDisplayId(), task.getWindowingMode());
|
||||
task.removeImmediately();
|
||||
return true;
|
||||
}
|
||||
@@ -614,6 +629,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set intercept back pressed on root=%b",
|
||||
interceptBackPressed);
|
||||
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
|
||||
if (state != null) {
|
||||
state.setInterceptBackPressedOnTaskRoot(interceptBackPressed);
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.wm;
|
||||
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
|
||||
import static android.Manifest.permission.READ_FRAME_BUFFER;
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
|
||||
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
|
||||
import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
|
||||
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
|
||||
@@ -45,6 +46,7 @@ import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.internal.util.function.pooled.PooledConsumer;
|
||||
import com.android.internal.util.function.pooled.PooledLambda;
|
||||
|
||||
@@ -127,6 +129,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
if (callback != null) {
|
||||
syncId = startSyncWithOrganizer(callback);
|
||||
}
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d",
|
||||
syncId);
|
||||
mService.deferWindowLayout();
|
||||
try {
|
||||
ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
|
||||
@@ -427,6 +431,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
|
||||
@VisibleForTesting
|
||||
void setSyncReady(int id) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set sync ready, syncId=%d", id);
|
||||
mBLASTSyncEngine.setReady(id);
|
||||
}
|
||||
|
||||
@@ -436,9 +441,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) {
|
||||
public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Transaction ready, syncId=%d", syncId);
|
||||
final IWindowContainerTransactionCallback callback =
|
||||
mTransactionCallbacksByPendingSyncId.get(mSyncId);
|
||||
mTransactionCallbacksByPendingSyncId.get(syncId);
|
||||
|
||||
SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction();
|
||||
for (WindowContainer container : windowContainersReady) {
|
||||
@@ -446,14 +452,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
}
|
||||
|
||||
try {
|
||||
callback.onTransactionReady(mSyncId, mergedTransaction);
|
||||
callback.onTransactionReady(syncId, mergedTransaction);
|
||||
} catch (RemoteException e) {
|
||||
// If there's an exception when trying to send the mergedTransaction to the client, we
|
||||
// should immediately apply it here so the transactions aren't lost.
|
||||
mergedTransaction.apply();
|
||||
}
|
||||
|
||||
mTransactionCallbacksByPendingSyncId.remove(mSyncId);
|
||||
mTransactionCallbacksByPendingSyncId.remove(syncId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user