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:
Winson Chung
2020-08-03 22:17:08 -07:00
parent af7f8f199e
commit b754f5281b
16 changed files with 1027 additions and 423 deletions

View 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();
}
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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"
},

View File

@@ -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",
}
}

View 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"
}
}
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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",
],
}

View File

@@ -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 { *; }

View File

@@ -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);
}
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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