Merge "Add prep_taieye"
diff --git a/Android.bp b/Android.bp
index c89cc40..5c1ccb7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -47,6 +47,7 @@
srcs: [
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
+ "tools/streaming_proto/stream.proto",
],
},
android: {
@@ -61,7 +62,7 @@
"core/proto/android/os/pagetypeinfo.proto",
"core/proto/android/os/procrank.proto",
"core/proto/android/service/graphicsstats.proto",
- "libs/incident/proto/android/privacy.proto",
+ "tools/streaming_proto/stream.proto",
],
shared: {
enabled: false,
@@ -70,6 +71,27 @@
},
}
+gensrcs {
+ name: "gen-platform-proto-constants",
+ depfile: true,
+
+ tools: [
+ "aprotoc",
+ "protoc-gen-cppstream",
+ ],
+
+ srcs: [
+ "core/proto/android/os/kernelwake.proto",
+ "core/proto/android/os/pagetypeinfo.proto",
+ "core/proto/android/os/procrank.proto",
+ ],
+
+ // Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool
+ cmd: "PATH=$$PATH:$$(dirname $(location protoc-gen-cppstream)) $(location aprotoc) --plugin=protoc-gen-cpp-stream=$(location protoc-gen-cppstream) --dependency_out=$(depfile) --cppstream_out=$(genDir)/ -Iexternal/protobuf/src -I . $(in)",
+
+ output_extension = "proto.h",
+}
+
subdirs = [
"cmds/*",
"core/*",
diff --git a/Android.mk b/Android.mk
index 9843f17..1ed8a25 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1553,6 +1553,7 @@
-Iexternal/protobuf/src
LOCAL_SOURCE_FILES_ALL_GENERATED := true
LOCAL_SRC_FILES := \
+ tools/streaming_proto/stream.proto \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto)
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/api/current.txt b/api/current.txt
index 5b986bc..5a947dd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12016,6 +12016,7 @@
method public java.lang.String[] getColumnNames();
method public int getCount();
method public android.database.sqlite.SQLiteDatabase getDatabase();
+ method public void setFillWindowForwardOnly(boolean);
method public void setSelectionArguments(java.lang.String[]);
}
@@ -18270,13 +18271,11 @@
method public synchronized void applyLocalizedPattern(java.lang.String);
method public synchronized void applyPattern(java.lang.String);
method public synchronized boolean areSignificantDigitsUsed();
- method public synchronized boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
- method public synchronized android.icu.util.Currency getCurrency();
method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -18284,12 +18283,8 @@
method public synchronized int getGroupingSize();
method public synchronized java.math.MathContext getMathContext();
method public synchronized android.icu.math.MathContext getMathContextICU();
- method public synchronized int getMaximumFractionDigits();
- method public synchronized int getMaximumIntegerDigits();
method public synchronized int getMaximumSignificantDigits();
method public synchronized byte getMinimumExponentDigits();
- method public synchronized int getMinimumFractionDigits();
- method public synchronized int getMinimumIntegerDigits();
method public synchronized int getMinimumSignificantDigits();
method public synchronized int getMultiplier();
method public synchronized java.lang.String getNegativePrefix();
@@ -18300,19 +18295,13 @@
method public synchronized java.lang.String getPositivePrefix();
method public synchronized java.lang.String getPositiveSuffix();
method public synchronized java.math.BigDecimal getRoundingIncrement();
- method public synchronized int getRoundingMode();
method public synchronized int getSecondaryGroupingSize();
- method public synchronized int hashCode();
method public synchronized boolean isDecimalPatternMatchRequired();
method public synchronized boolean isDecimalSeparatorAlwaysShown();
method public synchronized boolean isExponentSignAlwaysShown();
- method public synchronized boolean isGroupingUsed();
method public synchronized boolean isParseBigDecimal();
- method public synchronized boolean isParseIntegerOnly();
- method public synchronized boolean isParseStrict();
method public synchronized boolean isScientificNotation();
method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
- method public synchronized void setCurrency(android.icu.util.Currency);
method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -18321,15 +18310,10 @@
method public synchronized void setExponentSignAlwaysShown(boolean);
method public synchronized void setFormatWidth(int);
method public synchronized void setGroupingSize(int);
- method public synchronized void setGroupingUsed(boolean);
method public synchronized void setMathContext(java.math.MathContext);
method public synchronized void setMathContextICU(android.icu.math.MathContext);
- method public synchronized void setMaximumFractionDigits(int);
- method public synchronized void setMaximumIntegerDigits(int);
method public synchronized void setMaximumSignificantDigits(int);
method public synchronized void setMinimumExponentDigits(byte);
- method public synchronized void setMinimumFractionDigits(int);
- method public synchronized void setMinimumIntegerDigits(int);
method public synchronized void setMinimumSignificantDigits(int);
method public synchronized void setMultiplier(int);
method public synchronized void setNegativePrefix(java.lang.String);
@@ -18337,15 +18321,12 @@
method public synchronized void setPadCharacter(char);
method public synchronized void setPadPosition(int);
method public synchronized void setParseBigDecimal(boolean);
- method public synchronized void setParseIntegerOnly(boolean);
method public deprecated void setParseMaxDigits(int);
- method public synchronized void setParseStrict(boolean);
method public synchronized void setPositivePrefix(java.lang.String);
method public synchronized void setPositiveSuffix(java.lang.String);
method public synchronized void setRoundingIncrement(java.math.BigDecimal);
method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
method public synchronized void setRoundingIncrement(double);
- method public synchronized void setRoundingMode(int);
method public synchronized void setScientificNotation(boolean);
method public synchronized void setSecondaryGroupingSize(int);
method public synchronized void setSignificantDigitsUsed(boolean);
@@ -25834,7 +25815,6 @@
public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
method public void close();
- method protected void finalize();
method public int getSpi();
}
diff --git a/api/system-current.txt b/api/system-current.txt
index df8e034..c0d106f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -12760,6 +12760,7 @@
method public java.lang.String[] getColumnNames();
method public int getCount();
method public android.database.sqlite.SQLiteDatabase getDatabase();
+ method public void setFillWindowForwardOnly(boolean);
method public void setSelectionArguments(java.lang.String[]);
}
@@ -19826,13 +19827,11 @@
method public synchronized void applyLocalizedPattern(java.lang.String);
method public synchronized void applyPattern(java.lang.String);
method public synchronized boolean areSignificantDigitsUsed();
- method public synchronized boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
- method public synchronized android.icu.util.Currency getCurrency();
method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -19840,12 +19839,8 @@
method public synchronized int getGroupingSize();
method public synchronized java.math.MathContext getMathContext();
method public synchronized android.icu.math.MathContext getMathContextICU();
- method public synchronized int getMaximumFractionDigits();
- method public synchronized int getMaximumIntegerDigits();
method public synchronized int getMaximumSignificantDigits();
method public synchronized byte getMinimumExponentDigits();
- method public synchronized int getMinimumFractionDigits();
- method public synchronized int getMinimumIntegerDigits();
method public synchronized int getMinimumSignificantDigits();
method public synchronized int getMultiplier();
method public synchronized java.lang.String getNegativePrefix();
@@ -19856,19 +19851,13 @@
method public synchronized java.lang.String getPositivePrefix();
method public synchronized java.lang.String getPositiveSuffix();
method public synchronized java.math.BigDecimal getRoundingIncrement();
- method public synchronized int getRoundingMode();
method public synchronized int getSecondaryGroupingSize();
- method public synchronized int hashCode();
method public synchronized boolean isDecimalPatternMatchRequired();
method public synchronized boolean isDecimalSeparatorAlwaysShown();
method public synchronized boolean isExponentSignAlwaysShown();
- method public synchronized boolean isGroupingUsed();
method public synchronized boolean isParseBigDecimal();
- method public synchronized boolean isParseIntegerOnly();
- method public synchronized boolean isParseStrict();
method public synchronized boolean isScientificNotation();
method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
- method public synchronized void setCurrency(android.icu.util.Currency);
method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -19877,15 +19866,10 @@
method public synchronized void setExponentSignAlwaysShown(boolean);
method public synchronized void setFormatWidth(int);
method public synchronized void setGroupingSize(int);
- method public synchronized void setGroupingUsed(boolean);
method public synchronized void setMathContext(java.math.MathContext);
method public synchronized void setMathContextICU(android.icu.math.MathContext);
- method public synchronized void setMaximumFractionDigits(int);
- method public synchronized void setMaximumIntegerDigits(int);
method public synchronized void setMaximumSignificantDigits(int);
method public synchronized void setMinimumExponentDigits(byte);
- method public synchronized void setMinimumFractionDigits(int);
- method public synchronized void setMinimumIntegerDigits(int);
method public synchronized void setMinimumSignificantDigits(int);
method public synchronized void setMultiplier(int);
method public synchronized void setNegativePrefix(java.lang.String);
@@ -19893,15 +19877,12 @@
method public synchronized void setPadCharacter(char);
method public synchronized void setPadPosition(int);
method public synchronized void setParseBigDecimal(boolean);
- method public synchronized void setParseIntegerOnly(boolean);
method public deprecated void setParseMaxDigits(int);
- method public synchronized void setParseStrict(boolean);
method public synchronized void setPositivePrefix(java.lang.String);
method public synchronized void setPositiveSuffix(java.lang.String);
method public synchronized void setRoundingIncrement(java.math.BigDecimal);
method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
method public synchronized void setRoundingIncrement(double);
- method public synchronized void setRoundingMode(int);
method public synchronized void setScientificNotation(boolean);
method public synchronized void setSecondaryGroupingSize(int);
method public synchronized void setSignificantDigitsUsed(boolean);
@@ -28076,7 +28057,6 @@
public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
method public void close();
- method protected void finalize();
method public int getSpi();
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 6f251e9..c32ca21 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -12103,6 +12103,7 @@
method public java.lang.String[] getColumnNames();
method public int getCount();
method public android.database.sqlite.SQLiteDatabase getDatabase();
+ method public void setFillWindowForwardOnly(boolean);
method public void setSelectionArguments(java.lang.String[]);
}
@@ -18412,13 +18413,11 @@
method public synchronized void applyLocalizedPattern(java.lang.String);
method public synchronized void applyPattern(java.lang.String);
method public synchronized boolean areSignificantDigitsUsed();
- method public synchronized boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
- method public synchronized android.icu.util.Currency getCurrency();
method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -18426,12 +18425,8 @@
method public synchronized int getGroupingSize();
method public synchronized java.math.MathContext getMathContext();
method public synchronized android.icu.math.MathContext getMathContextICU();
- method public synchronized int getMaximumFractionDigits();
- method public synchronized int getMaximumIntegerDigits();
method public synchronized int getMaximumSignificantDigits();
method public synchronized byte getMinimumExponentDigits();
- method public synchronized int getMinimumFractionDigits();
- method public synchronized int getMinimumIntegerDigits();
method public synchronized int getMinimumSignificantDigits();
method public synchronized int getMultiplier();
method public synchronized java.lang.String getNegativePrefix();
@@ -18442,19 +18437,13 @@
method public synchronized java.lang.String getPositivePrefix();
method public synchronized java.lang.String getPositiveSuffix();
method public synchronized java.math.BigDecimal getRoundingIncrement();
- method public synchronized int getRoundingMode();
method public synchronized int getSecondaryGroupingSize();
- method public synchronized int hashCode();
method public synchronized boolean isDecimalPatternMatchRequired();
method public synchronized boolean isDecimalSeparatorAlwaysShown();
method public synchronized boolean isExponentSignAlwaysShown();
- method public synchronized boolean isGroupingUsed();
method public synchronized boolean isParseBigDecimal();
- method public synchronized boolean isParseIntegerOnly();
- method public synchronized boolean isParseStrict();
method public synchronized boolean isScientificNotation();
method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
- method public synchronized void setCurrency(android.icu.util.Currency);
method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -18463,15 +18452,10 @@
method public synchronized void setExponentSignAlwaysShown(boolean);
method public synchronized void setFormatWidth(int);
method public synchronized void setGroupingSize(int);
- method public synchronized void setGroupingUsed(boolean);
method public synchronized void setMathContext(java.math.MathContext);
method public synchronized void setMathContextICU(android.icu.math.MathContext);
- method public synchronized void setMaximumFractionDigits(int);
- method public synchronized void setMaximumIntegerDigits(int);
method public synchronized void setMaximumSignificantDigits(int);
method public synchronized void setMinimumExponentDigits(byte);
- method public synchronized void setMinimumFractionDigits(int);
- method public synchronized void setMinimumIntegerDigits(int);
method public synchronized void setMinimumSignificantDigits(int);
method public synchronized void setMultiplier(int);
method public synchronized void setNegativePrefix(java.lang.String);
@@ -18479,15 +18463,12 @@
method public synchronized void setPadCharacter(char);
method public synchronized void setPadPosition(int);
method public synchronized void setParseBigDecimal(boolean);
- method public synchronized void setParseIntegerOnly(boolean);
method public deprecated void setParseMaxDigits(int);
- method public synchronized void setParseStrict(boolean);
method public synchronized void setPositivePrefix(java.lang.String);
method public synchronized void setPositiveSuffix(java.lang.String);
method public synchronized void setRoundingIncrement(java.math.BigDecimal);
method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
method public synchronized void setRoundingIncrement(double);
- method public synchronized void setRoundingMode(int);
method public synchronized void setScientificNotation(boolean);
method public synchronized void setSecondaryGroupingSize(int);
method public synchronized void setSignificantDigitsUsed(boolean);
@@ -26034,7 +26015,6 @@
public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
method public void close();
- method protected void finalize();
method public int getSpi();
}
@@ -41342,6 +41322,7 @@
}
public final class FileServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable {
+ ctor public FileServiceInfo(java.util.Map<java.util.Locale, java.lang.String>, java.lang.String, java.util.List<java.util.Locale>, java.lang.String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>);
method public int describeContents();
method public java.util.List<android.telephony.mbms.FileInfo> getFiles();
method public void writeToParcel(android.os.Parcel, int);
@@ -41450,6 +41431,22 @@
package android.telephony.mbms.vendor {
+ public class MbmsDownloadServiceBase extends android.os.Binder {
+ ctor public MbmsDownloadServiceBase();
+ method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
+ method public void dispose(int) throws android.os.RemoteException;
+ method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
+ method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
+ method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
+ method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
+ method public void onAppCallbackDied(int, int);
+ method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+ method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
+ method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
+ method public int unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+ }
+
public class MbmsStreamingServiceBase extends android.os.Binder {
ctor public MbmsStreamingServiceBase();
method public void dispose(int) throws android.os.RemoteException;
diff --git a/cmds/am/proto/instrumentation_data.proto b/cmds/am/proto/instrumentation_data.proto
index 12a18a2..8e29f96 100644
--- a/cmds/am/proto/instrumentation_data.proto
+++ b/cmds/am/proto/instrumentation_data.proto
@@ -28,6 +28,7 @@
optional double value_double = 5;
optional sint64 value_long = 6;
optional ResultsBundle value_bundle = 7;
+ optional bytes value_bytes = 8;
}
message ResultsBundle {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 79e7fac..813335a 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -160,7 +160,11 @@
} else if (opt.equals("-r")) {
instrument.rawMode = true;
} else if (opt.equals("-m")) {
- instrument.proto = true;
+ instrument.protoStd = true;
+ } else if (opt.equals("-f")) {
+ instrument.protoFile = true;
+ if (peekNextArg() != null && !peekNextArg().startsWith("-"))
+ instrument.logPath = nextArg();
} else if (opt.equals("-e")) {
final String argKey = nextArgRequired();
final String argValue = nextArgRequired();
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index b69ef1c..93b9f58 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -25,23 +25,32 @@
import android.content.pm.InstrumentationInfo;
import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.AndroidException;
import android.util.proto.ProtoOutputStream;
import android.view.IWindowManager;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
+import java.util.Locale;
/**
* Runs the am instrument command
*/
public class Instrument {
+ public static final String DEFAULT_LOG_DIR = "instrument-logs";
+
private final IActivityManager mAm;
private final IPackageManager mPm;
private final IWindowManager mWm;
@@ -50,7 +59,9 @@
public String profileFile = null;
public boolean wait = false;
public boolean rawMode = false;
- public boolean proto = false;
+ boolean protoStd = false; // write proto to stdout
+ boolean protoFile = false; // write proto to a file
+ String logPath = null;
public boolean noWindowAnimation = false;
public String abi = null;
public int userId = UserHandle.USER_CURRENT;
@@ -178,18 +189,49 @@
* Printer for the protobuf based status reporting.
*/
private class ProtoStatusReporter implements StatusReporter {
+
+ private File mLog;
+
+ ProtoStatusReporter() {
+ if (protoFile) {
+ if (logPath == null) {
+ File logDir = new File(Environment.getLegacyExternalStorageDirectory(),
+ DEFAULT_LOG_DIR);
+ if (!logDir.exists() && !logDir.mkdirs()) {
+ System.err.format("Unable to create log directory: %s\n",
+ logDir.getAbsolutePath());
+ protoFile = false;
+ return;
+ }
+ SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd-hhmmss-SSS", Locale.US);
+ String fileName = String.format("log-%s.instrumentation_data_proto",
+ format.format(new Date()));
+ mLog = new File(logDir, fileName);
+ } else {
+ mLog = new File(Environment.getLegacyExternalStorageDirectory(), logPath);
+ File logDir = mLog.getParentFile();
+ if (!logDir.exists() && !logDir.mkdirs()) {
+ System.err.format("Unable to create log directory: %s\n",
+ logDir.getAbsolutePath());
+ protoFile = false;
+ return;
+ }
+ }
+ if (mLog.exists()) mLog.delete();
+ }
+ }
+
@Override
public void onInstrumentationStatusLocked(ComponentName name, int resultCode,
Bundle results) {
final ProtoOutputStream proto = new ProtoOutputStream();
- final long token = proto.startRepeatedObject(InstrumentationData.Session.TEST_STATUS);
-
- proto.writeSInt32(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
+ final long token = proto.start(InstrumentationData.Session.TEST_STATUS);
+ proto.write(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results);
+ proto.end(token);
- proto.endRepeatedObject(token);
- writeProtoToStdout(proto);
+ outputProto(proto);
}
@Override
@@ -197,80 +239,87 @@
Bundle results) {
final ProtoOutputStream proto = new ProtoOutputStream();
- final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS);
-
- proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE,
+ final long token = proto.start(InstrumentationData.Session.SESSION_STATUS);
+ proto.write(InstrumentationData.SessionStatus.STATUS_CODE,
InstrumentationData.SESSION_FINISHED);
- proto.writeSInt32(InstrumentationData.SessionStatus.RESULT_CODE, resultCode);
+ proto.write(InstrumentationData.SessionStatus.RESULT_CODE, resultCode);
writeBundle(proto, InstrumentationData.SessionStatus.RESULTS, results);
+ proto.end(token);
- proto.endObject(token);
- writeProtoToStdout(proto);
+ outputProto(proto);
}
@Override
public void onError(String errorText, boolean commandError) {
final ProtoOutputStream proto = new ProtoOutputStream();
- final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS);
-
- proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE,
+ final long token = proto.start(InstrumentationData.Session.SESSION_STATUS);
+ proto.write(InstrumentationData.SessionStatus.STATUS_CODE,
InstrumentationData.SESSION_ABORTED);
- proto.writeString(InstrumentationData.SessionStatus.ERROR_TEXT, errorText);
+ proto.write(InstrumentationData.SessionStatus.ERROR_TEXT, errorText);
+ proto.end(token);
- proto.endObject(token);
- writeProtoToStdout(proto);
+ outputProto(proto);
}
private void writeBundle(ProtoOutputStream proto, long fieldId, Bundle bundle) {
- final long bundleToken = proto.startObject(fieldId);
+ final long bundleToken = proto.start(fieldId);
for (final String key: sorted(bundle.keySet())) {
final long entryToken = proto.startRepeatedObject(
InstrumentationData.ResultsBundle.ENTRIES);
- proto.writeString(InstrumentationData.ResultsBundleEntry.KEY, key);
+ proto.write(InstrumentationData.ResultsBundleEntry.KEY, key);
final Object val = bundle.get(key);
if (val instanceof String) {
- proto.writeString(InstrumentationData.ResultsBundleEntry.VALUE_STRING,
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_STRING,
(String)val);
} else if (val instanceof Byte) {
- proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_INT,
((Byte)val).intValue());
} else if (val instanceof Double) {
- proto.writeDouble(InstrumentationData.ResultsBundleEntry.VALUE_DOUBLE,
- ((Double)val).doubleValue());
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_DOUBLE, (double)val);
} else if (val instanceof Float) {
- proto.writeFloat(InstrumentationData.ResultsBundleEntry.VALUE_FLOAT,
- ((Float)val).floatValue());
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_FLOAT, (float)val);
} else if (val instanceof Integer) {
- proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
- ((Integer)val).intValue());
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_INT, (int)val);
} else if (val instanceof Long) {
- proto.writeSInt64(InstrumentationData.ResultsBundleEntry.VALUE_LONG,
- ((Long)val).longValue());
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_LONG, (long)val);
} else if (val instanceof Short) {
- proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
- ((Short)val).intValue());
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_INT, (short)val);
} else if (val instanceof Bundle) {
writeBundle(proto, InstrumentationData.ResultsBundleEntry.VALUE_BUNDLE,
(Bundle)val);
+ } else if (val instanceof byte[]) {
+ proto.write(InstrumentationData.ResultsBundleEntry.VALUE_BYTES, (byte[])val);
}
- proto.endRepeatedObject(entryToken);
+ proto.end(entryToken);
}
- proto.endObject(bundleToken);
+ proto.end(bundleToken);
}
- private void writeProtoToStdout(ProtoOutputStream proto) {
- try {
- System.out.write(proto.getBytes());
- System.out.flush();
- } catch (IOException ex) {
- System.err.println("Error writing finished response: ");
- ex.printStackTrace(System.err);
+ private void outputProto(ProtoOutputStream proto) {
+ byte[] out = proto.getBytes();
+ if (protoStd) {
+ try {
+ System.out.write(out);
+ System.out.flush();
+ } catch (IOException ex) {
+ System.err.println("Error writing finished response: ");
+ ex.printStackTrace(System.err);
+ }
+ }
+ if (protoFile) {
+ try (OutputStream os = new FileOutputStream(mLog, true)) {
+ os.write(proto.getBytes());
+ os.flush();
+ } catch (IOException ex) {
+ System.err.format("Cannot write to %s:\n", mLog.getAbsolutePath());
+ ex.printStackTrace();
+ }
}
}
}
@@ -374,7 +423,7 @@
try {
// Choose which output we will do.
- if (proto) {
+ if (protoFile || protoStd) {
reporter = new ProtoStatusReporter();
} else if (wait) {
reporter = new TextStatusReporter(rawMode);
@@ -396,7 +445,7 @@
mWm.setAnimationScale(2, 0.0f);
}
- // Figure out which component we are tring to do.
+ // Figure out which component we are trying to do.
final ComponentName cn = parseComponentName(componentNameArg);
// Choose an ABI if necessary
diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp
index 0532083..2ef0371 100644
--- a/cmds/incident_helper/Android.bp
+++ b/cmds/incident_helper/Android.bp
@@ -8,44 +8,53 @@
"-O0"
],
- srcs: [
- "IncidentHelper.cpp",
- "ih_util.cpp",
+ local_include_dirs: [
+ "src/",
+ "src/parsers/",
],
+ srcs: [
+ "src/parsers/*.cpp",
+ "src/TextParserBase.cpp",
+ "src/ih_util.cpp",
+ ],
+
+ generated_headers: ["gen-platform-proto-constants"],
+
shared_libs: [
"libbase",
"liblog",
- "libprotobuf-cpp-full",
+ "libprotoutil",
"libutils",
],
-
- static_libs: [
- "libplatformprotos",
- ],
}
cc_binary {
name: "incident_helper",
defaults: ["incident_helper_defaults"],
- srcs: ["main.cpp"],
+ srcs: ["src/main.cpp"],
}
cc_test {
name: "incident_helper_test",
defaults: ["incident_helper_defaults"],
+ local_include_dirs: ["src/"],
srcs: [
- "tests/IncidentHelper_test.cpp",
- "tests/ih_util_test.cpp",
+ "tests/*.cpp",
],
data: [
"testdata/*",
],
+ shared_libs: [
+ "libprotobuf-cpp-full",
+ ],
+
static_libs: [
"libgmock",
+ "libplatformprotos"
],
-}
\ No newline at end of file
+}
diff --git a/cmds/incident_helper/IncidentHelper.cpp b/cmds/incident_helper/IncidentHelper.cpp
deleted file mode 100644
index 7b06d42..0000000
--- a/cmds/incident_helper/IncidentHelper.cpp
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#define LOG_TAG "incident_helper"
-
-#include "IncidentHelper.h"
-#include "ih_util.h"
-
-#include "frameworks/base/core/proto/android/os/kernelwake.pb.h"
-#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h"
-#include "frameworks/base/core/proto/android/os/procrank.pb.h"
-
-#include <android-base/file.h>
-#include <unistd.h>
-#include <string>
-#include <vector>
-
-using namespace android::base;
-using namespace android::os;
-using namespace google::protobuf;
-using namespace std;
-
-
-static const string TAB_DELIMITER = "\t";
-static const string COMMA_DELIMITER = ",";
-
-static inline int toInt(const string& s) {
- return atoi(s.c_str());
-}
-
-static inline long toLong(const string& s) {
- return atol(s.c_str());
-}
-
-/**
- * Sets the given protobuf message when the field name matches one of the
- * fields. It is useful to set values to proto from table-like plain texts.
- */
-static bool
-SetTableField(::google::protobuf::Message* message, string field_name, string field_value) {
- const Descriptor* descriptor = message->GetDescriptor();
- const Reflection* reflection = message->GetReflection();
-
- const FieldDescriptor* field = descriptor->FindFieldByName(field_name);
- switch (field->type()) {
- case FieldDescriptor::TYPE_STRING:
- reflection->SetString(message, field, field_value);
- return true;
- case FieldDescriptor::TYPE_INT64:
- reflection->SetInt64(message, field, toLong(field_value));
- return true;
- case FieldDescriptor::TYPE_UINT64:
- reflection->SetUInt64(message, field, toLong(field_value));
- return true;
- case FieldDescriptor::TYPE_INT32:
- reflection->SetInt32(message, field, toInt(field_value));
- return true;
- case FieldDescriptor::TYPE_UINT32:
- reflection->SetUInt32(message, field, toInt(field_value));
- return true;
- default:
- // Add new scalar types
- return false;
- }
-}
-
-// ================================================================================
-status_t NoopParser::Parse(const int in, const int out) const
-{
- string content;
- if (!ReadFdToString(in, &content)) {
- fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
- return -1;
- }
- if (!WriteStringToFd(content, out)) {
- fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
- return -1;
- }
- return NO_ERROR;
-}
-
-// ================================================================================
-status_t ReverseParser::Parse(const int in, const int out) const
-{
- string content;
- if (!ReadFdToString(in, &content)) {
- fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
- return -1;
- }
- // reverse the content
- reverse(content.begin(), content.end());
- if (!WriteStringToFd(content, out)) {
- fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
- return -1;
- }
- return NO_ERROR;
-}
-
-// ================================================================================
-status_t KernelWakesParser::Parse(const int in, const int out) const {
- Reader reader(in);
- string line;
- header_t header; // the header of /d/wakeup_sources
- record_t record; // retain each record
- int nline = 0;
-
- KernelWakeSources proto;
-
- // parse line by line
- while (reader.readLine(&line)) {
- if (line.empty()) continue;
- // parse head line
- if (nline++ == 0) {
- header = parseHeader(line, TAB_DELIMITER);
- continue;
- }
-
- // parse for each record, the line delimiter is \t only!
- record = parseRecord(line, TAB_DELIMITER);
-
- if (record.size() != header.size()) {
- // TODO: log this to incident report!
- fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str());
- continue;
- }
-
- WakeupSourceProto* source = proto.add_wakeup_sources();
- for (int i=0; i<(int)record.size(); i++) {
- if (!SetTableField(source, header[i], record[i])) {
- fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
- this->name.string(), nline, header[i].c_str(), record[i].c_str());
- }
- }
- }
-
- if (!reader.ok(&line)) {
- fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
- return -1;
- }
-
- if (!proto.SerializeToFileDescriptor(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
- return -1;
- }
- fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
- return NO_ERROR;
-}
-
-// ================================================================================
-status_t ProcrankParser::Parse(const int in, const int out) const {
- Reader reader(in);
- string line;
- header_t header; // the header of /d/wakeup_sources
- record_t record; // retain each record
- int nline = 0;
-
- Procrank proto;
-
- // parse line by line
- while (reader.readLine(&line)) {
- if (line.empty()) continue;
-
- // parse head line
- if (nline++ == 0) {
- header = parseHeader(line);
- continue;
- }
-
- if (hasPrefix(&line, "ZRAM:")) {
- proto.mutable_summary()->mutable_zram()->set_raw_text(line);
- continue;
- }
- if (hasPrefix(&line, "RAM:")) {
- proto.mutable_summary()->mutable_ram()->set_raw_text(line);
- continue;
- }
-
- record = parseRecord(line);
- if (record.size() != header.size()) {
- if (record[record.size() - 1] == "TOTAL") { // TOTAL record
- ProcessProto* total = proto.mutable_summary()->mutable_total();
- for (int i=1; i<=(int)record.size(); i++) {
- SetTableField(total, header[header.size() - i], record[record.size() - i]);
- }
- } else {
- fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline,
- line.c_str());
- }
- continue;
- }
-
- ProcessProto* process = proto.add_processes();
- for (int i=0; i<(int)record.size(); i++) {
- if (!SetTableField(process, header[i], record[i])) {
- fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
- this->name.string(), nline, header[i].c_str(), record[i].c_str());
- }
- }
- }
-
- if (!reader.ok(&line)) {
- fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
- return -1;
- }
-
- if (!proto.SerializeToFileDescriptor(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
- return -1;
- }
- fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
- return NO_ERROR;
-}
-
-// ================================================================================
-status_t PageTypeInfoParser::Parse(const int in, const int out) const {
- Reader reader(in);
- string line;
- bool migrateTypeSession = false;
- int pageBlockOrder;
- header_t blockHeader;
-
- PageTypeInfo pageTypeInfo;
-
- while (reader.readLine(&line)) {
- if (line.empty()) {
- migrateTypeSession = false;
- blockHeader.clear();
- continue;
- }
-
- if (hasPrefix(&line, "Page block order:")) {
- pageBlockOrder = toInt(line);
- pageTypeInfo.set_page_block_order(pageBlockOrder);
- continue;
- }
- if (hasPrefix(&line, "Pages per block:")) {
- pageTypeInfo.set_pages_per_block(toInt(line));
- continue;
- }
- if (hasPrefix(&line, "Free pages count per migrate type at order")) {
- migrateTypeSession = true;
- continue;
- }
- if (hasPrefix(&line, "Number of blocks type")) {
- blockHeader = parseHeader(line);
- continue;
- }
-
- record_t record = parseRecord(line, COMMA_DELIMITER);
- if (migrateTypeSession && record.size() == 3) {
- MigrateTypeProto* migrateType = pageTypeInfo.add_migrate_types();
- // expect part 0 starts with "Node"
- if (hasPrefix(&record[0], "Node")) {
- migrateType->set_node(toInt(record[0]));
- } else goto ERROR;
- // expect part 1 starts with "zone"
- if (hasPrefix(&record[1], "zone")) {
- migrateType->set_zone(record[1]);
- } else goto ERROR;
- // expect part 2 starts with "type"
- if (hasPrefix(&record[2], "type")) {
- // expect the rest of part 2 has number of (pageBlockOrder + 2) parts
- // An example looks like:
- // header line: type 0 1 2 3 4 5 6 7 8 9 10
- // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0
- // The pageBlockOrder = 10 and it's zero-indexed. so total parts
- // are 10 + 1(zero-indexed) + 1(the type part) = 12.
- record_t pageCounts = parseRecord(record[2]);
- int pageCountsSize = pageBlockOrder + 2;
- if ((int)pageCounts.size() != pageCountsSize) goto ERROR;
-
- migrateType->set_type(pageCounts[0]);
- for (auto i=1; i<pageCountsSize; i++) {
- migrateType->add_free_pages_count(toInt(pageCounts[i]));
- }
- } else goto ERROR;
- continue;
- }
-
- if (!blockHeader.empty() && record.size() == 2) {
- BlockProto* block = pageTypeInfo.add_blocks();
-
- if (hasPrefix(&record[0], "Node")) {
- block->set_node(toInt(record[0]));
- } else goto ERROR;
-
- if (hasPrefix(&record[1], "zone")) {
- record_t blockCounts = parseRecord(record[1]);
- block->set_zone(blockCounts[0]);
- for (size_t i=0; i<blockHeader.size(); i++) {
- if (!SetTableField(block, blockHeader[i], blockCounts[i+1])) goto ERROR;
- }
- } else goto ERROR;
-
- continue;
- }
-
-ERROR: // print out error for this single line and continue parsing
- fprintf(stderr, "[%s]Bad line: %s\n", this->name.string(), line.c_str());
- }
-
- if (!reader.ok(&line)) {
- fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
- return -1;
- }
-
- if (!pageTypeInfo.SerializeToFileDescriptor(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
- return -1;
- }
-
- fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), pageTypeInfo.ByteSize());
- return NO_ERROR;
-}
diff --git a/cmds/incident_helper/src/TextParserBase.cpp b/cmds/incident_helper/src/TextParserBase.cpp
new file mode 100644
index 0000000..a8f9968
--- /dev/null
+++ b/cmds/incident_helper/src/TextParserBase.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "incident_helper"
+
+#include "TextParserBase.h"
+
+#include <android-base/file.h>
+
+using namespace android::base;
+using namespace std;
+
+// ================================================================================
+status_t NoopParser::Parse(const int in, const int out) const
+{
+ string content;
+ if (!ReadFdToString(in, &content)) {
+ fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
+ return -1;
+ }
+ if (!WriteStringToFd(content, out)) {
+ fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
+ return -1;
+ }
+ return NO_ERROR;
+}
+
+// ================================================================================
+status_t ReverseParser::Parse(const int in, const int out) const
+{
+ string content;
+ if (!ReadFdToString(in, &content)) {
+ fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
+ return -1;
+ }
+ // reverse the content
+ reverse(content.begin(), content.end());
+ if (!WriteStringToFd(content, out)) {
+ fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
+ return -1;
+ }
+ return NO_ERROR;
+}
\ No newline at end of file
diff --git a/cmds/incident_helper/IncidentHelper.h b/cmds/incident_helper/src/TextParserBase.h
similarity index 64%
rename from cmds/incident_helper/IncidentHelper.h
rename to cmds/incident_helper/src/TextParserBase.h
index d24d717..c41612d 100644
--- a/cmds/incident_helper/IncidentHelper.h
+++ b/cmds/incident_helper/src/TextParserBase.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef INCIDENT_HELPER_H
-#define INCIDENT_HELPER_H
+#ifndef TEXT_PARSER_BASE_H
+#define TEXT_PARSER_BASE_H
#include <utils/Errors.h>
#include <utils/String8.h>
@@ -68,37 +68,4 @@
virtual status_t Parse(const int in, const int out) const;
};
-/**
- * Kernel wakeup sources parser, parses text to protobuf in /d/wakeup_sources
- */
-class KernelWakesParser : public TextParserBase {
-public:
- KernelWakesParser() : TextParserBase(String8("KernelWakeSources")) {};
- ~KernelWakesParser() {};
-
- virtual status_t Parse(const int in, const int out) const;
-};
-
-/**
- * PageTypeInfo parser, parses text to protobuf in /proc/pageinfotype
- */
-class PageTypeInfoParser : public TextParserBase {
-public:
- PageTypeInfoParser() : TextParserBase(String8("PageTypeInfo")) {};
- ~PageTypeInfoParser() {};
-
- virtual status_t Parse(const int in, const int out) const;
-};
-
-/**
- * Procrank parser, parses text produced by command procrank
- */
-class ProcrankParser : public TextParserBase {
-public:
- ProcrankParser() : TextParserBase(String8("ProcrankParser")) {};
- ~ProcrankParser() {};
-
- virtual status_t Parse(const int in, const int out) const;
-};
-
-#endif // INCIDENT_HELPER_H
+#endif // TEXT_PARSER_BASE_H
\ No newline at end of file
diff --git a/cmds/incident_helper/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
similarity index 73%
rename from cmds/incident_helper/ih_util.cpp
rename to cmds/incident_helper/src/ih_util.cpp
index 2ab4b54..c7d1ca2 100644
--- a/cmds/incident_helper/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -87,6 +87,15 @@
return true;
}
+int toInt(const std::string& s) {
+ return atoi(s.c_str());
+}
+
+long long toLongLong(const std::string& s) {
+ return atoll(s.c_str());
+}
+
+// ==============================================================================
Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {};
Reader::Reader(const int fd, const size_t capacity)
@@ -151,3 +160,57 @@
error->assign(mStatus);
return mStatus.empty();
}
+
+// ==============================================================================
+Table::Table(const char* names[], const uint64_t ids[], const int count)
+ :mFieldNames(names),
+ mFieldIds(ids),
+ mFieldCount(count)
+{
+}
+
+Table::~Table()
+{
+}
+
+bool
+Table::insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value)
+{
+ uint64_t found = 0;
+ for (int i=0; i<mFieldCount; i++) {
+ if (strcmp(name.c_str(), mFieldNames[i]) == 0) {
+ found = mFieldIds[i];
+ break;
+ }
+ }
+
+ switch (found & FIELD_TYPE_MASK) {
+ case FIELD_TYPE_DOUBLE:
+ case FIELD_TYPE_FLOAT:
+ // TODO: support parse string to float/double
+ return false;
+ case FIELD_TYPE_STRING:
+ case FIELD_TYPE_BYTES:
+ proto.write(found, value);
+ break;
+ case FIELD_TYPE_INT64:
+ case FIELD_TYPE_SINT64:
+ case FIELD_TYPE_UINT64:
+ case FIELD_TYPE_FIXED64:
+ case FIELD_TYPE_SFIXED64:
+ proto.write(found, toLongLong(value));
+ break;
+ case FIELD_TYPE_BOOL:
+ case FIELD_TYPE_ENUM:
+ case FIELD_TYPE_INT32:
+ case FIELD_TYPE_SINT32:
+ case FIELD_TYPE_UINT32:
+ case FIELD_TYPE_FIXED32:
+ case FIELD_TYPE_SFIXED32:
+ proto.write(found, toInt(value));
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
diff --git a/cmds/incident_helper/ih_util.h b/cmds/incident_helper/src/ih_util.h
similarity index 79%
rename from cmds/incident_helper/ih_util.h
rename to cmds/incident_helper/src/ih_util.h
index ce5baee..86761e9 100644
--- a/cmds/incident_helper/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -21,6 +21,10 @@
#include <vector>
#include <sstream>
+#include <android/util/ProtoOutputStream.h>
+
+using namespace android::util;
+
typedef std::vector<std::string> header_t;
typedef std::vector<std::string> record_t;
typedef std::string (*trans_func) (const std::string&);
@@ -52,6 +56,12 @@
bool hasPrefix(std::string* line, const char* key);
/**
+ * Converts string to the desired type
+ */
+int toInt(const std::string& s);
+long long toLongLong(const std::string& s);
+
+/**
* Reader class reads data from given fd in streaming fashion.
* The buffer size is controlled by capacity parameter.
*/
@@ -78,4 +88,22 @@
inline bool EOR() { return mFd == -1 && mBufSize == 0; };
};
+/**
+ * The class contains a mapping between table headers to its field ids.
+ * And allow users to insert the field values to proto based on its header name.
+ */
+class Table
+{
+public:
+ Table(const char* names[], const uint64_t ids[], const int count);
+ ~Table();
+
+ bool insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value);
+
+private:
+ const char** mFieldNames;
+ const uint64_t* mFieldIds;
+ const int mFieldCount;
+};
+
#endif // INCIDENT_HELPER_UTIL_H
diff --git a/cmds/incident_helper/main.cpp b/cmds/incident_helper/src/main.cpp
similarity index 96%
rename from cmds/incident_helper/main.cpp
rename to cmds/incident_helper/src/main.cpp
index 52ff777..3da87b9c 100644
--- a/cmds/incident_helper/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -16,7 +16,9 @@
#define LOG_TAG "incident_helper"
-#include "IncidentHelper.h"
+#include "parsers/KernelWakesParser.h"
+#include "parsers/PageTypeInfoParser.h"
+#include "parsers/ProcrankParser.h"
#include <android-base/file.h>
#include <getopt.h>
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
new file mode 100644
index 0000000..cc4a1e1
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#define LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+
+#include "frameworks/base/core/proto/android/os/kernelwake.proto.h"
+#include "ih_util.h"
+#include "KernelWakesParser.h"
+
+using namespace android::os;
+
+const std::string LINE_DELIMITER = "\t";
+
+status_t
+KernelWakesParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ header_t header; // the header of /d/wakeup_sources
+ record_t record; // retain each record
+ int nline = 0;
+
+ ProtoOutputStream proto;
+ Table table(WakeupSourceProto::_FIELD_NAMES, WakeupSourceProto::_FIELD_IDS, WakeupSourceProto::_FIELD_COUNT);
+
+ // parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+ // parse head line
+ if (nline++ == 0) {
+ header = parseHeader(line, LINE_DELIMITER);
+ continue;
+ }
+
+ // parse for each record, the line delimiter is \t only!
+ record = parseRecord(line, LINE_DELIMITER);
+
+ if (record.size() != header.size()) {
+ // TODO: log this to incident report!
+ fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str());
+ continue;
+ }
+
+ long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES);
+ for (int i=0; i<(int)record.size(); i++) {
+ if (!table.insertField(proto, header[i], record[i])) {
+ fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
+ this->name.string(), nline, header[i].c_str(), record[i].c_str());
+ }
+ }
+ proto.end(token);
+ }
+
+ if (!reader.ok(&line)) {
+ fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+ return -1;
+ }
+
+ if (!proto.flush(out)) {
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ return -1;
+ }
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ return NO_ERROR;
+}
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.h b/cmds/incident_helper/src/parsers/KernelWakesParser.h
new file mode 100644
index 0000000..aabab7c
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef KERNEL_WAKES_PARSER_H
+#define KERNEL_WAKES_PARSER_H
+
+#include "TextParserBase.h"
+
+/**
+ * Kernel wakeup sources parser, parses text to protobuf in /d/wakeup_sources
+ */
+class KernelWakesParser : public TextParserBase {
+public:
+ KernelWakesParser() : TextParserBase(String8("KernelWakeSources")) {};
+ ~KernelWakesParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // KERNEL_WAKES_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
new file mode 100644
index 0000000..6047bd1
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#define LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+
+#include "frameworks/base/core/proto/android/os/pagetypeinfo.proto.h"
+#include "ih_util.h"
+#include "PageTypeInfoParser.h"
+
+using namespace android::os;
+
+const std::string LINE_DELIMITER = ",";
+
+status_t
+PageTypeInfoParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ bool migrateTypeSession = false;
+ int pageBlockOrder;
+ header_t blockHeader;
+
+ ProtoOutputStream proto;
+ Table table(BlockProto::_FIELD_NAMES, BlockProto::_FIELD_IDS, BlockProto::_FIELD_COUNT);
+
+ while (reader.readLine(&line)) {
+ if (line.empty()) {
+ migrateTypeSession = false;
+ blockHeader.clear();
+ continue;
+ }
+
+ if (hasPrefix(&line, "Page block order:")) {
+ pageBlockOrder = toInt(line);
+ proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder);
+ continue;
+ }
+ if (hasPrefix(&line, "Pages per block:")) {
+ proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line));
+ continue;
+ }
+ if (hasPrefix(&line, "Free pages count per migrate type at order")) {
+ migrateTypeSession = true;
+ continue;
+ }
+ if (hasPrefix(&line, "Number of blocks type")) {
+ blockHeader = parseHeader(line);
+ continue;
+ }
+
+ record_t record = parseRecord(line, LINE_DELIMITER);
+ if (migrateTypeSession && record.size() == 3) {
+ long long token = proto.start(PageTypeInfo::MIGRATE_TYPES);
+ // expect part 0 starts with "Node"
+ if (hasPrefix(&record[0], "Node")) {
+ proto.write(MigrateTypeProto::NODE, toInt(record[0]));
+ } else return BAD_VALUE;
+ // expect part 1 starts with "zone"
+ if (hasPrefix(&record[1], "zone")) {
+ proto.write(MigrateTypeProto::ZONE, record[1]);
+ } else return BAD_VALUE;
+ // expect part 2 starts with "type"
+ if (hasPrefix(&record[2], "type")) {
+ // expect the rest of part 2 has number of (pageBlockOrder + 2) parts
+ // An example looks like:
+ // header line: type 0 1 2 3 4 5 6 7 8 9 10
+ // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0
+ // The pageBlockOrder = 10 and it's zero-indexed. so total parts
+ // are 10 + 1(zero-indexed) + 1(the type part) = 12.
+ record_t pageCounts = parseRecord(record[2]);
+ int pageCountsSize = pageBlockOrder + 2;
+ if ((int)pageCounts.size() != pageCountsSize) return BAD_VALUE;
+
+ proto.write(MigrateTypeProto::TYPE, pageCounts[0]);
+ for (auto i=1; i<pageCountsSize; i++) {
+ proto.write(MigrateTypeProto::FREE_PAGES_COUNT, toInt(pageCounts[i]));
+ }
+ } else return BAD_VALUE;
+
+ proto.end(token);
+ } else if (!blockHeader.empty() && record.size() == 2) {
+ long long token = proto.start(PageTypeInfo::BLOCKS);
+ if (hasPrefix(&record[0], "Node")) {
+ proto.write(BlockProto::NODE, toInt(record[0]));
+ } else return BAD_VALUE;
+
+ if (hasPrefix(&record[1], "zone")) {
+ record_t blockCounts = parseRecord(record[1]);
+ proto.write(BlockProto::ZONE, blockCounts[0]);
+
+ for (size_t i=0; i<blockHeader.size(); i++) {
+ if (!table.insertField(proto, blockHeader[i], blockCounts[i+1])) {
+ return BAD_VALUE;
+ }
+ }
+ } else return BAD_VALUE;
+ proto.end(token);
+ }
+ }
+
+ if (!reader.ok(&line)) {
+ fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+ return -1;
+ }
+
+ if (!proto.flush(out)) {
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ return -1;
+ }
+
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ return NO_ERROR;
+}
\ No newline at end of file
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.h b/cmds/incident_helper/src/parsers/PageTypeInfoParser.h
new file mode 100644
index 0000000..fb84d91
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef PAGE_TYPE_INFO_PARSER_H
+#define PAGE_TYPE_INFO_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * PageTypeInfo parser, parses text to protobuf in /proc/pageinfotype
+ */
+class PageTypeInfoParser : public TextParserBase {
+public:
+ PageTypeInfoParser() : TextParserBase(String8("PageTypeInfo")) {};
+ ~PageTypeInfoParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // PAGE_TYPE_INFO_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.cpp b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
new file mode 100644
index 0000000..93f970f
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#define LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+
+#include "frameworks/base/core/proto/android/os/procrank.proto.h"
+#include "ih_util.h"
+#include "ProcrankParser.h"
+
+using namespace android::os;
+
+status_t
+ProcrankParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ header_t header; // the header of /d/wakeup_sources
+ record_t record; // retain each record
+ int nline = 0;
+
+ ProtoOutputStream proto;
+ Table table(ProcessProto::_FIELD_NAMES, ProcessProto::_FIELD_IDS, ProcessProto::_FIELD_COUNT);
+ string zram, ram, total;
+
+ // parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ // parse head line
+ if (nline++ == 0) {
+ header = parseHeader(line);
+ continue;
+ }
+
+ if (hasPrefix(&line, "ZRAM:")) {
+ zram = line;
+ continue;
+ }
+ if (hasPrefix(&line, "RAM:")) {
+ ram = line;
+ continue;
+ }
+
+ record = parseRecord(line);
+ if (record.size() != header.size()) {
+ if (record[record.size() - 1] == "TOTAL") { // TOTAL record
+ total = line;
+ } else {
+ fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline,
+ line.c_str());
+ }
+ continue;
+ }
+
+ long long token = proto.start(Procrank::PROCESSES);
+ for (int i=0; i<(int)record.size(); i++) {
+ if (!table.insertField(proto, header[i], record[i])) {
+ fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
+ this->name.string(), nline, header[i].c_str(), record[i].c_str());
+ }
+ }
+ proto.end(token);
+ }
+
+ // add summary
+ long long token = proto.start(Procrank::SUMMARY);
+ if (!total.empty()) {
+ record = parseRecord(total);
+ long long token = proto.start(SummaryProto::TOTAL);
+ for (int i=(int)record.size(); i>0; i--) {
+ table.insertField(proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
+ }
+ proto.end(token);
+ }
+ if (!zram.empty()) {
+ long long token = proto.start(SummaryProto::ZRAM);
+ proto.write(ZramProto::RAW_TEXT, zram);
+ proto.end(token);
+ }
+ if (!ram.empty()) {
+ long long token = proto.start(SummaryProto::RAM);
+ proto.write(RamProto::RAW_TEXT, ram);
+ proto.end(token);
+ }
+ proto.end(token);
+
+ if (!reader.ok(&line)) {
+ fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+ return -1;
+ }
+
+ if (!proto.flush(out)) {
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ return -1;
+ }
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ return NO_ERROR;
+}
diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.h b/cmds/incident_helper/src/parsers/ProcrankParser.h
new file mode 100644
index 0000000..5d0ee48
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef PROCRANK_PARSER_H
+#define PROCRANK_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Procrank parser, parses text produced by command procrank
+ */
+class ProcrankParser : public TextParserBase {
+public:
+ ProcrankParser() : TextParserBase(String8("ProcrankParser")) {};
+ ~ProcrankParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // PROCRANK_PARSER_H
diff --git a/cmds/incident_helper/testdata/kernel_wakeups_short.txt b/cmds/incident_helper/testdata/kernel_wakeups_short.txt
new file mode 100644
index 0000000..a51926e
--- /dev/null
+++ b/cmds/incident_helper/testdata/kernel_wakeups_short.txt
@@ -0,0 +1,3 @@
+name active_count last_change
+ab 8 123456123456
+df 143 0
diff --git a/cmds/incident_helper/tests/IncidentHelper_test.cpp b/cmds/incident_helper/tests/IncidentHelper_test.cpp
deleted file mode 100644
index c44a163..0000000
--- a/cmds/incident_helper/tests/IncidentHelper_test.cpp
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#include "IncidentHelper.h"
-
-#include "frameworks/base/core/proto/android/os/kernelwake.pb.h"
-#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h"
-#include "frameworks/base/core/proto/android/os/procrank.pb.h"
-
-#include <android-base/file.h>
-#include <android-base/test_utils.h>
-#include <gmock/gmock.h>
-#include <google/protobuf/message.h>
-#include <gtest/gtest.h>
-#include <string.h>
-#include <fcntl.h>
-
-using namespace android::base;
-using namespace android::os;
-using namespace std;
-using ::testing::StrEq;
-using ::testing::Test;
-using ::testing::internal::CaptureStderr;
-using ::testing::internal::CaptureStdout;
-using ::testing::internal::GetCapturedStderr;
-using ::testing::internal::GetCapturedStdout;
-
-class IncidentHelperTest : public Test {
-public:
- virtual void SetUp() override {
- ASSERT_TRUE(tf.fd != -1);
- }
-
- string getSerializedString(::google::protobuf::Message& message) {
- string expectedStr;
- message.SerializeToFileDescriptor(tf.fd);
- ReadFileToString(tf.path, &expectedStr);
- return expectedStr;
- }
-
-protected:
- TemporaryFile tf;
-
- const string kTestPath = GetExecutableDirectory();
- const string kTestDataPath = kTestPath + "/testdata/";
-};
-
-TEST_F(IncidentHelperTest, ReverseParser) {
- ReverseParser parser;
- TemporaryFile tf;
-
- ASSERT_TRUE(tf.fd != -1);
- ASSERT_TRUE(WriteStringToFile("TestData", tf.path, false));
-
- CaptureStdout();
- ASSERT_EQ(NO_ERROR, parser.Parse(tf.fd, STDOUT_FILENO));
- EXPECT_THAT(GetCapturedStdout(), StrEq("ataDtseT"));
-}
-
-TEST_F(IncidentHelperTest, KernelWakesParser) {
- const string testFile = kTestDataPath + "kernel_wakeups.txt";
- KernelWakesParser parser;
- KernelWakeSources expected;
-
- WakeupSourceProto* record1 = expected.add_wakeup_sources();
- record1->set_name("ipc000000ab_ATFWD-daemon");
- record1->set_active_count(8);
- record1->set_event_count(8);
- record1->set_wakeup_count(0);
- record1->set_expire_count(0);
- record1->set_active_since(0l);
- record1->set_total_time(0l);
- record1->set_max_time(0l);
- record1->set_last_change(131348l);
- record1->set_prevent_suspend_time(0l);
-
- WakeupSourceProto* record2 = expected.add_wakeup_sources();
- record2->set_name("ipc000000aa_ATFWD-daemon");
- record2->set_active_count(143);
- record2->set_event_count(143);
- record2->set_wakeup_count(0);
- record2->set_expire_count(0);
- record2->set_active_since(0l);
- record2->set_total_time(123l);
- record2->set_max_time(3l);
- record2->set_last_change(2067286206l);
- record2->set_prevent_suspend_time(0l);
-
- int fd = open(testFile.c_str(), O_RDONLY);
- ASSERT_TRUE(fd != -1);
-
- CaptureStdout();
- ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
- close(fd);
-}
-
-TEST_F(IncidentHelperTest, ProcrankParser) {
- const string testFile = kTestDataPath + "procrank.txt";
- ProcrankParser parser;
- Procrank expected;
-
- ProcessProto* process1 = expected.add_processes();
- process1->set_pid(1119);
- process1->set_vss(2607640);
- process1->set_rss(339564);
- process1->set_pss(180278);
- process1->set_uss(114216);
- process1->set_swap(1584);
- process1->set_pswap(46);
- process1->set_uswap(0);
- process1->set_zswap(10);
- process1->set_cmdline("system_server");
-
- ProcessProto* process2 = expected.add_processes();
- process2->set_pid(649);
- process2->set_vss(11016);
- process2->set_rss(1448);
- process2->set_pss(98);
- process2->set_uss(48);
- process2->set_swap(472);
- process2->set_pswap(342);
- process2->set_uswap(212);
- process2->set_zswap(75);
- process2->set_cmdline("/vendor/bin/qseecomd");
-
- ProcessProto* total = expected.mutable_summary()->mutable_total();
- total->set_pss(1201993);
- total->set_uss(935300);
- total->set_swap(88164);
- total->set_pswap(31069);
- total->set_uswap(27612);
- total->set_zswap(6826);
- total->set_cmdline("TOTAL");
-
- expected.mutable_summary()->mutable_zram()
- ->set_raw_text("6828K physical used for 31076K in swap (524284K total swap)");
- expected.mutable_summary()->mutable_ram()
- ->set_raw_text("3843972K total, 281424K free, 116764K buffers, 1777452K cached, 1136K shmem, 217916K slab");
-
- int fd = open(testFile.c_str(), O_RDONLY);
- ASSERT_TRUE(fd != -1);
-
- CaptureStdout();
- ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
- close(fd);
-}
-
-TEST_F(IncidentHelperTest, ProcrankParserShortHeader) {
- const string testFile = kTestDataPath + "procrank_short.txt";
- ProcrankParser parser;
- Procrank expected;
-
- ProcessProto* process1 = expected.add_processes();
- process1->set_pid(1119);
- process1->set_vss(2607640);
- process1->set_rss(339564);
- process1->set_pss(180278);
- process1->set_uss(114216);
- process1->set_cmdline("system_server");
-
- ProcessProto* process2 = expected.add_processes();
- process2->set_pid(649);
- process2->set_vss(11016);
- process2->set_rss(1448);
- process2->set_pss(98);
- process2->set_uss(48);
- process2->set_cmdline("/vendor/bin/qseecomd");
-
- ProcessProto* total = expected.mutable_summary()->mutable_total();
- total->set_pss(1201993);
- total->set_uss(935300);
- total->set_cmdline("TOTAL");
-
- expected.mutable_summary()->mutable_ram()
- ->set_raw_text("3843972K total, 281424K free, 116764K buffers, 1777452K cached, 1136K shmem, 217916K slab");
-
- int fd = open(testFile.c_str(), O_RDONLY);
- ASSERT_TRUE(fd != -1);
-
- CaptureStdout();
- ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
- close(fd);
-}
-
-TEST_F(IncidentHelperTest, PageTypeInfoParser) {
- const string testFile = kTestDataPath + "pagetypeinfo.txt";
- PageTypeInfoParser parser;
- PageTypeInfo expected;
-
- expected.set_page_block_order(10);
- expected.set_pages_per_block(1024);
-
- MigrateTypeProto* mt1 = expected.add_migrate_types();
- mt1->set_node(0);
- mt1->set_zone("DMA");
- mt1->set_type("Unmovable");
- int arr1[] = { 426, 279, 226, 1, 1, 1, 0, 0, 2, 2, 0};
- for (auto i=0; i<11; i++) {
- mt1->add_free_pages_count(arr1[i]);
- }
-
- MigrateTypeProto* mt2 = expected.add_migrate_types();
- mt2->set_node(0);
- mt2->set_zone("Normal");
- mt2->set_type("Reclaimable");
- int arr2[] = { 953, 773, 437, 154, 92, 26, 15, 14, 12, 7, 0};
- for (auto i=0; i<11; i++) {
- mt2->add_free_pages_count(arr2[i]);
- }
-
- BlockProto* block1 = expected.add_blocks();
- block1->set_node(0);
- block1->set_zone("DMA");
- block1->set_unmovable(74);
- block1->set_reclaimable(9);
- block1->set_movable(337);
- block1->set_cma(41);
- block1->set_reserve(1);
- block1->set_isolate(0);
-
-
- BlockProto* block2 = expected.add_blocks();
- block2->set_node(0);
- block2->set_zone("Normal");
- block2->set_unmovable(70);
- block2->set_reclaimable(12);
- block2->set_movable(423);
- block2->set_cma(0);
- block2->set_reserve(1);
- block2->set_isolate(0);
-
- int fd = open(testFile.c_str(), O_RDONLY);
- ASSERT_TRUE(fd != -1);
-
- CaptureStdout();
- ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
- close(fd);
-}
\ No newline at end of file
diff --git a/cmds/incident_helper/tests/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
new file mode 100644
index 0000000..a8fa6208
--- /dev/null
+++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "KernelWakesParser.h"
+
+#include "frameworks/base/core/proto/android/os/kernelwake.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class KernelWakesParserTest : public Test {
+public:
+ virtual void SetUp() override {
+ ASSERT_TRUE(tf.fd != -1);
+ }
+
+ string getSerializedString(::google::protobuf::Message& message) {
+ string expectedStr;
+ message.SerializeToFileDescriptor(tf.fd);
+ ReadFileToString(tf.path, &expectedStr);
+ return expectedStr;
+ }
+
+protected:
+ TemporaryFile tf;
+
+ const string kTestPath = GetExecutableDirectory();
+ const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(KernelWakesParserTest, Short) {
+ const string testFile = kTestDataPath + "kernel_wakeups_short.txt";
+ KernelWakesParser parser;
+ KernelWakeSources expected;
+
+ WakeupSourceProto* record1 = expected.add_wakeup_sources();
+ record1->set_name("ab");
+ record1->set_active_count(8);
+ record1->set_last_change(123456123456LL);
+
+ WakeupSourceProto* record2 = expected.add_wakeup_sources();
+ record2->set_name("df");
+ record2->set_active_count(143);
+ record2->set_last_change(0LL);
+
+ int fd = open(testFile.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+ EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ close(fd);
+}
+
+TEST_F(KernelWakesParserTest, Normal) {
+ const string testFile = kTestDataPath + "kernel_wakeups.txt";
+ KernelWakesParser parser;
+ KernelWakeSources expected;
+
+ WakeupSourceProto* record1 = expected.add_wakeup_sources();
+ record1->set_name("ipc000000ab_ATFWD-daemon");
+ record1->set_active_count(8);
+ record1->set_event_count(8);
+ record1->set_wakeup_count(0);
+ record1->set_expire_count(0);
+ record1->set_active_since(0l);
+ record1->set_total_time(0l);
+ record1->set_max_time(0l);
+ record1->set_last_change(131348LL);
+ record1->set_prevent_suspend_time(0LL);
+
+ WakeupSourceProto* record2 = expected.add_wakeup_sources();
+ record2->set_name("ipc000000aa_ATFWD-daemon");
+ record2->set_active_count(143);
+ record2->set_event_count(143);
+ record2->set_wakeup_count(0);
+ record2->set_expire_count(0);
+ record2->set_active_since(0l);
+ record2->set_total_time(123l);
+ record2->set_max_time(3l);
+ record2->set_last_change(2067286206LL);
+ record2->set_prevent_suspend_time(0LL);
+
+ int fd = open(testFile.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+ EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ close(fd);
+}
diff --git a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
new file mode 100644
index 0000000..de64e70
--- /dev/null
+++ b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "PageTypeInfoParser.h"
+
+#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class PageTypeInfoParserTest : public Test {
+public:
+ virtual void SetUp() override {
+ ASSERT_TRUE(tf.fd != -1);
+ }
+
+ string getSerializedString(::google::protobuf::Message& message) {
+ string expectedStr;
+ message.SerializeToFileDescriptor(tf.fd);
+ ReadFileToString(tf.path, &expectedStr);
+ return expectedStr;
+ }
+
+protected:
+ TemporaryFile tf;
+
+ const string kTestPath = GetExecutableDirectory();
+ const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(PageTypeInfoParserTest, Success) {
+ const string testFile = kTestDataPath + "pagetypeinfo.txt";
+ PageTypeInfoParser parser;
+ PageTypeInfo expected;
+
+ expected.set_page_block_order(10);
+ expected.set_pages_per_block(1024);
+
+ MigrateTypeProto* mt1 = expected.add_migrate_types();
+ mt1->set_node(0);
+ mt1->set_zone("DMA");
+ mt1->set_type("Unmovable");
+ int arr1[] = { 426, 279, 226, 1, 1, 1, 0, 0, 2, 2, 0};
+ for (auto i=0; i<11; i++) {
+ mt1->add_free_pages_count(arr1[i]);
+ }
+
+ MigrateTypeProto* mt2 = expected.add_migrate_types();
+ mt2->set_node(0);
+ mt2->set_zone("Normal");
+ mt2->set_type("Reclaimable");
+ int arr2[] = { 953, 773, 437, 154, 92, 26, 15, 14, 12, 7, 0};
+ for (auto i=0; i<11; i++) {
+ mt2->add_free_pages_count(arr2[i]);
+ }
+
+ BlockProto* block1 = expected.add_blocks();
+ block1->set_node(0);
+ block1->set_zone("DMA");
+ block1->set_unmovable(74);
+ block1->set_reclaimable(9);
+ block1->set_movable(337);
+ block1->set_cma(41);
+ block1->set_reserve(1);
+ block1->set_isolate(0);
+
+
+ BlockProto* block2 = expected.add_blocks();
+ block2->set_node(0);
+ block2->set_zone("Normal");
+ block2->set_unmovable(70);
+ block2->set_reclaimable(12);
+ block2->set_movable(423);
+ block2->set_cma(0);
+ block2->set_reserve(1);
+ block2->set_isolate(0);
+
+ int fd = open(testFile.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+ EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ close(fd);
+}
\ No newline at end of file
diff --git a/cmds/incident_helper/tests/ProcrankParser_test.cpp b/cmds/incident_helper/tests/ProcrankParser_test.cpp
new file mode 100644
index 0000000..e86647a
--- /dev/null
+++ b/cmds/incident_helper/tests/ProcrankParser_test.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "ProcrankParser.h"
+
+#include "frameworks/base/core/proto/android/os/procrank.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class ProcrankParserTest : public Test {
+public:
+ virtual void SetUp() override {
+ ASSERT_TRUE(tf.fd != -1);
+ }
+
+ string getSerializedString(::google::protobuf::Message& message) {
+ string expectedStr;
+ message.SerializeToFileDescriptor(tf.fd);
+ ReadFileToString(tf.path, &expectedStr);
+ return expectedStr;
+ }
+
+protected:
+ TemporaryFile tf;
+
+ const string kTestPath = GetExecutableDirectory();
+ const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(ProcrankParserTest, HasSwapInfo) {
+ const string testFile = kTestDataPath + "procrank.txt";
+ ProcrankParser parser;
+ Procrank expected;
+
+ ProcessProto* process1 = expected.add_processes();
+ process1->set_pid(1119);
+ process1->set_vss(2607640);
+ process1->set_rss(339564);
+ process1->set_pss(180278);
+ process1->set_uss(114216);
+ process1->set_swap(1584);
+ process1->set_pswap(46);
+ process1->set_uswap(0);
+ process1->set_zswap(10);
+ process1->set_cmdline("system_server");
+
+ ProcessProto* process2 = expected.add_processes();
+ process2->set_pid(649);
+ process2->set_vss(11016);
+ process2->set_rss(1448);
+ process2->set_pss(98);
+ process2->set_uss(48);
+ process2->set_swap(472);
+ process2->set_pswap(342);
+ process2->set_uswap(212);
+ process2->set_zswap(75);
+ process2->set_cmdline("/vendor/bin/qseecomd");
+
+ ProcessProto* total = expected.mutable_summary()->mutable_total();
+ total->set_pss(1201993);
+ total->set_uss(935300);
+ total->set_swap(88164);
+ total->set_pswap(31069);
+ total->set_uswap(27612);
+ total->set_zswap(6826);
+ total->set_cmdline("TOTAL");
+
+ expected.mutable_summary()->mutable_zram()
+ ->set_raw_text("6828K physical used for 31076K in swap (524284K total swap)");
+ expected.mutable_summary()->mutable_ram()
+ ->set_raw_text("3843972K total, 281424K free, 116764K buffers, 1777452K cached, 1136K shmem, 217916K slab");
+
+ int fd = open(testFile.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+ EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ close(fd);
+}
+
+TEST_F(ProcrankParserTest, NoSwapInfo) {
+ const string testFile = kTestDataPath + "procrank_short.txt";
+ ProcrankParser parser;
+ Procrank expected;
+
+ ProcessProto* process1 = expected.add_processes();
+ process1->set_pid(1119);
+ process1->set_vss(2607640);
+ process1->set_rss(339564);
+ process1->set_pss(180278);
+ process1->set_uss(114216);
+ process1->set_cmdline("system_server");
+
+ ProcessProto* process2 = expected.add_processes();
+ process2->set_pid(649);
+ process2->set_vss(11016);
+ process2->set_rss(1448);
+ process2->set_pss(98);
+ process2->set_uss(48);
+ process2->set_cmdline("/vendor/bin/qseecomd");
+
+ ProcessProto* total = expected.mutable_summary()->mutable_total();
+ total->set_pss(1201993);
+ total->set_uss(935300);
+ total->set_cmdline("TOTAL");
+
+ expected.mutable_summary()->mutable_ram()
+ ->set_raw_text("3843972K total, 281424K free, 116764K buffers, 1777452K cached, 1136K shmem, 217916K slab");
+
+ int fd = open(testFile.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+ EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ close(fd);
+}
diff --git a/cmds/statsd/.clang-format b/cmds/statsd/.clang-format
index 3d64bee..cead3a0 100644
--- a/cmds/statsd/.clang-format
+++ b/cmds/statsd/.clang-format
@@ -12,3 +12,6 @@
PointerAlignment: Left
TabWidth: 4
AccessModifierOffset: -4
+IncludeCategories:
+ - Regex: '^"Log\.h"'
+ Priority: -1
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index db634d4..54ade35 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -42,6 +42,9 @@
src/metrics/EventMetricProducer.cpp \
src/metrics/CountMetricProducer.cpp \
src/metrics/DurationMetricProducer.cpp \
+ src/metrics/duration_helper/OringDurationTracker.cpp \
+ src/metrics/duration_helper/MaxDurationTracker.cpp \
+ src/metrics/ValueMetricProducer.cpp \
src/metrics/MetricsManager.cpp \
src/metrics/metrics_manager_util.cpp \
src/packages/UidMap.cpp \
@@ -103,7 +106,8 @@
LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
LOCAL_C_INCLUDES += $(statsd_common_c_includes)
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries)
+LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
+ libgtest_prod
LOCAL_MODULE_CLASS := EXECUTABLES
@@ -142,7 +146,9 @@
tests/LogEntryMatcher_test.cpp \
tests/LogReader_test.cpp \
tests/MetricsManager_test.cpp \
- tests/UidMap_test.cpp
+ tests/UidMap_test.cpp \
+ tests/OringDurationTracker_test.cpp \
+ tests/MaxDurationTracker_test.cpp
LOCAL_STATIC_LIBRARIES := \
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index cdaca1b..c0cedb1 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -60,6 +60,7 @@
unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
if (newMetricsManager->isConfigValid()) {
+ mUidMap->OnConfigUpdated(key);
mMetricsManagers[key] = std::move(newMetricsManager);
// Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)});
ALOGD("StatsdConfig valid");
@@ -69,14 +70,27 @@
}
}
-vector<StatsLogReport> StatsLogProcessor::onDumpReport(const ConfigKey& key) {
+ConfigMetricsReport StatsLogProcessor::onDumpReport(const ConfigKey& key) {
+ ConfigMetricsReport report;
+
auto it = mMetricsManagers.find(key);
if (it == mMetricsManagers.end()) {
ALOGW("Config source %s does not exist", key.ToString().c_str());
- return vector<StatsLogReport>();
+ return report;
}
- return it->second->onDumpReport();
+ auto set_key = report.mutable_config_key();
+ set_key->set_uid(key.GetUid());
+ set_key->set_name(key.GetName());
+ for (auto m : it->second->onDumpReport()) {
+ // Transfer the vector of StatsLogReport into a field
+ // TODO: perhaps we just have bytes being returned from onDumpReport and transfer bytes
+ auto dest = report.add_metrics();
+ *dest = m;
+ }
+ auto temp = mUidMap->getOutput(key);
+ report.set_allocated_uid_map(&temp);
+ return report;
}
void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
@@ -84,6 +98,7 @@
if (it != mMetricsManagers.end()) {
it->second->finish();
mMetricsManagers.erase(it);
+ mUidMap->OnConfigRemoved(key);
}
auto flushTime = mLastFlushTimes.find(key);
if (flushTime != mLastFlushTimes.end()) {
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 6463441..0083827 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -42,7 +42,7 @@
void OnConfigRemoved(const ConfigKey& key);
// TODO: Once we have the ProtoOutputStream in c++, we can just return byte array.
- std::vector<StatsLogReport> onDumpReport(const ConfigKey& key);
+ ConfigMetricsReport onDumpReport(const ConfigKey& key);
/* Request a flush through a binder call. */
void flush();
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 604753e..edb1a0f 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -65,7 +65,6 @@
StatsService::StatsService(const sp<Looper>& handlerLooper)
: mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Put this comment somewhere better
{
- mStatsPullerManager = new StatsPullerManager();
mUidMap = new UidMap();
mConfigManager = new ConfigManager();
mProcessor = new StatsLogProcessor(mUidMap, [this](const vector<uint8_t>& log) {
@@ -231,6 +230,14 @@
fprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
fprintf(out, " uid is used.\n");
fprintf(out, " NAME The per-uid name to use\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
+ fprintf(out, "usage: adb shell cmd stats dump-report [UID] NAME\n");
+ fprintf(out, " Dump all metric data for a configuration.\n");
+ fprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
+ fprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
+ fprintf(out, " calling uid is used.\n");
+ fprintf(out, " NAME The name of the configuration\n");
}
status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
@@ -312,7 +319,7 @@
// Automatically pick the UID
uid = IPCThreadState::self()->getCallingUid();
// TODO: What if this isn't a binder call? Should we fail?
- name.assign(args[2].c_str(), args[2].size());
+ name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
// If it's a userdebug or eng build, then the shell user can
@@ -366,7 +373,7 @@
status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
- auto stats = mStatsPullerManager->Pull(s);
+ auto stats = m_stats_puller_manager.Pull(s, time(nullptr));
for (const auto& it : stats) {
fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
@@ -433,8 +440,9 @@
"Only system uid can call informPollAlarmFired");
}
+ m_stats_puller_manager.OnAlarmFired();
+
if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded");
- // TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them.
return Status::ok();
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 7f04658..3930d31 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -27,7 +27,6 @@
#include <android/os/IStatsCallbacks.h>
#include <android/os/IStatsCompanionService.h>
#include <binder/IResultReceiver.h>
-#include <binder/IShellCallback.h>
#include <utils/Looper.h>
#include <deque>
@@ -158,7 +157,7 @@
/**
* Fetches external metrics.
*/
- sp<StatsPullerManager> mStatsPullerManager;
+ StatsPullerManager& m_stats_puller_manager = StatsPullerManager::GetInstance();
/**
* Tracks the configurations that have been passed to statsd.
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index f56c15a..953bcb3 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -16,7 +16,6 @@
#define DEBUG true // STOPSHIP if true
#include "Log.h"
-
#include "CombinationConditionTracker.h"
#include <log/logprint.h>
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index fc88a88..dbdb3b7 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -46,8 +46,6 @@
const std::vector<sp<ConditionTracker>>& allConditions,
std::vector<ConditionState>& conditionCache) override;
- void addDimensions(const std::vector<KeyMatcher>& keyMatchers) override{};
-
private:
LogicalOperation mLogicalOperation;
// Store index of the children Conditions.
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 055b478..bb5ddeb 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -16,8 +16,6 @@
#pragma once
-#include "Log.h"
-
#include "condition/condition_util.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/LogMatchingTracker.h"
@@ -103,8 +101,6 @@
mSliced = mSliced | sliced;
}
- virtual void addDimensions(const std::vector<KeyMatcher>& keyMatchers) = 0;
-
protected:
// We don't really need the string name, but having a name here makes log messages
// easy to debug.
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 4889b64..1a01afa 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -27,19 +27,23 @@
// Held by MetricProducer, to query a condition state with input defined in EventConditionLink.
class ConditionWizard : public virtual android::RefBase {
public:
+ ConditionWizard(){}; // for testing
ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers)
: mAllConditions(conditionTrackers){};
+ virtual ~ConditionWizard(){};
+
// Query condition state, for a ConditionTracker at [conditionIndex], with [conditionParameters]
// [conditionParameters] mapping from condition name to the HashableDimensionKey to query the
// condition.
// The ConditionTracker at [conditionIndex] can be a CombinationConditionTracker. In this case,
// the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
- ConditionState query(const int conditionIndex,
- const std::map<std::string, HashableDimensionKey>& conditionParameters);
+ virtual ConditionState query(
+ const int conditionIndex,
+ const std::map<std::string, HashableDimensionKey>& conditionParameters);
private:
- std::vector<sp<ConditionTracker>>& mAllConditions;
+ std::vector<sp<ConditionTracker>> mAllConditions;
};
} // namespace statsd
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index aff4768..b691faea 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -74,6 +74,13 @@
mStopAllLogMatcherIndex = -1;
}
+ mDimension.insert(mDimension.begin(), simpleCondition.dimension().begin(),
+ simpleCondition.dimension().end());
+
+ if (mDimension.size() > 0) {
+ mSliced = true;
+ }
+
mInitialized = true;
}
@@ -98,12 +105,6 @@
}
}
-void SimpleConditionTracker::addDimensions(const std::vector<KeyMatcher>& keyMatchers) {
- VLOG("Added dimensions size %lu", (unsigned long)keyMatchers.size());
- mDimensionsList.push_back(keyMatchers);
- mSliced = true;
-}
-
bool SimpleConditionTracker::evaluateCondition(const LogEvent& event,
const vector<MatchingState>& eventMatcherValues,
const vector<sp<ConditionTracker>>& mAllConditions,
@@ -157,18 +158,15 @@
// TODO: handle stop all; all dimension should be cleared.
}
- if (mDimensionsList.size() > 0) {
- for (size_t i = 0; i < mDimensionsList.size(); i++) {
- const auto& dim = mDimensionsList[i];
- vector<KeyValuePair> key = getDimensionKey(event, dim);
- HashableDimensionKey hashableKey = getHashableKey(key);
- if (mSlicedConditionState.find(hashableKey) == mSlicedConditionState.end() ||
- mSlicedConditionState[hashableKey] != newCondition) {
- slicedChanged = true;
- mSlicedConditionState[hashableKey] = newCondition;
- }
- VLOG("key: %s %d", hashableKey.c_str(), newCondition);
+
+ if (mDimension.size() > 0) {
+ HashableDimensionKey hashableKey = getHashableKey(getDimensionKey(event, mDimension));
+ if (mSlicedConditionState.find(hashableKey) == mSlicedConditionState.end() ||
+ mSlicedConditionState[hashableKey] != newCondition) {
+ slicedChanged = true;
+ mSlicedConditionState[hashableKey] = newCondition;
}
+ VLOG("key: %s %d", hashableKey.c_str(), newCondition);
// dump all dimensions for debugging
if (DEBUG) {
print(mSlicedConditionState, mName);
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 1f357f0..b72157b 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -27,8 +27,6 @@
class SimpleConditionTracker : public virtual ConditionTracker {
public:
- // dimensions is a vector of vector because for one single condition, different metrics may be
- // interested in slicing in different ways. one vector<KeyMatcher> defines one type of slicing.
SimpleConditionTracker(const std::string& name, const int index,
const SimpleCondition& simpleCondition,
const std::unordered_map<std::string, int>& trackerNameIndexMap);
@@ -51,8 +49,6 @@
const std::vector<sp<ConditionTracker>>& allConditions,
std::vector<ConditionState>& conditionCache) override;
- void addDimensions(const std::vector<KeyMatcher>& keyMatchers) override;
-
private:
// The index of the LogEventMatcher which defines the start.
int mStartLogMatcherIndex;
@@ -66,8 +62,11 @@
// The index of the LogEventMatcher which defines the stop all.
int mStopAllLogMatcherIndex;
- // Different metrics may subscribe to different types of slicings. So it's a vector of vector.
- std::vector<std::vector<KeyMatcher>> mDimensionsList;
+ // The dimension defines at the atom level, how start and stop should match.
+ // e.g., APP_IN_FOREGROUND, the dimension should be the uid field. Each "start" and
+ // "stop" tells you the state change of a particular app. Without this dimension, this
+ // condition does not make sense.
+ std::vector<KeyMatcher> mDimension;
// Keep the map from the internal HashableDimensionKey to std::vector<KeyValuePair>
// that StatsLogReport wants.
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index c16971a..8812719 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -118,9 +118,10 @@
StatsdConfig config;
config.set_config_id(12345L);
- int WAKE_LOCK_TAG_ID = 11;
+ int WAKE_LOCK_TAG_ID = 1111; // put a fake id here to make testing easier.
int WAKE_LOCK_UID_KEY_ID = 1;
- int WAKE_LOCK_STATE_KEY = 3;
+ int WAKE_LOCK_NAME_KEY = 3;
+ int WAKE_LOCK_STATE_KEY = 4;
int WAKE_LOCK_ACQUIRE_VALUE = 1;
int WAKE_LOCK_RELEASE_VALUE = 0;
@@ -138,6 +139,9 @@
int UID_PROCESS_STATE_TAG_ID = 27;
int UID_PROCESS_STATE_UID_KEY = 1;
+ int KERNEL_WAKELOCK_TAG_ID = 41;
+ int KERNEL_WAKELOCK_NAME_KEY = 4;
+
// Count Screen ON events.
CountMetric* metric = config.add_count_metric();
metric->set_metric_id(1);
@@ -180,24 +184,67 @@
link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
- // Duration of an app holding wl, while screen on and app in background
+ // Duration of an app holding any wl, while screen on and app in background, slice by uid
DurationMetric* durationMetric = config.add_duration_metric();
durationMetric->set_metric_id(5);
- durationMetric->set_start("APP_GET_WL");
- durationMetric->set_stop("APP_RELEASE_WL");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
keyMatcher = durationMetric->add_dimension();
keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
+ durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+ // max Duration of an app holding any wl, while screen on and app in background, slice by uid
+ durationMetric = config.add_duration_metric();
+ durationMetric->set_metric_id(6);
+ durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
+ keyMatcher = durationMetric->add_dimension();
+ keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
+ durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+ durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
+ link = durationMetric->add_links();
+ link->set_condition("APP_IS_BACKGROUND");
+ link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
+ link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+
+ // Duration of an app holding any wl, while screen on and app in background
+ durationMetric = config.add_duration_metric();
+ durationMetric->set_metric_id(7);
+ durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
+ durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+ durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
+ link = durationMetric->add_links();
+ link->set_condition("APP_IS_BACKGROUND");
+ link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
+ link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+
+ // Duration of screen on time.
+ durationMetric = config.add_duration_metric();
+ durationMetric->set_metric_id(8);
+ durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
+ durationMetric->set_what("SCREEN_IS_ON");
+
+ // Value metric to count KERNEL_WAKELOCK when screen turned on
+ ValueMetric* valueMetric = config.add_value_metric();
+ valueMetric->set_metric_id(6);
+ valueMetric->set_what("KERNEL_WAKELOCK");
+ valueMetric->set_value_field(1);
+ valueMetric->set_condition("SCREEN_IS_ON");
+ keyMatcher = valueMetric->add_dimension();
+ keyMatcher->set_key(KERNEL_WAKELOCK_NAME_KEY);
+ // This is for testing easier. We should never set bucket size this small.
+ valueMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
+
// Add an EventMetric to log process state change events.
EventMetric* eventMetric = config.add_event_metric();
- eventMetric->set_metric_id(6);
+ eventMetric->set_metric_id(9);
eventMetric->set_what("SCREEN_TURNED_ON");
// Event matchers............
@@ -272,6 +319,8 @@
simpleCondition = condition->mutable_simple_condition();
simpleCondition->set_start("APP_GOES_BACKGROUND");
simpleCondition->set_stop("APP_GOES_FOREGROUND");
+ KeyMatcher* condition_dimension1 = simpleCondition->add_dimension();
+ condition_dimension1->set_key(APP_USAGE_UID_KEY_ID);
condition = config.add_condition();
condition->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON");
@@ -280,6 +329,16 @@
combination_condition->add_condition("APP_IS_BACKGROUND");
combination_condition->add_condition("SCREEN_IS_ON");
+ condition = config.add_condition();
+ condition->set_name("WL_STATE_PER_APP_PER_NAME");
+ simpleCondition = condition->mutable_simple_condition();
+ simpleCondition->set_start("APP_GET_WL");
+ simpleCondition->set_stop("APP_RELEASE_WL");
+ KeyMatcher* condition_dimension = simpleCondition->add_dimension();
+ condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
+ condition_dimension = simpleCondition->add_dimension();
+ condition_dimension->set_key(WAKE_LOCK_NAME_KEY);
+
return config;
}
diff --git a/cmds/statsd/src/external/KernelWakelockPuller.cpp b/cmds/statsd/src/external/KernelWakelockPuller.cpp
index ee072f8..00259a8 100644
--- a/cmds/statsd/src/external/KernelWakelockPuller.cpp
+++ b/cmds/statsd/src/external/KernelWakelockPuller.cpp
@@ -14,14 +14,14 @@
* limitations under the License.
*/
+#define DEBUG true
#include "Log.h"
#include <android/os/IStatsCompanionService.h>
#include <binder/IPCThreadState.h>
#include <private/android_filesystem_config.h>
+#include "KernelWakelockPuller.h"
#include "StatsService.h"
-#include "external/KernelWakelockPuller.h"
-#include "external/StatsPuller.h"
using namespace android;
using namespace android::base;
@@ -33,11 +33,11 @@
namespace os {
namespace statsd {
-const int KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS = 20;
+const int KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS = 1004;
// The reading and parsing are implemented in Java. It is not difficult to port over. But for now
// let StatsCompanionService handle that and send the data back.
-vector<StatsLogEventWrapper> KernelWakelockPuller::pull() {
+vector<StatsLogEventWrapper> KernelWakelockPuller::Pull() {
sp<IStatsCompanionService> statsCompanion = StatsService::getStatsCompanionService();
vector<StatsLogEventWrapper> returned_value;
if (statsCompanion != NULL) {
diff --git a/cmds/statsd/src/external/KernelWakelockPuller.h b/cmds/statsd/src/external/KernelWakelockPuller.h
index c12806c..cc8059d 100644
--- a/cmds/statsd/src/external/KernelWakelockPuller.h
+++ b/cmds/statsd/src/external/KernelWakelockPuller.h
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-#ifndef STATSD_KERNELWAKELOCKPULLER_H
-#define STATSD_KERNELWAKELOCKPULLER_H
+#pragma once
#include <utils/String16.h>
-#include "external/StatsPuller.h"
+#include "StatsPuller.h"
namespace android {
namespace os {
@@ -29,11 +28,9 @@
// a number of stats need to be pulled from StatsCompanionService
//
const static int PULL_CODE_KERNEL_WAKELOCKS;
- vector<StatsLogEventWrapper> pull() override;
+ vector<StatsLogEventWrapper> Pull() override;
};
} // namespace statsd
} // namespace os
} // namespace android
-
-#endif // STATSD_KERNELWAKELOCKPULLER_H
diff --git a/cmds/statsd/src/external/PullDataReceiver.h b/cmds/statsd/src/external/PullDataReceiver.h
new file mode 100644
index 0000000..0d505cb
--- /dev/null
+++ b/cmds/statsd/src/external/PullDataReceiver.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#pragma once
+
+#include <utils/String16.h>
+#include <unordered_map>
+#include <utils/RefBase.h>
+#include "StatsPuller.h"
+#include "logd/LogEvent.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class PullDataReceiver : virtual public RefBase{
+ public:
+ virtual ~PullDataReceiver() {}
+ virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) = 0;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 6655629..774e7f0 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef STATSD_STATSPULLER_H
-#define STATSD_STATSPULLER_H
+#pragma once
#include <android/os/StatsLogEventWrapper.h>
#include <utils/String16.h>
@@ -32,11 +31,9 @@
public:
virtual ~StatsPuller(){};
- virtual vector<StatsLogEventWrapper> pull() = 0;
+ virtual vector<StatsLogEventWrapper> Pull() = 0;
};
} // namespace statsd
} // namespace os
} // namespace android
-
-#endif // STATSD_STATSPULLER_H
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 7f554d3..f45cb1c 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -18,44 +18,58 @@
#include "Log.h"
#include <android/os/IStatsCompanionService.h>
-#include "KernelWakelockPuller.h"
-#include "StatsService.h"
-#include "external/StatsPullerManager.h"
-#include "logd/LogEvent.h"
#include <cutils/log.h>
#include <algorithm>
+#include <climits>
+#include "KernelWakelockPuller.h"
+#include "StatsPullerManager.h"
+#include "StatsService.h"
+#include "logd/LogEvent.h"
#include <iostream>
-using namespace android;
+using std::string;
+using std::vector;
namespace android {
namespace os {
namespace statsd {
-const int StatsPullerManager::KERNEL_WAKELOCKS = 1;
+const int kernel_wakelock = 1;
+const unordered_map<string, int> StatsPullerManager::kPullCodes({{"KERNEL_WAKELOCK",
+ kernel_wakelock}});
-StatsPullerManager::StatsPullerManager() {
- mStatsPullers.insert(
- {static_cast<int>(KERNEL_WAKELOCKS), std::make_unique<KernelWakelockPuller>()});
+StatsPullerManager::StatsPullerManager()
+ : mCurrentPullingInterval(LONG_MAX), mPullStartTimeMs(get_pull_start_time_ms()) {
+ mPullers.insert({kernel_wakelock, make_unique<KernelWakelockPuller>()});
+ mStatsCompanionService = get_stats_companion_service();
+ if (mStatsCompanionService != nullptr) {
+ mStatsCompanionService->cancelPullingAlarms();
+ } else {
+ VLOG("Failed to update pulling interval");
+ }
}
-vector<std::shared_ptr<LogEvent>> StatsPullerManager::Pull(int pullCode) {
+static const int log_msg_header_size = 28;
+
+vector<shared_ptr<LogEvent>> StatsPullerManager::Pull(int pullCode, uint64_t timestampSec) {
if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
- vector<std::shared_ptr<LogEvent>> ret;
- if (mStatsPullers.find(pullCode) != mStatsPullers.end()) {
- vector<StatsLogEventWrapper> outputs = (mStatsPullers.find(pullCode)->second)->pull();
+ vector<shared_ptr<LogEvent>> ret;
+ auto itr = mPullers.find(pullCode);
+ if (itr != mPullers.end()) {
+ vector<StatsLogEventWrapper> outputs = itr->second->Pull();
for (const StatsLogEventWrapper& it : outputs) {
log_msg tmp;
+ tmp.entry_v1.sec = timestampSec;
+ tmp.entry_v1.nsec = 0;
tmp.entry_v1.len = it.bytes.size();
// Manually set the header size to 28 bytes to match the pushed log events.
- tmp.entry.hdr_size = 28;
+ tmp.entry.hdr_size = log_msg_header_size;
// And set the received bytes starting after the 28 bytes reserved for header.
- std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + 28);
- std::shared_ptr<LogEvent> evt = std::make_shared<LogEvent>(tmp);
+ copy(it.bytes.begin(), it.bytes.end(), tmp.buf + log_msg_header_size);
+ shared_ptr<LogEvent> evt = make_shared<LogEvent>(tmp);
ret.push_back(evt);
- // ret.emplace_back(tmp);
}
return ret;
} else {
@@ -64,6 +78,112 @@
}
}
+sp<IStatsCompanionService> StatsPullerManager::get_stats_companion_service() {
+ sp<IStatsCompanionService> statsCompanion = nullptr;
+ // Get statscompanion service from service manager
+ const sp<IServiceManager> sm(defaultServiceManager());
+ if (sm != nullptr) {
+ const String16 name("statscompanion");
+ statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name));
+ if (statsCompanion == nullptr) {
+ ALOGW("statscompanion service unavailable!");
+ return nullptr;
+ }
+ }
+ return statsCompanion;
+}
+
+StatsPullerManager& StatsPullerManager::GetInstance() {
+ static StatsPullerManager instance;
+ return instance;
+}
+
+int StatsPullerManager::GetPullCode(string atomName) {
+ if (kPullCodes.find(atomName) != kPullCodes.end()) {
+ return kPullCodes.find(atomName)->second;
+ } else {
+ return -1;
+ }
+}
+
+long StatsPullerManager::get_pull_start_time_ms() {
+ // TODO: limit and align pull intervals to 10min boundaries if this turns out to be a problem
+ return time(nullptr) * 1000;
+}
+
+void StatsPullerManager::RegisterReceiver(int pullCode, sp<PullDataReceiver> receiver, long intervalMs) {
+ AutoMutex _l(mReceiversLock);
+ vector<ReceiverInfo>& receivers = mReceivers[pullCode];
+ for (auto it = receivers.begin(); it != receivers.end(); it++) {
+ if (it->receiver.get() == receiver.get()) {
+ VLOG("Receiver already registered of %d", (int)receivers.size());
+ return;
+ }
+ }
+ ReceiverInfo receiverInfo;
+ receiverInfo.receiver = receiver;
+ receiverInfo.timeInfo.first = intervalMs;
+ receivers.push_back(receiverInfo);
+
+ // There is only one alarm for all pulled events. So only set it to the smallest denom.
+ if (intervalMs < mCurrentPullingInterval) {
+ VLOG("Updating pulling interval %ld", intervalMs);
+ mCurrentPullingInterval = intervalMs;
+ if (mStatsCompanionService != nullptr) {
+ mStatsCompanionService->setPullingAlarms(mPullStartTimeMs, mCurrentPullingInterval);
+ } else {
+ VLOG("Failed to update pulling interval");
+ }
+ }
+ VLOG("Puller for pullcode %d registered of %d", pullCode, (int)receivers.size());
+}
+
+void StatsPullerManager::UnRegisterReceiver(int pullCode, sp<PullDataReceiver> receiver) {
+ AutoMutex _l(mReceiversLock);
+ if (mReceivers.find(pullCode) == mReceivers.end()) {
+ VLOG("Unknown pull code or no receivers: %d", pullCode);
+ return;
+ }
+ auto& receivers = mReceivers.find(pullCode)->second;
+ for (auto it = receivers.begin(); it != receivers.end(); it++) {
+ if (receiver.get() == it->receiver.get()) {
+ receivers.erase(it);
+ VLOG("Puller for pullcode %d unregistered of %d", pullCode, (int)receivers.size());
+ return;
+ }
+ }
+}
+
+void StatsPullerManager::OnAlarmFired() {
+ AutoMutex _l(mReceiversLock);
+
+ uint64_t currentTimeMs = time(nullptr) * 1000;
+
+ vector<pair<int, vector<ReceiverInfo*>>> needToPull =
+ vector<pair<int, vector<ReceiverInfo*>>>();
+ for (auto& pair : mReceivers) {
+ vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
+ if (pair.second.size() != 0){
+ for(auto& receiverInfo : pair.second) {
+ if (receiverInfo.timeInfo.first + receiverInfo.timeInfo.second > currentTimeMs) {
+ receivers.push_back(&receiverInfo);
+ }
+ }
+ if (receivers.size() > 0) {
+ needToPull.push_back(make_pair(pair.first, receivers));
+ }
+ }
+ }
+
+ for (const auto& pullInfo : needToPull) {
+ const vector<shared_ptr<LogEvent>>& data = Pull(pullInfo.first, currentTimeMs/1000);
+ for(const auto& receiverInfo : pullInfo.second) {
+ receiverInfo->receiver->onDataPulled(data);
+ receiverInfo->timeInfo.second = currentTimeMs;
+ }
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index e46aec1..e599b69 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -14,38 +14,79 @@
* limitations under the License.
*/
-#ifndef STATSD_STATSPULLERMANAGER_H
-#define STATSD_STATSPULLERMANAGER_H
+#pragma once
+#include <android/os/IStatsCompanionService.h>
+#include <binder/IServiceManager.h>
+#include <utils/RefBase.h>
#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
+#include <string>
#include <unordered_map>
-#include "external/StatsPuller.h"
+#include <vector>
+#include "PullDataReceiver.h"
+#include "StatsPuller.h"
#include "logd/LogEvent.h"
-#include "matchers/matcher_util.h"
namespace android {
namespace os {
namespace statsd {
-const static int KERNEL_WAKELOCKS = 1;
-
class StatsPullerManager : public virtual RefBase {
public:
- // Enums of pulled data types (pullCodes)
- // These values must be kept in sync with com/android/server/stats/StatsCompanionService.java.
- // TODO: pull the constant from stats_events.proto instead
- const static int KERNEL_WAKELOCKS;
- StatsPullerManager();
+ static StatsPullerManager& GetInstance();
+
+ void RegisterReceiver(int pullCode, sp<PullDataReceiver> receiver, long intervalMs);
+
+ void UnRegisterReceiver(int pullCode, sp<PullDataReceiver> receiver);
// We return a vector of shared_ptr since LogEvent's copy constructor is not available.
- vector<std::shared_ptr<LogEvent>> Pull(const int pullCode);
+ vector<std::shared_ptr<LogEvent>> Pull(const int pullCode, const uint64_t timestampSec);
+
+ // Translate metric name to pullCodes.
+ // return -1 if no valid pullCode is found
+ int GetPullCode(std::string metricName);
+
+ void OnAlarmFired();
private:
- std::unordered_map<int, std::unique_ptr<StatsPuller>> mStatsPullers;
+ StatsPullerManager();
+
+ sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+
+ sp<IStatsCompanionService> get_stats_companion_service();
+
+ std::unordered_map<int, std::unique_ptr<StatsPuller>> mPullers;
+
+
+
+ // internal state of a bucket.
+ typedef struct {
+ // pull_interval_sec : last_pull_time_sec
+ std::pair<uint64_t, uint64_t> timeInfo;
+ sp<PullDataReceiver> receiver;
+ } ReceiverInfo;
+
+ std::map<int, std::vector<ReceiverInfo>> mReceivers;
+
+ Mutex mReceiversLock;
+
+ long mCurrentPullingInterval;
+
+ // for value metrics, it is important for the buckets to be aligned to multiple of smallest
+ // bucket size. All pulled metrics start pulling based on this time, so that they can be
+ // correctly attributed to the correct buckets. Pulled data attach a timestamp which is the
+ // request time.
+ const long mPullStartTimeMs;
+
+ long get_pull_start_time_ms();
+
+ LogEvent parse_pulled_data(String16 data);
+
+ static const std::unordered_map<std::string, int> kPullCodes;
};
} // namespace statsd
} // namespace os
-} // namespace android
-
-#endif // STATSD_STATSPULLERMANAGER_H
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 1a039f6..26a82cb 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#define DEBUG true // STOPSHIP if true
#include "logd/LogEvent.h"
#include <sstream>
@@ -29,7 +30,7 @@
// We need to keep a copy of the android_log_event_list owned by this instance so that the char*
// for strings is not cleared before we can read them.
-LogEvent::LogEvent(log_msg msg) : mList(msg) {
+LogEvent::LogEvent(log_msg& msg) : mList(msg) {
init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &mList);
}
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 9ef20ea..df75d9f 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -42,7 +42,7 @@
/**
* Read a LogEvent from a log_msg.
*/
- explicit LogEvent(log_msg msg);
+ explicit LogEvent(log_msg& msg);
/**
* Constructs a LogEvent with the specified tag and creates an android_log_event_list in write
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 69f336f..9f8558d 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -21,7 +21,6 @@
#include "CountMetricProducer.h"
#include "stats_util.h"
-#include <cutils/log.h>
#include <limits.h>
#include <stdlib.h>
@@ -94,7 +93,7 @@
}
}
-void CountMetricProducer::onSlicedConditionMayChange() {
+void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
}
@@ -128,7 +127,7 @@
// TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped.
}
-void CountMetricProducer::onConditionChanged(const bool conditionMet) {
+void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
mCondition = conditionMet;
}
@@ -136,7 +135,7 @@
void CountMetricProducer::onMatchedLogEventInternal(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const map<string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event) {
+ const LogEvent& event, bool scheduledPull) {
uint64_t eventTimeNs = event.GetTimestampNs();
flushCounterIfNeeded(eventTimeNs);
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index be77e47..80e80d9 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -41,23 +41,26 @@
virtual ~CountMetricProducer();
- void onConditionChanged(const bool conditionMet) override;
+ void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
void finish() override;
StatsLogReport onDumpReport() override;
- void onSlicedConditionMayChange() override;
+ void onSlicedConditionMayChange(const uint64_t eventTime) override;
size_t byteSize() override;
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+ // TODO: Implement this later.
+ virtual void notifyAppRemoved(const string& apk, const int uid) override{};
protected:
void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey,
- bool condition, const LogEvent& event) override;
+ bool condition, const LogEvent& event,
+ bool scheduledPull) override;
private:
const CountMetric mMetric;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index a590bc8..340f503 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -15,11 +15,11 @@
*/
#define DEBUG true
-#include "DurationMetricProducer.h"
+
#include "Log.h"
+#include "DurationMetricProducer.h"
#include "stats_util.h"
-#include <cutils/log.h>
#include <limits.h>
#include <stdlib.h>
@@ -34,13 +34,15 @@
DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
const int conditionIndex, const size_t startIndex,
const size_t stopIndex, const size_t stopAllIndex,
- const sp<ConditionWizard>& wizard)
+ const sp<ConditionWizard>& wizard,
+ const vector<KeyMatcher>& internalDimension)
// TODO: Pass in the start time from MetricsManager, instead of calling time() here.
: MetricProducer(time(nullptr) * NANO_SECONDS_IN_A_SECOND, conditionIndex, wizard),
mMetric(metric),
mStartIndex(startIndex),
mStopIndex(stopIndex),
- mStopAllIndex(stopAllIndex) {
+ mStopAllIndex(stopAllIndex),
+ mInternalDimension(internalDimension) {
// TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
// them in the base class, because the proto generated CountMetric, and DurationMetric are
// not related. Maybe we should add a template in the future??
@@ -67,34 +69,43 @@
VLOG("~DurationMetric() called");
}
+unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
+ vector<DurationBucketInfo>& bucket) {
+ switch (mMetric.type()) {
+ case DurationMetric_AggregationType_DURATION_SUM:
+ return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex,
+ mCurrentBucketStartTimeNs, mBucketSizeNs,
+ bucket);
+ case DurationMetric_AggregationType_DURATION_MAX_SPARSE:
+ return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex,
+ mCurrentBucketStartTimeNs, mBucketSizeNs,
+ bucket);
+ }
+}
+
void DurationMetricProducer::finish() {
// TODO: write the StatsLogReport to dropbox using
// DropboxWriter.
}
-void DurationMetricProducer::onSlicedConditionMayChange() {
+void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
// Now for each of the on-going event, check if the condition has changed for them.
+ flushIfNeeded(eventTime);
for (auto& pair : mCurrentSlicedDuration) {
- VLOG("Metric %lld current %s state: %d", mMetric.metric_id(), pair.first.c_str(),
- pair.second.state);
- if (pair.second.state == kStopped) {
- continue;
- }
- bool conditionMet = mWizard->query(mConditionTrackerIndex, pair.second.conditionKeys) ==
- ConditionState::kTrue;
- VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet);
- noteConditionChanged(pair.first, conditionMet, time(nullptr) * 1000000000);
+ pair.second->onSlicedConditionMayChange(eventTime);
}
}
-void DurationMetricProducer::onConditionChanged(const bool conditionMet) {
+void DurationMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
mCondition = conditionMet;
// TODO: need to populate the condition change time from the event which triggers the condition
// change, instead of using current time.
+
+ flushIfNeeded(eventTime);
for (auto& pair : mCurrentSlicedDuration) {
- noteConditionChanged(pair.first, conditionMet, time(nullptr) * 1000000000);
+ pair.second->onConditionChanged(conditionMet, eventTime);
}
}
@@ -107,7 +118,7 @@
}
for (const auto& bucket : buckets) {
data->add_bucket_info()->CopyFrom(bucket);
- VLOG("\t bucket [%lld - %lld] count: %lld", bucket.start_bucket_nanos(),
+ VLOG("\t bucket [%lld - %lld] duration(ns): %lld", bucket.start_bucket_nanos(),
bucket.end_bucket_nanos(), bucket.duration_nanos());
}
}
@@ -120,7 +131,7 @@
// Dump current bucket if it's stale.
// If current bucket is still on-going, don't force dump current bucket.
// In finish(), We can force dump current bucket.
- flushDurationIfNeeded(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
+ flushIfNeeded(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
report.set_end_report_nanos(mCurrentBucketStartTimeNs);
StatsLogReport_DurationMetricDataWrapper* wrapper = report.mutable_duration_metrics();
@@ -137,216 +148,50 @@
return report;
};
-void DurationMetricProducer::onMatchedLogEventInternal(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const map<string, HashableDimensionKey>& conditionKeys, bool condition,
- const LogEvent& event) {
- flushDurationIfNeeded(event.GetTimestampNs());
-
- if (matcherIndex == mStopAllIndex) {
- noteStopAll(event.GetTimestampNs());
- return;
- }
-
- if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end() && mConditionSliced) {
- // add the durationInfo for the current bucket.
- auto& durationInfo = mCurrentSlicedDuration[eventKey];
- durationInfo.conditionKeys = conditionKeys;
- }
-
- if (matcherIndex == mStartIndex) {
- VLOG("Metric %lld Key: %s Start, Condition %d", mMetric.metric_id(), eventKey.c_str(),
- condition);
- noteStart(eventKey, condition, event.GetTimestampNs());
- } else if (matcherIndex == mStopIndex) {
- VLOG("Metric %lld Key: %s Stop, Condition %d", mMetric.metric_id(), eventKey.c_str(),
- condition);
- noteStop(eventKey, event.GetTimestampNs());
- }
-}
-
-void DurationMetricProducer::noteConditionChanged(const HashableDimensionKey& key,
- const bool conditionMet,
- const uint64_t eventTime) {
- flushDurationIfNeeded(eventTime);
-
- auto it = mCurrentSlicedDuration.find(key);
- if (it == mCurrentSlicedDuration.end()) {
- return;
- }
-
- switch (it->second.state) {
- case kStarted:
- // if condition becomes false, kStarted -> kPaused. Record the current duration.
- if (!conditionMet) {
- it->second.state = DurationState::kPaused;
- it->second.lastDuration =
- updateDuration(it->second.lastDuration,
- eventTime - it->second.lastStartTime, mMetric.type());
- VLOG("Metric %lld Key: %s Paused because condition is false ", mMetric.metric_id(),
- key.c_str());
- }
- break;
- case kStopped:
- // nothing to do if it's stopped.
- break;
- case kPaused:
- // if condition becomes true, kPaused -> kStarted. and the start time is the condition
- // change time.
- if (conditionMet) {
- it->second.state = DurationState::kStarted;
- it->second.lastStartTime = eventTime;
- VLOG("Metric %lld Key: %s Paused->Started", mMetric.metric_id(), key.c_str());
- }
- break;
- }
-}
-
-void DurationMetricProducer::noteStart(const HashableDimensionKey& key, const bool conditionMet,
- const uint64_t eventTime) {
- // this will add an empty bucket for this key if it didn't exist before.
- DurationInfo& duration = mCurrentSlicedDuration[key];
-
- switch (duration.state) {
- case kStarted:
- // It's safe to do nothing here. even if condition is not true, it means we are about
- // to receive the condition change event.
- break;
- case kPaused:
- // Safe to do nothing here. kPaused is waiting for the condition change.
- break;
- case kStopped:
- if (!conditionMet) {
- // event started, but we need to wait for the condition to become true.
- duration.state = DurationState::kPaused;
- break;
- }
- duration.state = DurationState::kStarted;
- duration.lastStartTime = eventTime;
- break;
- }
-}
-
-void DurationMetricProducer::noteStop(const HashableDimensionKey& key, const uint64_t eventTime) {
- if (mCurrentSlicedDuration.find(key) == mCurrentSlicedDuration.end()) {
- // we didn't see a start event before. do nothing.
- return;
- }
- DurationInfo& duration = mCurrentSlicedDuration[key];
-
- switch (duration.state) {
- case DurationState::kStopped:
- // already stopped, do nothing.
- break;
- case DurationState::kStarted: {
- duration.state = DurationState::kStopped;
- int64_t durationTime = eventTime - duration.lastStartTime;
- VLOG("Metric %lld, key %s, Stop %lld %lld %lld", mMetric.metric_id(), key.c_str(),
- (long long)duration.lastStartTime, (long long)eventTime, (long long)durationTime);
- duration.lastDuration =
- updateDuration(duration.lastDuration, durationTime, mMetric.type());
- VLOG(" record duration: %lld ", (long long)duration.lastDuration);
- break;
- }
- case DurationState::kPaused: {
- duration.state = DurationState::kStopped;
- break;
- }
- }
-}
-
-int64_t DurationMetricProducer::updateDuration(const int64_t lastDuration,
- const int64_t durationTime,
- const DurationMetric_AggregationType type) {
- int64_t result = lastDuration;
- switch (type) {
- case DurationMetric_AggregationType_DURATION_SUM:
- result += durationTime;
- break;
- case DurationMetric_AggregationType_DURATION_MAX_SPARSE:
- if (lastDuration < durationTime) {
- result = durationTime;
- }
- break;
- case DurationMetric_AggregationType_DURATION_MIN_SPARSE:
- if (lastDuration > durationTime) {
- result = durationTime;
- }
- break;
- }
- return result;
-}
-
-void DurationMetricProducer::noteStopAll(const uint64_t eventTime) {
- for (auto& duration : mCurrentSlicedDuration) {
- noteStop(duration.first, eventTime);
- }
-}
-
-// When a new matched event comes in, we check if event falls into the current
-// bucket. If not, flush the old counter to past buckets and initialize the current buckt.
-void DurationMetricProducer::flushDurationIfNeeded(const uint64_t eventTime) {
+void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) {
if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
return;
}
- // adjust the bucket start time
- int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
-
- DurationBucketInfo info;
- uint64_t endTime = mCurrentBucketStartTimeNs + mBucketSizeNs;
- info.set_start_bucket_nanos(mCurrentBucketStartTimeNs);
- info.set_end_bucket_nanos(endTime);
-
- uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
- mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
- VLOG("Metric %lld: new bucket start time: %lld", mMetric.metric_id(),
- (long long)mCurrentBucketStartTimeNs);
-
+ VLOG("flushing...........");
for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end(); ++it) {
- int64_t finalDuration = it->second.lastDuration;
- if (it->second.state == kStarted) {
- // the event is still on-going, duration needs to be updated.
- int64_t durationTime = endTime - it->second.lastStartTime;
- finalDuration = updateDuration(it->second.lastDuration, durationTime, mMetric.type());
- }
-
- VLOG(" final duration for last bucket: %lld", (long long)finalDuration);
-
- // Don't record empty bucket.
- if (finalDuration != 0) {
- info.set_duration_nanos(finalDuration);
- // it will auto create new vector of CountbucketInfo if the key is not found.
- auto& bucketList = mPastBuckets[it->first];
- bucketList.push_back(info);
- }
-
- // if the event is still on-going, add the buckets between previous bucket and now. Because
- // the event has been going on across all the buckets in between.
- // |prev_bucket|...|..|...|now_bucket|
- if (it->second.state == kStarted) {
- for (int i = 1; i < numBucketsForward; i++) {
- DurationBucketInfo info;
- info.set_start_bucket_nanos(oldBucketStartTimeNs + mBucketSizeNs * i);
- info.set_end_bucket_nanos(endTime + mBucketSizeNs * i);
- info.set_duration_nanos(mBucketSizeNs);
- auto& bucketList = mPastBuckets[it->first];
- bucketList.push_back(info);
- VLOG(" add filling bucket with duration %lld", (long long)mBucketSizeNs);
- }
- }
-
- if (it->second.state == DurationState::kStopped) {
- // No need to keep buckets for events that were stopped before. If the event starts
- // again, we will add it back.
+ if (it->second->flushIfNeeded(eventTime)) {
+ VLOG("erase bucket for key %s", it->first.c_str());
mCurrentSlicedDuration.erase(it);
- } else {
- // for kPaused, and kStarted event, we will keep the buckets, and reset the start time
- // and duration.
- it->second.lastStartTime = mCurrentBucketStartTimeNs;
- it->second.lastDuration = 0;
}
}
+
+ int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+ mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
+}
+
+void DurationMetricProducer::onMatchedLogEventInternal(
+ const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const map<string, HashableDimensionKey>& conditionKeys, bool condition,
+ const LogEvent& event, bool scheduledPull) {
+ flushIfNeeded(event.GetTimestampNs());
+
+ if (matcherIndex == mStopAllIndex) {
+ for (auto& pair : mCurrentSlicedDuration) {
+ pair.second->noteStopAll(event.GetTimestampNs());
+ }
+ return;
+ }
+
+ HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
+
+ if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
+ mCurrentSlicedDuration[eventKey] = createDurationTracker(mPastBuckets[eventKey]);
+ }
+
+ auto it = mCurrentSlicedDuration.find(eventKey);
+
+ if (matcherIndex == mStartIndex) {
+ it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
+
+ } else if (matcherIndex == mStopIndex) {
+ it->second->noteStop(atomKey, event.GetTimestampNs());
+ }
}
size_t DurationMetricProducer::byteSize() {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 8820403..febf25d 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -22,6 +22,9 @@
#include "../condition/ConditionTracker.h"
#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
+#include "duration_helper/DurationTracker.h"
+#include "duration_helper/MaxDurationTracker.h"
+#include "duration_helper/OringDurationTracker.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
@@ -32,52 +35,35 @@
namespace os {
namespace statsd {
-enum DurationState {
- kStopped = 0, // The event is stopped.
- kStarted = 1, // The event is on going.
- kPaused = 2, // The event is started, but condition is false, clock is paused. When condition
- // turns to true, kPaused will become kStarted.
-};
-
-// Hold duration information for current on-going bucket.
-struct DurationInfo {
- DurationState state;
- // most recent start time.
- int64_t lastStartTime;
- // existing duration in current bucket. Eventually, the duration will be aggregated in
- // the way specified in AggregateType (Sum, Max, or Min).
- int64_t lastDuration;
- // cache the HashableDimensionKeys we need to query the condition for this duration event.
- std::map<string, HashableDimensionKey> conditionKeys;
-
- DurationInfo() : state(kStopped), lastStartTime(0), lastDuration(0){};
-};
-
class DurationMetricProducer : public MetricProducer {
public:
DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex,
const size_t startIndex, const size_t stopIndex,
- const size_t stopAllIndex, const sp<ConditionWizard>& wizard);
+ const size_t stopAllIndex, const sp<ConditionWizard>& wizard,
+ const vector<KeyMatcher>& internalDimension);
virtual ~DurationMetricProducer();
- void onConditionChanged(const bool conditionMet) override;
+ void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
void finish() override;
StatsLogReport onDumpReport() override;
- void onSlicedConditionMayChange() override;
+ void onSlicedConditionMayChange(const uint64_t eventTime) override;
size_t byteSize() override;
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+ // TODO: Implement this later.
+ virtual void notifyAppRemoved(const string& apk, const int uid) override{};
protected:
void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKeys,
- bool condition, const LogEvent& event) override;
+ bool condition, const LogEvent& event,
+ bool scheduledPull) override;
private:
const DurationMetric mMetric;
@@ -91,26 +77,21 @@
// Index of the SimpleLogEntryMatcher which defines the stop all for all dimensions.
const size_t mStopAllIndex;
+ // The dimension from the atom predicate. e.g., uid, wakelock name.
+ const vector<KeyMatcher> mInternalDimension;
+
// Save the past buckets and we can clear when the StatsLogReport is dumped.
std::unordered_map<HashableDimensionKey, std::vector<DurationBucketInfo>> mPastBuckets;
// The current bucket.
- std::unordered_map<HashableDimensionKey, DurationInfo> mCurrentSlicedDuration;
+ std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
+ mCurrentSlicedDuration;
void flushDurationIfNeeded(const uint64_t newEventTime);
- void noteStart(const HashableDimensionKey& key, const bool conditionMet,
- const uint64_t eventTime);
+ std::unique_ptr<DurationTracker> createDurationTracker(std::vector<DurationBucketInfo>& bucket);
- void noteStop(const HashableDimensionKey& key, const uint64_t eventTime);
-
- void noteStopAll(const uint64_t eventTime);
-
- static int64_t updateDuration(const int64_t lastDuration, const int64_t durationTime,
- const DurationMetric_AggregationType type);
-
- void noteConditionChanged(const HashableDimensionKey& key, const bool conditionMet,
- const uint64_t eventTime);
+ void flushIfNeeded(uint64_t timestamp);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 7e06105..d714179 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -20,7 +20,6 @@
#include "EventMetricProducer.h"
#include "stats_util.h"
-#include <cutils/log.h>
#include <limits.h>
#include <stdlib.h>
@@ -78,7 +77,7 @@
void EventMetricProducer::finish() {
}
-void EventMetricProducer::onSlicedConditionMayChange() {
+void EventMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
}
StatsLogReport EventMetricProducer::onDumpReport() {
@@ -105,7 +104,7 @@
return StatsLogReport();
}
-void EventMetricProducer::onConditionChanged(const bool conditionMet) {
+void EventMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
mCondition = conditionMet;
}
@@ -113,7 +112,7 @@
void EventMetricProducer::onMatchedLogEventInternal(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event) {
+ const LogEvent& event, bool scheduledPull) {
if (!condition) {
return;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 14fa31c..7dd0e38 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -41,20 +41,22 @@
void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey,
- bool condition, const LogEvent& event) override;
+ bool condition, const LogEvent& event, bool scheduledPull) override;
- void onConditionChanged(const bool conditionMet) override;
+ void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
void finish() override;
StatsLogReport onDumpReport() override;
- void onSlicedConditionMayChange() override;
+ void onSlicedConditionMayChange(const uint64_t eventTime) override;
size_t byteSize() override;
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+ // TODO: Implement this later.
+ virtual void notifyAppRemoved(const string& apk, const int uid) override{};
private:
const EventMetric mMetric;
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 3c8ce6e..535f4a2 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -21,7 +21,8 @@
using std::map;
-void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
+void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event,
+ bool scheduledPull) {
uint64_t eventTimeNs = event.GetTimestampNs();
// this is old event, maybe statsd restarted?
if (eventTimeNs < mStartTimeNs) {
@@ -59,7 +60,8 @@
condition = mCondition;
}
- onMatchedLogEventInternal(matcherIndex, eventKey, conditionKeys, condition, event);
+ onMatchedLogEventInternal(matcherIndex, eventKey, conditionKeys, condition, event,
+ scheduledPull);
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 80eb527..3b117ec 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -48,11 +48,11 @@
virtual ~MetricProducer(){};
// Consume the parsed stats log entry that already matched the "what" of the metric.
- void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event);
+ void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event, bool scheduledPull);
- virtual void onConditionChanged(const bool condition) = 0;
+ virtual void onConditionChanged(const bool condition, const uint64_t eventTime) = 0;
- virtual void onSlicedConditionMayChange() = 0;
+ virtual void onSlicedConditionMayChange(const uint64_t eventTime) = 0;
// This is called when the metric collecting is done, e.g., when there is a new configuration
// coming. MetricProducer should do the clean up, and dump existing data to dropbox.
@@ -107,7 +107,7 @@
virtual void onMatchedLogEventInternal(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event) = 0;
+ const LogEvent& event, bool scheduledPull) = 0;
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 4fa3965..521fcc3 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -74,6 +74,7 @@
}
int tagId = event.GetTagId();
+ uint64_t eventTime = event.GetTimestampNs();
if (mTagIds.find(tagId) == mTagIds.end()) {
// not interesting...
return;
@@ -124,13 +125,14 @@
// metric cares about non sliced condition, and it's changed.
// Push the new condition to it directly.
if (!mAllMetricProducers[metricIndex]->isConditionSliced() && changedCache[i]) {
- mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i]);
+ mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
+ eventTime);
// metric cares about sliced conditions, and it may have changed. Send
// notification, and the metric can query the sliced conditions that are
// interesting to it.
} else if (mAllMetricProducers[metricIndex]->isConditionSliced() &&
slicedChangedCache[i]) {
- mAllMetricProducers[metricIndex]->onSlicedConditionMayChange();
+ mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(eventTime);
}
}
}
@@ -143,7 +145,8 @@
if (pair != mTrackerToMetricMap.end()) {
auto& metricList = pair->second;
for (const int metricIndex : metricList) {
- mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event);
+ // pushed metrics are never scheduled pulls
+ mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event, false);
}
}
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
new file mode 100644
index 0000000..cb6166d
--- /dev/null
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "ValueMetricProducer.h"
+
+#include <cutils/log.h>
+#include <limits.h>
+#include <stdlib.h>
+
+using std::map;
+using std::unordered_map;
+using std::list;
+using std::make_shared;
+using std::shared_ptr;
+using std::unique_ptr;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
+ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard)
+ : MetricProducer((time(nullptr) / 600 * 600 * NANO_SECONDS_IN_A_SECOND), conditionIndex,
+ wizard),
+ mMetric(metric),
+ mPullCode(mStatsPullerManager.GetPullCode(mMetric.what())) {
+ // TODO: valuemetric for pushed events may need unlimited bucket length
+ mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
+
+ mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
+
+ if (metric.links().size() > 0) {
+ mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
+ metric.links().end());
+ mConditionSliced = true;
+ }
+
+ if (!metric.has_condition() && mPullCode != -1) {
+ mStatsPullerManager.RegisterReceiver(mPullCode, this, metric.bucket().bucket_size_millis());
+ }
+
+ VLOG("value metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ (long long)mBucketSizeNs, (long long)mStartTimeNs);
+}
+
+ValueMetricProducer::~ValueMetricProducer() {
+ VLOG("~ValueMetricProducer() called");
+}
+
+void ValueMetricProducer::finish() {
+ // TODO: write the StatsLogReport to dropbox using
+ // DropboxWriter.
+}
+
+static void addSlicedCounterToReport(StatsLogReport_ValueMetricDataWrapper& wrapper,
+ const vector<KeyValuePair>& key,
+ const vector<ValueBucketInfo>& buckets) {
+ ValueMetricData* data = wrapper.add_data();
+ for (const auto& kv : key) {
+ data->add_dimension()->CopyFrom(kv);
+ }
+ for (const auto& bucket : buckets) {
+ data->add_bucket_info()->CopyFrom(bucket);
+ VLOG("\t bucket [%lld - %lld] value: %lld", bucket.start_bucket_nanos(),
+ bucket.end_bucket_nanos(), bucket.value());
+ }
+}
+
+void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
+ VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+}
+
+StatsLogReport ValueMetricProducer::onDumpReport() {
+ VLOG("metric %lld dump report now...", mMetric.metric_id());
+
+ StatsLogReport report;
+ report.set_metric_id(mMetric.metric_id());
+ report.set_start_report_nanos(mStartTimeNs);
+
+ // Dump current bucket if it's stale.
+ // If current bucket is still on-going, don't force dump current bucket.
+ // In finish(), We can force dump current bucket.
+ // flush_if_needed(time(nullptr) * NANO_SECONDS_IN_A_SECOND);
+ report.set_end_report_nanos(mCurrentBucketStartTimeNs);
+
+ StatsLogReport_ValueMetricDataWrapper* wrapper = report.mutable_value_metrics();
+
+ for (const auto& pair : mPastBuckets) {
+ const HashableDimensionKey& hashableKey = pair.first;
+ auto it = mDimensionKeyMap.find(hashableKey);
+ if (it == mDimensionKeyMap.end()) {
+ ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
+ continue;
+ }
+
+ VLOG(" dimension key %s", hashableKey.c_str());
+ addSlicedCounterToReport(*wrapper, it->second, pair.second);
+ }
+ return report;
+ // TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped.
+}
+
+void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_t eventTime) {
+ mCondition = condition;
+
+ if (mPullCode != -1) {
+ vector<shared_ptr<LogEvent>> allData = mStatsPullerManager.Pull(mPullCode, eventTime);
+ if (mCondition == true) {
+ mStatsPullerManager.RegisterReceiver(mPullCode, this,
+ mMetric.bucket().bucket_size_millis());
+ } else if (mCondition == ConditionState::kFalse) {
+ mStatsPullerManager.UnRegisterReceiver(mPullCode, this);
+ }
+ if (allData.size() == 0) {
+ return;
+ }
+ AutoMutex _l(mLock);
+ if (allData.size() == 0) {
+ return;
+ }
+ for (const auto& data : allData) {
+ onMatchedLogEvent(0, *data, false);
+ }
+ flush_if_needed(eventTime);
+ }
+ return;
+}
+
+void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
+ if (mCondition == ConditionState::kTrue || !mMetric.has_condition()) {
+ AutoMutex _l(mLock);
+ if (allData.size() == 0) {
+ return;
+ }
+ uint64_t eventTime = allData.at(0)->GetTimestampNs();
+ for (const auto& data : allData) {
+ onMatchedLogEvent(0, *data, true);
+ }
+ flush_if_needed(eventTime);
+ }
+}
+
+void ValueMetricProducer::onMatchedLogEventInternal(
+ const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const map<string, HashableDimensionKey>& conditionKey, bool condition,
+ const LogEvent& event, bool scheduledPull) {
+ uint64_t eventTimeNs = event.GetTimestampNs();
+ if (eventTimeNs < mCurrentBucketStartTimeNs) {
+ VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
+ (long long)mCurrentBucketStartTimeNs);
+ return;
+ }
+
+ Interval& interval = mCurrentSlicedBucket[eventKey];
+
+ long value = get_value(event);
+
+ if (scheduledPull) {
+ if (interval.raw.size() > 0) {
+ interval.raw.back().second = value;
+ } else {
+ interval.raw.push_back(std::make_pair(value, value));
+ }
+ mNextSlicedBucket[eventKey].raw[0].first = value;
+ } else {
+ if (mCondition == ConditionState::kTrue) {
+ interval.raw.push_back(std::make_pair(value, 0));
+ } else {
+ if (interval.raw.size() != 0) {
+ interval.raw.back().second = value;
+ }
+ }
+ }
+ if (mPullCode == -1) {
+ flush_if_needed(eventTimeNs);
+ }
+}
+
+long ValueMetricProducer::get_value(const LogEvent& event) {
+ status_t err = NO_ERROR;
+ long val = event.GetLong(mMetric.value_field(), &err);
+ if (err == NO_ERROR) {
+ return val;
+ } else {
+ VLOG("Can't find value in message.");
+ return 0;
+ }
+}
+
+void ValueMetricProducer::flush_if_needed(const uint64_t eventTimeNs) {
+ if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
+ VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
+ (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
+ return;
+ }
+
+ VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
+ (int)mCurrentSlicedBucket.size());
+ ValueBucketInfo info;
+ info.set_start_bucket_nanos(mCurrentBucketStartTimeNs);
+ info.set_end_bucket_nanos(mCurrentBucketStartTimeNs + mBucketSizeNs);
+
+ for (const auto& slice : mCurrentSlicedBucket) {
+ long value = 0;
+ for (const auto& pair : slice.second.raw) {
+ value += pair.second - pair.first;
+ }
+ info.set_value(value);
+ VLOG(" %s, %ld", slice.first.c_str(), value);
+ // it will auto create new vector of ValuebucketInfo if the key is not found.
+ auto& bucketList = mPastBuckets[slice.first];
+ bucketList.push_back(info);
+ }
+
+ // Reset counters
+ mCurrentSlicedBucket.swap(mNextSlicedBucket);
+ mNextSlicedBucket.clear();
+ int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+ if (numBucketsForward >1) {
+ VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
+ }
+ mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
+ VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
+ (long long)mCurrentBucketStartTimeNs);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
new file mode 100644
index 0000000..4f17913
--- /dev/null
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#include <utils/threads.h>
+#include <list>
+#include "../condition/ConditionTracker.h"
+#include "../external/PullDataReceiver.h"
+#include "../external/StatsPullerManager.h"
+#include "CountAnomalyTracker.h"
+#include "MetricProducer.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
+public:
+ ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard);
+
+ virtual ~ValueMetricProducer();
+
+ void onConditionChanged(const bool condition, const uint64_t eventTime) override;
+
+ void finish() override;
+
+ StatsLogReport onDumpReport() override;
+
+ void onSlicedConditionMayChange(const uint64_t eventTime);
+
+ void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
+ // TODO: Implement this later.
+ size_t byteSize() override{return 0;};
+
+ // TODO: Implement this later.
+ virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+ // TODO: Implement this later.
+ virtual void notifyAppRemoved(const string& apk, const int uid) override{};
+
+protected:
+ void onMatchedLogEventInternal(const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const std::map<std::string, HashableDimensionKey>& conditionKey,
+ bool condition, const LogEvent& event,
+ bool scheduledPull) override;
+
+private:
+ const ValueMetric mMetric;
+
+ StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
+
+ Mutex mLock;
+
+ const int mPullCode;
+
+ // internal state of a bucket.
+ typedef struct {
+ std::vector<std::pair<long, long>> raw;
+ } Interval;
+
+ std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket;
+ // If condition is true and pulling on schedule, the previous bucket value needs to be carried
+ // over to the next bucket.
+ std::unordered_map<HashableDimensionKey, Interval> mNextSlicedBucket;
+
+ // Save the past buckets and we can clear when the StatsLogReport is dumped.
+ std::unordered_map<HashableDimensionKey, std::vector<ValueBucketInfo>> mPastBuckets;
+
+ long get_value(const LogEvent& event);
+
+ void flush_if_needed(const uint64_t eventTimeNs);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
new file mode 100644
index 0000000..0d0d9a4
--- /dev/null
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef DURATION_TRACKER_H
+#define DURATION_TRACKER_H
+
+#include "condition/ConditionWizard.h"
+#include "stats_util.h"
+
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+enum DurationState {
+ kStopped = 0, // The event is stopped.
+ kStarted = 1, // The event is on going.
+ kPaused = 2, // The event is started, but condition is false, clock is paused. When condition
+ // turns to true, kPaused will become kStarted.
+};
+
+// Hold duration information for one atom level duration in current on-going bucket.
+struct DurationInfo {
+ DurationState state;
+ // most recent start time.
+ int64_t lastStartTime;
+ // existing duration in current bucket.
+ int64_t lastDuration;
+ // TODO: Optimize the way we track sliced condition in duration metrics.
+ // cache the HashableDimensionKeys we need to query the condition for this duration event.
+ ConditionKey conditionKeys;
+
+ DurationInfo() : state(kStopped), lastStartTime(0), lastDuration(0){};
+};
+
+class DurationTracker {
+public:
+ DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, uint64_t currentBucketStartNs,
+ uint64_t bucketSizeNs, std::vector<DurationBucketInfo>& bucket)
+ : mWizard(wizard),
+ mConditionTrackerIndex(conditionIndex),
+ mCurrentBucketStartTimeNs(currentBucketStartNs),
+ mBucketSizeNs(bucketSizeNs),
+ mBucket(bucket),
+ mDuration(0){};
+ virtual ~DurationTracker(){};
+ virtual void noteStart(const HashableDimensionKey& key, bool condition,
+ const uint64_t eventTime, const ConditionKey& conditionKey) = 0;
+ virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) = 0;
+ virtual void noteStopAll(const uint64_t eventTime) = 0;
+ virtual void onSlicedConditionMayChange(const uint64_t timestamp) = 0;
+ virtual void onConditionChanged(bool condition, const uint64_t timestamp) = 0;
+ // Flush stale buckets if needed, and return true if the tracker has no on-going duration
+ // events, so that the owner can safely remove the tracker.
+ virtual bool flushIfNeeded(uint64_t timestampNs) = 0;
+
+protected:
+ sp<ConditionWizard> mWizard;
+
+ int mConditionTrackerIndex;
+
+ uint64_t mCurrentBucketStartTimeNs;
+
+ int64_t mBucketSizeNs;
+
+ std::vector<DurationBucketInfo>& mBucket; // where to write output
+
+ int64_t mDuration; // current recorded duration result
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // DURATION_TRACKER_H
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
new file mode 100644
index 0000000..856ca9d
--- /dev/null
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define DEBUG true
+
+#include "Log.h"
+#include "MaxDurationTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ std::vector<DurationBucketInfo>& bucket)
+ : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket) {
+}
+
+void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
+ const uint64_t eventTime, const ConditionKey& conditionKey) {
+ // this will construct a new DurationInfo if this key didn't exist.
+ DurationInfo& duration = mInfos[key];
+ duration.conditionKeys = conditionKey;
+ VLOG("MaxDuration: key %s start condition %d", key.c_str(), condition);
+
+ switch (duration.state) {
+ case kStarted:
+ // The same event is already started. Because we are not counting nesting, so ignore.
+ break;
+ case kPaused:
+ // Safe to do nothing here. Paused means started but condition is false.
+ break;
+ case kStopped:
+ if (!condition) {
+ // event started, but we need to wait for the condition to become true.
+ duration.state = DurationState::kPaused;
+ } else {
+ duration.state = DurationState::kStarted;
+ duration.lastStartTime = eventTime;
+ }
+ break;
+ }
+}
+
+void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime) {
+ VLOG("MaxDuration: key %s stop", key.c_str());
+ if (mInfos.find(key) == mInfos.end()) {
+ // we didn't see a start event before. do nothing.
+ return;
+ }
+ DurationInfo& duration = mInfos[key];
+
+ switch (duration.state) {
+ case DurationState::kStopped:
+ // already stopped, do nothing.
+ break;
+ case DurationState::kStarted: {
+ duration.state = DurationState::kStopped;
+ int64_t durationTime = eventTime - duration.lastStartTime;
+ VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(), (long long)duration.lastStartTime,
+ (long long)eventTime, (long long)durationTime);
+ duration.lastDuration = duration.lastDuration + durationTime;
+ VLOG(" record duration: %lld ", (long long)duration.lastDuration);
+ break;
+ }
+ case DurationState::kPaused: {
+ duration.state = DurationState::kStopped;
+ break;
+ }
+ }
+
+ if (duration.lastDuration > mDuration) {
+ mDuration = duration.lastDuration;
+ VLOG("Max: new max duration: %lld", (long long)mDuration);
+ }
+ // Once an atom duration ends, we erase it. Next time, if we see another atom event with the
+ // same name, they are still considered as different atom durations.
+ mInfos.erase(key);
+}
+void MaxDurationTracker::noteStopAll(const uint64_t eventTime) {
+ for (auto& pair : mInfos) {
+ noteStop(pair.first, eventTime);
+ }
+}
+
+bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) {
+ if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
+ return false;
+ }
+
+ VLOG("MaxDurationTracker flushing.....");
+
+ // adjust the bucket start time
+ int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+
+ DurationBucketInfo info;
+ uint64_t endTime = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ info.set_start_bucket_nanos(mCurrentBucketStartTimeNs);
+ info.set_end_bucket_nanos(endTime);
+
+ uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
+ mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
+
+ bool hasOnGoingStartedEvent = false; // a kStarted event last across bucket boundaries.
+ bool hasPendingEvent =
+ false; // has either a kStarted or kPaused event across bucket boundaries
+ // meaning we need to carry them over to the new bucket.
+ for (auto it = mInfos.begin(); it != mInfos.end(); ++it) {
+ int64_t finalDuration = it->second.lastDuration;
+ if (it->second.state == kStarted) {
+ // the event is still on-going, duration needs to be updated.
+ // |..lastDurationTime_recorded...last_start -----|bucket_end. We need to record the
+ // duration between lastStartTime and bucketEnd.
+ int64_t durationTime = endTime - it->second.lastStartTime;
+
+ finalDuration += durationTime;
+ VLOG(" unrecorded %lld -> %lld", (long long)(durationTime), (long long)finalDuration);
+ // if the event is still on-going, we need to fill the buckets between prev_bucket and
+ // now_bucket. |prev_bucket|...|..|...|now_bucket|
+ hasOnGoingStartedEvent = true;
+ }
+
+ if (finalDuration > mDuration) {
+ mDuration = finalDuration;
+ }
+
+ if (it->second.state == DurationState::kStopped) {
+ // No need to keep buckets for events that were stopped before.
+ mInfos.erase(it);
+ } else {
+ hasPendingEvent = true;
+ // for kPaused, and kStarted event, we will keep track of them, and reset the start time
+ // and duration.
+ it->second.lastStartTime = mCurrentBucketStartTimeNs;
+ it->second.lastDuration = 0;
+ }
+ }
+
+ if (mDuration != 0) {
+ info.set_duration_nanos(mDuration);
+ mBucket.push_back(info);
+ VLOG(" final duration for last bucket: %lld", (long long)mDuration);
+ }
+
+ mDuration = 0;
+ if (hasOnGoingStartedEvent) {
+ for (int i = 1; i < numBucketsForward; i++) {
+ DurationBucketInfo info;
+ info.set_start_bucket_nanos(oldBucketStartTimeNs + mBucketSizeNs * i);
+ info.set_end_bucket_nanos(endTime + mBucketSizeNs * i);
+ info.set_duration_nanos(mBucketSizeNs);
+ mBucket.push_back(info);
+ VLOG(" filling gap bucket with duration %lld", (long long)mBucketSizeNs);
+ }
+ }
+ // If this tracker has no pending events, tell owner to remove.
+ return !hasPendingEvent;
+}
+
+void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
+ // VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+ // Now for each of the on-going event, check if the condition has changed for them.
+ for (auto& pair : mInfos) {
+ if (pair.second.state == kStopped) {
+ continue;
+ }
+ bool conditionMet = mWizard->query(mConditionTrackerIndex, pair.second.conditionKeys) ==
+ ConditionState::kTrue;
+ VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet);
+ noteConditionChanged(pair.first, conditionMet, timestamp);
+ }
+}
+
+void MaxDurationTracker::onConditionChanged(bool condition, const uint64_t timestamp) {
+ for (auto& pair : mInfos) {
+ noteConditionChanged(pair.first, condition, timestamp);
+ }
+}
+
+void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
+ const uint64_t timestamp) {
+ auto it = mInfos.find(key);
+ if (it == mInfos.end()) {
+ return;
+ }
+
+ switch (it->second.state) {
+ case kStarted:
+ // if condition becomes false, kStarted -> kPaused. Record the current duration.
+ if (!conditionMet) {
+ it->second.state = DurationState::kPaused;
+ it->second.lastDuration += (timestamp - it->second.lastStartTime);
+
+ VLOG("MaxDurationTracker Key: %s Started->Paused ", key.c_str());
+ }
+ break;
+ case kStopped:
+ // nothing to do if it's stopped.
+ break;
+ case kPaused:
+ // if condition becomes true, kPaused -> kStarted. and the start time is the condition
+ // change time.
+ if (conditionMet) {
+ it->second.state = DurationState::kStarted;
+ it->second.lastStartTime = timestamp;
+ VLOG("MaxDurationTracker Key: %s Paused->Started", key.c_str());
+ }
+ break;
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
new file mode 100644
index 0000000..c74d070
--- /dev/null
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef MAX_DURATION_TRACKER_H
+#define MAX_DURATION_TRACKER_H
+
+#include "DurationTracker.h"
+
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Tracks a pool of atom durations, and output the max duration for each bucket.
+// To get max duration, we need to keep track of each individual durations, and compare them when
+// they stop or bucket expires.
+class MaxDurationTracker : public DurationTracker {
+public:
+ MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ std::vector<DurationBucketInfo>& bucket);
+ void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
+ const ConditionKey& conditionKey) override;
+ void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override;
+ void noteStopAll(const uint64_t eventTime) override;
+ bool flushIfNeeded(uint64_t timestampNs) override;
+ void onSlicedConditionMayChange(const uint64_t timestamp) override;
+ void onConditionChanged(bool condition, const uint64_t timestamp) override;
+
+private:
+ std::map<HashableDimensionKey, DurationInfo> mInfos;
+
+ void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
+ const uint64_t timestamp);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // MAX_DURATION_TRACKER_H
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
new file mode 100644
index 0000000..e045fb4
--- /dev/null
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#define DEBUG true
+#include "Log.h"
+#include "OringDurationTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+OringDurationTracker::OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ std::vector<DurationBucketInfo>& bucket)
+ : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket),
+ mStarted(),
+ mPaused() {
+ mLastStartTime = 0;
+}
+
+void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
+ const uint64_t eventTime, const ConditionKey& conditionKey) {
+ if (condition) {
+ if (mStarted.size() == 0) {
+ mLastStartTime = eventTime;
+ VLOG("record first start....");
+ }
+ mStarted.insert(key);
+ } else {
+ mPaused.insert(key);
+ }
+
+ if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
+ mConditionKeyMap[key] = conditionKey;
+ }
+
+ VLOG("Oring: %s start, condition %d", key.c_str(), condition);
+}
+
+void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp) {
+ VLOG("Oring: %s stop", key.c_str());
+ auto it = mStarted.find(key);
+ if (it != mStarted.end()) {
+ mStarted.erase(it);
+ if (mStarted.empty()) {
+ mDuration += (timestamp - mLastStartTime);
+ VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
+ (long long)mDuration);
+ }
+ }
+
+ mPaused.erase(key);
+ mConditionKeyMap.erase(key);
+}
+void OringDurationTracker::noteStopAll(const uint64_t timestamp) {
+ if (!mStarted.empty()) {
+ mDuration += (timestamp - mLastStartTime);
+ VLOG("Oring Stop all: record duration %lld %lld ", (long long)timestamp - mLastStartTime,
+ (long long)mDuration);
+ }
+
+ mStarted.clear();
+ mPaused.clear();
+ mConditionKeyMap.clear();
+}
+
+bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) {
+ if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
+ return false;
+ }
+ VLOG("OringDurationTracker Flushing.............");
+ // adjust the bucket start time
+ int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+ DurationBucketInfo info;
+ uint64_t endTime = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ info.set_start_bucket_nanos(mCurrentBucketStartTimeNs);
+ info.set_end_bucket_nanos(endTime);
+
+ uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
+ mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
+
+ if (mStarted.size() > 0) {
+ mDuration += (endTime - mLastStartTime);
+ }
+ if (mDuration != 0) {
+ info.set_duration_nanos(mDuration);
+ // it will auto create new vector of CountbucketInfo if the key is not found.
+ mBucket.push_back(info);
+ VLOG(" duration: %lld", (long long)mDuration);
+ }
+
+ if (mStarted.size() > 0) {
+ for (int i = 1; i < numBucketsForward; i++) {
+ DurationBucketInfo info;
+ info.set_start_bucket_nanos(oldBucketStartTimeNs + mBucketSizeNs * i);
+ info.set_end_bucket_nanos(endTime + mBucketSizeNs * i);
+ info.set_duration_nanos(mBucketSizeNs);
+ mBucket.push_back(info);
+ VLOG(" add filling bucket with duration %lld", (long long)mBucketSizeNs);
+ }
+ }
+ mLastStartTime = mCurrentBucketStartTimeNs;
+ mDuration = 0;
+
+ // if all stopped, then tell owner it's safe to remove this tracker.
+ return mStarted.empty() && mPaused.empty();
+}
+
+void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
+ vector<HashableDimensionKey> startedToPaused;
+ vector<HashableDimensionKey> pausedToStarted;
+ if (!mStarted.empty()) {
+ for (auto it = mStarted.begin(); it != mStarted.end();) {
+ auto key = *it;
+ if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
+ VLOG("Key %s dont have condition key", key.c_str());
+ ++it;
+ continue;
+ }
+ if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) !=
+ ConditionState::kTrue) {
+ it = mStarted.erase(it);
+ startedToPaused.push_back(key);
+ VLOG("Key %s started -> paused", key.c_str());
+ } else {
+ ++it;
+ }
+ }
+
+ if (mStarted.empty()) {
+ mDuration += (timestamp - mLastStartTime);
+ VLOG("Duration add %lld , to %lld ", (long long)(timestamp - mLastStartTime),
+ (long long)mDuration);
+ }
+ }
+
+ if (!mPaused.empty()) {
+ for (auto it = mPaused.begin(); it != mPaused.end();) {
+ auto key = *it;
+ if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
+ VLOG("Key %s dont have condition key", key.c_str());
+ ++it;
+ continue;
+ }
+ if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) ==
+ ConditionState::kTrue) {
+ it = mPaused.erase(it);
+ pausedToStarted.push_back(key);
+ VLOG("Key %s paused -> started", key.c_str());
+ } else {
+ ++it;
+ }
+ }
+
+ if (mStarted.empty() && pausedToStarted.size() > 0) {
+ mLastStartTime = timestamp;
+ }
+ }
+
+ mStarted.insert(pausedToStarted.begin(), pausedToStarted.end());
+ mPaused.insert(startedToPaused.begin(), startedToPaused.end());
+}
+
+void OringDurationTracker::onConditionChanged(bool condition, const uint64_t timestamp) {
+ if (condition) {
+ if (!mPaused.empty()) {
+ VLOG("Condition true, all started");
+ if (mStarted.empty()) {
+ mLastStartTime = timestamp;
+ }
+ mStarted.insert(mPaused.begin(), mPaused.end());
+ }
+ } else {
+ if (!mStarted.empty()) {
+ VLOG("Condition false, all paused");
+ mDuration += (timestamp - mLastStartTime);
+ mPaused.insert(mStarted.begin(), mStarted.end());
+ }
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
new file mode 100644
index 0000000..5425251
--- /dev/null
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ORING_DURATION_TRACKER_H
+#define ORING_DURATION_TRACKER_H
+
+#include "DurationTracker.h"
+
+#include <set>
+namespace android {
+namespace os {
+namespace statsd {
+
+// Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
+class OringDurationTracker : public DurationTracker {
+public:
+ OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ std::vector<DurationBucketInfo>& bucket);
+ void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
+ const ConditionKey& conditionKey) override;
+ void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override;
+ void noteStopAll(const uint64_t eventTime) override;
+ void onSlicedConditionMayChange(const uint64_t timestamp) override;
+ void onConditionChanged(bool condition, const uint64_t timestamp) override;
+ bool flushIfNeeded(uint64_t timestampNs) override;
+
+private:
+ // We don't need to keep track of individual durations. The information that's needed is:
+ // 1) which keys are started. We record the first start time.
+ // 2) which keys are paused (started but condition was false)
+ // 3) whenever a key stops, we remove it from the started set. And if the set becomes empty,
+ // it means everything has stopped, we then record the end time.
+ std::set<HashableDimensionKey> mStarted;
+ std::set<HashableDimensionKey> mPaused;
+ int64_t mLastStartTime;
+ std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // ORING_DURATION_TRACKER_H
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index e90f998..3d4036e 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -16,11 +16,13 @@
#include "../condition/CombinationConditionTracker.h"
#include "../condition/SimpleConditionTracker.h"
+#include "../external/StatsPullerManager.h"
#include "../matchers/CombinationLogMatchingTracker.h"
#include "../matchers/SimpleLogMatchingTracker.h"
#include "CountMetricProducer.h"
#include "DurationMetricProducer.h"
#include "EventMetricProducer.h"
+#include "ValueMetricProducer.h"
#include "stats_util.h"
using std::set;
@@ -33,6 +35,8 @@
namespace statsd {
bool handleMetricWithLogTrackers(const string what, const int metricIndex,
+ const bool usedForDimension,
+ const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
const unordered_map<string, int>& logTrackerMap,
unordered_map<int, std::vector<int>>& trackerToMetricMap,
int& logTrackerIndex) {
@@ -41,6 +45,12 @@
ALOGW("cannot find the LogEntryMatcher %s in config", what.c_str());
return false;
}
+ if (usedForDimension && allLogEntryMatchers[logTrackerIt->second]->getTagIds().size() > 1) {
+ ALOGE("LogEntryMatcher %s has more than one tag ids. When a metric has dimension, the "
+ "\"what\" can only about one atom type.",
+ what.c_str());
+ return false;
+ }
logTrackerIndex = logTrackerIt->second;
auto& metric_list = trackerToMetricMap[logTrackerIndex];
metric_list.push_back(metricIndex);
@@ -68,8 +78,7 @@
}
allConditionTrackers[condition_it->second]->setSliced(true);
allConditionTrackers[it->second]->setSliced(true);
- allConditionTrackers[it->second]->addDimensions(
- vector<KeyMatcher>(link.key_in_condition().begin(), link.key_in_condition().end()));
+ // TODO: We need to verify the link is valid.
}
conditionIndex = condition_it->second;
@@ -176,6 +185,7 @@
bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
const unordered_map<string, int>& conditionTrackerMap,
+ const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
vector<sp<ConditionTracker>>& allConditionTrackers,
vector<sp<MetricProducer>>& allMetricProducers,
unordered_map<int, std::vector<int>>& conditionToMetricMap,
@@ -184,6 +194,7 @@
const int allMetricsCount =
config.count_metric_size() + config.duration_metric_size() + config.event_metric_size();
allMetricProducers.reserve(allMetricsCount);
+ StatsPullerManager& statsPullerManager = StatsPullerManager::GetInstance();
// Build MetricProducers for each metric defined in config.
// (1) build CountMetricProducer
@@ -196,8 +207,9 @@
int metricIndex = allMetricProducers.size();
int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex, logTrackerMap,
- trackerToMetricMap, trackerIndex)) {
+ if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
+ allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
+ trackerIndex)) {
return false;
}
@@ -215,26 +227,49 @@
for (int i = 0; i < config.duration_metric_size(); i++) {
int metricIndex = allMetricProducers.size();
const DurationMetric& metric = config.duration_metric(i);
+
+ auto what_it = conditionTrackerMap.find(metric.what());
+ if (what_it == conditionTrackerMap.end()) {
+ ALOGE("DurationMetric's \"what\" is invalid");
+ return false;
+ }
+
+ const Condition& durationWhat = config.condition(what_it->second);
+
+ if (durationWhat.contents_case() != Condition::ContentsCase::kSimpleCondition) {
+ ALOGE("DurationMetric's \"what\" must be a simple condition");
+ return false;
+ }
+
+ const auto& simpleCondition = durationWhat.simple_condition();
+
int trackerIndices[3] = {-1, -1, -1};
- if (!metric.has_start() ||
- !handleMetricWithLogTrackers(metric.start(), metricIndex, logTrackerMap,
- trackerToMetricMap, trackerIndices[0])) {
+ if (!simpleCondition.has_start() ||
+ !handleMetricWithLogTrackers(simpleCondition.start(), metricIndex,
+ metric.dimension_size() > 0, allLogEntryMatchers,
+ logTrackerMap, trackerToMetricMap, trackerIndices[0])) {
ALOGE("Duration metrics must specify a valid the start event matcher");
return false;
}
- if (metric.has_stop() &&
- !handleMetricWithLogTrackers(metric.stop(), metricIndex, logTrackerMap,
- trackerToMetricMap, trackerIndices[1])) {
+ if (simpleCondition.has_stop() &&
+ !handleMetricWithLogTrackers(simpleCondition.stop(), metricIndex,
+ metric.dimension_size() > 0, allLogEntryMatchers,
+ logTrackerMap, trackerToMetricMap, trackerIndices[1])) {
return false;
}
- if (metric.has_stop_all() &&
- !handleMetricWithLogTrackers(metric.stop_all(), metricIndex, logTrackerMap,
- trackerToMetricMap, trackerIndices[2])) {
+ if (simpleCondition.has_stop_all() &&
+ !handleMetricWithLogTrackers(simpleCondition.stop_all(), metricIndex,
+ metric.dimension_size() > 0, allLogEntryMatchers,
+ logTrackerMap, trackerToMetricMap, trackerIndices[2])) {
return false;
}
+ vector<KeyMatcher> internalDimension;
+ internalDimension.insert(internalDimension.begin(), simpleCondition.dimension().begin(),
+ simpleCondition.dimension().end());
+
int conditionIndex = -1;
if (metric.has_predicate()) {
@@ -243,9 +278,9 @@
conditionToMetricMap);
}
- sp<MetricProducer> durationMetric =
- new DurationMetricProducer(metric, conditionIndex, trackerIndices[0],
- trackerIndices[1], trackerIndices[2], wizard);
+ sp<MetricProducer> durationMetric = new DurationMetricProducer(
+ metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
+ wizard, internalDimension);
allMetricProducers.push_back(durationMetric);
}
@@ -258,8 +293,8 @@
return false;
}
int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex, logTrackerMap,
- trackerToMetricMap, trackerIndex)) {
+ if (!handleMetricWithLogTrackers(metric.what(), metricIndex, false, allLogEntryMatchers,
+ logTrackerMap, trackerToMetricMap, trackerIndex)) {
return false;
}
@@ -275,6 +310,34 @@
allMetricProducers.push_back(eventMetric);
}
+ // value metrics
+ for (int i = 0; i < config.value_metric_size(); i++) {
+ const ValueMetric& metric = config.value_metric(i);
+ if (!metric.has_what()) {
+ ALOGW("cannot find what in ValueMetric %lld", metric.metric_id());
+ return false;
+ }
+
+ int pullCode = statsPullerManager.GetPullCode(metric.what());
+ if (pullCode == -1) {
+ ALOGW("cannot find %s in pulled metrics", metric.what().c_str());
+ return false;
+ }
+
+ sp<MetricProducer> valueProducer;
+ auto condition_it = conditionTrackerMap.find(metric.condition());
+ if (condition_it == conditionTrackerMap.end()) {
+ ALOGW("cannot find the Condition %s in the config", metric.condition().c_str());
+ return false;
+ }
+ int metricIndex = allMetricProducers.size();
+ valueProducer = new ValueMetricProducer(metric, condition_it->second, wizard);
+ // will create new vector if not exist before.
+ auto& metricList = conditionToMetricMap[condition_it->second];
+ metricList.push_back(metricIndex);
+
+ allMetricProducers.push_back(valueProducer);
+ }
return true;
}
@@ -299,8 +362,9 @@
return false;
}
- if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
- allMetricProducers, conditionToMetricMap, trackerToMetricMap)) {
+ if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
+ allConditionTrackers, allMetricProducers, conditionToMetricMap,
+ trackerToMetricMap)) {
ALOGE("initMetricProducers failed");
return false;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 6722eb3..e089d065 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -21,6 +21,7 @@
#include <vector>
#include "../condition/ConditionTracker.h"
+#include "../external/StatsPullerManager.h"
#include "../matchers/LogMatchingTracker.h"
namespace android {
@@ -75,6 +76,7 @@
const StatsdConfig& config, const std::unordered_map<std::string, int>& logTrackerMap,
const std::unordered_map<std::string, int>& conditionTrackerMap,
const std::unordered_map<int, std::vector<EventConditionLink>>& eventConditionLinks,
+ const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
vector<sp<ConditionTracker>>& allConditionTrackers,
std::vector<sp<MetricProducer>>& allMetricProducers,
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
diff --git a/cmds/statsd/src/packages/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h
index 8b948de..13e776f 100644
--- a/cmds/statsd/src/packages/PackageInfoListener.h
+++ b/cmds/statsd/src/packages/PackageInfoListener.h
@@ -29,6 +29,9 @@
// Uid map will notify this listener that the app with apk name and uid has been upgraded to
// the specified version.
virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int version) = 0;
+
+ // Notify interested listeners that the given apk and uid combination no longer exits.
+ virtual void notifyAppRemoved(const std::string& apk, const int uid) = 0;
};
} // namespace statsd
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index f4621ee..d83c3a4 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -52,25 +52,35 @@
void UidMap::updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
const vector<String16>& packageName) {
+ updateMap(time(nullptr) * 1000000000, uid, versionCode, packageName);
+}
+
+void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
+ const vector<int32_t>& versionCode, const vector<String16>& packageName) {
lock_guard<mutex> lock(mMutex); // Exclusively lock for updates.
mMap.clear();
- for (unsigned long j = 0; j < uid.size(); j++) {
+ for (size_t j = 0; j < uid.size(); j++) {
mMap.insert(make_pair(uid[j],
AppData(string(String8(packageName[j]).string()), versionCode[j])));
}
- if (mOutput.initial_size() == 0) { // Provide the initial states in the mOutput proto
- for (unsigned long j = 0; j < uid.size(); j++) {
- auto t = mOutput.add_initial();
- t->set_app(string(String8(packageName[j]).string()));
- t->set_version(int(versionCode[j]));
- t->set_uid(uid[j]);
- }
+ auto snapshot = mOutput.add_snapshots();
+ snapshot->set_timestamp_nanos(timestamp);
+ for (size_t j = 0; j < uid.size(); j++) {
+ auto t = snapshot->add_package_info();
+ t->set_name(string(String8(packageName[j]).string()));
+ t->set_version(int(versionCode[j]));
+ t->set_uid(uid[j]);
}
}
void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode) {
+ updateApp(time(nullptr) * 1000000000, app_16, uid, versionCode);
+}
+
+void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
+ const int32_t& versionCode) {
lock_guard<mutex> lock(mMutex);
string app = string(String8(app_16).string());
@@ -82,7 +92,7 @@
auto log = mOutput.add_changes();
log->set_deletion(false);
- // log.timestamp = TODO: choose how timestamps are computed
+ log->set_timestamp_nanos(timestamp);
log->set_app(app);
log->set_uid(uid);
log->set_version(versionCode);
@@ -102,13 +112,20 @@
}
void UidMap::removeApp(const String16& app_16, const int32_t& uid) {
+ removeApp(time(nullptr) * 1000000000, app_16, uid);
+}
+void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) {
lock_guard<mutex> lock(mMutex);
string app = string(String8(app_16).string());
+ for (auto it : mSubscribers) {
+ it->notifyAppRemoved(app, uid);
+ }
+
auto log = mOutput.add_changes();
log->set_deletion(true);
- // log.timestamp = TODO: choose how timestamps are computed
+ log->set_timestamp_nanos(timestamp);
log->set_app(app);
log->set_uid(uid);
@@ -133,21 +150,67 @@
mSubscribers.erase(producer);
}
-UidMapping UidMap::getAndClearOutput() {
- lock_guard<mutex> lock(mMutex); // Lock for updates
-
- auto ret = UidMapping(mOutput); // Copy that will be returned.
+void UidMap::clearOutput() {
mOutput.Clear();
// Re-initialize the initial state for the outputs. This results in extra data being uploaded
- // but helps ensure we can't re-construct the UID->app name, versionCode mapping in server.
+ // but helps ensure we can re-construct the UID->app name, versionCode mapping in server.
+ auto snapshot = mOutput.add_snapshots();
for (auto it : mMap) {
- auto t = mOutput.add_initial();
- t->set_app(it.second.packageName);
+ auto t = snapshot->add_package_info();
+ t->set_name(it.second.packageName);
t->set_version(it.second.versionCode);
t->set_uid(it.first);
}
+}
+int64_t UidMap::getMinimumTimestampNs() {
+ int64_t m = 0;
+ for (auto it : mLastUpdatePerConfigKey) {
+ if (m == 0) {
+ m = it.second;
+ } else if (it.second < m) {
+ m = it.second;
+ }
+ }
+ return m;
+}
+
+UidMapping UidMap::getOutput(const ConfigKey& key) {
+ return getOutput(time(nullptr) * 1000000000, key);
+}
+
+UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) {
+ lock_guard<mutex> lock(mMutex); // Lock for updates
+
+ auto ret = UidMapping(mOutput); // Copy that will be returned.
+ int64_t prevMin = getMinimumTimestampNs();
+ mLastUpdatePerConfigKey[key] = timestamp;
+ int64_t newMin = getMinimumTimestampNs();
+
+ if (newMin > prevMin) {
+ int64_t cutoff_nanos = newMin;
+ auto snapshots = mOutput.mutable_snapshots();
+ auto it_snapshots = snapshots->cbegin();
+ while (it_snapshots != snapshots->cend()) {
+ if (it_snapshots->timestamp_nanos() < cutoff_nanos) {
+ // it_snapshots now points to the following element.
+ it_snapshots = snapshots->erase(it_snapshots);
+ } else {
+ ++it_snapshots;
+ }
+ }
+ auto deltas = mOutput.mutable_changes();
+ auto it_deltas = deltas->cbegin();
+ while (it_deltas != deltas->cend()) {
+ if (it_deltas->timestamp_nanos() < cutoff_nanos) {
+ // it_deltas now points to the following element.
+ it_deltas = deltas->erase(it_deltas);
+ } else {
+ ++it_deltas;
+ }
+ }
+ }
return ret;
}
@@ -160,6 +223,14 @@
}
}
+void UidMap::OnConfigUpdated(const ConfigKey& key) {
+ mLastUpdatePerConfigKey[key] = -1;
+}
+
+void UidMap::OnConfigRemoved(const ConfigKey& key) {
+ mLastUpdatePerConfigKey.erase(key);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index d550372..bf120e0 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -17,11 +17,14 @@
#ifndef STATSD_UIDMAP_H
#define STATSD_UIDMAP_H
+#include "config/ConfigKey.h"
+#include "config/ConfigListener.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "packages/PackageInfoListener.h"
#include <binder/IResultReceiver.h>
#include <binder/IShellCallback.h>
+#include <gtest/gtest_prod.h>
#include <log/logprint.h>
#include <stdio.h>
#include <utils/RefBase.h>
@@ -51,17 +54,19 @@
* All three inputs must be the same size, and the jth element in each array refers to the same
* tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
*/
+ // TODO: Add safeguards to call clearOutput if there's too much data already stored.
void updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
const vector<String16>& packageName);
+ // TODO: Add safeguards to call clearOutput if there's too much data already stored.
+ void updateApp(const String16& packageName, const int32_t& uid, const int32_t& versionCode);
+ void removeApp(const String16& packageName, const int32_t& uid);
+
// Returns true if the given uid contains the specified app (eg. com.google.android.gms).
bool hasApp(int uid, const string& packageName) const;
int getAppVersion(int uid, const string& packageName) const;
- void updateApp(const String16& packageName, const int32_t& uid, const int32_t& versionCode);
- void removeApp(const String16& packageName, const int32_t& uid);
-
// Helper for debugging contents of this uid map. Can be triggered with:
// adb shell cmd stats print-uid-map
void printUidMap(FILE* out);
@@ -73,13 +78,36 @@
// Remove the listener from the set of metric producers that subscribe to updates.
void removeListener(sp<PackageInfoListener> producer);
- // Grabs the current output contents and then clears it.
- UidMapping getAndClearOutput();
+ // Informs uid map that a config is added/updated. Used for keeping mConfigKeys up to date.
+ void OnConfigUpdated(const ConfigKey& key);
+
+ // Informs uid map that a config is removed. Used for keeping mConfigKeys up to date.
+ void OnConfigRemoved(const ConfigKey& key);
+
+ // Gets the output. If every config key has received the output, then the output is cleared.
+ UidMapping getOutput(const ConfigKey& key);
+
+ // Forces the output to be cleared. We still generate a snapshot based on the current state.
+ // This results in extra data uploaded but helps us reconstruct the uid mapping on the server
+ // in case we lose a previous upload.
+ void clearOutput();
private:
+ void updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
+ const vector<int32_t>& versionCode, const vector<String16>& packageName);
+
+ // TODO: Add safeguards to call clearOutput if there's too much data already stored.
+ void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid,
+ const int32_t& versionCode);
+ void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid);
+
+ UidMapping getOutput(const int64_t& timestamp, const ConfigKey& key);
+
// TODO: Use shared_mutex for improved read-locking if a library can be found in Android.
mutable mutex mMutex;
+ // Maps uid to application data. This must be multimap since there is a feature in Android for
+ // multiple apps to share the same uid.
std::unordered_multimap<int, AppData> mMap;
// We prepare the output proto as apps are updated, so that we can grab the current output.
@@ -87,6 +115,17 @@
// Metric producers that should be notified if there's an upgrade in any app.
set<sp<PackageInfoListener>> mSubscribers;
+
+ // Mapping of config keys we're aware of to the epoch time they last received an update. This
+ // lets us know it's safe to delete events older than the oldest update. The value is nanosec.
+ // Value of -1 denotes this config key has never received an upload.
+ std::unordered_map<ConfigKey, int64_t> mLastUpdatePerConfigKey;
+
+ // Returns the minimum value from mConfigKeys.
+ int64_t getMinimumTimestampNs();
+
+ // Allows unit-test to access private methods.
+ FRIEND_TEST(UidMapTest, TestClearingOutput);
};
} // namespace statsd
diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/stats_events.proto
index 74ee332..51244c6 100644
--- a/cmds/statsd/src/stats_events.proto
+++ b/cmds/statsd/src/stats_events.proto
@@ -35,7 +35,8 @@
* in the format defined here and in stats_log.proto.
*/
message StatsEvent {
- oneof event {
+ // Pushed events start at 2.
+ oneof pushed {
// For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
BleScanStateChanged ble_scan_state_changed = 2;
BleUnoptimizedScanStateChanged ble_unoptimized_scan_state_changed = 3;
@@ -69,9 +70,19 @@
WifiSignalStrengthChanged wifi_signal_strength_changed = 38;
WifiScanStateChanged wifi_scan_state_changed = 39;
PhoneSignalStrengthChanged phone_signal_strength_changed = 40;
-
+ SettingChanged setting_changed = 41;
+ ActivityForegroundStateChanged activity_foreground_state_changed = 42;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
+
+ // Pulled events will start at field 1000.
+ oneof pulled {
+ WifiBytesTransferred wifi_bytes_transferred = 1000;
+ WifiBytesTransferredByFgBg wifi_bytes_transferred_by_fg_bg = 1001;
+ MobileBytesTransferred mobile_bytes_transferred = 1002;
+ MobileBytesTransferredByFgBg mobile_bytes_transferred_by_fg_bg = 1003;
+ KernelWakelocksReported kernel_wakelocks_reported = 1004;
+ }
}
/**
@@ -479,7 +490,7 @@
* Logs battery level (percent full, from 0 to 100).
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message BatteryLevelChanged {
// Battery level. Should be in [0, 100].
@@ -490,7 +501,7 @@
* Logs change in charging status of the device.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message ChargingStateChanged {
// TODO: Link directly to BatteryManager.java's constants (via a proto).
@@ -508,7 +519,7 @@
* Logs whether the device is plugged in, and what power source it is using.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message PluggedStateChanged {
// TODO: Link directly to BatteryManager.java's constants (via a proto).
@@ -529,7 +540,7 @@
* Logs the temperature of the device, in tenths of a degree Celsius.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message DeviceTemperatureReported {
// Temperature in tenths of a degree C.
@@ -542,7 +553,7 @@
* Logs when the device turns off or on.
*
* Logged from:
- * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
*/
message DeviceOnStatusChanged {
enum State {
@@ -556,7 +567,7 @@
* Logs when an app's wakeup alarm fires.
*
* Logged from:
- * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
*/
message WakeupAlarmOccurred {
// TODO: Add attribution instead of uid?
@@ -581,7 +592,7 @@
* Logs wifi locks held by an app.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message WifiLockStateChanged {
// TODO: Add attribution instead of uid.
@@ -598,7 +609,7 @@
* Logs wifi signal strength changes.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message WifiSignalStrengthChanged {
// TODO: Reference the actual telephony/java/android/telephony/SignalStrength.java states.
@@ -616,7 +627,7 @@
* Logs wifi scans performed by an app.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message WifiScanStateChanged {
// TODO: Add attribution instead of uid.
@@ -633,7 +644,7 @@
* Logs phone signal strength changes.
*
* Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message PhoneSignalStrengthChanged {
// TODO: Reference the actual telephony/java/android/telephony/SignalStrength.java states.
@@ -645,4 +656,150 @@
SIGNAL_STRENGTH_GREAT = 4;
}
optional SignalStrength signal_strength = 1;
-}
\ No newline at end of file
+}
+
+/**
+ * Logs that a setting was updated.
+ * Logged from:
+ * frameworks/base/core/java/android/provider/Settings.java
+ * The tag and is_default allow resetting of settings to default values based on the specified
+ * tag. See Settings#putString(ContentResolver, String, String, String, boolean) for more details.
+ */
+message SettingChanged {
+ // The name of the setting.
+ optional string setting = 1;
+
+ // The change being imposed on this setting. May represent a number, eg "3".
+ optional string value = 2;
+
+ // The new value of this setting. For most settings, this is same as value. For some settings,
+ // value is +X or -X where X represents an element in a set. For example, if the previous value
+ // is A,B,C and value is -B, then new_value is A,C and prev_value is A,B,C.
+ // The +/- feature is currently only used for location_providers_allowed.
+ optional string new_value = 3;
+
+ // The previous value of this setting.
+ optional string prev_value = 4;
+
+ // The tag used with the is_default for resetting sets of settings. This is generally null.
+ optional string tag = 5;
+
+ // 1 indicates that this setting with tag should be resettable.
+ optional int32 is_default = 6;
+
+ // The user ID associated. Defined in android/os/UserHandle.java
+ optional int32 user = 7;
+}
+
+
+/*
+ * Logs activity going to foreground or background
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
+ */
+message ActivityForegroundStateChanged {
+ enum Activity {
+ MOVE_TO_BACKGROUND = 0;
+ MOVE_TO_FOREGROUND = 1;
+ }
+ optional int32 uid = 1;
+ optional string pkg_name = 2;
+ optional string class_name = 3;
+ optional Activity activity = 4;
+}
+
+/**
+ * Pulls bytes transferred via wifi (Sum of foreground and background usage).
+ *
+ * Pulled from:
+ * StatsCompanionService (using BatteryStats to get which interfaces are wifi)
+ */
+message WifiBytesTransferred {
+ optional int32 uid = 1;
+
+ optional int64 rx_bytes = 2;
+
+ optional int64 rx_packets = 3;
+
+ optional int64 tx_bytes = 4;
+
+ optional int64 tx_packets = 5;
+}
+
+/**
+ * Pulls bytes transferred via wifi (separated by foreground and background usage).
+ *
+ * Pulled from:
+ * StatsCompanionService (using BatteryStats to get which interfaces are wifi)
+ */
+message WifiBytesTransferredByFgBg {
+ optional int32 uid = 1;
+
+ // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
+ optional int32 is_foreground = 2;
+
+ optional int64 rx_bytes = 3;
+
+ optional int64 rx_packets = 4;
+
+ optional int64 tx_bytes = 5;
+
+ optional int64 tx_packets = 6;
+}
+
+/**
+ * Pulls bytes transferred via mobile networks (Sum of foreground and background usage).
+ *
+ * Pulled from:
+ * StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
+ */
+message MobileBytesTransferred {
+ optional int32 uid = 1;
+
+ optional int64 rx_bytes = 2;
+
+ optional int64 rx_packets = 3;
+
+ optional int64 tx_bytes = 4;
+
+ optional int64 tx_packets = 5;
+}
+
+/**
+ * Pulls bytes transferred via mobile networks (separated by foreground and background usage).
+ *
+ * Pulled from:
+ * StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
+ */
+message MobileBytesTransferredByFgBg {
+ optional int32 uid = 1;
+
+ // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
+ optional int32 is_foreground = 2;
+
+ optional int64 rx_bytes = 3;
+
+ optional int64 rx_packets = 4;
+
+ optional int64 tx_bytes = 5;
+
+ optional int64 tx_packets = 6;
+}
+
+/**
+ * Pulls the kernel wakelock durations. This atom is adapted from
+ * android/internal/os/KernelWakelockStats.java
+ *
+ * Pulled from:
+ * StatsCompanionService using KernelWakelockReader.
+ */
+message KernelWakelocksReported {
+ optional string name = 1;
+
+ optional int32 count = 2;
+
+ optional int32 version = 3;
+
+ optional int64 time = 4;
+}
diff --git a/cmds/statsd/src/stats_events_copy.proto b/cmds/statsd/src/stats_events_copy.proto
index 5e8ef24..9470372 100644
--- a/cmds/statsd/src/stats_events_copy.proto
+++ b/cmds/statsd/src/stats_events_copy.proto
@@ -40,9 +40,28 @@
*/
message StatsEvent {
oneof event {
- ScreenStateChanged screen_state_changed = 1;
- ProcessStateChanged process_state_changed = 2;
- WakeLockChanged wakelock_changed = 3;
+ // For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
+ BleScanStateChanged ble_scan_state_changed = 2;
+ BleUnoptimizedScanStateChanged ble_unoptimized_scan_state_changed = 3;
+ BleScanResultReceived ble_scan_result_received = 4;
+ SensorStateChanged sensor_state_changed = 5;
+ GpsScanStateChanged gps_scan_state_changed = 6; // TODO: untested
+ SyncStateChanged sync_state_changed = 7;
+ ScheduledJobStateChanged scheduled_job_state_changed = 8;
+ ScreenBrightnessChanged screen_brightness_changed = 9;
+ // 10-20 are temporarily reserved for wakelocks etc.
+ UidWakelockStateChanged uid_wakelock_state_changed = 11;
+ LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 12;
+ BatterySaverModeStateChanged battery_saver_mode_state_changed = 21;
+ DeviceIdleModeStateChanged device_idle_mode_state_changed = 22;
+ AudioStateChanged audio_state_changed = 23;
+ MediaCodecActivityChanged media_codec_activity_changed = 24;
+ CameraStateChanged camera_state_changed = 25;
+ FlashlightStateChanged flashlight_state_changed = 26;
+ UidProcessStateChanged uid_process_state_changed = 27;
+ ProcessLifeCycleStateChanged process_life_cycle_state_changed = 28;
+ ScreenStateChanged screen_state_changed = 29;
+ // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
}
@@ -76,7 +95,7 @@
* and those UIDs will be translated in xxx to those strings.
*
* CONVENTIONS:
- * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange
+ * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange.
* - If there is a UID, it goes first. Think in an object-oriented fashion.
* *****************************************************************************
*/
@@ -102,33 +121,347 @@
}
/**
- * Logs that the state of a process state, as per the activity manager has changed.
+ * Logs that the state of a process state, as per the activity manager, has changed.
*
* Logged from:
* frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
*/
-message ProcessStateChanged {
- // TODO: Use the real (mapped) process states.
+message UidProcessStateChanged {
optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation
// The state.
+ // TODO: Use the real (mapped) process states.
optional int32 state = 2;
}
/**
- * Logs that the state of a wakelock has changed.
+ * Logs that a process started, finished, crashed, or ANRed.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+ */
+message ProcessLifeCycleStateChanged {
+ // TODO: Use the real (mapped) process states.
+ optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation
+
+ // TODO: What is this?
+ optional string name = 2;
+
+ // The state.
+ // TODO: Use an enum.
+ optional int32 event = 3;
+}
+
+
+
+/**
+ * Logs when the ble scan state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message BleScanStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs when an unoptimized ble scan state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
+message BleUnoptimizedScanStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs reporting of a ble scan finding results.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
+message BleScanResultReceived {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ // Number of ble scan results returned.
+ optional int32 num_of_results = 2;
+}
+
+/**
+ * Logs when a sensor state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message SensorStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ // TODO: Is there a way to get the actual name of the sensor?
+ // The id (int) of the sensor.
+ optional int32 sensor_id = 2;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 3;
+}
+
+
+/**
+ * Logs when GPS state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message GpsScanStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+
+/**
+ * Logs when a sync manager sync state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message SyncStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ // Name of the sync (as named in the app)
+ optional string name = 2;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 3;
+}
+
+/**
+ * Logs when a job scheduler job state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message ScheduledJobStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ // Name of the job (as named in the app)
+ optional string name = 2;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 3;
+
+ // TODO: Consider adding the stopReason (int)
+}
+
+/**
+ * Logs when the audio state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message AudioStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs when the video codec state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message MediaCodecActivityChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs when the flashlight state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message FlashlightStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs when the camera state changes.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message CameraStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 2;
+}
+
+/**
+ * Logs that the state of a wakelock (per app and per wakelock name) has changed.
*
* Logged from:
* TODO
*/
-message WakeLockChanged {
+message WakelockChanged {
// TODO: Add attribution instead of uid.
optional int32 uid = 1;
+ // Type of wakelock.
+ enum Type {
+ PARTIAL = 0;
+ FULL = 1;
+ WINDOW = 2;
+ }
+ optional int32 type = 2;
+
+ // The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
+ optional string tag = 3;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 4;
+}
+
+/**
+ * Logs when an app is holding a wakelock, regardless of the wakelock's name.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message UidWakelockStateChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ // Type of wakelock.
+ enum Type {
+ PARTIAL = 0;
+ FULL = 1;
+ WINDOW = 2;
+ }
+ optional int32 type = 2;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 3;
+}
+
+/**
+ * Logs when a partial wakelock is considered 'long' (over 1 min).
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message LongPartialWakelockStateChanged {
+ // TODO: Add attribution instead of uid?
+ optional int32 uid = 1;
+
// The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
optional string tag = 2;
- // TODO: Use a constant instead of boolean?
- optional bool state = 3;
+ // TODO: I have no idea what this is.
+ optional string history_tag = 3;
+
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 4;
}
+/**
+ * Logs Battery Saver state change.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message BatterySaverModeStateChanged {
+ enum State {
+ OFF = 0;
+ ON = 1;
+ }
+ optional State state = 1;
+}
+
+/**
+ * Logs Doze mode state change.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+ */
+message DeviceIdleModeStateChanged {
+ // TODO: Use the enum matching BatteryStats.DEVICE_IDLE_MODE_.
+ optional int32 state = 1;
+}
+
+/**
+ * Logs screen brightness level.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+ */
+message ScreenBrightnessChanged {
+ // Screen brightness level. Should be in [-1, 255] according to PowerManager.java.
+ optional int32 level = 1;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index ec91509..1e37ff8 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -98,15 +98,19 @@
}
message UidMapping {
- message AppInfo {
- optional string app = 1;
+ message PackageInfoSnapshot {
+ message PackageInfo {
+ optional string name = 1;
- optional int32 version = 2;
+ optional int32 version = 2;
- optional int32 uid = 3;
+ optional int32 uid = 3;
+ }
+ optional int64 timestamp_nanos = 1;
+
+ repeated PackageInfo package_info = 2;
}
-
- repeated AppInfo initial = 1;
+ repeated PackageInfoSnapshot snapshots = 1;
message Change {
optional bool deletion = 1;
@@ -139,9 +143,11 @@
message ValueMetricDataWrapper {
repeated ValueMetricData data = 1;
}
+
message GaugeMetricDataWrapper {
repeated GaugeMetricData data = 1;
}
+
oneof data {
EventMetricDataWrapper event_metrics = 4;
CountMetricDataWrapper count_metrics = 5;
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index 39c1d59..a428752 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -40,6 +40,8 @@
typedef std::string HashableDimensionKey;
+typedef std::map<std::string, HashableDimensionKey> ConditionKey;
+
EventMetricData parse(log_msg msg);
int getTagId(log_msg msg);
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index a4d2421..87884b33 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -82,6 +82,8 @@
optional bool count_nesting = 3 [default = true];
optional string stop_all = 4;
+
+ repeated KeyMatcher dimension = 5;
}
message Condition {
@@ -148,29 +150,24 @@
message DurationMetric {
optional int64 metric_id = 1;
- optional string start = 2;
-
- optional string stop = 3;
-
- optional string stop_all = 4;
+ optional string what = 2;
enum AggregationType {
DURATION_SUM = 1;
DURATION_MAX_SPARSE = 2;
- DURATION_MIN_SPARSE = 3;
}
- optional AggregationType type = 5;
+ optional AggregationType type = 3;
- optional string predicate = 6;
+ optional string predicate = 4;
- repeated KeyMatcher dimension = 7;
+ repeated KeyMatcher dimension = 5;
- optional Bucket bucket = 8;
+ optional Bucket bucket = 6;
- repeated Alert alerts = 9;
+ repeated Alert alerts = 7;
- repeated EventConditionLink links = 10;
+ repeated EventConditionLink links = 8;
}
@@ -210,14 +207,7 @@
repeated EventConditionLink links = 8;
enum Operation {
- SUM_DIFF = 1;
- MIN_DIFF = 2;
- MAX_DIFF = 3;
- SUM = 4;
- MIN = 5;
- MAX = 6;
- FIRST = 7;
- LAST = 8;
+ SUM = 1;
}
optional Operation operation = 9 [default = SUM];
}
diff --git a/cmds/statsd/tests/MaxDurationTracker_test.cpp b/cmds/statsd/tests/MaxDurationTracker_test.cpp
new file mode 100644
index 0000000..ae8bf42
--- /dev/null
+++ b/cmds/statsd/tests/MaxDurationTracker_test.cpp
@@ -0,0 +1,115 @@
+// Copyright (C) 2017 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.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "src/condition/ConditionWizard.h"
+#include "src/metrics/duration_helper/MaxDurationTracker.h"
+
+#include <stdio.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+using namespace android::os::statsd;
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+class MockConditionWizard : public ConditionWizard {
+public:
+ MOCK_METHOD2(
+ query,
+ ConditionState(const int conditionIndex,
+ const std::map<std::string, HashableDimensionKey>& conditionParameters));
+};
+
+TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ vector<DurationBucketInfo> buckets;
+ ConditionKey key1;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ tracker.noteStart("", true, bucketStartTimeNs, key1);
+ tracker.noteStop("", bucketStartTimeNs + 10);
+
+ tracker.noteStart("", true, bucketStartTimeNs + 20, key1);
+ tracker.noteStop("", bucketStartTimeNs + 40);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(20, buckets[0].duration_nanos());
+}
+
+TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ vector<DurationBucketInfo> buckets;
+ ConditionKey key1;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
+ tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
+
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ((long long)(bucketSizeNs - 1), buckets[0].duration_nanos());
+ EXPECT_EQ((long long)bucketSizeNs, buckets[1].duration_nanos());
+}
+
+TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ EXPECT_CALL(*wizard, query(_, key1)) // #4
+ .WillOnce(Return(ConditionState::kFalse));
+
+ vector<DurationBucketInfo> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ int64_t durationTimeNs = 2 * 1000;
+
+ MaxDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+
+ tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+
+ tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(5, buckets[0].duration_nanos());
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index b000e13..e8e4d8b 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -18,6 +18,7 @@
#include "src/matchers/LogMatchingTracker.h"
#include "src/metrics/CountMetricProducer.h"
#include "src/metrics/MetricProducer.h"
+#include "src/metrics/ValueMetricProducer.h"
#include "src/metrics/metrics_manager_util.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
@@ -124,6 +125,39 @@
return config;
}
+StatsdConfig buildDimensionMetricsWithMultiTags() {
+ StatsdConfig config;
+ config.set_config_id(12345L);
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("BATTERY_VERY_LOW");
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->set_tag(2);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("BATTERY_VERY_VERY_LOW");
+ simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->set_tag(3);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("BATTERY_LOW");
+
+ LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher("BATTERY_VERY_LOW");
+ combination->add_matcher("BATTERY_VERY_VERY_LOW");
+
+ // Count process state changes, slice by uid, while SCREEN_IS_OFF
+ CountMetric* metric = config.add_count_metric();
+ metric->set_metric_id(3);
+ metric->set_what("BATTERY_LOW");
+ metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ KeyMatcher* keyMatcher = metric->add_dimension();
+ keyMatcher->set_key(1);
+
+ return config;
+}
+
StatsdConfig buildCircleConditions() {
StatsdConfig config;
config.set_config_id(12345L);
@@ -180,6 +214,21 @@
trackerToConditionMap));
}
+TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
+ StatsdConfig config = buildDimensionMetricsWithMultiTags();
+ set<int> allTagIds;
+ vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+ EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+ allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap));
+}
+
TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
StatsdConfig config = buildCircleMatchers();
set<int> allTagIds;
diff --git a/cmds/statsd/tests/OringDurationTracker_test.cpp b/cmds/statsd/tests/OringDurationTracker_test.cpp
new file mode 100644
index 0000000..0b79819
--- /dev/null
+++ b/cmds/statsd/tests/OringDurationTracker_test.cpp
@@ -0,0 +1,99 @@
+// Copyright (C) 2017 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.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "src/condition/ConditionWizard.h"
+#include "src/metrics/duration_helper/OringDurationTracker.h"
+
+#include <stdio.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+using namespace android::os::statsd;
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+class MockConditionWizard : public ConditionWizard {
+public:
+ MOCK_METHOD2(
+ query,
+ ConditionState(const int conditionIndex,
+ const std::map<std::string, HashableDimensionKey>& conditionParameters));
+};
+
+TEST(OringDurationTrackerTest, TestDurationOverlap) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ vector<DurationBucketInfo> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ int64_t durationTimeNs = 2 * 1000;
+
+ OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl
+
+ tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(durationTimeNs, buckets[0].duration_nanos());
+}
+
+TEST(OringDurationTrackerTest, TestDurationConditionChange) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ EXPECT_CALL(*wizard, query(_, key1)) // #4
+ .WillOnce(Return(ConditionState::kFalse));
+
+ vector<DurationBucketInfo> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ int64_t durationTimeNs = 2 * 1000;
+
+ OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+
+ tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+
+ tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(5, buckets[0].duration_nanos());
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index f9a90e4..671f6d4 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -13,13 +13,17 @@
// limitations under the License.
#include "packages/UidMap.h"
+#include "config/ConfigKey.h"
#include <gtest/gtest.h>
#include <stdio.h>
using namespace android;
-using namespace android::os::statsd;
+
+namespace android {
+namespace os {
+namespace statsd {
#ifdef __ANDROID__
const string kApp1 = "app1.sharing.1";
@@ -64,6 +68,57 @@
EXPECT_FALSE(m.hasApp(1000, kApp1));
EXPECT_TRUE(m.hasApp(1000, kApp2));
}
+
+TEST(UidMapTest, TestClearingOutput) {
+ UidMap m;
+
+ ConfigKey config1(1, "config1");
+ ConfigKey config2(1, "config2");
+
+ m.OnConfigUpdated(config1);
+
+ vector<int32_t> uids;
+ vector<int32_t> versions;
+ vector<String16> apps;
+ uids.push_back(1000);
+ uids.push_back(1000);
+ apps.push_back(String16(kApp1.c_str()));
+ apps.push_back(String16(kApp2.c_str()));
+ versions.push_back(4);
+ versions.push_back(5);
+ m.updateMap(1, uids, versions, apps);
+
+ UidMapping results = m.getOutput(2, config1);
+ EXPECT_EQ(1, results.snapshots_size());
+
+ // It should be cleared now
+ results = m.getOutput(3, config1);
+ EXPECT_EQ(0, results.snapshots_size());
+
+ // Now add another configuration.
+ m.OnConfigUpdated(config2);
+ m.updateApp(5, String16(kApp1.c_str()), 1000, 40);
+ results = m.getOutput(6, config1);
+ EXPECT_EQ(0, results.snapshots_size());
+ EXPECT_EQ(1, results.changes_size());
+
+ // Now we still haven't been able to delete anything
+ m.updateApp(7, String16(kApp2.c_str()), 1001, 41);
+ results = m.getOutput(8, config1);
+ EXPECT_EQ(0, results.snapshots_size());
+ EXPECT_EQ(2, results.changes_size());
+
+ results = m.getOutput(9, config2);
+ EXPECT_EQ(0, results.snapshots_size());
+ EXPECT_EQ(2, results.changes_size());
+ // At this point both should be cleared.
+ EXPECT_EQ(0, m.mOutput.snapshots_size());
+ EXPECT_EQ(0, m.mOutput.changes_size());
+}
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 076c2bc..a8863bf 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,8 +16,6 @@
package android.app;
-import static android.os.Build.VERSION_CODES.O_MR1;
-
import static java.lang.Character.MIN_VALUE;
import android.annotation.CallSuper;
@@ -136,6 +134,7 @@
import java.util.HashMap;
import java.util.List;
+
/**
* An activity is a single, focused thing that the user can do. Almost all
* activities interact with the user, so the Activity class takes care of
@@ -991,17 +990,6 @@
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
- if (getApplicationInfo().targetSdkVersion >= O_MR1 && mActivityInfo.isFixedOrientation()) {
- final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window);
- final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta);
- ta.recycle();
-
- if (isTranslucentOrFloating) {
- throw new IllegalStateException(
- "Only fullscreen opaque activities can request orientation");
- }
- }
-
if (mLastNonConfigurationInstances != null) {
mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
}
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 33825b4..da718dc 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -19,15 +19,19 @@
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
+import android.os.Process;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
+import android.os.UserHandle;
import android.util.Log;
import java.util.concurrent.CountDownLatch;
@@ -143,9 +147,13 @@
@Override
public Bundle call(String method, String arg, Bundle extras) {
if (method.equals(METHOD_SLICE)) {
- getContext().enforceCallingPermission(permission.BIND_SLICE,
- "Slice binding requires the permission BIND_SLICE");
Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
+ getContext().enforceUriPermission(uri, permission.BIND_SLICE,
+ permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ "Slice binding requires the permission BIND_SLICE");
+ }
Slice s = handleBindSlice(uri);
Bundle b = new Bundle();
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 41667c4..837c00a 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -455,7 +455,6 @@
*/
public static final int FLAG_TURN_SCREEN_ON = 0x1000000;
-
/**
* @hide Bit in {@link #flags}: If set, this component will only be seen
* by the system user. Only works with broadcast receivers. Set from the
@@ -1001,20 +1000,12 @@
* Returns true if the activity's orientation is fixed.
* @hide
*/
- public boolean isFixedOrientation() {
+ boolean isFixedOrientation() {
return isFixedOrientationLandscape() || isFixedOrientationPortrait()
|| screenOrientation == SCREEN_ORIENTATION_LOCKED;
}
/**
- * Returns true if the specified orientation is considered fixed.
- * @hide
- */
- static public boolean isFixedOrientation(int orientation) {
- return isFixedOrientationLandscape(orientation) || isFixedOrientationPortrait(orientation);
- }
-
- /**
* Returns true if the activity's orientation is fixed to landscape.
* @hide
*/
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 386239c..3239212 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -49,6 +49,8 @@
import android.util.Xml;
import android.view.DisplayAdjustments;
+import com.android.internal.util.GrowingArrayUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -117,6 +119,13 @@
private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
new ConfigurationBoundResourceCache<>();
+ // A stack of all the resourceIds already referenced when parsing a resource. This is used to
+ // detect circular references in the xml.
+ // Using a ThreadLocal variable ensures that we have different stacks for multiple parallel
+ // calls to ResourcesImpl
+ private final ThreadLocal<LookupStack> mLookupStack =
+ ThreadLocal.withInitial(() -> new LookupStack());
+
/** Size of the cyclical cache used to map XML files to blocks. */
private static final int XML_BLOCK_CACHE_SIZE = 4;
@@ -784,19 +793,29 @@
final Drawable dr;
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+ LookupStack stack = mLookupStack.get();
try {
- if (file.endsWith(".xml")) {
- final XmlResourceParser rp = loadXmlResourceParser(
- file, id, value.assetCookie, "drawable");
- dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme);
- rp.close();
- } else {
- final InputStream is = mAssets.openNonAsset(
- value.assetCookie, file, AssetManager.ACCESS_STREAMING);
- dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
- is.close();
+ // Perform a linear search to check if we have already referenced this resource before.
+ if (stack.contains(id)) {
+ throw new Exception("Recursive reference in drawable");
}
- } catch (Exception | StackOverflowError e) {
+ stack.push(id);
+ try {
+ if (file.endsWith(".xml")) {
+ final XmlResourceParser rp = loadXmlResourceParser(
+ file, id, value.assetCookie, "drawable");
+ dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme);
+ rp.close();
+ } else {
+ final InputStream is = mAssets.openNonAsset(
+ value.assetCookie, file, AssetManager.ACCESS_STREAMING);
+ dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
+ is.close();
+ }
+ } finally {
+ stack.pop();
+ }
+ } catch (Exception e) {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
final NotFoundException rnf = new NotFoundException(
"File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
@@ -1377,4 +1396,29 @@
}
}
}
+
+ private static class LookupStack {
+
+ // Pick a reasonable default size for the array, it is grown as needed.
+ private int[] mIds = new int[4];
+ private int mSize = 0;
+
+ public void push(int id) {
+ mIds = GrowingArrayUtils.append(mIds, mSize, id);
+ mSize++;
+ }
+
+ public boolean contains(int id) {
+ for (int i = 0; i < mSize; i++) {
+ if (mIds[i] == id) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void pop() {
+ mSize--;
+ }
+ }
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index c28583e..361b81b 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -289,7 +289,9 @@
private void setWalModeFromConfiguration() {
if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
- if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
+ boolean walEnabled =
+ (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
+ if (walEnabled || mConfiguration.useCompatibilityWal) {
setJournalMode("WAL");
setSyncMode(SQLiteGlobal.getWALSyncMode());
} else {
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 2dc5ca4..13e6f71 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -22,6 +22,8 @@
import android.os.StrictMode;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import java.util.HashMap;
import java.util.Map;
@@ -60,6 +62,9 @@
/** Used to find out where a cursor was allocated in case it never got released. */
private final Throwable mStackTrace;
+ /** Controls fetching of rows relative to requested position **/
+ private boolean mFillWindowForwardOnly;
+
/**
* Execute a query and provide access to its result set through a Cursor
* interface. For a query such as: {@code SELECT name, birth, phone FROM
@@ -136,18 +141,19 @@
private void fillWindow(int requiredPos) {
clearOrCreateWindow(getDatabase().getPath());
-
try {
+ Preconditions.checkArgumentNonnegative(requiredPos,
+ "requiredPos cannot be negative, but was " + requiredPos);
+
if (mCount == NO_COUNT) {
- int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0);
- mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true);
+ mCount = mQuery.fillWindow(mWindow, requiredPos, requiredPos, true);
mCursorWindowCapacity = mWindow.getNumRows();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "received count(*) from native_fill_window: " + mCount);
}
} else {
- int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos,
- mCursorWindowCapacity);
+ int startPos = mFillWindowForwardOnly ? requiredPos : DatabaseUtils
+ .cursorPickFillWindowStartPosition(requiredPos, mCursorWindowCapacity);
mQuery.fillWindow(mWindow, startPos, requiredPos, false);
}
} catch (RuntimeException ex) {
@@ -252,6 +258,20 @@
}
/**
+ * Controls fetching of rows relative to requested position.
+ *
+ * <p>Calling this method defines how rows will be loaded, but it doesn't affect rows that
+ * are already in the window. This setting is preserved if a new window is
+ * {@link #setWindow(CursorWindow) set}
+ *
+ * @param fillWindowForwardOnly if true, rows will be fetched starting from requested position
+ * up to the window's capacity. Default value is false.
+ */
+ public void setFillWindowForwardOnly(boolean fillWindowForwardOnly) {
+ mFillWindowForwardOnly = fillWindowForwardOnly;
+ }
+
+ /**
* Release the native resources, if they haven't been released yet.
*/
@Override
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index df0e262..83b8dc7 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -285,6 +285,7 @@
}
}
mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs;
+ mConfigurationLocked.useCompatibilityWal = SQLiteGlobal.isCompatibilityWalSupported();
}
@Override
@@ -2070,15 +2071,21 @@
synchronized (mLock) {
throwIfNotOpenLocked();
- if ((mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0) {
+ final boolean oldUseCompatibilityWal = mConfigurationLocked.useCompatibilityWal;
+ final int oldFlags = mConfigurationLocked.openFlags;
+ if (!oldUseCompatibilityWal && (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0) {
return;
}
mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING;
+ // If an app explicitly disables WAL, do not even use compatibility mode
+ mConfigurationLocked.useCompatibilityWal = false;
+
try {
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
} catch (RuntimeException ex) {
- mConfigurationLocked.openFlags |= ENABLE_WRITE_AHEAD_LOGGING;
+ mConfigurationLocked.openFlags = oldFlags;
+ mConfigurationLocked.useCompatibilityWal = oldUseCompatibilityWal;
throw ex;
}
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index 34c9b33..905da724 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -111,6 +111,15 @@
public long idleConnectionTimeoutMs = Long.MAX_VALUE;
/**
+ * Enables compatibility WAL mode. Applications cannot explicitly choose compatibility WAL mode,
+ * therefore it is not exposed as a flag.
+ *
+ * <p>In this mode, only database journal mode will be changed, connection pool
+ * size will still be limited to a single connection.
+ */
+ public boolean useCompatibilityWal;
+
+ /**
* Creates a database configuration with the required parameters for opening a
* database and default values for all other parameters.
*
@@ -170,6 +179,7 @@
lookasideSlotSize = other.lookasideSlotSize;
lookasideSlotCount = other.lookasideSlotCount;
idleConnectionTimeoutMs = other.idleConnectionTimeoutMs;
+ useCompatibilityWal = other.useCompatibilityWal;
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java
index 94d5555..bb2a517 100644
--- a/core/java/android/database/sqlite/SQLiteGlobal.java
+++ b/core/java/android/database/sqlite/SQLiteGlobal.java
@@ -81,6 +81,17 @@
}
/**
+ * Returns true if compatibility WAL mode is supported. In this mode, only
+ * database journal mode is changed. Connection pool will use at most one connection.
+ * @hide
+ */
+ public static boolean isCompatibilityWalSupported() {
+ return SystemProperties.getBoolean("debug.sqlite.compatibility_wal_supported",
+ Resources.getSystem().getBoolean(
+ com.android.internal.R.bool.db_compatibility_wal_supported));
+ }
+
+ /**
* Gets the journal size limit in bytes.
*/
public static int getJournalSizeLimit() {
diff --git a/core/java/android/os/BatteryStatsInternal.java b/core/java/android/os/BatteryStatsInternal.java
new file mode 100644
index 0000000..b0436eb
--- /dev/null
+++ b/core/java/android/os/BatteryStatsInternal.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 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 android.os;
+
+/**
+ * Battery stats local system service interface. This is used to pass internal data out of
+ * BatteryStatsImpl.
+ *
+ * @hide Only for use within Android OS.
+ */
+public abstract class BatteryStatsInternal {
+ /**
+ * Returns the wifi interfaces.
+ */
+ public abstract String[] getWifiIfaces();
+
+ /**
+ * Returns the mobile data interfaces.
+ */
+ public abstract String[] getMobileIfaces();
+}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 017c213..2e62eb6 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -23,7 +23,6 @@
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.TypedProperties;
-import dalvik.bytecode.OpcodeInfo;
import dalvik.system.VMDebug;
import org.apache.harmony.dalvik.ddmc.Chunk;
@@ -48,8 +47,6 @@
import java.util.Map;
-
-
/**
* Provides various debugging methods for Android applications, including
* tracing and allocation counts.
@@ -1959,13 +1956,7 @@
*/
@Deprecated
public static class InstructionCount {
- private static final int NUM_INSTR =
- OpcodeInfo.MAXIMUM_PACKED_VALUE + 1;
-
- private int[] mCounts;
-
public InstructionCount() {
- mCounts = new int[NUM_INSTR];
}
/**
@@ -1975,13 +1966,7 @@
* @return true if counting was started
*/
public boolean resetAndStart() {
- try {
- VMDebug.startInstructionCounting();
- VMDebug.resetInstructionCount();
- } catch (UnsupportedOperationException uoe) {
- return false;
- }
- return true;
+ return false;
}
/**
@@ -1989,13 +1974,7 @@
* counting process.
*/
public boolean collect() {
- try {
- VMDebug.stopInstructionCounting();
- VMDebug.getInstructionCount(mCounts);
- } catch (UnsupportedOperationException uoe) {
- return false;
- }
- return true;
+ return false;
}
/**
@@ -2003,13 +1982,7 @@
* all threads).
*/
public int globalTotal() {
- int count = 0;
-
- for (int i = 0; i < NUM_INSTR; i++) {
- count += mCounts[i];
- }
-
- return count;
+ return 0;
}
/**
@@ -2017,15 +1990,7 @@
* executed globally.
*/
public int globalMethodInvocations() {
- int count = 0;
-
- for (int i = 0; i < NUM_INSTR; i++) {
- if (OpcodeInfo.isInvoke(i)) {
- count += mCounts[i];
- }
- }
-
- return count;
+ return 0;
}
}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 5b0e5bbc..f977c1d 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -836,7 +836,6 @@
* physically removed.
*/
public static boolean isExternalStorageRemovable() {
- if (isStorageDisabled()) return false;
final File externalDir = sCurrentUser.getExternalDirs()[0];
return isExternalStorageRemovable(externalDir);
}
@@ -875,7 +874,6 @@
* boolean)
*/
public static boolean isExternalStorageEmulated() {
- if (isStorageDisabled()) return false;
final File externalDir = sCurrentUser.getExternalDirs()[0];
return isExternalStorageEmulated(externalDir);
}
@@ -951,9 +949,6 @@
return cur;
}
- private static boolean isStorageDisabled() {
- return SystemProperties.getBoolean("config.disable_storage", false);
- }
/**
* If the given path exists on emulated external storage, return the
diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java
index 7dec4d7..3544ea1 100644
--- a/core/java/android/os/HidlSupport.java
+++ b/core/java/android/os/HidlSupport.java
@@ -156,4 +156,27 @@
// Should not reach here.
throw new UnsupportedOperationException();
}
+
+ /**
+ * Test that two interfaces are equal. This is the Java equivalent to C++
+ * interfacesEqual function.
+ * This essentially calls .equals on the internal binder objects (via Binder()).
+ * - If both interfaces are proxies, asBinder() returns a {@link HwRemoteBinder}
+ * object, and they are compared in {@link HwRemoteBinder#equals}.
+ * - If both interfaces are stubs, asBinder() returns the object itself. By default,
+ * auto-generated IFoo.Stub does not override equals(), but an implementation can
+ * optionally override it, and {@code interfacesEqual} will use it here.
+ */
+ public static boolean interfacesEqual(IHwInterface lft, Object rgt) {
+ if (lft == rgt) {
+ return true;
+ }
+ if (lft == null || rgt == null) {
+ return false;
+ }
+ if (!(rgt instanceof IHwInterface)) {
+ return false;
+ }
+ return Objects.equals(lft.asBinder(), ((IHwInterface) rgt).asBinder());
+ }
}
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 5e2a081..dd9e774 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -63,6 +63,13 @@
public static native final void joinRpcThreadpool();
+ /**
+ * Call configureRpcThreadpool, then actually spawn
+ * (maxThreads - (callerWillJoin ? 0 : 1)) threads.
+ */
+ public static final native void startRpcThreadPool(
+ long maxThreads, boolean callerWillJoin);
+
// Returns address of the "freeFunction".
private static native final long native_init();
diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java
index 2f89ce6..a07e42c 100644
--- a/core/java/android/os/HwRemoteBinder.java
+++ b/core/java/android/os/HwRemoteBinder.java
@@ -63,4 +63,9 @@
}
private long mNativeContext;
+
+ @Override
+ public final native boolean equals(Object other);
+ @Override
+ public final native int hashCode();
}
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index 20f6c8e..c0a95cc 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -46,10 +46,10 @@
* Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately,
* and alarm is inexact.
*/
- oneway void setPollingAlarms(long timestampMs, long intervalMs);
+ oneway void setPullingAlarms(long timestampMs, long intervalMs);
/** Cancel any repeating polling alarm. */
- oneway void cancelPollingAlarms();
+ oneway void cancelPullingAlarms();
/** Pull the specified data. Results will be sent to statsd when complete. */
StatsLogEventWrapper[] pullData(int pullCode);
diff --git a/core/java/android/os/TokenWatcher.java b/core/java/android/os/TokenWatcher.java
index 9b3a2d6..00333dad 100644
--- a/core/java/android/os/TokenWatcher.java
+++ b/core/java/android/os/TokenWatcher.java
@@ -16,17 +16,23 @@
package android.os;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.WeakHashMap;
-import java.util.Set;
import android.util.Log;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.WeakHashMap;
+
/**
- * Helper class that helps you use IBinder objects as reference counted
- * tokens. IBinders make good tokens because we find out when they are
- * removed
+ * A TokenWatcher watches a collection of {@link IBinder}s. IBinders are added
+ * to the collection by calling {@link #acquire}, and removed by calling {@link
+ * #release}. IBinders are also implicitly removed when they become weakly
+ * reachable. Each IBinder may be added at most once.
*
+ * The {@link #acquired} method is invoked by posting to the specified handler
+ * whenever the size of the watched collection becomes nonzero. The {@link
+ * #released} method is invoked on the specified handler whenever the size of
+ * the watched collection becomes zero.
*/
public abstract class TokenWatcher
{
@@ -59,15 +65,23 @@
* Record that this token has been acquired. When acquire is called, and
* the current count is 0, the acquired method is called on the given
* handler.
- *
- * @param token An IBinder object. If this token has already been acquired,
- * no action is taken.
+ *
+ * Note that the same {@code token} can only be acquired once. If this
+ * {@code token} has already been acquired, no action is taken. The first
+ * subsequent call to {@link #release} will release this {@code token}
+ * immediately.
+ *
+ * @param token An IBinder object.
* @param tag A string used by the {@link #dump} method for debugging,
* to see who has references.
*/
public void acquire(IBinder token, String tag)
{
synchronized (mTokens) {
+ if (mTokens.containsKey(token)) {
+ return;
+ }
+
// explicitly checked to avoid bogus sendNotification calls because
// of the WeakHashMap and the GC
int oldSize = mTokens.size();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 62f4bf5..2d6a7b0 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -72,6 +72,7 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.MemoryIntArray;
+import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
@@ -1886,7 +1887,11 @@
arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
IContentProvider cp = mProviderHolder.getProvider(cr);
+ String prevValue = getStringForUser(cr, name, userHandle);
cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
+ String newValue = getStringForUser(cr, name, userHandle);
+ StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newValue, prevValue, tag,
+ makeDefault ? 1 : 0, userHandle);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
return false;
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 4500862..c44c8dd 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -92,7 +92,7 @@
* Defines the duration in milliseconds a user needs to hold down the
* appropriate button to enable the accessibility shortcut once it's configured.
*/
- private static final int A11Y_SHORTCUT_KEY_TIMEOUT_AFTER_CONFIRMATION = 1500;
+ private static final int A11Y_SHORTCUT_KEY_TIMEOUT_AFTER_CONFIRMATION = 1000;
/**
* Defines the duration in milliseconds we will wait to see if a touch event
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
index 3baccee..05ec9e9 100644
--- a/core/java/com/android/internal/os/BaseCommand.java
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -106,6 +106,14 @@
}
/**
+ * Peek the next argument on the command line, whatever it is; if there are
+ * no arguments left, return null.
+ */
+ public String peekNextArg() {
+ return mArgs.peekNextArg();
+ }
+
+ /**
* Return the next argument on the command line, whatever it is; if there are
* no arguments left, throws an IllegalArgumentException to report this to the user.
*/
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index f0d05da..0535ebe 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5412,6 +5412,18 @@
}
}
+ public String[] getWifiIfaces() {
+ synchronized (mWifiNetworkLock) {
+ return mWifiIfaces;
+ }
+ }
+
+ public String[] getMobileIfaces() {
+ synchronized (mModemNetworkLock) {
+ return mModemIfaces;
+ }
+ }
+
@Override public long getScreenOnTime(long elapsedRealtimeUs, int which) {
return mScreenOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index f88db25..fb8b9f7 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -185,7 +185,6 @@
"android/opengl/poly_clip.cpp", // TODO: .arm
"android/opengl/util.cpp",
"android_server_NetworkManagementSocketTagger.cpp",
- "android_server_Watchdog.cpp",
"android_ddm_DdmHandleNativeHeap.cpp",
"android_backup_BackupDataInput.cpp",
"android_backup_BackupDataOutput.cpp",
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 08d9527..f6783e1 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -344,6 +344,17 @@
IPCThreadState::self()->joinThreadPool();
}
+void JHwBinder_native_startRpcThreadPool(JNIEnv *, jclass,
+ jlong maxThreads, jboolean callerWillJoin) {
+ CHECK(maxThreads > 0);
+ ProcessState::self()->setThreadPoolConfiguration(maxThreads,
+ callerWillJoin /* callerJoinsPool */);
+ ssize_t threadsNeeded = maxThreads - (callerWillJoin ? 0 : 1);
+ for (ssize_t i = 0; i < threadsNeeded; ++i) {
+ ProcessState::self()->spawnPooledThread(false /* isMain */);
+ }
+}
+
static void JHwBinder_report_sysprop_change(JNIEnv * /*env*/, jclass /*clazz*/)
{
report_sysprop_change();
@@ -369,6 +380,9 @@
{ "joinRpcThreadpool", "()V",
(void *)JHwBinder_native_joinRpcThreadpool },
+ { "startRpcThreadPool", "(JZ)V",
+ (void *)JHwBinder_native_startRpcThreadPool },
+
{ "native_report_sysprop_change", "()V",
(void *)JHwBinder_report_sysprop_change },
};
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index cf59a56a..ca5e1e4 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -22,9 +22,13 @@
#include "android_os_HwParcel.h"
-#include <nativehelper/JNIHelp.h>
+#include <android/hidl/base/1.0/IBase.h>
+#include <android/hidl/base/1.0/BpHwBase.h>
+#include <android/hidl/base/1.0/BnHwBase.h>
#include <android_runtime/AndroidRuntime.h>
#include <hidl/Status.h>
+#include <hidl/HidlTransportSupport.h>
+#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include <nativehelper/ScopedLocalRef.h>
@@ -413,6 +417,44 @@
return res;
}
+static sp<hidl::base::V1_0::IBase> toIBase(JNIEnv* env, jclass hwRemoteBinderClazz, jobject jbinder)
+{
+ if (jbinder == nullptr) {
+ return nullptr;
+ }
+ if (!env->IsInstanceOf(jbinder, hwRemoteBinderClazz)) {
+ return nullptr;
+ }
+ sp<JHwRemoteBinder> context = JHwRemoteBinder::GetNativeContext(env, jbinder);
+ sp<hardware::IBinder> cbinder = context->getBinder();
+ return hardware::fromBinder<hidl::base::V1_0::IBase, hidl::base::V1_0::BpHwBase,
+ hidl::base::V1_0::BnHwBase>(cbinder);
+}
+
+// equals iff other is also a non-null android.os.HwRemoteBinder object
+// and getBinder() returns the same object.
+// In particular, if other is an android.os.HwBinder object (for stubs) then
+// it returns false.
+static jboolean JHwRemoteBinder_equals(JNIEnv* env, jobject thiz, jobject other)
+{
+ if (env->IsSameObject(thiz, other)) {
+ return true;
+ }
+ if (other == NULL) {
+ return false;
+ }
+
+ ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
+
+ return hardware::interfacesEqual(toIBase(env, clazz.get(), thiz), toIBase(env, clazz.get(), other));
+}
+
+static jint JHwRemoteBinder_hashCode(JNIEnv* env, jobject thiz) {
+ jlong longHash = reinterpret_cast<jlong>(
+ JHwRemoteBinder::GetNativeContext(env, thiz)->getBinder().get());
+ return static_cast<jint>(longHash ^ (longHash >> 32)); // See Long.hashCode()
+}
+
static JNINativeMethod gMethods[] = {
{ "native_init", "()J", (void *)JHwRemoteBinder_native_init },
@@ -430,6 +472,11 @@
{"unlinkToDeath",
"(Landroid/os/IHwBinder$DeathRecipient;)Z",
(void*)JHwRemoteBinder_unlinkToDeath},
+
+ {"equals", "(Ljava/lang/Object;)Z",
+ (void*)JHwRemoteBinder_equals},
+
+ {"hashCode", "()I", (void*)JHwRemoteBinder_hashCode},
};
namespace android {
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 5ef2a9e..6243fad 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -102,8 +102,11 @@
cPackageInfo[i] = cString;
env->ReleaseStringUTFChars(element, cString);
}
+ // If we can run this code, the device should already pass AVB.
+ // So, we don't need to check AVB here.
std::string error;
- int32_t status = VintfObject::CheckCompatibility(cPackageInfo, &error);
+ int32_t status = VintfObject::CheckCompatibility(
+ cPackageInfo, &error, ::android::vintf::DISABLE_AVB_CHECK);
if (status)
LOG(WARNING) << "VintfObject.verify() returns " << status << ": " << error;
return status;
diff --git a/core/jni/android_server_Watchdog.cpp b/core/jni/android_server_Watchdog.cpp
deleted file mode 100644
index 01d565b..0000000
--- a/core/jni/android_server_Watchdog.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- ** Copyright 2010, 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.
- */
-
-#define LOG_TAG "Watchdog_N"
-#include <utils/Log.h>
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <string.h>
-#include <errno.h>
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-
-static void dumpOneStack(int tid, int outFd) {
- char buf[64];
-
- snprintf(buf, sizeof(buf), "/proc/%d/stack", tid);
- int stackFd = open(buf, O_RDONLY);
- if (stackFd >= 0) {
- // header for readability
- strncat(buf, ":\n", sizeof(buf) - strlen(buf) - 1);
- write(outFd, buf, strlen(buf));
-
- // copy the stack dump text
- int nBytes;
- while ((nBytes = read(stackFd, buf, sizeof(buf))) > 0) {
- write(outFd, buf, nBytes);
- }
-
- // footer and done
- write(outFd, "\n", 1);
- close(stackFd);
- } else {
- ALOGE("Unable to open stack of tid %d : %d (%s)", tid, errno, strerror(errno));
- }
-}
-
-static void dumpKernelStacks(JNIEnv* env, jobject clazz, jstring pathStr) {
- char buf[128];
- DIR* taskdir;
-
- ALOGI("dumpKernelStacks");
- if (!pathStr) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Null path");
- return;
- }
-
- const char *path = env->GetStringUTFChars(pathStr, NULL);
-
- int outFd = open(path, O_WRONLY | O_APPEND | O_CREAT,
- S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
- if (outFd < 0) {
- ALOGE("Unable to open stack dump file: %d (%s)", errno, strerror(errno));
- goto done;
- }
-
- snprintf(buf, sizeof(buf), "\n----- begin pid %d kernel stacks -----\n", getpid());
- write(outFd, buf, strlen(buf));
-
- // look up the list of all threads in this process
- snprintf(buf, sizeof(buf), "/proc/%d/task", getpid());
- taskdir = opendir(buf);
- if (taskdir != NULL) {
- struct dirent * ent;
- while ((ent = readdir(taskdir)) != NULL) {
- int tid = atoi(ent->d_name);
- if (tid > 0 && tid <= 65535) {
- // dump each stack trace
- dumpOneStack(tid, outFd);
- }
- }
- closedir(taskdir);
- }
-
- snprintf(buf, sizeof(buf), "----- end pid %d kernel stacks -----\n", getpid());
- write(outFd, buf, strlen(buf));
-
- close(outFd);
-done:
- env->ReleaseStringUTFChars(pathStr, path);
-}
-
-// ----------------------------------------
-
-namespace android {
-
-static const JNINativeMethod g_methods[] = {
- { "native_dumpKernelStacks", "(Ljava/lang/String;)V", (void*)dumpKernelStacks },
-};
-
-int register_android_server_Watchdog(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "com/android/server/Watchdog", g_methods, NELEM(g_methods));
-}
-
-}
diff --git a/core/proto/android/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto
index 12649e1..d032a45 100644
--- a/core/proto/android/os/kernelwake.proto
+++ b/core/proto/android/os/kernelwake.proto
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
syntax = "proto2";
+
option java_multiple_files = true;
option java_outer_classname = "WakeupSourcesProto";
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
package android.os;
message KernelWakeSources {
@@ -27,6 +29,8 @@
// Next Tag: 11
message WakeupSourceProto {
+ option (stream_proto.stream).enable_fields_mapping = true;
+
// Name of the event which triggers application processor
optional string name = 1;
diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto
index f82ea76..22b3d73 100644
--- a/core/proto/android/os/pagetypeinfo.proto
+++ b/core/proto/android/os/pagetypeinfo.proto
@@ -18,6 +18,8 @@
option java_multiple_files = true;
option java_outer_classname = "PageTypeInfoProto";
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
package android.os;
/*
@@ -61,6 +63,7 @@
// Next tag: 9
message BlockProto {
+ option (stream_proto.stream).enable_fields_mapping = true;
optional int32 node = 1;
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
index ab6a6a3..4d62a60 100644
--- a/core/proto/android/os/procrank.proto
+++ b/core/proto/android/os/procrank.proto
@@ -18,6 +18,8 @@
option java_multiple_files = true;
option java_outer_classname = "ProcrankProto";
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
package android.os;
//Memory usage of running processes
@@ -31,6 +33,8 @@
// Next Tag: 11
message ProcessProto {
+ option (stream_proto.stream).enable_fields_mapping = true;
+
// ID of the process
optional int32 pid = 1;
diff --git a/core/proto/android/server/windowmanagertrace.proto b/core/proto/android/server/windowmanagertrace.proto
new file mode 100644
index 0000000..0c65bb2
--- /dev/null
+++ b/core/proto/android/server/windowmanagertrace.proto
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+
+import "frameworks/base/core/proto/android/content/configuration.proto";
+import "frameworks/base/core/proto/android/graphics/rect.proto";
+import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
+import "frameworks/base/core/proto/android/view/displayinfo.proto";
+import "frameworks/base/core/proto/android/view/windowlayoutparams.proto";
+
+package com.android.server.wm.proto;
+
+option java_multiple_files = true;
+
+/* represents a file full of window manager trace entries.
+ Encoded, it should start with 0x9 0x57 0x49 0x4e 0x54 0x52 0x41 0x43 0x45 (.WINTRACE), such
+ that they can be easily identified. */
+message WindowManagerTraceFileProto {
+
+ /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+ (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+ constants into .proto files. */
+ enum MagicNumber {
+ INVALID = 0;
+ MAGIC_NUMBER_L = 0x544e4957; /* WINT (little-endian ASCII) */
+ MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */
+ }
+
+ optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
+ repeated WindowManagerTraceProto entry = 2;
+}
+
+/* one window manager trace entry. */
+message WindowManagerTraceProto {
+ /* required: elapsed realtime in nanos since boot of when this entry was logged */
+ optional fixed64 elapsed_realtime_nanos = 1;
+
+ /* where the trace originated */
+ optional string where = 2;
+
+ optional WindowManagerServiceProto window_manager_service = 3;
+}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4a8bc13..bce950d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1698,6 +1698,11 @@
a transaction, so it interacts poorly with SECURE_DELETE. -->
<string name="db_default_journal_mode" translatable="false">TRUNCATE</string>
+ <!-- Enables compatibility WAL mode.
+ In this mode, only database journal mode will be changed, connection pool
+ size will still be limited to a single connection. -->
+ <bool name="db_compatibility_wal_supported">true</bool>
+
<!-- Maximum size of the persistent journal file in bytes.
If the journal file grows to be larger than this amount then SQLite will
truncate it after committing the transaction. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e513816..32758e8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -667,6 +667,7 @@
<java-symbol type="string" name="date_time" />
<java-symbol type="string" name="date_time_set" />
<java-symbol type="string" name="date_time_done" />
+ <java-symbol type="bool" name="db_compatibility_wal_supported" />
<java-symbol type="string" name="db_default_journal_mode" />
<java-symbol type="string" name="db_default_sync_mode" />
<java-symbol type="string" name="db_wal_sync_mode" />
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 115dcb6..0919e82 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -66,6 +66,7 @@
mHas1BitStencil = extensions.has("GL_OES_stencil1");
mHas4BitStencil = extensions.has("GL_OES_stencil4");
mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage");
+ mHasRenderableFloatTexture = extensions.has("GL_OES_texture_half_float");
mHasSRGB = mVersionMajor >= 3 || extensions.has("GL_EXT_sRGB");
mHasSRGBWriteControl = extensions.has("GL_EXT_sRGB_write_control");
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 0ecfdb1..7af7f79 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -38,6 +38,9 @@
inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
+ inline bool hasRenderableFloatTextures() const {
+ return (mVersionMajor >= 3 && mVersionMinor >= 2) || mHasRenderableFloatTexture;
+ }
inline bool hasSRGB() const { return mHasSRGB; }
inline bool hasSRGBWriteControl() const { return hasSRGB() && mHasSRGBWriteControl; }
inline bool hasLinearBlending() const { return hasSRGB() && mHasLinearBlending; }
@@ -56,6 +59,7 @@
bool mHasSRGB;
bool mHasSRGBWriteControl;
bool mHasLinearBlending;
+ bool mHasRenderableFloatTexture;
int mVersionMajor;
int mVersionMinor;
diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp
index 2687410..751e203 100644
--- a/libs/hwui/OpenGLReadback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -128,7 +128,8 @@
return CopyResult::DestinationInvalid;
}
- if (bitmap->colorType() == kRGBA_F16_SkColorType && !caches.extensions().hasFloatTextures()) {
+ if (bitmap->colorType() == kRGBA_F16_SkColorType &&
+ !caches.extensions().hasRenderableFloatTextures()) {
ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
return CopyResult::DestinationInvalid;
}
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
index 608e694..049018cc 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -73,7 +73,7 @@
* for reading back float buffers (skbug.com/6945).
*/
if (pixelConfig == kRGBA_half_GrPixelConfig &&
- !DeviceInfo::get()->extensions().hasFloatTextures()) {
+ !DeviceInfo::get()->extensions().hasRenderableFloatTextures()) {
ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
return CopyResult::DestinationInvalid;
}
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
new file mode 100644
index 0000000..4f1d2d5
--- /dev/null
+++ b/libs/protoutil/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2017 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.
+
+cc_library {
+ name: "libprotoutil",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+ ],
+
+ srcs: [
+ "src/EncodedBuffer.cpp",
+ "src/ProtoOutputStream.cpp",
+ "src/protobuf.cpp",
+ ],
+
+ export_include_dirs: ["include"],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+}
diff --git a/libs/protoutil/Android.mk b/libs/protoutil/Android.mk
deleted file mode 100644
index 2a2b087..0000000
--- a/libs/protoutil/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Copyright (C) 2017 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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libprotoutil
-
-LOCAL_CFLAGS := \
- -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog \
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include
-
-LOCAL_SRC_FILES := \
- src/EncodedBuffer.cpp \
- src/ProtoOutputStream.cpp \
- src/protobuf.cpp \
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index b8415b2..2155084 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -19,13 +19,59 @@
#include <android/util/EncodedBuffer.h>
-#include <stdint.h>
#include <string>
namespace android {
namespace util {
/**
+ * Position of the field type in a (long long) fieldId.
+ */
+const uint64_t FIELD_TYPE_SHIFT = 32;
+
+/**
+ * Mask for the field types stored in a fieldId. Leaves a whole
+ * byte for future expansion, even though there are currently only 17 types.
+ */
+const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT;
+
+const uint64_t FIELD_TYPE_UNKNOWN = 0;
+const uint64_t FIELD_TYPE_DOUBLE = 1ULL << FIELD_TYPE_SHIFT; // double, exactly eight bytes on the wire.
+const uint64_t FIELD_TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; // float, exactly four bytes on the wire.
+const uint64_t FIELD_TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; // int64, varint on the wire. Negative numbers
+ // take 10 bytes. Use TYPE_SINT64 if negative
+ // values are likely.
+const uint64_t FIELD_TYPE_UINT64 = 4ULL << FIELD_TYPE_SHIFT; // uint64, varint on the wire.
+const uint64_t FIELD_TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; // int32, varint on the wire. Negative numbers
+ // take 10 bytes. Use TYPE_SINT32 if negative
+ // values are likely.
+const uint64_t FIELD_TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exactly eight bytes on the wire.
+const uint64_t FIELD_TYPE_FIXED32 = 7ULL << FIELD_TYPE_SHIFT; // uint32, exactly four bytes on the wire.
+const uint64_t FIELD_TYPE_BOOL = 8ULL << FIELD_TYPE_SHIFT; // bool, varint on the wire.
+const uint64_t FIELD_TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; // UTF-8 text.
+const uint64_t FIELD_TYPE_GROUP = 10ULL << FIELD_TYPE_SHIFT; // Tag-delimited message. Deprecated.
+const uint64_t FIELD_TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; // Length-delimited message.
+
+const uint64_t FIELD_TYPE_BYTES = 12ULL << FIELD_TYPE_SHIFT; // Arbitrary byte array.
+const uint64_t FIELD_TYPE_UINT32 = 13ULL << FIELD_TYPE_SHIFT; // uint32, varint on the wire
+const uint64_t FIELD_TYPE_ENUM = 14ULL << FIELD_TYPE_SHIFT; // Enum, varint on the wire
+const uint64_t FIELD_TYPE_SFIXED32 = 15ULL << FIELD_TYPE_SHIFT; // int32, exactly four bytes on the wire
+const uint64_t FIELD_TYPE_SFIXED64 = 16ULL << FIELD_TYPE_SHIFT; // int64, exactly eight bytes on the wire
+const uint64_t FIELD_TYPE_SINT32 = 17ULL << FIELD_TYPE_SHIFT; // int32, ZigZag-encoded varint on the wire
+const uint64_t FIELD_TYPE_SINT64 = 18ULL << FIELD_TYPE_SHIFT; // int64, ZigZag-encoded varint on the wire
+
+//
+// FieldId flags for whether the field is single, repeated or packed.
+// TODO: packed is not supported yet.
+//
+const uint64_t FIELD_COUNT_SHIFT = 40;
+const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_UNKNOWN = 0;
+const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
+
+/**
* Class to write to a protobuf stream.
*
* Each write method takes an ID code from the protoc generated classes
@@ -60,11 +106,12 @@
void end(long long token);
/**
- * Flushes the protobuf data out to given fd.
+ * Flushes the protobuf data out to given fd. When the following functions are called,
+ * it is not able to write to ProtoOutputStream any more since the data is compact.
*/
- size_t size();
- EncodedBuffer::iterator data();
- bool flush(int fd);
+ size_t size(); // Get the size of the serialized protobuf.
+ EncodedBuffer::iterator data(); // Get the reader apis of the data.
+ bool flush(int fd); // Flush data directly to a file descriptor.
// Please don't use the following functions to dump protos unless you are familiar with protobuf encoding.
void writeRawVarint(uint64_t varint);
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index b91e3db0..9d8ee729 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -18,58 +18,10 @@
#include <android/util/protobuf.h>
#include <android/util/ProtoOutputStream.h>
#include <cutils/log.h>
-#include <cstring>
namespace android {
namespace util {
-/**
- * Position of the field type in a (long long) fieldId.
- */
-const uint64_t FIELD_TYPE_SHIFT = 32;
-
-/**
- * Mask for the field types stored in a fieldId. Leaves a whole
- * byte for future expansion, even though there are currently only 17 types.
- */
-const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT;
-
-const uint64_t FIELD_TYPE_UNKNOWN = 0;
-const uint64_t TYPE_DOUBLE = 1ULL << FIELD_TYPE_SHIFT; // double, exactly eight bytes on the wire.
-const uint64_t TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; // float, exactly four bytes on the wire.
-const uint64_t TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; // int64, varint on the wire. Negative numbers
- // take 10 bytes. Use TYPE_SINT64 if negative
- // values are likely.
-const uint64_t TYPE_UINT64 = 4ULL << FIELD_TYPE_SHIFT; // uint64, varint on the wire.
-const uint64_t TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; // int32, varint on the wire. Negative numbers
- // take 10 bytes. Use TYPE_SINT32 if negative
- // values are likely.
-const uint64_t TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exactly eight bytes on the wire.
-const uint64_t TYPE_FIXED32 = 7ULL << FIELD_TYPE_SHIFT; // uint32, exactly four bytes on the wire.
-const uint64_t TYPE_BOOL = 8ULL << FIELD_TYPE_SHIFT; // bool, varint on the wire.
-const uint64_t TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; // UTF-8 text.
-const uint64_t TYPE_GROUP = 10ULL << FIELD_TYPE_SHIFT; // Tag-delimited message. Deprecated.
-const uint64_t TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; // Length-delimited message.
-
-const uint64_t TYPE_BYTES = 12ULL << FIELD_TYPE_SHIFT; // Arbitrary byte array.
-const uint64_t TYPE_UINT32 = 13ULL << FIELD_TYPE_SHIFT; // uint32, varint on the wire
-const uint64_t TYPE_ENUM = 14ULL << FIELD_TYPE_SHIFT; // Enum, varint on the wire
-const uint64_t TYPE_SFIXED32 = 15ULL << FIELD_TYPE_SHIFT; // int32, exactly four bytes on the wire
-const uint64_t TYPE_SFIXED64 = 16ULL << FIELD_TYPE_SHIFT; // int64, exactly eight bytes on the wire
-const uint64_t TYPE_SINT32 = 17ULL << FIELD_TYPE_SHIFT; // int32, ZigZag-encoded varint on the wire
-const uint64_t TYPE_SINT64 = 18ULL << FIELD_TYPE_SHIFT; // int64, ZigZag-encoded varint on the wire
-
-//
-// FieldId flags for whether the field is single, repeated or packed.
-// TODO: packed is not supported yet.
-//
-const uint64_t FIELD_COUNT_SHIFT = 40;
-const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_UNKNOWN = 0;
-const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
-
ProtoOutputStream::ProtoOutputStream()
:mBuffer(),
mCopyBegin(0),
@@ -90,18 +42,18 @@
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
default:
ALOGW("Field type %d is not supported when writing double val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -116,18 +68,18 @@
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
default:
ALOGW("Field type %d is not supported when writing float val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -142,20 +94,20 @@
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
- case TYPE_ENUM: writeEnumImpl(id, (int)val); break;
- case TYPE_BOOL: writeBoolImpl(id, val != 0); break;
+ case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break;
+ case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break;
default:
ALOGW("Field type %d is not supported when writing int val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -170,20 +122,20 @@
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
- case TYPE_ENUM: writeEnumImpl(id, (int)val); break;
- case TYPE_BOOL: writeBoolImpl(id, val != 0); break;
+ case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break;
+ case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break;
default:
ALOGW("Field type %d is not supported when writing long long val.",
(int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
@@ -198,7 +150,7 @@
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_BOOL:
+ case FIELD_TYPE_BOOL:
writeBoolImpl(id, val);
return true;
default:
@@ -214,7 +166,7 @@
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_STRING:
+ case FIELD_TYPE_STRING:
writeUtf8StringImpl(id, val.c_str(), val.size());
return true;
default:
@@ -230,11 +182,11 @@
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
- case TYPE_STRING:
- case TYPE_BYTES:
+ case FIELD_TYPE_STRING:
+ case FIELD_TYPE_BYTES:
writeUtf8StringImpl(id, val, size);
return true;
- case TYPE_MESSAGE:
+ case FIELD_TYPE_MESSAGE:
// can directly write valid format of message bytes into ProtoOutputStream without calling start/end
writeMessageBytesImpl(id, val, size);
return true;
@@ -291,7 +243,7 @@
long long
ProtoOutputStream::start(uint64_t fieldId)
{
- if ((fieldId & FIELD_TYPE_MASK) != TYPE_MESSAGE) {
+ if ((fieldId & FIELD_TYPE_MASK) != FIELD_TYPE_MESSAGE) {
ALOGE("Can't call start for non-message type field: 0x%llx", (long long)fieldId);
return 0;
}
@@ -564,7 +516,6 @@
inline void
ProtoOutputStream::writeDoubleImpl(uint32_t id, double val)
{
- if (val == 0.0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
mBuffer.writeRawFixed64(bit_cast<double, uint64_t>(val));
}
@@ -572,7 +523,6 @@
inline void
ProtoOutputStream::writeFloatImpl(uint32_t id, float val)
{
- if (val == 0.0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
mBuffer.writeRawFixed32(bit_cast<float, uint32_t>(val));
}
@@ -580,7 +530,6 @@
inline void
ProtoOutputStream::writeInt64Impl(uint32_t id, long long val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint64((uint64_t)val);
}
@@ -588,7 +537,6 @@
inline void
ProtoOutputStream::writeInt32Impl(uint32_t id, int val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32((uint32_t)val);
}
@@ -596,7 +544,6 @@
inline void
ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint64(val);
}
@@ -604,7 +551,6 @@
inline void
ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32(val);
}
@@ -612,7 +558,6 @@
inline void
ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
mBuffer.writeRawFixed64(val);
}
@@ -620,7 +565,6 @@
inline void
ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
mBuffer.writeRawFixed32(val);
}
@@ -628,7 +572,6 @@
inline void
ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
mBuffer.writeRawFixed64((uint64_t)val);
}
@@ -636,7 +579,6 @@
inline void
ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
mBuffer.writeRawFixed32((uint32_t)val);
}
@@ -644,7 +586,6 @@
inline void
ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint64((val << 1) ^ (val >> 63));
}
@@ -652,7 +593,6 @@
inline void
ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val)
{
- if (val == 0) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32((val << 1) ^ (val >> 31));
}
@@ -667,7 +607,6 @@
inline void
ProtoOutputStream::writeBoolImpl(uint32_t id, bool val)
{
- if (!val) return;
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32(val ? 1 : 0);
}
@@ -675,7 +614,7 @@
inline void
ProtoOutputStream::writeUtf8StringImpl(uint32_t id, const char* val, size_t size)
{
- if (val == NULL || size == 0) return;
+ if (val == NULL) return;
writeLengthDelimitedHeader(id, size);
for (size_t i=0; i<size; i++) {
mBuffer.writeRawByte((uint8_t)val[i]);
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index ba41a7b..9175416 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -2564,51 +2564,66 @@
});
}
+ String hasImage = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
String hasVideo = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
- final String METADATA_HAS_VIDEO_VALUE_YES = "yes";
- if (METADATA_HAS_VIDEO_VALUE_YES.equals(hasVideo)) {
- String width = retriever.extractMetadata(
+ String width = null;
+ String height = null;
+ String rotation = null;
+ final String METADATA_VALUE_YES = "yes";
+ // If the file has both image and video, prefer image info over video info.
+ // App querying ExifInterface is most likely using the bitmap path which
+ // picks the image first.
+ if (METADATA_VALUE_YES.equals(hasImage)) {
+ width = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH);
+ height = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT);
+ rotation = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION);
+ } else if (METADATA_VALUE_YES.equals(hasVideo)) {
+ width = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
- String height = retriever.extractMetadata(
+ height = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
-
- if (width != null) {
- mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
- ExifAttribute.createUShort(Integer.parseInt(width), mExifByteOrder));
- }
-
- if (height != null) {
- mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
- ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
- }
-
- String rotation = retriever.extractMetadata(
+ rotation = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
- if (rotation != null) {
- int orientation = ExifInterface.ORIENTATION_NORMAL;
+ }
- // all rotation angles in CW
- switch (Integer.parseInt(rotation)) {
- case 90:
- orientation = ExifInterface.ORIENTATION_ROTATE_90;
- break;
- case 180:
- orientation = ExifInterface.ORIENTATION_ROTATE_180;
- break;
- case 270:
- orientation = ExifInterface.ORIENTATION_ROTATE_270;
- break;
- }
+ if (width != null) {
+ mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
+ ExifAttribute.createUShort(Integer.parseInt(width), mExifByteOrder));
+ }
- mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
- ExifAttribute.createUShort(orientation, mExifByteOrder));
+ if (height != null) {
+ mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
+ ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
+ }
+
+ if (rotation != null) {
+ int orientation = ExifInterface.ORIENTATION_NORMAL;
+
+ // all rotation angles in CW
+ switch (Integer.parseInt(rotation)) {
+ case 90:
+ orientation = ExifInterface.ORIENTATION_ROTATE_90;
+ break;
+ case 180:
+ orientation = ExifInterface.ORIENTATION_ROTATE_180;
+ break;
+ case 270:
+ orientation = ExifInterface.ORIENTATION_ROTATE_270;
+ break;
}
- if (DEBUG) {
- Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation);
- }
+ mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
+ ExifAttribute.createUShort(orientation, mExifByteOrder));
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation);
}
} finally {
retriever.release();
diff --git a/packages/SettingsLib/res/layout/preference_two_target.xml b/packages/SettingsLib/res/layout/preference_two_target.xml
index c2167f3..4658924 100644
--- a/packages/SettingsLib/res/layout/preference_two_target.xml
+++ b/packages/SettingsLib/res/layout/preference_two_target.xml
@@ -33,19 +33,17 @@
android:background="?android:attr/selectableItemBackground"
android:gravity="start|center_vertical"
android:clipToPadding="false"
- android:layout_marginStart="4dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<LinearLayout
android:id="@+id/icon_frame"
- style="@style/preference_icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
+ android:minWidth="56dp"
android:orientation="horizontal"
android:clipToPadding="false"
- android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<android.support.v7.internal.widget.PreferenceImageView
diff --git a/packages/SettingsLib/res/values/styles.xml b/packages/SettingsLib/res/values/styles.xml
index b7ea1d4..3f312f4 100644
--- a/packages/SettingsLib/res/values/styles.xml
+++ b/packages/SettingsLib/res/values/styles.xml
@@ -21,9 +21,4 @@
<style name="TextAppearanceMedium">
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
</style>
-
- <style name="preference_icon_frame">
- <item name="android:layout_marginStart">-4dp</item>
- <item name="android:minWidth">60dp</item>
- </style>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
index 6aae226..3c02f6a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
@@ -16,11 +16,13 @@
package com.android.settingslib.development;
+import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.UserManager;
import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
import android.support.v14.preference.SwitchPreference;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.preference.Preference;
@@ -95,6 +97,10 @@
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
+ if (isUserAMonkey()) {
+ return false;
+ }
+
if (TextUtils.equals(KEY_ENABLE_ADB, preference.getKey())) {
if (!isAdbEnabled()) {
showConfirmationDialog(preference);
@@ -117,4 +123,9 @@
LocalBroadcastManager.getInstance(mContext)
.sendBroadcast(new Intent(ACTION_ENABLE_ADB_STATE_CHANGED));
}
+
+ @VisibleForTesting
+ boolean isUserAMonkey() {
+ return ActivityManager.isUserAMonkey();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
index bcabff3..32fa01c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -21,6 +21,7 @@
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -119,6 +120,18 @@
}
@Test
+ public void handlePreferenceTreeClick_isMonkeyUser_shouldBeFalse() {
+ mController = spy(mController);
+ doReturn(true).when(mController).isUserAMonkey();
+ when(mUserManager.isAdminUser()).thenReturn(true);
+ mController.displayPreference(mScreen);
+
+ final boolean handled = mController.handlePreferenceTreeClick(mPreference);
+
+ assertThat(handled).isFalse();
+ }
+
+ @Test
public void updateState_settingsOn_shouldCheck() {
when(mUserManager.isAdminUser()).thenReturn(true);
Settings.Secure.putInt(mContext.getContentResolver(),
@@ -161,6 +174,7 @@
}
@Override
- public void dismissConfirmationDialog() {}
+ public void dismissConfirmationDialog() {
+ }
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 236fbfd..02a62e1 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -28,6 +28,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
@@ -112,4 +113,12 @@
}
return log;
}
+
+ public static void printlnRedactedText(@NonNull PrintWriter pw, @Nullable String text) {
+ if (text == null) {
+ pw.println("null");
+ } else {
+ pw.print(text.length()); pw.println("_chars");
+ }
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 6d3d792..dac4586 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -49,6 +49,8 @@
import com.android.internal.R;
import com.android.server.UiThread;
+import com.android.server.autofill.Helper;
+
import libcore.util.Objects;
import java.io.PrintWriter;
@@ -466,7 +468,8 @@
pw.print(prefix); pw.print("mCallback: "); pw.println(mCallback != null);
pw.print(prefix); pw.print("mListView: "); pw.println(mListView);
pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter != null);
- pw.print(prefix); pw.print("mFilterText: "); pw.println(mFilterText);
+ pw.print(prefix); pw.print("mFilterText: ");
+ Helper.printlnRedactedText(pw, mFilterText);
pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth);
pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight);
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 8d46d1e..35f83e4 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -60,9 +60,6 @@
// Set this to true to use debug default values.
static final boolean DB = false;
- // Set this to true to have the watchdog record kernel thread stacks when it fires
- static final boolean RECORD_KERNEL_THREADS = true;
-
// Note 1: Do not lower this value below thirty seconds without tightening the invoke-with
// timeout in com.android.internal.os.ZygoteConnection, or wrapped applications
// can trigger the watchdog.
@@ -509,11 +506,6 @@
// The system's been hanging for a minute, another second or two won't hurt much.
SystemClock.sleep(2000);
- // Pull our own kernel thread stacks as well if we're configured for that
- if (RECORD_KERNEL_THREADS) {
- dumpKernelStackTraces();
- }
-
// Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log
doSysRq('w');
doSysRq('l');
@@ -591,18 +583,6 @@
}
}
- private File dumpKernelStackTraces() {
- String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
- if (tracesPath == null || tracesPath.length() == 0) {
- return null;
- }
-
- native_dumpKernelStacks(tracesPath);
- return new File(tracesPath);
- }
-
- private native void native_dumpKernelStacks(String tracesPath);
-
public static final class OpenFdMonitor {
/**
* Number of FDs below the soft limit that we trigger a runtime restart at. This was
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e98bb1a..d2d9aab 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4043,10 +4043,14 @@
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
"updateUsageStats: comp=" + component + "res=" + resumed);
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
+ component.userId, component.realActivity.getPackageName(),
+ component.realActivity.getShortClassName(), resumed ? 1 : 0);
if (resumed) {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
UsageEvents.Event.MOVE_TO_FOREGROUND);
+
}
synchronized (stats) {
stats.noteActivityResumedLocked(component.app.uid);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index d6bd2b31..7eb922c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2476,7 +2476,10 @@
pw.println(" -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a");
pw.println(" common form is [-e <testrunner_flag> <value>[,<value>...]].");
pw.println(" -p <FILE>: write profiling data to <FILE>");
- pw.println(" -m: Write output as protobuf (machine readable)");
+ pw.println(" -m: Write output as protobuf to stdout (machine readable)");
+ pw.println(" -f <Optional PATH/TO/FILE>: Write output as protobuf to a file (machine");
+ pw.println(" readable). If path is not specified, default directory and file name will");
+ pw.println(" be used: /sdcard/instrument-logs/log-yyyyMMdd-hhmmss-SSS.instrumentation_data_proto");
pw.println(" -w: wait for instrumentation to finish before returning. Required for");
pw.println(" test runners.");
pw.println(" --user <USER_ID> | current: Specify user instrumentation runs in;");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 2f0b649..481575e 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -77,7 +77,6 @@
import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET;
import static android.os.Build.VERSION_CODES.HONEYCOMB;
import static android.os.Build.VERSION_CODES.O;
-import static android.os.Build.VERSION_CODES.O_MR1;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.WindowManagerPolicy.NAV_BAR_LEFT;
@@ -886,6 +885,7 @@
Entry ent = AttributeCache.instance().get(packageName,
realTheme, com.android.internal.R.styleable.Window, userId);
+
if (ent != null) {
fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array);
hasWallpaper = ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
@@ -2119,11 +2119,6 @@
}
void setRequestedOrientation(int requestedOrientation) {
- if (ActivityInfo.isFixedOrientation(requestedOrientation) && !fullscreen
- && appInfo.targetSdkVersion >= O_MR1) {
- throw new IllegalStateException("Only fullscreen activities can request orientation");
- }
-
final int displayId = getDisplayId();
final Configuration displayConfig =
mStackSupervisor.getDisplayOverrideConfiguration(displayId);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index db12ae2..52f9702 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -23,6 +23,7 @@
import android.net.wifi.WifiActivityEnergyInfo;
import android.os.PowerSaveState;
import android.os.BatteryStats;
+import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -177,9 +178,22 @@
}
public void publish() {
+ LocalServices.addService(BatteryStatsInternal.class, new LocalService());
ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
}
+ private final class LocalService extends BatteryStatsInternal {
+ @Override
+ public String[] getWifiIfaces() {
+ return mStats.getWifiIfaces().clone();
+ }
+
+ @Override
+ public String[] getMobileIfaces() {
+ return mStats.getMobileIfaces().clone();
+ }
+ }
+
private static void awaitUninterruptibly(Future<?> future) {
while (true) {
try {
diff --git a/services/core/java/com/android/server/am/RunningTasks.java b/services/core/java/com/android/server/am/RunningTasks.java
index 400b03a..c860df8 100644
--- a/services/core/java/com/android/server/am/RunningTasks.java
+++ b/services/core/java/com/android/server/am/RunningTasks.java
@@ -47,8 +47,10 @@
void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType,
@WindowingMode int ignoreWindowingMode, SparseArray<ActivityDisplay> activityDisplays,
int callingUid, boolean allowed) {
- // For each stack on each display, add the tasks into the sorted set and then pull the first
- // {@param maxNum} from the set
+ // Return early if there are no tasks to fetch
+ if (maxNum <= 0) {
+ return;
+ }
// Gather all of the tasks across all of the tasks, and add them to the sorted set
mTmpSortedSet.clear();
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 6e1c21e..c4e6ff6 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -163,10 +163,6 @@
};
private SyncManager getSyncManager() {
- if (SystemProperties.getBoolean("config.disable_network", false)) {
- return null;
- }
-
synchronized(mSyncManagerLock) {
try {
// Try to create the SyncManager, return null if it fails (e.g. the disk is full).
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 4cf35bc..660659f 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -803,18 +803,6 @@
}
};
mGnssMetrics = new GnssMetrics();
-
- /*
- * A cycle of native_init() and native_cleanup() is needed so that callbacks are registered
- * after bootup even when location is disabled. This will allow Emergency SUPL to work even
- * when location is disabled before device restart.
- * */
- boolean isInitialized = native_init();
- if(!isInitialized) {
- Log.d(TAG, "Failed to initialize at bootup");
- } else {
- native_cleanup();
- }
}
/**
@@ -2272,6 +2260,19 @@
* this handler.
*/
private void handleInitialize() {
+ /*
+ * A cycle of native_init() and native_cleanup() is needed so that callbacks are
+ * registered after bootup even when location is disabled.
+ * This will allow Emergency SUPL to work even when location is disabled before device
+ * restart.
+ */
+ boolean isInitialized = native_init();
+ if(!isInitialized) {
+ Log.w(TAG, "Native initialization failed at bootup");
+ } else {
+ native_cleanup();
+ }
+
// load default GPS configuration
// (this configuration might change in the future based on SIM changes)
reloadGpsProperties(mContext, mProperties);
diff --git a/services/core/java/com/android/server/media/AudioPlaybackMonitor.java b/services/core/java/com/android/server/media/AudioPlaybackMonitor.java
new file mode 100644
index 0000000..791ee82
--- /dev/null
+++ b/services/core/java/com/android/server/media/AudioPlaybackMonitor.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2017 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.server.media;
+
+import android.content.Context;
+import android.media.AudioManager.AudioPlaybackCallback;
+import android.media.AudioPlaybackConfiguration;
+import android.media.IAudioService;
+import android.media.IPlaybackConfigDispatcher;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.IntArray;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Monitors changes in audio playback, and notify the newly started audio playback through the
+ * {@link OnAudioPlaybackStartedListener} and the activeness change through the
+ * {@link OnAudioPlaybackActiveStateListener}.
+ */
+class AudioPlaybackMonitor extends IPlaybackConfigDispatcher.Stub {
+ private static boolean DEBUG = MediaSessionService.DEBUG;
+ private static String TAG = "AudioPlaybackMonitor";
+
+ private static AudioPlaybackMonitor sInstance;
+
+ /**
+ * Called when audio playback is started for a given UID.
+ */
+ interface OnAudioPlaybackStartedListener {
+ void onAudioPlaybackStarted(int uid);
+ }
+
+ /**
+ * Called when audio player state is changed.
+ */
+ interface OnAudioPlayerActiveStateChangedListener {
+ void onAudioPlayerActiveStateChanged(int uid, boolean active);
+ }
+
+ private final Object mLock = new Object();
+ private final Context mContext;
+ private final List<OnAudioPlaybackStartedListener> mAudioPlaybackStartedListeners
+ = new ArrayList<>();
+ private final List<OnAudioPlayerActiveStateChangedListener>
+ mAudioPlayerActiveStateChangedListeners = new ArrayList<>();
+ private final Map<Integer, Integer> mAudioPlaybackStates = new HashMap<>();
+ private final Set<Integer> mActiveAudioPlaybackClientUids = new HashSet<>();
+
+ // Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video)
+ // The UID whose audio playback becomes active at the last comes first.
+ // TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.
+ private final IntArray mSortedAudioPlaybackClientUids = new IntArray();
+
+ static AudioPlaybackMonitor getInstance(Context context, IAudioService audioService) {
+ if (sInstance == null) {
+ sInstance = new AudioPlaybackMonitor(context, audioService);
+ }
+ return sInstance;
+ }
+
+ private AudioPlaybackMonitor(Context context, IAudioService audioService) {
+ mContext = context;
+ try {
+ audioService.registerPlaybackCallback(this);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Failed to register playback callback", e);
+ }
+ }
+
+ /**
+ * Called when the {@link AudioPlaybackConfiguration} is updated.
+ * <p>If an app starts audio playback, the app's local media session will be the media button
+ * session. If the app has multiple media sessions, the playback active local session will be
+ * picked.
+ *
+ * @param configs List of the current audio playback configuration
+ */
+ @Override
+ public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
+ boolean flush) {
+ if (flush) {
+ Binder.flushPendingCommands();
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ List<Integer> newActiveAudioPlaybackClientUids = new ArrayList<>();
+ List<OnAudioPlayerActiveStateChangedListener> audioPlayerActiveStateChangedListeners;
+ List<OnAudioPlaybackStartedListener> audioPlaybackStartedListeners;
+ synchronized (mLock) {
+ // Update mActiveAudioPlaybackClientUids and mSortedAudioPlaybackClientUids,
+ // and find newly activated audio playbacks.
+ mActiveAudioPlaybackClientUids.clear();
+ for (AudioPlaybackConfiguration config : configs) {
+ // Ignore inactive (i.e. not playing) or PLAYER_TYPE_JAM_SOUNDPOOL
+ // (i.e. playback from the SoundPool class which is only for sound effects)
+ // playback.
+ // Note that we shouldn't ignore PLAYER_TYPE_UNKNOWN because it might be OEM
+ // specific audio/video players.
+ if (!config.isActive() || config.getPlayerType()
+ == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+ continue;
+ }
+
+ mActiveAudioPlaybackClientUids.add(config.getClientUid());
+ Integer oldState = mAudioPlaybackStates.get(config.getPlayerInterfaceId());
+ if (!isActiveState(oldState)) {
+ if (DEBUG) {
+ Log.d(TAG, "Found a new active media playback. " +
+ AudioPlaybackConfiguration.toLogFriendlyString(config));
+ }
+ // New active audio playback.
+ newActiveAudioPlaybackClientUids.add(config.getClientUid());
+ int index = mSortedAudioPlaybackClientUids.indexOf(config.getClientUid());
+ if (index == 0) {
+ // It's the lastly played music app already. Skip updating.
+ continue;
+ } else if (index > 0) {
+ mSortedAudioPlaybackClientUids.remove(index);
+ }
+ mSortedAudioPlaybackClientUids.add(0, config.getClientUid());
+ }
+ }
+ audioPlayerActiveStateChangedListeners = new ArrayList<>(
+ mAudioPlayerActiveStateChangedListeners);
+ audioPlaybackStartedListeners = new ArrayList<>(mAudioPlaybackStartedListeners);
+ }
+ // Notify the change of audio playback states.
+ for (AudioPlaybackConfiguration config : configs) {
+ boolean wasActive = isActiveState(
+ mAudioPlaybackStates.get(config.getPlayerInterfaceId()));
+ boolean isActive = config.isActive();
+ if (wasActive != isActive) {
+ for (OnAudioPlayerActiveStateChangedListener listener
+ : audioPlayerActiveStateChangedListeners) {
+ listener.onAudioPlayerActiveStateChanged(config.getClientUid(),
+ isActive);
+ }
+ }
+ }
+ // Notify the start of audio playback
+ for (int uid : newActiveAudioPlaybackClientUids) {
+ for (OnAudioPlaybackStartedListener listener : audioPlaybackStartedListeners) {
+ listener.onAudioPlaybackStarted(uid);
+ }
+ }
+ mAudioPlaybackStates.clear();
+ for (AudioPlaybackConfiguration config : configs) {
+ mAudioPlaybackStates.put(config.getPlayerInterfaceId(), config.getPlayerState());
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Registers OnAudioPlaybackStartedListener.
+ */
+ public void registerOnAudioPlaybackStartedListener(OnAudioPlaybackStartedListener listener) {
+ synchronized (mLock) {
+ mAudioPlaybackStartedListeners.add(listener);
+ }
+ }
+
+ /**
+ * Unregisters OnAudioPlaybackStartedListener.
+ */
+ public void unregisterOnAudioPlaybackStartedListener(OnAudioPlaybackStartedListener listener) {
+ synchronized (mLock) {
+ mAudioPlaybackStartedListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Registers OnAudioPlayerActiveStateChangedListener.
+ */
+ public void registerOnAudioPlayerActiveStateChangedListener(
+ OnAudioPlayerActiveStateChangedListener listener) {
+ synchronized (mLock) {
+ mAudioPlayerActiveStateChangedListeners.add(listener);
+ }
+ }
+
+ /**
+ * Unregisters OnAudioPlayerActiveStateChangedListener.
+ */
+ public void unregisterOnAudioPlayerActiveStateChangedListener(
+ OnAudioPlayerActiveStateChangedListener listener) {
+ synchronized (mLock) {
+ mAudioPlayerActiveStateChangedListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Returns the sorted list of UIDs that have had active audio playback. (i.e. playing an
+ * audio/video) The UID whose audio playback becomes active at the last comes first.
+ */
+ public IntArray getSortedAudioPlaybackClientUids() {
+ IntArray sortedAudioPlaybackClientUids = new IntArray();
+ synchronized (mLock) {
+ sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids);
+ }
+ return sortedAudioPlaybackClientUids;
+ }
+
+ /**
+ * Returns if the audio playback is active for the uid.
+ */
+ public boolean isPlaybackActive(int uid) {
+ synchronized (mLock) {
+ return mActiveAudioPlaybackClientUids.contains(uid);
+ }
+ }
+
+ /**
+ * Cleans up the sorted list of audio playback client UIDs with given {@param
+ * mediaButtonSessionUid}.
+ * <p>UIDs whose audio playback started after the media button session's audio playback
+ * cannot be the lastly played media app. So they won't needed anymore.
+ *
+ * @param mediaButtonSessionUid UID of the media button session.
+ */
+ public void cleanUpAudioPlaybackUids(int mediaButtonSessionUid) {
+ synchronized (mLock) {
+ int userId = UserHandle.getUserId(mediaButtonSessionUid);
+ for (int i = mSortedAudioPlaybackClientUids.size() - 1; i >= 0; i--) {
+ if (mSortedAudioPlaybackClientUids.get(i) == mediaButtonSessionUid) {
+ break;
+ }
+ int uid = mSortedAudioPlaybackClientUids.get(i);
+ if (userId == UserHandle.getUserId(uid) && !isPlaybackActive(uid)) {
+ // Clean up unnecessary UIDs.
+ // It doesn't need to be managed profile aware because it's just to prevent
+ // the list from increasing indefinitely. The media button session updating
+ // shouldn't be affected by cleaning up.
+ mSortedAudioPlaybackClientUids.remove(i);
+ }
+ }
+ }
+ }
+
+ /**
+ * Dumps {@link AudioPlaybackMonitor}.
+ */
+ public void dump(PrintWriter pw, String prefix) {
+ synchronized (mLock) {
+ pw.println(prefix + "Audio playback (lastly played comes first)");
+ String indent = prefix + " ";
+ for (int i = 0; i < mSortedAudioPlaybackClientUids.size(); i++) {
+ int uid = mSortedAudioPlaybackClientUids.get(i);
+ pw.print(indent + "uid=" + uid + " packages=");
+ String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packages != null && packages.length > 0) {
+ for (int j = 0; j < packages.length; j++) {
+ pw.print(packages[j] + " ");
+ }
+ }
+ pw.println();
+ }
+ }
+ }
+
+ private boolean isActiveState(Integer state) {
+ return state != null && state.equals(AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
+ }
+}
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
deleted file mode 100644
index 110f26d..0000000
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.media;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.media.AudioPlaybackConfiguration;
-import android.media.IAudioService;
-import android.media.IPlaybackConfigDispatcher;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.IntArray;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.io.PrintWriter;
-import java.util.HashSet;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Monitors the state changes of audio players.
- */
-class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub {
- private static boolean DEBUG = MediaSessionService.DEBUG;
- private static String TAG = "AudioPlayerStateMonitor";
-
- private static AudioPlayerStateMonitor sInstance = new AudioPlayerStateMonitor();
-
- /**
- * Called when the state of audio player is changed.
- */
- interface OnAudioPlayerStateChangedListener {
- void onAudioPlayerStateChanged(
- int uid, int prevState, @Nullable AudioPlaybackConfiguration config);
- }
-
- private final static class MessageHandler extends Handler {
- private static final int MSG_AUDIO_PLAYER_STATE_CHANGED = 1;
-
- private final OnAudioPlayerStateChangedListener mListsner;
-
- public MessageHandler(Looper looper, OnAudioPlayerStateChangedListener listener) {
- super(looper);
- mListsner = listener;
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_AUDIO_PLAYER_STATE_CHANGED:
- mListsner.onAudioPlayerStateChanged(
- msg.arg1, msg.arg2, (AudioPlaybackConfiguration) msg.obj);
- break;
- }
- }
-
- public void sendAudioPlayerStateChangedMessage(int uid, int prevState,
- AudioPlaybackConfiguration config) {
- obtainMessage(MSG_AUDIO_PLAYER_STATE_CHANGED, uid, prevState, config).sendToTarget();
- }
- }
-
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private final Map<OnAudioPlayerStateChangedListener, MessageHandler> mListenerMap =
- new HashMap<>();
- @GuardedBy("mLock")
- private final Map<Integer, Integer> mAudioPlayerStates = new HashMap<>();
- @GuardedBy("mLock")
- private final Map<Integer, HashSet<Integer>> mAudioPlayersForUid = new HashMap<>();
- // Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video)
- // The UID whose audio playback becomes active at the last comes first.
- // TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.
- @GuardedBy("mLock")
- private final IntArray mSortedAudioPlaybackClientUids = new IntArray();
-
- @GuardedBy("mLock")
- private boolean mRegisteredToAudioService;
-
- static AudioPlayerStateMonitor getInstance() {
- return sInstance;
- }
-
- private AudioPlayerStateMonitor() {
- }
-
- /**
- * Called when the {@link AudioPlaybackConfiguration} is updated.
- * <p>If an app starts audio playback, the app's local media session will be the media button
- * session. If the app has multiple media sessions, the playback active local session will be
- * picked.
- *
- * @param configs List of the current audio playback configuration
- */
- @Override
- public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
- boolean flush) {
- if (flush) {
- Binder.flushPendingCommands();
- }
- final long token = Binder.clearCallingIdentity();
- try {
- final Map<Integer, Integer> prevAudioPlayerStates = new HashMap<>(mAudioPlayerStates);
- final Map<Integer, HashSet<Integer>> prevAudioPlayersForUid =
- new HashMap<>(mAudioPlayersForUid);
- synchronized (mLock) {
- mAudioPlayerStates.clear();
- mAudioPlayersForUid.clear();
- for (AudioPlaybackConfiguration config : configs) {
- int pii = config.getPlayerInterfaceId();
- int uid = config.getClientUid();
- mAudioPlayerStates.put(pii, config.getPlayerState());
- HashSet<Integer> players = mAudioPlayersForUid.get(uid);
- if (players == null) {
- players = new HashSet<Integer>();
- players.add(pii);
- mAudioPlayersForUid.put(uid, players);
- } else {
- players.add(pii);
- }
- }
- for (AudioPlaybackConfiguration config : configs) {
- if (!config.isActive()) {
- continue;
- }
-
- int uid = config.getClientUid();
- if (!isActiveState(prevAudioPlayerStates.get(config.getPlayerInterfaceId()))) {
- if (DEBUG) {
- Log.d(TAG, "Found a new active media playback. " +
- AudioPlaybackConfiguration.toLogFriendlyString(config));
- }
- // New active audio playback.
- int index = mSortedAudioPlaybackClientUids.indexOf(uid);
- if (index == 0) {
- // It's the lastly played music app already. Skip updating.
- continue;
- } else if (index > 0) {
- mSortedAudioPlaybackClientUids.remove(index);
- }
- mSortedAudioPlaybackClientUids.add(0, uid);
- }
- }
- // Notify the change of audio player states.
- for (AudioPlaybackConfiguration config : configs) {
- Integer prevState = prevAudioPlayerStates.get(config.getPlayerInterfaceId());
- if (prevState == null || prevState != config.getPlayerState()) {
- sendAudioPlayerStateChangedMessageLocked(
- config.getClientUid(), prevState, config);
- }
- }
- for (Integer prevUid : prevAudioPlayersForUid.keySet()) {
- // If all players for prevUid is removed, notify the prev state was
- // PLAYER_STATE_STARTED only when there were a player whose state was
- // PLAYER_STATE_STARTED, otherwise any inactive state is okay to notify.
- if (!mAudioPlayersForUid.containsKey(prevUid)) {
- Set<Integer> players = mAudioPlayersForUid.get(prevUid);
- int prevState = AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN;
- for (int pii : players) {
- Integer state = prevAudioPlayerStates.get(pii);
- if (state == null) {
- continue;
- }
- if (state == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
- prevState = state;
- break;
- } else if (prevState
- == AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN) {
- prevState = state;
- }
- }
- sendAudioPlayerStateChangedMessageLocked(prevUid, prevState, null);
- }
- }
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Registers OnAudioPlayerStateChangedListener.
- */
- public void registerListener(OnAudioPlayerStateChangedListener listener, Handler handler) {
- synchronized (mLock) {
- mListenerMap.put(listener, new MessageHandler((handler == null) ?
- Looper.myLooper() : handler.getLooper(), listener));
- }
- }
-
- /**
- * Unregisters OnAudioPlayerStateChangedListener.
- */
- public void unregisterListener(OnAudioPlayerStateChangedListener listener) {
- synchronized (mLock) {
- mListenerMap.remove(listener);
- }
- }
-
- /**
- * Returns the sorted list of UIDs that have had active audio playback. (i.e. playing an
- * audio/video) The UID whose audio playback becomes active at the last comes first.
- */
- public IntArray getSortedAudioPlaybackClientUids() {
- IntArray sortedAudioPlaybackClientUids = new IntArray();
- synchronized (mLock) {
- sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids);
- }
- return sortedAudioPlaybackClientUids;
- }
-
- /**
- * Returns if the audio playback is active for the uid.
- */
- public boolean isPlaybackActive(int uid) {
- synchronized (mLock) {
- Set<Integer> players = mAudioPlayersForUid.get(uid);
- if (players == null) {
- return false;
- }
- for (Integer pii : players) {
- if (isActiveState(mAudioPlayerStates.get(pii))) {
- return true;
- }
- }
- return false;
- }
- }
-
- /**
- * Cleans up the sorted list of audio playback client UIDs with given {@param
- * mediaButtonSessionUid}.
- * <p>UIDs whose audio playback are inactive and have started before the media button session's
- * audio playback cannot be the lastly played media app. So they won't needed anymore.
- *
- * @param mediaButtonSessionUid UID of the media button session.
- */
- public void cleanUpAudioPlaybackUids(int mediaButtonSessionUid) {
- synchronized (mLock) {
- int userId = UserHandle.getUserId(mediaButtonSessionUid);
- for (int i = mSortedAudioPlaybackClientUids.size() - 1; i >= 0; i--) {
- if (mSortedAudioPlaybackClientUids.get(i) == mediaButtonSessionUid) {
- break;
- }
- int uid = mSortedAudioPlaybackClientUids.get(i);
- if (userId == UserHandle.getUserId(uid) && !isPlaybackActive(uid)) {
- // Clean up unnecessary UIDs.
- // It doesn't need to be managed profile aware because it's just to prevent
- // the list from increasing indefinitely. The media button session updating
- // shouldn't be affected by cleaning up.
- mSortedAudioPlaybackClientUids.remove(i);
- }
- }
- }
- }
-
- /**
- * Dumps {@link AudioPlayerStateMonitor}.
- */
- public void dump(Context context, PrintWriter pw, String prefix) {
- synchronized (mLock) {
- pw.println(prefix + "Audio playback (lastly played comes first)");
- String indent = prefix + " ";
- for (int i = 0; i < mSortedAudioPlaybackClientUids.size(); i++) {
- int uid = mSortedAudioPlaybackClientUids.get(i);
- pw.print(indent + "uid=" + uid + " packages=");
- String[] packages = context.getPackageManager().getPackagesForUid(uid);
- if (packages != null && packages.length > 0) {
- for (int j = 0; j < packages.length; j++) {
- pw.print(packages[j] + " ");
- }
- }
- pw.println();
- }
- }
- }
-
- public void registerSelfIntoAudioServiceIfNeeded(IAudioService audioService) {
- synchronized (mLock) {
- try {
- if (!mRegisteredToAudioService) {
- audioService.registerPlaybackCallback(this);
- mRegisteredToAudioService = true;
- }
- } catch (RemoteException e) {
- Log.wtf(TAG, "Failed to register playback callback", e);
- mRegisteredToAudioService = false;
- }
- }
- }
-
- private void sendAudioPlayerStateChangedMessageLocked(
- final int uid, final int prevState, final AudioPlaybackConfiguration config) {
- for (MessageHandler messageHandler : mListenerMap.values()) {
- messageHandler.sendAudioPlayerStateChangedMessage(uid, prevState, config);
- }
- }
-
- private static boolean isActiveState(Integer state) {
- return state != null && state.equals(AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
- }
-}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 3c9e1d4..1cfd5f0 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -19,14 +19,12 @@
import com.android.internal.util.DumpUtils;
import com.android.server.Watchdog;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.media.AudioPlaybackConfiguration;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.IAudioRoutesObserver;
@@ -98,8 +96,7 @@
private int mCurrentUserId = -1;
private boolean mGlobalBluetoothA2dpOn = false;
private final IAudioService mAudioService;
- private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
- private final Handler mHandler = new Handler();
+ private final AudioPlaybackMonitor mAudioPlaybackMonitor;
private final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
public MediaRouterService(Context context) {
@@ -109,57 +106,31 @@
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
- mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
- mAudioPlayerStateMonitor.registerListener(
- new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() {
- static final long WAIT_MS = 500;
- final Runnable mRestoreBluetoothA2dpRunnable = new Runnable() {
- @Override
- public void run() {
- restoreBluetoothA2dp();
- }
- };
-
+ mAudioPlaybackMonitor = AudioPlaybackMonitor.getInstance(context, mAudioService);
+ mAudioPlaybackMonitor.registerOnAudioPlayerActiveStateChangedListener(
+ new AudioPlaybackMonitor.OnAudioPlayerActiveStateChangedListener() {
@Override
- public void onAudioPlayerStateChanged(
- int uid, int prevState, @Nullable AudioPlaybackConfiguration config) {
- int restoreUid = -1;
- boolean active = config == null ? false : config.isActive();
+ public void onAudioPlayerActiveStateChanged(int uid, boolean active) {
if (active) {
- restoreUid = uid;
- } else if (prevState != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
- // Noting to do if the prev state is not an active state.
- return;
+ restoreRoute(uid);
} else {
IntArray sortedAudioPlaybackClientUids =
- mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
- for (int i = 0; i < sortedAudioPlaybackClientUids.size(); ++i) {
- if (mAudioPlayerStateMonitor.isPlaybackActive(
+ mAudioPlaybackMonitor.getSortedAudioPlaybackClientUids();
+ boolean restored = false;
+ for (int i = 0; i < sortedAudioPlaybackClientUids.size(); i++) {
+ if (mAudioPlaybackMonitor.isPlaybackActive(
sortedAudioPlaybackClientUids.get(i))) {
- restoreUid = sortedAudioPlaybackClientUids.get(i);
+ restoreRoute(sortedAudioPlaybackClientUids.get(i));
+ restored = true;
break;
}
}
- }
-
- mHandler.removeCallbacks(mRestoreBluetoothA2dpRunnable);
- if (restoreUid >= 0) {
- restoreRoute(restoreUid);
- if (DEBUG) {
- Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid
- + " active " + active + " restoring " + restoreUid);
- }
- } else {
- mHandler.postDelayed(mRestoreBluetoothA2dpRunnable, WAIT_MS);
- if (DEBUG) {
- Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid
- + " active " + active + " delaying");
+ if (!restored) {
+ restoreBluetoothA2dp();
}
}
}
- }, mHandler);
- mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
-
+ });
AudioRoutesInfo audioRoutes = null;
try {
audioRoutes = mAudioService.startWatchingRoutes(new IAudioRoutesObserver.Stub() {
@@ -290,14 +261,9 @@
final long token = Binder.clearCallingIdentity();
try {
- ClientRecord clientRecord;
synchronized (mLock) {
- clientRecord = mAllClientRecords.get(client.asBinder());
+ return isPlaybackActiveLocked(client);
}
- if (clientRecord != null) {
- return mAudioPlayerStateMonitor.isPlaybackActive(clientRecord.mUid);
- }
- return false;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -514,6 +480,14 @@
return null;
}
+ private boolean isPlaybackActiveLocked(IMediaRouterClient client) {
+ ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
+ if (clientRecord != null) {
+ return mAudioPlaybackMonitor.isPlaybackActive(clientRecord.mUid);
+ }
+ return false;
+ }
+
private void setDiscoveryRequestLocked(IMediaRouterClient client,
int routeTypes, boolean activeScan) {
final IBinder binder = client.asBinder();
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index f6a81d0..aa65244 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -16,7 +16,6 @@
package com.android.server.media;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.KeyguardManager;
@@ -32,7 +31,7 @@
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.media.AudioManager;
-import android.media.AudioPlaybackConfiguration;
+import android.media.AudioManagerInternal;
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.IRemoteVolumeController;
@@ -69,6 +68,7 @@
import android.view.ViewConfiguration;
import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
import com.android.server.Watchdog.Monitor;
@@ -104,6 +104,7 @@
private KeyguardManager mKeyguardManager;
private IAudioService mAudioService;
+ private AudioManagerInternal mAudioManagerInternal;
private ContentResolver mContentResolver;
private SettingsObserver mSettingsObserver;
private INotificationManager mNotificationManager;
@@ -113,7 +114,7 @@
// It's always not null after the MediaSessionService is started.
private FullUserRecord mCurrentFullUserRecord;
private MediaSessionRecord mGlobalPrioritySession;
- private AudioPlayerStateMonitor mAudioPlayerStateMonitor;
+ private AudioPlaybackMonitor mAudioPlaybackMonitor;
// Used to notify system UI when remote volume was changed. TODO find a
// better way to handle this.
@@ -136,16 +137,11 @@
mKeyguardManager =
(KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
mAudioService = getAudioService();
- mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
- mAudioPlayerStateMonitor.registerListener(
- new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() {
+ mAudioPlaybackMonitor = AudioPlaybackMonitor.getInstance(getContext(), mAudioService);
+ mAudioPlaybackMonitor.registerOnAudioPlaybackStartedListener(
+ new AudioPlaybackMonitor.OnAudioPlaybackStartedListener() {
@Override
- public void onAudioPlayerStateChanged(
- int uid, int prevState, @Nullable AudioPlaybackConfiguration config) {
- if (config == null || !config.isActive() || config.getPlayerType()
- == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
- return;
- }
+ public void onAudioPlaybackStarted(int uid) {
synchronized (mLock) {
FullUserRecord user =
getFullUserRecordLocked(UserHandle.getUserId(uid));
@@ -154,8 +150,8 @@
}
}
}
- }, null /* handler */);
- mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
+ });
+ mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
mContentResolver = getContext().getContentResolver();
mSettingsObserver = new SettingsObserver();
mSettingsObserver.observe();
@@ -654,7 +650,7 @@
public FullUserRecord(int fullUserId) {
mFullUserId = fullUserId;
- mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this);
+ mPriorityStack = new MediaSessionStack(mAudioPlaybackMonitor, this);
// Restore the remembered media button receiver before the boot.
String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver,
Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
@@ -1313,7 +1309,7 @@
for (int i = 0; i < count; i++) {
mUserRecords.valueAt(i).dumpLocked(pw, "");
}
- mAudioPlayerStateMonitor.dump(getContext(), pw, "");
+ mAudioPlaybackMonitor.dump(pw, "");
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 719ec36..d9fe72e 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -75,7 +75,7 @@
*/
private final List<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
- private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
+ private final AudioPlaybackMonitor mAudioPlaybackMonitor;
private final OnMediaButtonSessionChangedListener mOnMediaButtonSessionChangedListener;
/**
@@ -84,6 +84,7 @@
*/
private MediaSessionRecord mMediaButtonSession;
+ private MediaSessionRecord mCachedDefault;
private MediaSessionRecord mCachedVolumeDefault;
/**
@@ -92,8 +93,8 @@
private final SparseArray<ArrayList<MediaSessionRecord>> mCachedActiveLists =
new SparseArray<>();
- MediaSessionStack(AudioPlayerStateMonitor monitor, OnMediaButtonSessionChangedListener listener) {
- mAudioPlayerStateMonitor = monitor;
+ MediaSessionStack(AudioPlaybackMonitor monitor, OnMediaButtonSessionChangedListener listener) {
+ mAudioPlaybackMonitor = monitor;
mOnMediaButtonSessionChangedListener = listener;
}
@@ -186,13 +187,13 @@
if (DEBUG) {
Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2));
}
- IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
+ IntArray audioPlaybackUids = mAudioPlaybackMonitor.getSortedAudioPlaybackClientUids();
for (int i = 0; i < audioPlaybackUids.size(); i++) {
MediaSessionRecord mediaButtonSession =
findMediaButtonSession(audioPlaybackUids.get(i));
if (mediaButtonSession != null) {
// Found the media button session.
- mAudioPlayerStateMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid());
+ mAudioPlaybackMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid());
if (mMediaButtonSession != mediaButtonSession) {
updateMediaButtonSession(mediaButtonSession);
}
@@ -215,7 +216,7 @@
for (MediaSessionRecord session : mSessions) {
if (uid == session.getUid()) {
if (session.getPlaybackState() != null && session.isPlaybackActive() ==
- mAudioPlayerStateMonitor.isPlaybackActive(session.getUid())) {
+ mAudioPlaybackMonitor.isPlaybackActive(session.getUid())) {
// If there's a media session whose PlaybackState matches
// the audio playback state, return it immediately.
return session;
@@ -375,6 +376,7 @@
}
private void clearCache(int userId) {
+ mCachedDefault = null;
mCachedVolumeDefault = null;
mCachedActiveLists.remove(userId);
// mCachedActiveLists may also include the list of sessions for UserHandle.USER_ALL,
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 86a1c03..50ac409 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -209,7 +209,7 @@
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
// flags.
- final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());
+ final int dexoptFlags = getDexFlags(pkg, compilerFilter, options);
for (String dexCodeIsa : dexCodeInstructionSets) {
int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
@@ -349,8 +349,7 @@
dexUseInfo.isUsedByOtherApps());
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
// Secondary dex files are currently not compiled at boot.
- int dexoptFlags = getDexFlags(info, compilerFilter, /* bootComplete */ true)
- | DEXOPT_SECONDARY_DEX;
+ int dexoptFlags = getDexFlags(info, compilerFilter, options) | DEXOPT_SECONDARY_DEX;
// Check the app storage and add the appropriate flags.
if (info.deviceProtectedDataDir != null &&
FileUtils.contains(info.deviceProtectedDataDir, path)) {
@@ -486,11 +485,11 @@
* filter.
*/
private int getDexFlags(PackageParser.Package pkg, String compilerFilter,
- boolean bootComplete) {
- return getDexFlags(pkg.applicationInfo, compilerFilter, bootComplete);
+ DexoptOptions options) {
+ return getDexFlags(pkg.applicationInfo, compilerFilter, options);
}
- private int getDexFlags(ApplicationInfo info, String compilerFilter, boolean bootComplete) {
+ private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
int flags = info.flags;
boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
// Profile guide compiled oat files should not be public.
@@ -501,7 +500,8 @@
(isPublic ? DEXOPT_PUBLIC : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
| profileFlag
- | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0);
+ | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
+ | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
return adjustDexoptFlags(dexFlags);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 19b0d9b..cf9e6f3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -52,7 +52,16 @@
// Load the property for the given reason and check for validity. This will throw an
// exception in case the reason or value are invalid.
private static String getAndCheckValidity(int reason) {
- String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
+ String sysPropName = getSystemPropertyName(reason);
+ String sysPropValue;
+ // TODO: This is a temporary hack to keep marlin booting on aosp/master while we
+ // figure out how to deal with these system properties that currently appear on
+ // vendor.
+ if ("pm.dexopt.inactive".equals(sysPropName)) {
+ sysPropValue = "verify";
+ } else {
+ sysPropValue = SystemProperties.get(sysPropName);
+ }
if (sysPropValue == null || sysPropValue.isEmpty() ||
!DexFile.isValidCompilerFilter(sysPropValue)) {
throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 22d2bcf..41534cb 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -24,7 +24,8 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.content.IntentFilter;
+import android.net.NetworkStats;
+import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -37,15 +38,17 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
+import android.util.StatsLog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.os.KernelWakelockReader;
+import com.android.internal.os.KernelWakelockStats;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
import java.util.ArrayList;
import java.util.List;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.KernelWakelockReader;
-import com.android.internal.os.KernelWakelockStats;
-import com.android.server.SystemService;
-
import java.util.Map;
/**
@@ -65,7 +68,7 @@
private static final Object sStatsdLock = new Object();
private final PendingIntent mAnomalyAlarmIntent;
- private final PendingIntent mPollingAlarmIntent;
+ private final PendingIntent mPullingAlarmIntent;
private final BroadcastReceiver mAppUpdateReceiver;
private final BroadcastReceiver mUserUpdateReceiver;
@@ -76,8 +79,8 @@
mAnomalyAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(mContext, AnomalyAlarmReceiver.class), 0);
- mPollingAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
- new Intent(mContext, PollingAlarmReceiver.class), 0);
+ mPullingAlarmIntent = PendingIntent.getBroadcast(
+ mContext, 0, new Intent(mContext, PullingAlarmReceiver.class), 0);
mAppUpdateReceiver = new AppUpdateReceiver();
mUserUpdateReceiver = new BroadcastReceiver() {
@Override
@@ -144,15 +147,16 @@
public final static class AppUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
/**
* App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid
* waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
+ * If we can't find the value for EXTRA_REPLACING, we default to false.
*/
- if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED) &&
- intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)
+ && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
return; // Keep only replacing or normal add and remove.
}
+ Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
synchronized (sStatsdLock) {
if (sStatsd == null) {
Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
@@ -205,24 +209,25 @@
}
}
- public final static class PollingAlarmReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) Slog.d(TAG, "Time to poll something.");
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd to inform it of polling alarm firing");
- return;
- }
- try {
- // Two-way call to statsd to retain AlarmManager wakelock
- sStatsd.informPollAlarmFired();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to inform statsd of polling alarm firing", e);
- }
- }
- // AlarmManager releases its own wakelock here.
+ public final static class PullingAlarmReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG)
+ Slog.d(TAG, "Time to poll something.");
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing");
+ return;
+ }
+ try {
+ // Two-way call to statsd to retain AlarmManager wakelock
+ sStatsd.informPollAlarmFired();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to inform statsd of pulling alarm firing", e);
+ }
}
+ // AlarmManager releases its own wakelock here.
+ }
}
@Override // Binder call
@@ -253,32 +258,32 @@
}
@Override // Binder call
- public void setPollingAlarms(long timestampMs, long intervalMs) {
- enforceCallingPermission();
- if (DEBUG) Slog.d(TAG, "Setting polling alarm for " + timestampMs
- + " every " + intervalMs + "ms");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
- // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
- // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
- mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs,
- mPollingAlarmIntent);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
+ public void setPullingAlarms(long timestampMs, long intervalMs) {
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
+ // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
+ // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
+ mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs, mPullingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
}
@Override // Binder call
- public void cancelPollingAlarms() {
- enforceCallingPermission();
- if (DEBUG) Slog.d(TAG, "Cancelling polling alarm");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- mAlarmManager.cancel(mPollingAlarmIntent);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
+ public void cancelPullingAlarms() {
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG, "Cancelling pulling alarm");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.cancel(mPullingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
}
// These values must be kept in sync with cmd/statsd/StatsPullerManager.h.
@@ -288,35 +293,168 @@
private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+ private StatsLogEventWrapper[] addNetworkStats(int tag, NetworkStats stats, boolean withFGBG) {
+ List<StatsLogEventWrapper> ret = new ArrayList<>();
+ int size = stats.size();
+ NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
+ for (int j = 0; j < size; j++) {
+ stats.getValues(j, entry);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tag, withFGBG ? 6 : 5);
+ e.writeInt(entry.uid);
+ if (withFGBG) {
+ e.writeInt(entry.set);
+ }
+ e.writeLong(entry.rxBytes);
+ e.writeLong(entry.rxPackets);
+ e.writeLong(entry.txBytes);
+ e.writeLong(entry.txPackets);
+ ret.add(e);
+ }
+ return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+ }
+
+ /**
+ * Allows rollups per UID but keeping the set (foreground/background) slicing.
+ * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java
+ */
+ private NetworkStats rollupNetworkStatsByFGBG(NetworkStats stats) {
+ final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
+
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+ entry.iface = NetworkStats.IFACE_ALL;
+ entry.tag = NetworkStats.TAG_NONE;
+ entry.metered = NetworkStats.METERED_ALL;
+ entry.roaming = NetworkStats.ROAMING_ALL;
+
+ int size = stats.size();
+ NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values
+ for (int i = 0; i < size; i++) {
+ stats.getValues(i, recycle);
+
+ // Skip specific tags, since already counted in TAG_NONE
+ if (recycle.tag != NetworkStats.TAG_NONE) continue;
+
+ entry.set = recycle.set; // Allows slicing by background/foreground
+ entry.uid = recycle.uid;
+ entry.rxBytes = recycle.rxBytes;
+ entry.rxPackets = recycle.rxPackets;
+ entry.txBytes = recycle.txBytes;
+ entry.txPackets = recycle.txPackets;
+ // Operations purposefully omitted since we don't use them for statsd.
+ ret.combineValues(entry);
+ }
+ return ret;
+ }
+
@Override // Binder call
public StatsLogEventWrapper[] pullData(int pullCode) {
enforceCallingPermission();
- if (DEBUG) {
+ if (DEBUG)
Slog.d(TAG, "Pulling " + pullCode);
- }
- List<StatsLogEventWrapper> ret = new ArrayList<>();
switch (pullCode) {
- case PULL_CODE_KERNEL_WAKELOCKS: {
+ case StatsLog.WIFI_BYTES_TRANSFERRED: {
+ long token = Binder.clearCallingIdentity();
+ try {
+ // TODO: Consider caching the following call to get BatteryStatsInternal.
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getWifiIfaces();
+ if (ifaces.length == 0) {
+ return null;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ // Combine all the metrics per Uid into one record.
+ NetworkStats stats = nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
+ NetworkStats.TAG_NONE, null).groupedByUid();
+ return addNetworkStats(pullCode, stats, false);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ break;
+ }
+ case StatsLog.MOBILE_BYTES_TRANSFERRED: {
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getMobileIfaces();
+ if (ifaces.length == 0) {
+ return null;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ // Combine all the metrics per Uid into one record.
+ NetworkStats stats = nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
+ NetworkStats.TAG_NONE, null).groupedByUid();
+ return addNetworkStats(pullCode, stats, false);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ break;
+ }
+ case StatsLog.WIFI_BYTES_TRANSFERRED_BY_FG_BG: {
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getWifiIfaces();
+ if (ifaces.length == 0) {
+ return null;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ NetworkStats stats = rollupNetworkStatsByFGBG(
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
+ NetworkStats.TAG_NONE, null));
+ return addNetworkStats(pullCode, stats, true);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ break;
+ }
+ case StatsLog.MOBILE_BYTES_TRANSFERRED_BY_FG_BG: {
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getMobileIfaces();
+ if (ifaces.length == 0) {
+ return null;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ NetworkStats stats = rollupNetworkStatsByFGBG(
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
+ NetworkStats.TAG_NONE, null));
+ return addNetworkStats(pullCode, stats, true);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ break;
+ }
+ case StatsLog.KERNEL_WAKELOCKS_REPORTED: {
final KernelWakelockStats wakelockStats =
mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
+ List<StatsLogEventWrapper> ret = new ArrayList();
for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
String name = ent.getKey();
KernelWakelockStats.Entry kws = ent.getValue();
- StatsLogEventWrapper e = new StatsLogEventWrapper(101, 4);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(pullCode, 4);
+ e.writeString(name);
e.writeInt(kws.mCount);
e.writeInt(kws.mVersion);
e.writeLong(kws.mTotalTime);
- e.writeString(name);
ret.add(e);
}
- break;
+ return ret.toArray(new StatsLogEventWrapper[ret.size()]);
}
default:
Slog.w(TAG, "No such pollable data as " + pullCode);
return null;
}
- return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+ return null;
}
@Override // Binder call
@@ -440,7 +578,7 @@
mContext.unregisterReceiver(mAppUpdateReceiver);
mContext.unregisterReceiver(mUserUpdateReceiver);
cancelAnomalyAlarm();
- cancelPollingAlarms();
+ cancelPullingAlarms();
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index e873d32..98db80e 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -21,7 +21,6 @@
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.os.Build.VERSION_CODES.O_MR1;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
@@ -1101,52 +1100,54 @@
+ " from " + fromToken + " to " + this);
final long origId = Binder.clearCallingIdentity();
+ try {
+ // Transfer the starting window over to the new token.
+ startingData = fromToken.startingData;
+ startingSurface = fromToken.startingSurface;
+ startingDisplayed = fromToken.startingDisplayed;
+ fromToken.startingDisplayed = false;
+ startingWindow = tStartingWindow;
+ reportedVisible = fromToken.reportedVisible;
+ fromToken.startingData = null;
+ fromToken.startingSurface = null;
+ fromToken.startingWindow = null;
+ fromToken.startingMoved = true;
+ tStartingWindow.mToken = this;
+ tStartingWindow.mAppToken = this;
- // Transfer the starting window over to the new token.
- startingData = fromToken.startingData;
- startingSurface = fromToken.startingSurface;
- startingDisplayed = fromToken.startingDisplayed;
- fromToken.startingDisplayed = false;
- startingWindow = tStartingWindow;
- reportedVisible = fromToken.reportedVisible;
- fromToken.startingData = null;
- fromToken.startingSurface = null;
- fromToken.startingWindow = null;
- fromToken.startingMoved = true;
- tStartingWindow.mToken = this;
- tStartingWindow.mAppToken = this;
+ if (DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
+ "Removing starting " + tStartingWindow + " from " + fromToken);
+ fromToken.removeChild(tStartingWindow);
+ fromToken.postWindowRemoveStartingWindowCleanup(tStartingWindow);
+ fromToken.mHiddenSetFromTransferredStartingWindow = false;
+ addWindow(tStartingWindow);
- if (DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
- "Removing starting " + tStartingWindow + " from " + fromToken);
- fromToken.removeChild(tStartingWindow);
- fromToken.postWindowRemoveStartingWindowCleanup(tStartingWindow);
- fromToken.mHiddenSetFromTransferredStartingWindow = false;
- addWindow(tStartingWindow);
+ // Propagate other interesting state between the tokens. If the old token is displayed,
+ // we should immediately force the new one to be displayed. If it is animating, we need
+ // to move that animation to the new one.
+ if (fromToken.allDrawn) {
+ allDrawn = true;
+ deferClearAllDrawn = fromToken.deferClearAllDrawn;
+ }
+ if (fromToken.firstWindowDrawn) {
+ firstWindowDrawn = true;
+ }
+ if (!fromToken.hidden) {
+ hidden = false;
+ hiddenRequested = false;
+ mHiddenSetFromTransferredStartingWindow = true;
+ }
+ setClientHidden(fromToken.mClientHidden);
+ fromToken.mAppAnimator.transferCurrentAnimation(
+ mAppAnimator, tStartingWindow.mWinAnimator);
- // Propagate other interesting state between the tokens. If the old token is displayed,
- // we should immediately force the new one to be displayed. If it is animating, we need
- // to move that animation to the new one.
- if (fromToken.allDrawn) {
- allDrawn = true;
- deferClearAllDrawn = fromToken.deferClearAllDrawn;
+ mService.updateFocusedWindowLocked(
+ UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
+ getDisplayContent().setLayoutNeeded();
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- if (fromToken.firstWindowDrawn) {
- firstWindowDrawn = true;
- }
- if (!fromToken.hidden) {
- hidden = false;
- hiddenRequested = false;
- mHiddenSetFromTransferredStartingWindow = true;
- }
- setClientHidden(fromToken.mClientHidden);
- fromToken.mAppAnimator.transferCurrentAnimation(
- mAppAnimator, tStartingWindow.mWinAnimator);
-
- mService.updateFocusedWindowLocked(
- UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
- getDisplayContent().setLayoutNeeded();
- mService.mWindowPlacerLocked.performSurfacePlacement();
- Binder.restoreCallingIdentity(origId);
return true;
} else if (fromToken.startingData != null) {
// The previous app was getting ready to show a
@@ -1201,15 +1202,6 @@
*/
@Override
int getOrientation(int candidate) {
- // We do not allow non-fullscreen apps to influence orientation starting in O-MR1. While we
- // do throw an exception in {@link Activity#onCreate} and
- // {@link Activity#setRequestedOrientation}, we also ignore the orientation here so that
- // other calculations aren't affected.
- if (!fillsParent() && mTargetSdk >= O_MR1) {
- // Can't specify orientation if app doesn't fill parent.
- return SCREEN_ORIENTATION_UNSET;
- }
-
if (candidate == SCREEN_ORIENTATION_BEHIND) {
// Allow app to specify orientation regardless of its visibility state if the current
// candidate want us to use orientation behind. I.e. the visible app on-top of this one
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index 401547e..8fb2be8 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -119,7 +119,7 @@
} catch (Exception e) {
Slog.e(TAG_WM, "Exception creating Dim surface", e);
} finally {
- service.closeSurfaceTransaction();
+ service.closeSurfaceTransaction("DimLayer.constructSurface");
}
}
@@ -235,7 +235,7 @@
} catch (RuntimeException e) {
Slog.w(TAG, "Failure setting size", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("DimLayer.setBounds");
}
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fe34834..2f54e0e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1083,7 +1083,7 @@
mService.mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
} finally {
if (!inTransaction) {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setRotationUnchecked");
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG_WM, "<<< CLOSE TRANSACTION setRotationUnchecked");
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 69f557b..5ea0e1d 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -543,7 +543,7 @@
mDimLayer.setBounds(mTmpRect);
mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setResizeDimLayer");
}
}
mLastDimLayerRect.set(mTmpRect);
@@ -558,7 +558,7 @@
mService.openSurfaceTransaction();
mDimLayer.hide();
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setResizeDimLayer");
}
}
mLastDimLayerAlpha = 0f;
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 860ff38..79d46ce 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -185,7 +185,7 @@
surfaceControl.setLayerStack(display.getLayerStack());
surfaceControl.show();
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("performDrag");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
TAG_WM, "<<< CLOSE TRANSACTION performDrag");
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 9a955de..861fb44 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -458,7 +458,7 @@
+ mSurfaceControl + ": pos=(" +
(int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("notifyMoveLw");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
TAG_WM, "<<< CLOSE TRANSACTION notifyMoveLocked");
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index a710441..bcb6e673 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -457,7 +457,7 @@
try {
forAllWindows(sRemoveReplacedWindowsConsumer, true /* traverseTopToBottom */);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("removeReplacedWindows");
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
}
}
@@ -599,7 +599,7 @@
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index c25b19c..3350fea 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -296,7 +296,7 @@
setRotationInTransaction(originalRotation);
} finally {
if (!inTransaction) {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("ScreenRotationAnimation");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
"<<< CLOSE TRANSACTION ScreenRotationAnimation");
}
@@ -567,7 +567,7 @@
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("ScreenRotationAnimation.startAnimation");
if (SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
TAG_WM,
"<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
@@ -607,7 +607,7 @@
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("ScreenRotationAnimation.startAnimation");
if (SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
TAG_WM,
"<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
@@ -629,7 +629,7 @@
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("ScreenRotationAnimation.startAnimation");
if (SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
TAG_WM,
"<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d83630d..13435d7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -678,7 +678,7 @@
mChildren.get(i).forceWindowsScaleableInTransaction(force);
}
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("forceWindowsScaleable");
}
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index a11b333..12f6b5a 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -637,7 +637,7 @@
} else {
showDimLayer();
}
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("updateDimLayerVisibility");
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index e409a68..1912095 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -233,7 +233,7 @@
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("WindowAnimator");
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index af1fa88..bf5f4bc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -161,7 +161,9 @@
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -361,6 +363,8 @@
private static final int TRANSITION_ANIMATION_SCALE = 1;
private static final int ANIMATION_DURATION_SCALE = 2;
+ final WindowTracing mWindowTracing;
+
final private KeyguardDisableHandler mKeyguardDisableHandler;
boolean mKeyguardGoingAway;
// VR Vr2d Display Id.
@@ -820,15 +824,20 @@
/**
* Closes a surface transaction.
+ * @param where debug string indicating where the transaction originated
*/
- void closeSurfaceTransaction() {
+ void closeSurfaceTransaction(String where) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction");
synchronized (mWindowMap) {
- if (mRoot.mSurfaceTraceEnabled) {
- mRoot.mRemoteEventTrace.closeSurfaceTransaction();
+ try {
+ traceStateLocked(where);
+ } finally {
+ if (mRoot.mSurfaceTraceEnabled) {
+ mRoot.mRemoteEventTrace.closeSurfaceTransaction();
+ }
+ SurfaceControl.closeTransaction();
}
- SurfaceControl.closeTransaction();
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -929,6 +938,12 @@
}, 0);
}
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver result) {
+ new WindowManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
+ }
+
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
WindowManagerPolicy policy) {
@@ -959,6 +974,8 @@
mPolicy = policy;
mTaskSnapshotController = new TaskSnapshotController(this);
+ mWindowTracing = WindowTracing.createDefaultAndStartLooper(context);
+
LocalServices.addService(WindowManagerPolicy.class, mPolicy);
if(mInputManager != null) {
@@ -1068,7 +1085,7 @@
try {
createWatermarkInTransaction();
} finally {
- closeSurfaceTransaction();
+ closeSurfaceTransaction("createWatermarkInTransaction");
}
showEmulatorDisplayOverlayIfNeeded();
@@ -3572,7 +3589,7 @@
mCircularDisplayMask = null;
}
} finally {
- closeSurfaceTransaction();
+ closeSurfaceTransaction("showCircularMask");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
"<<< CLOSE TRANSACTION showCircularMask(visible=" + visible + ")");
}
@@ -3597,7 +3614,7 @@
}
mEmulatorDisplayOverlay.setVisibility(true);
} finally {
- closeSurfaceTransaction();
+ closeSurfaceTransaction("showEmulatorDisplayOverlay");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
"<<< CLOSE TRANSACTION showEmulatorDisplayOverlay");
}
@@ -6320,7 +6337,7 @@
* @param proto Stream to write the WindowContainer object to.
* @param trim If true, reduce the amount of data written.
*/
- private void writeToProtoLocked(ProtoOutputStream proto, boolean trim) {
+ void writeToProtoLocked(ProtoOutputStream proto, boolean trim) {
mPolicy.writeToProto(proto, POLICY);
mRoot.writeToProto(proto, ROOT_WINDOW_CONTAINER, trim);
if (mCurrentFocus != null) {
@@ -6339,6 +6356,17 @@
mAppTransition.writeToProto(proto, APP_TRANSITION);
}
+ void traceStateLocked(String where) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
+ try {
+ mWindowTracing.traceStateLocked(where, this);
+ } catch (Exception e) {
+ Log.wtf(TAG, "Exception while tracing state", e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll,
ArrayList<WindowState> windows) {
pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)");
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
new file mode 100644
index 0000000..4b98d9d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * ShellCommands for WindowManagerService.
+ *
+ * Use with {@code adb shell cmd window ...}.
+ */
+public class WindowManagerShellCommand extends ShellCommand {
+
+ private final WindowManagerService mService;
+
+ public WindowManagerShellCommand(WindowManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ switch (cmd) {
+ case "tracing":
+ return mService.mWindowTracing.onShellCommand(this, getNextArgRequired());
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Window Manager (window) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(" tracing (start | stop)");
+ pw.println(" start or stop window tracing");
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6bed30c..6b1932d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1849,110 +1849,109 @@
final long origId = Binder.clearCallingIdentity();
- disposeInputChannel();
+ try {
+ disposeInputChannel();
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Remove " + this
- + ": mSurfaceController=" + mWinAnimator.mSurfaceController
- + " mAnimatingExit=" + mAnimatingExit
- + " mRemoveOnExit=" + mRemoveOnExit
- + " mHasSurface=" + mHasSurface
- + " surfaceShowing=" + mWinAnimator.getShown()
- + " isAnimationSet=" + mWinAnimator.isAnimationSet()
- + " app-animation="
- + (mAppToken != null ? mAppToken.mAppAnimator.animation : null)
- + " mWillReplaceWindow=" + mWillReplaceWindow
- + " inPendingTransaction="
- + (mAppToken != null ? mAppToken.inPendingTransaction : false)
- + " mDisplayFrozen=" + mService.mDisplayFrozen
- + " callers=" + Debug.getCallers(6));
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Remove " + this
+ + ": mSurfaceController=" + mWinAnimator.mSurfaceController
+ + " mAnimatingExit=" + mAnimatingExit
+ + " mRemoveOnExit=" + mRemoveOnExit
+ + " mHasSurface=" + mHasSurface
+ + " surfaceShowing=" + mWinAnimator.getShown()
+ + " isAnimationSet=" + mWinAnimator.isAnimationSet()
+ + " app-animation="
+ + (mAppToken != null ? mAppToken.mAppAnimator.animation : null)
+ + " mWillReplaceWindow=" + mWillReplaceWindow
+ + " inPendingTransaction="
+ + (mAppToken != null ? mAppToken.inPendingTransaction : false)
+ + " mDisplayFrozen=" + mService.mDisplayFrozen
+ + " callers=" + Debug.getCallers(6));
- // Visibility of the removed window. Will be used later to update orientation later on.
- boolean wasVisible = false;
+ // Visibility of the removed window. Will be used later to update orientation later on.
+ boolean wasVisible = false;
- final int displayId = getDisplayId();
+ final int displayId = getDisplayId();
- // First, see if we need to run an animation. If we do, we have to hold off on removing the
- // window until the animation is done. If the display is frozen, just remove immediately,
- // since the animation wouldn't be seen.
- if (mHasSurface && mToken.okToAnimate()) {
- if (mWillReplaceWindow) {
- // This window is going to be replaced. We need to keep it around until the new one
- // gets added, then we will get rid of this one.
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
- "Preserving " + this + " until the new one is " + "added");
- // TODO: We are overloading mAnimatingExit flag to prevent the window state from
- // been removed. We probably need another flag to indicate that window removal
- // should be deffered vs. overloading the flag that says we are playing an exit
- // animation.
- mAnimatingExit = true;
- mReplacingRemoveRequested = true;
- Binder.restoreCallingIdentity(origId);
- return;
- }
-
- // If we are not currently running the exit animation, we need to see about starting one
- wasVisible = isWinVisibleLw();
-
- if (keepVisibleDeadWindow) {
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
- "Not removing " + this + " because app died while it's visible");
-
- mAppDied = true;
- setDisplayLayoutNeeded();
- mService.mWindowPlacerLocked.performSurfacePlacement();
-
- // Set up a replacement input channel since the app is now dead.
- // We need to catch tapping on the dead window to restart the app.
- openInputChannel(null);
- mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
-
- Binder.restoreCallingIdentity(origId);
- return;
- }
-
- if (wasVisible) {
- final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
-
- // Try starting an animation.
- if (mWinAnimator.applyAnimationLocked(transit, false)) {
+ // First, see if we need to run an animation. If we do, we have to hold off on removing the
+ // window until the animation is done. If the display is frozen, just remove immediately,
+ // since the animation wouldn't be seen.
+ if (mHasSurface && mToken.okToAnimate()) {
+ if (mWillReplaceWindow) {
+ // This window is going to be replaced. We need to keep it around until the new one
+ // gets added, then we will get rid of this one.
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ "Preserving " + this + " until the new one is " + "added");
+ // TODO: We are overloading mAnimatingExit flag to prevent the window state from
+ // been removed. We probably need another flag to indicate that window removal
+ // should be deffered vs. overloading the flag that says we are playing an exit
+ // animation.
mAnimatingExit = true;
+ mReplacingRemoveRequested = true;
+ return;
}
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onWindowTransitionLocked(this, transit);
- }
- }
- final boolean isAnimating =
- mWinAnimator.isAnimationSet() && !mWinAnimator.isDummyAnimation();
- final boolean lastWindowIsStartingWindow = startingWindow && mAppToken != null
- && mAppToken.isLastWindow(this);
- // We delay the removal of a window if it has a showing surface that can be used to run
- // exit animation and it is marked as exiting.
- // Also, If isn't the an animating starting window that is the last window in the app.
- // We allow the removal of the non-animating starting window now as there is no
- // additional window or animation that will trigger its removal.
- if (mWinAnimator.getShown() && mAnimatingExit
- && (!lastWindowIsStartingWindow || isAnimating)) {
- // The exit animation is running or should run... wait for it!
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
- "Not removing " + this + " due to exit animation ");
- setupWindowForRemoveOnExit();
- if (mAppToken != null) {
- mAppToken.updateReportedVisibilityLocked();
- }
- Binder.restoreCallingIdentity(origId);
- return;
- }
- }
- removeImmediately();
- // Removing a visible window will effect the computed orientation
- // So just update orientation if needed.
- if (wasVisible && mService.updateOrientationFromAppTokensLocked(false, displayId)) {
- mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+ // If we are not currently running the exit animation, we need to see about starting one
+ wasVisible = isWinVisibleLw();
+
+ if (keepVisibleDeadWindow) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ "Not removing " + this + " because app died while it's visible");
+
+ mAppDied = true;
+ setDisplayLayoutNeeded();
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+
+ // Set up a replacement input channel since the app is now dead.
+ // We need to catch tapping on the dead window to restart the app.
+ openInputChannel(null);
+ mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+ return;
+ }
+
+ if (wasVisible) {
+ final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
+
+ // Try starting an animation.
+ if (mWinAnimator.applyAnimationLocked(transit, false)) {
+ mAnimatingExit = true;
+ }
+ //TODO (multidisplay): Magnification is supported only for the default display.
+ if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
+ mService.mAccessibilityController.onWindowTransitionLocked(this, transit);
+ }
+ }
+ final boolean isAnimating =
+ mWinAnimator.isAnimationSet() && !mWinAnimator.isDummyAnimation();
+ final boolean lastWindowIsStartingWindow = startingWindow && mAppToken != null
+ && mAppToken.isLastWindow(this);
+ // We delay the removal of a window if it has a showing surface that can be used to run
+ // exit animation and it is marked as exiting.
+ // Also, If isn't the an animating starting window that is the last window in the app.
+ // We allow the removal of the non-animating starting window now as there is no
+ // additional window or animation that will trigger its removal.
+ if (mWinAnimator.getShown() && mAnimatingExit
+ && (!lastWindowIsStartingWindow || isAnimating)) {
+ // The exit animation is running or should run... wait for it!
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+ "Not removing " + this + " due to exit animation ");
+ setupWindowForRemoveOnExit();
+ if (mAppToken != null) {
+ mAppToken.updateReportedVisibilityLocked();
+ }
+ return;
+ }
+ }
+
+ removeImmediately();
+ // Removing a visible window will effect the computed orientation
+ // So just update orientation if needed.
+ if (wasVisible && mService.updateOrientationFromAppTokensLocked(false, displayId)) {
+ mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+ }
+ mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
- Binder.restoreCallingIdentity(origId);
}
private void setupWindowForRemoveOnExit() {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 5266903..86397ae 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -732,7 +732,7 @@
mSurfaceController.setLayerStackInTransaction(getLayerStack());
mSurfaceController.setLayer(mAnimLayer);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("createSurfaceLocked");
}
mLastHidden = true;
@@ -1711,7 +1711,7 @@
Slog.w(TAG, "Error positioning surface of " + mWin
+ " pos=(" + left + "," + top + ")", e);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setWallpaperOffset");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION setWallpaperOffset");
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index edd650a..a214523 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -259,7 +259,7 @@
mSurfaceControl.setLayer(layer);
}
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setLayer");
}
}
}
@@ -385,7 +385,7 @@
try {
mSurfaceControl.setTransparentRegionHint(region);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setTransparentRegion");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION setTransparentRegion");
}
@@ -403,7 +403,7 @@
try {
mSurfaceControl.setOpaque(isOpaque);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setOpaqueLocked");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaqueLocked");
}
}
@@ -420,7 +420,7 @@
try {
mSurfaceControl.setSecure(isSecure);
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("setSecure");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setSecureLocked");
}
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index d57fdd2..cd5e475 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -429,7 +429,7 @@
try {
mService.mAnimator.orAnimating(appAnimator.showAllWindowsLocked());
} finally {
- mService.closeSurfaceTransaction();
+ mService.closeSurfaceTransaction("handleAppTransitionReadyLocked");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION handleAppTransitionReadyLocked()");
}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
new file mode 100644
index 0000000..5657f6c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static com.android.server.wm.proto.WindowManagerTraceFileProto.ENTRY;
+import static com.android.server.wm.proto.WindowManagerTraceFileProto.MAGIC_NUMBER;
+import static com.android.server.wm.proto.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.proto.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
+import static com.android.server.wm.proto.WindowManagerTraceProto.ELAPSED_REALTIME_NANOS;
+import static com.android.server.wm.proto.WindowManagerTraceProto.WHERE;
+import static com.android.server.wm.proto.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
+
+import android.content.Context;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * A class that allows window manager to dump its state continuously to a trace file, such that a
+ * time series of window manager state can be analyzed after the fact.
+ */
+class WindowTracing {
+
+ private static final String TAG = "WindowTracing";
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ private final Object mLock = new Object();
+ private final File mTraceFile;
+ private final BlockingQueue<ProtoOutputStream> mWriteQueue = new ArrayBlockingQueue<>(200);
+
+ private boolean mEnabled;
+ private volatile boolean mEnabledLockFree;
+
+ WindowTracing(File file) {
+ mTraceFile = file;
+ }
+
+ void startTrace(PrintWriter pw) throws IOException {
+ synchronized (mLock) {
+ logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
+ mWriteQueue.clear();
+ mTraceFile.delete();
+ try (OutputStream os = new FileOutputStream(mTraceFile)) {
+ ProtoOutputStream proto = new ProtoOutputStream(os);
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ proto.flush();
+ }
+ mEnabled = mEnabledLockFree = true;
+ }
+ }
+
+ private void logAndPrintln(PrintWriter pw, String msg) {
+ Log.i(TAG, msg);
+ pw.println(msg);
+ pw.flush();
+ }
+
+ void stopTrace(PrintWriter pw) {
+ synchronized (mLock) {
+ logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
+ mEnabled = mEnabledLockFree = false;
+ while (!mWriteQueue.isEmpty()) {
+ if (mEnabled) {
+ logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
+ throw new IllegalStateException("tracing enabled while waiting for flush.");
+ }
+ try {
+ mLock.wait();
+ mLock.notify();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
+ }
+ }
+
+ void appendTraceEntry(ProtoOutputStream proto) {
+ if (!mEnabledLockFree) {
+ return;
+ }
+
+ if (!mWriteQueue.offer(proto)) {
+ Log.e(TAG, "Dropping window trace entry, queue full");
+ }
+ }
+
+ void loop() {
+ for (;;) {
+ loopOnce();
+ }
+ }
+
+ @VisibleForTesting
+ void loopOnce() {
+ ProtoOutputStream proto;
+ try {
+ proto = mWriteQueue.take();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+
+ synchronized (mLock) {
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToFile");
+ try (OutputStream os = new FileOutputStream(mTraceFile, true /* append */)) {
+ os.write(proto.getBytes());
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write file " + mTraceFile, e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ mLock.notify();
+ }
+ }
+
+ boolean isEnabled() {
+ return mEnabledLockFree;
+ }
+
+ static WindowTracing createDefaultAndStartLooper(Context context) {
+ File file = new File("/data/system/window_trace.proto");
+ WindowTracing windowTracing = new WindowTracing(file);
+ new Thread(windowTracing::loop, "window_tracing").start();
+ return windowTracing;
+ }
+
+ int onShellCommand(ShellCommand shell, String cmd) {
+ PrintWriter pw = shell.getOutPrintWriter();
+ try {
+ switch (cmd) {
+ case "start":
+ startTrace(pw);
+ return 0;
+ case "stop":
+ stopTrace(pw);
+ return 0;
+ default:
+ pw.println("Unknown command: " + cmd);
+ return -1;
+ }
+ } catch (IOException e) {
+ logAndPrintln(pw, e.toString());
+ throw new RuntimeException(e);
+ }
+ }
+
+ void traceStateLocked(String where, WindowManagerService service) {
+ if (!isEnabled()) {
+ return;
+ }
+ ProtoOutputStream os = new ProtoOutputStream();
+ long tokenOuter = os.start(ENTRY);
+ os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+ os.write(WHERE, where);
+
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked");
+ try {
+ long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
+ service.writeToProtoLocked(os, true /* trim */);
+ os.end(tokenInner);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ os.end(tokenOuter);
+ appendTraceEntry(os);
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+}
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 3901ceb..d4ffa70 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -48,8 +48,6 @@
status_t err;
- configureRpcThreadpool(5, false /* callerWillJoin */);
-
JavaVM *vm;
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Cannot get Java VM");
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index e8ef168..2f45181 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -92,7 +92,6 @@
register_android_server_tv_TvUinputBridge(env);
register_android_server_tv_TvInputHal(env);
register_android_server_PersistentDataBlockService(env);
- register_android_server_Watchdog(env);
register_android_server_HardwarePropertiesManagerService(env);
register_android_server_storage_AppFuse(env);
register_android_server_SyntheticPasswordManager(env);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index de5d879..a2c2aeb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -545,11 +545,9 @@
traceEnd();
// Bring up recovery system in case a rescue party needs a reboot
- if (!SystemProperties.getBoolean("config.disable_noncore", false)) {
- traceBeginAndSlog("StartRecoverySystemService");
- mSystemServiceManager.startService(RecoverySystemService.class);
- traceEnd();
- }
+ traceBeginAndSlog("StartRecoverySystemService");
+ mSystemServiceManager.startService(RecoverySystemService.class);
+ traceEnd();
// Now that we have the bare essentials of the OS up and running, take
// note that we just booted, which might send out a rescue party if
@@ -660,6 +658,9 @@
mSystemServiceManager.startService(DropBoxManagerService.class);
traceEnd();
+ // First hwbinder call is in BatteryService.
+ android.os.HwBinder.startRpcThreadPool(5, false /* callerWillJoin */);
+
traceBeginAndSlog("StartBatteryService");
// Tracks the battery level. Requires LightService.
mSystemServiceManager.startService(BatteryService.class);
@@ -705,13 +706,7 @@
MmsServiceBroker mmsService = null;
HardwarePropertiesManagerService hardwarePropertiesService = null;
- boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
- boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
- boolean disableLocation = SystemProperties.getBoolean("config.disable_location", false);
boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
- boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false);
- boolean disableNetwork = SystemProperties.getBoolean("config.disable_network", false);
- boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime", false);
boolean disableRtt = SystemProperties.getBoolean("config.disable_rtt", false);
boolean disableMediaProjection = SystemProperties.getBoolean("config.disable_mediaproj",
false);
@@ -875,8 +870,6 @@
} else if (!context.getPackageManager().hasSystemFeature
(PackageManager.FEATURE_BLUETOOTH)) {
Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)");
- } else if (disableBluetooth) {
- Slog.i(TAG, "Bluetooth Service disabled by config");
} else {
traceBeginAndSlog("StartBluetoothService");
mSystemServiceManager.startService(BluetoothService.class);
@@ -927,8 +920,7 @@
traceEnd();
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
- if (!disableStorage &&
- !"0".equals(SystemProperties.get("system_init.startmountservice"))) {
+ if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
traceBeginAndSlog("StartStorageManagerService");
try {
/*
@@ -978,42 +970,40 @@
traceEnd();
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartLockSettingsService");
- try {
- mSystemServiceManager.startService(LOCK_SETTINGS_SERVICE_CLASS);
- lockSettings = ILockSettings.Stub.asInterface(
- ServiceManager.getService("lock_settings"));
- } catch (Throwable e) {
- reportWtf("starting LockSettingsService service", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartLockSettingsService");
+ try {
+ mSystemServiceManager.startService(LOCK_SETTINGS_SERVICE_CLASS);
+ lockSettings = ILockSettings.Stub.asInterface(
+ ServiceManager.getService("lock_settings"));
+ } catch (Throwable e) {
+ reportWtf("starting LockSettingsService service", e);
+ }
+ traceEnd();
- final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("");
- if (hasPdb) {
- traceBeginAndSlog("StartPersistentDataBlock");
- mSystemServiceManager.startService(PersistentDataBlockService.class);
- traceEnd();
- }
-
- if (hasPdb || OemLockService.isHalPresent()) {
- // Implementation depends on pdb or the OemLock HAL
- traceBeginAndSlog("StartOemLockService");
- mSystemServiceManager.startService(OemLockService.class);
- traceEnd();
- }
-
- traceBeginAndSlog("StartDeviceIdleController");
- mSystemServiceManager.startService(DeviceIdleController.class);
- traceEnd();
-
- // Always start the Device Policy Manager, so that the API is compatible with
- // API8.
- traceBeginAndSlog("StartDevicePolicyManager");
- mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
+ final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("");
+ if (hasPdb) {
+ traceBeginAndSlog("StartPersistentDataBlock");
+ mSystemServiceManager.startService(PersistentDataBlockService.class);
traceEnd();
}
+ if (hasPdb || OemLockService.isHalPresent()) {
+ // Implementation depends on pdb or the OemLock HAL
+ traceBeginAndSlog("StartOemLockService");
+ mSystemServiceManager.startService(OemLockService.class);
+ traceEnd();
+ }
+
+ traceBeginAndSlog("StartDeviceIdleController");
+ mSystemServiceManager.startService(DeviceIdleController.class);
+ traceEnd();
+
+ // Always start the Device Policy Manager, so that the API is compatible with
+ // API8.
+ traceBeginAndSlog("StartDevicePolicyManager");
+ mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
+ traceEnd();
+
if (!disableSystemUI) {
traceBeginAndSlog("StartStatusBarManagerService");
try {
@@ -1025,153 +1015,145 @@
traceEnd();
}
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartClipboardService");
- mSystemServiceManager.startService(ClipboardService.class);
- traceEnd();
+ traceBeginAndSlog("StartClipboardService");
+ mSystemServiceManager.startService(ClipboardService.class);
+ traceEnd();
+
+ traceBeginAndSlog("StartNetworkManagementService");
+ try {
+ networkManagement = NetworkManagementService.create(context);
+ ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkManagement Service", e);
}
+ traceEnd();
- if (!disableNetwork) {
- traceBeginAndSlog("StartNetworkManagementService");
- try {
- networkManagement = NetworkManagementService.create(context);
- ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
- } catch (Throwable e) {
- reportWtf("starting NetworkManagement Service", e);
- }
- traceEnd();
-
- traceBeginAndSlog("StartIpSecService");
- try {
- ipSecService = IpSecService.create(context);
- ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);
- } catch (Throwable e) {
- reportWtf("starting IpSec Service", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartIpSecService");
+ try {
+ ipSecService = IpSecService.create(context);
+ ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);
+ } catch (Throwable e) {
+ reportWtf("starting IpSec Service", e);
}
+ traceEnd();
- if (!disableNonCoreServices && !disableTextServices) {
+ if (!disableTextServices) {
traceBeginAndSlog("StartTextServicesManager");
mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
traceEnd();
}
- if (!disableNetwork) {
- traceBeginAndSlog("StartNetworkScoreService");
- try {
- networkScore = new NetworkScoreService(context);
- ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
- } catch (Throwable e) {
- reportWtf("starting Network Score Service", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartNetworkScoreService");
+ try {
+ networkScore = new NetworkScoreService(context);
+ ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
+ } catch (Throwable e) {
+ reportWtf("starting Network Score Service", e);
+ }
+ traceEnd();
- traceBeginAndSlog("StartNetworkStatsService");
- try {
- networkStats = NetworkStatsService.create(context, networkManagement);
- ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
- } catch (Throwable e) {
- reportWtf("starting NetworkStats Service", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartNetworkStatsService");
+ try {
+ networkStats = NetworkStatsService.create(context, networkManagement);
+ ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkStats Service", e);
+ }
+ traceEnd();
- traceBeginAndSlog("StartNetworkPolicyManagerService");
- try {
- networkPolicy = new NetworkPolicyManagerService(context,
- mActivityManagerService, networkStats, networkManagement);
- ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
- } catch (Throwable e) {
- reportWtf("starting NetworkPolicy Service", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartNetworkPolicyManagerService");
+ try {
+ networkPolicy = new NetworkPolicyManagerService(context,
+ mActivityManagerService, networkStats, networkManagement);
+ ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkPolicy Service", e);
+ }
+ traceEnd();
- // Wifi Service must be started first for wifi-related services.
- traceBeginAndSlog("StartWifi");
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
- traceEnd();
- traceBeginAndSlog("StartWifiScanning");
- mSystemServiceManager.startService(
- "com.android.server.wifi.scanner.WifiScanningService");
- traceEnd();
+ // Wifi Service must be started first for wifi-related services.
+ traceBeginAndSlog("StartWifi");
+ mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+ traceEnd();
+ traceBeginAndSlog("StartWifiScanning");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.scanner.WifiScanningService");
+ traceEnd();
- if (!disableRtt) {
- traceBeginAndSlog("StartWifiRtt");
- mSystemServiceManager.startService("com.android.server.wifi.RttService");
- traceEnd();
-
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_RTT)) {
- traceBeginAndSlog("StartRttService");
- mSystemServiceManager.startService(
- "com.android.server.wifi.rtt.RttService");
- traceEnd();
- }
- }
+ if (!disableRtt) {
+ traceBeginAndSlog("StartWifiRtt");
+ mSystemServiceManager.startService("com.android.server.wifi.RttService");
+ traceEnd();
if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_AWARE)) {
- traceBeginAndSlog("StartWifiAware");
- mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
+ PackageManager.FEATURE_WIFI_RTT)) {
+ traceBeginAndSlog("StartRttService");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.rtt.RttService");
traceEnd();
}
+ }
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_DIRECT)) {
- traceBeginAndSlog("StartWifiP2P");
- mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
- traceEnd();
- }
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_AWARE)) {
+ traceBeginAndSlog("StartWifiAware");
+ mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
+ traceEnd();
+ }
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_LOWPAN)) {
- traceBeginAndSlog("StartLowpan");
- mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS);
- traceEnd();
- }
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_DIRECT)) {
+ traceBeginAndSlog("StartWifiP2P");
+ mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
+ traceEnd();
+ }
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
- mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
- traceBeginAndSlog("StartEthernet");
- mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
- traceEnd();
- }
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LOWPAN)) {
+ traceBeginAndSlog("StartLowpan");
+ mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS);
+ traceEnd();
+ }
- traceBeginAndSlog("StartConnectivityService");
- try {
- connectivity = new ConnectivityService(
- context, networkManagement, networkStats, networkPolicy);
- ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity,
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
+ mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
+ traceBeginAndSlog("StartEthernet");
+ mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
+ traceEnd();
+ }
+
+ traceBeginAndSlog("StartConnectivityService");
+ try {
+ connectivity = new ConnectivityService(
+ context, networkManagement, networkStats, networkPolicy);
+ ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity,
/* allowIsolated= */ false,
- DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
- networkStats.bindConnectivityManager(connectivity);
- networkPolicy.bindConnectivityManager(connectivity);
- } catch (Throwable e) {
- reportWtf("starting Connectivity Service", e);
- }
- traceEnd();
-
- traceBeginAndSlog("StartNsdService");
- try {
- serviceDiscovery = NsdService.create(context);
- ServiceManager.addService(
- Context.NSD_SERVICE, serviceDiscovery);
- } catch (Throwable e) {
- reportWtf("starting Service Discovery Service", e);
- }
- traceEnd();
+ DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+ networkStats.bindConnectivityManager(connectivity);
+ networkPolicy.bindConnectivityManager(connectivity);
+ } catch (Throwable e) {
+ reportWtf("starting Connectivity Service", e);
}
+ traceEnd();
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartUpdateLockService");
- try {
- ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
- new UpdateLockService(context));
- } catch (Throwable e) {
- reportWtf("starting UpdateLockService", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartNsdService");
+ try {
+ serviceDiscovery = NsdService.create(context);
+ ServiceManager.addService(
+ Context.NSD_SERVICE, serviceDiscovery);
+ } catch (Throwable e) {
+ reportWtf("starting Service Discovery Service", e);
}
+ traceEnd();
+
+ traceBeginAndSlog("StartUpdateLockService");
+ try {
+ ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
+ new UpdateLockService(context));
+ } catch (Throwable e) {
+ reportWtf("starting UpdateLockService", e);
+ }
+ traceEnd();
traceBeginAndSlog("StartNotificationManager");
mSystemServiceManager.startService(NotificationManagerService.class);
@@ -1185,27 +1167,25 @@
mSystemServiceManager.startService(DeviceStorageMonitorService.class);
traceEnd();
- if (!disableLocation) {
- traceBeginAndSlog("StartLocationManagerService");
- try {
- location = new LocationManagerService(context);
- ServiceManager.addService(Context.LOCATION_SERVICE, location);
- } catch (Throwable e) {
- reportWtf("starting Location Manager", e);
- }
- traceEnd();
-
- traceBeginAndSlog("StartCountryDetectorService");
- try {
- countryDetector = new CountryDetectorService(context);
- ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
- } catch (Throwable e) {
- reportWtf("starting Country Detector", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartLocationManagerService");
+ try {
+ location = new LocationManagerService(context);
+ ServiceManager.addService(Context.LOCATION_SERVICE, location);
+ } catch (Throwable e) {
+ reportWtf("starting Location Manager", e);
}
+ traceEnd();
- if (!disableNonCoreServices && !disableSearchManager) {
+ traceBeginAndSlog("StartCountryDetectorService");
+ try {
+ countryDetector = new CountryDetectorService(context);
+ ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
+ } catch (Throwable e) {
+ reportWtf("starting Country Detector", e);
+ }
+ traceEnd();
+
+ if (!disableSearchManager) {
traceBeginAndSlog("StartSearchManagerService");
try {
mSystemServiceManager.startService(SEARCH_MANAGER_SERVICE_CLASS);
@@ -1215,8 +1195,7 @@
traceEnd();
}
- if (!disableNonCoreServices && context.getResources().getBoolean(
- R.bool.config_enableWallpaperService)) {
+ if (context.getResources().getBoolean(R.bool.config_enableWallpaperService)) {
traceBeginAndSlog("StartWallpaperManagerService");
mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS);
traceEnd();
@@ -1232,16 +1211,14 @@
traceEnd();
}
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartDockObserver");
- mSystemServiceManager.startService(DockObserver.class);
- traceEnd();
+ traceBeginAndSlog("StartDockObserver");
+ mSystemServiceManager.startService(DockObserver.class);
+ traceEnd();
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
- traceBeginAndSlog("StartThermalObserver");
- mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS);
- traceEnd();
- }
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ traceBeginAndSlog("StartThermalObserver");
+ mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS);
+ traceEnd();
}
traceBeginAndSlog("StartWiredAccessoryManager");
@@ -1254,46 +1231,44 @@
}
traceEnd();
- if (!disableNonCoreServices) {
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MIDI)) {
- // Start MIDI Manager service
- traceBeginAndSlog("StartMidiManager");
- mSystemServiceManager.startService(MIDI_SERVICE_CLASS);
- traceEnd();
- }
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MIDI)) {
+ // Start MIDI Manager service
+ traceBeginAndSlog("StartMidiManager");
+ mSystemServiceManager.startService(MIDI_SERVICE_CLASS);
+ traceEnd();
+ }
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
- || mPackageManager.hasSystemFeature(
- PackageManager.FEATURE_USB_ACCESSORY)) {
- // Manage USB host and device support
- traceBeginAndSlog("StartUsbService");
- mSystemServiceManager.startService(USB_SERVICE_CLASS);
- traceEnd();
- }
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
+ || mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_USB_ACCESSORY)) {
+ // Manage USB host and device support
+ traceBeginAndSlog("StartUsbService");
+ mSystemServiceManager.startService(USB_SERVICE_CLASS);
+ traceEnd();
+ }
- if (!disableSerial) {
- traceBeginAndSlog("StartSerialService");
- try {
- // Serial port support
- serial = new SerialService(context);
- ServiceManager.addService(Context.SERIAL_SERVICE, serial);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting SerialService", e);
- }
- traceEnd();
- }
-
- traceBeginAndSlog("StartHardwarePropertiesManagerService");
+ if (!disableSerial) {
+ traceBeginAndSlog("StartSerialService");
try {
- hardwarePropertiesService = new HardwarePropertiesManagerService(context);
- ServiceManager.addService(Context.HARDWARE_PROPERTIES_SERVICE,
- hardwarePropertiesService);
+ // Serial port support
+ serial = new SerialService(context);
+ ServiceManager.addService(Context.SERIAL_SERVICE, serial);
} catch (Throwable e) {
- Slog.e(TAG, "Failure starting HardwarePropertiesManagerService", e);
+ Slog.e(TAG, "Failure starting SerialService", e);
}
traceEnd();
}
+ traceBeginAndSlog("StartHardwarePropertiesManagerService");
+ try {
+ hardwarePropertiesService = new HardwarePropertiesManagerService(context);
+ ServiceManager.addService(Context.HARDWARE_PROPERTIES_SERVICE,
+ hardwarePropertiesService);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting HardwarePropertiesManagerService", e);
+ }
+ traceEnd();
+
traceBeginAndSlog("StartTwilightService");
mSystemServiceManager.startService(TwilightService.class);
traceEnd();
@@ -1312,48 +1287,46 @@
mSystemServiceManager.startService(SoundTriggerService.class);
traceEnd();
- if (!disableNonCoreServices) {
- if (!disableTrustManager) {
- traceBeginAndSlog("StartTrustManager");
- mSystemServiceManager.startService(TrustManagerService.class);
- traceEnd();
- }
-
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
- traceBeginAndSlog("StartBackupManager");
- mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
- traceEnd();
- }
-
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)
- || context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) {
- traceBeginAndSlog("StartAppWidgerService");
- mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
- traceEnd();
- }
-
- // We need to always start this service, regardless of whether the
- // FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care
- // of initializing various settings. It will internally modify its behavior
- // based on that feature.
- traceBeginAndSlog("StartVoiceRecognitionManager");
- mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
- traceEnd();
-
- if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
- traceBeginAndSlog("StartGestureLauncher");
- mSystemServiceManager.startService(GestureLauncherService.class);
- traceEnd();
- }
- traceBeginAndSlog("StartSensorNotification");
- mSystemServiceManager.startService(SensorNotificationService.class);
- traceEnd();
-
- traceBeginAndSlog("StartContextHubSystemService");
- mSystemServiceManager.startService(ContextHubSystemService.class);
+ if (!disableTrustManager) {
+ traceBeginAndSlog("StartTrustManager");
+ mSystemServiceManager.startService(TrustManagerService.class);
traceEnd();
}
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
+ traceBeginAndSlog("StartBackupManager");
+ mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
+ traceEnd();
+ }
+
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)
+ || context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) {
+ traceBeginAndSlog("StartAppWidgerService");
+ mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
+ traceEnd();
+ }
+
+ // We need to always start this service, regardless of whether the
+ // FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care
+ // of initializing various settings. It will internally modify its behavior
+ // based on that feature.
+ traceBeginAndSlog("StartVoiceRecognitionManager");
+ mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
+ traceEnd();
+
+ if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
+ traceBeginAndSlog("StartGestureLauncher");
+ mSystemServiceManager.startService(GestureLauncherService.class);
+ traceEnd();
+ }
+ traceBeginAndSlog("StartSensorNotification");
+ mSystemServiceManager.startService(SensorNotificationService.class);
+ traceEnd();
+
+ traceBeginAndSlog("StartContextHubSystemService");
+ mSystemServiceManager.startService(ContextHubSystemService.class);
+ traceEnd();
+
traceBeginAndSlog("StartDiskStatsService");
try {
ServiceManager.addService("diskstats", new DiskStatsService(context));
@@ -1375,16 +1348,14 @@
traceEnd();
}
- if (!disableNetwork && !disableNetworkTime) {
- traceBeginAndSlog("StartNetworkTimeUpdateService");
- try {
- networkTimeUpdater = new NetworkTimeUpdateService(context);
- ServiceManager.addService("network_time_update_service", networkTimeUpdater);
- } catch (Throwable e) {
- reportWtf("starting NetworkTimeUpdate service", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartNetworkTimeUpdateService");
+ try {
+ networkTimeUpdater = new NetworkTimeUpdateService(context);
+ ServiceManager.addService("network_time_update_service", networkTimeUpdater);
+ } catch (Throwable e) {
+ reportWtf("starting NetworkTimeUpdate service", e);
}
+ traceEnd();
traceBeginAndSlog("StartCommonTimeManagementService");
try {
@@ -1395,38 +1366,32 @@
}
traceEnd();
- if (!disableNetwork) {
- traceBeginAndSlog("CertBlacklister");
- try {
- CertBlacklister blacklister = new CertBlacklister(context);
- } catch (Throwable e) {
- reportWtf("starting CertBlacklister", e);
- }
- traceEnd();
+ traceBeginAndSlog("CertBlacklister");
+ try {
+ CertBlacklister blacklister = new CertBlacklister(context);
+ } catch (Throwable e) {
+ reportWtf("starting CertBlacklister", e);
}
+ traceEnd();
- if (!disableNetwork && !disableNonCoreServices && EmergencyAffordanceManager.ENABLED) {
+ if (EmergencyAffordanceManager.ENABLED) {
// EmergencyMode service
traceBeginAndSlog("StartEmergencyAffordanceService");
mSystemServiceManager.startService(EmergencyAffordanceService.class);
traceEnd();
}
- if (!disableNonCoreServices) {
- // Dreams (interactive idle-time views, a/k/a screen savers, and doze mode)
- traceBeginAndSlog("StartDreamManager");
- mSystemServiceManager.startService(DreamManagerService.class);
- traceEnd();
- }
+ // Dreams (interactive idle-time views, a/k/a screen savers, and doze mode)
+ traceBeginAndSlog("StartDreamManager");
+ mSystemServiceManager.startService(DreamManagerService.class);
+ traceEnd();
- if (!disableNonCoreServices) {
- traceBeginAndSlog("AddGraphicsStatsService");
- ServiceManager.addService(GraphicsStatsService.GRAPHICS_STATS_SERVICE,
- new GraphicsStatsService(context));
- traceEnd();
- }
+ traceBeginAndSlog("AddGraphicsStatsService");
+ ServiceManager.addService(GraphicsStatsService.GRAPHICS_STATS_SERVICE,
+ new GraphicsStatsService(context));
+ traceEnd();
- if (!disableNonCoreServices && CoverageService.ENABLED) {
+ if (CoverageService.ENABLED) {
traceBeginAndSlog("AddCoverageService");
ServiceManager.addService(CoverageService.COVERAGE_SERVICE, new CoverageService());
traceEnd();
@@ -1477,38 +1442,37 @@
traceEnd();
}
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartMediaRouterService");
- try {
- mediaRouter = new MediaRouterService(context);
- ServiceManager.addService(Context.MEDIA_ROUTER_SERVICE, mediaRouter);
- } catch (Throwable e) {
- reportWtf("starting MediaRouterService", e);
- }
- traceEnd();
+ traceBeginAndSlog("StartMediaRouterService");
+ try {
+ mediaRouter = new MediaRouterService(context);
+ ServiceManager.addService(Context.MEDIA_ROUTER_SERVICE, mediaRouter);
+ } catch (Throwable e) {
+ reportWtf("starting MediaRouterService", e);
+ }
+ traceEnd();
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
- traceBeginAndSlog("StartFingerprintSensor");
- mSystemServiceManager.startService(FingerprintService.class);
- traceEnd();
- }
-
- traceBeginAndSlog("StartBackgroundDexOptService");
- try {
- BackgroundDexOptService.schedule(context);
- } catch (Throwable e) {
- reportWtf("starting StartBackgroundDexOptService", e);
- }
- traceEnd();
-
- traceBeginAndSlog("StartPruneInstantAppsJobService");
- try {
- PruneInstantAppsJobService.schedule(context);
- } catch (Throwable e) {
- reportWtf("StartPruneInstantAppsJobService", e);
- }
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ traceBeginAndSlog("StartFingerprintSensor");
+ mSystemServiceManager.startService(FingerprintService.class);
traceEnd();
}
+
+ traceBeginAndSlog("StartBackgroundDexOptService");
+ try {
+ BackgroundDexOptService.schedule(context);
+ } catch (Throwable e) {
+ reportWtf("starting StartBackgroundDexOptService", e);
+ }
+ traceEnd();
+
+ traceBeginAndSlog("StartPruneInstantAppsJobService");
+ try {
+ PruneInstantAppsJobService.schedule(context);
+ } catch (Throwable e) {
+ reportWtf("StartPruneInstantAppsJobService", e);
+ }
+ traceEnd();
+
// LauncherAppsService uses ShortcutService.
traceBeginAndSlog("StartShortcutServiceLifecycle");
mSystemServiceManager.startService(ShortcutService.Lifecycle.class);
@@ -1519,7 +1483,7 @@
traceEnd();
}
- if (!disableNonCoreServices && !disableMediaProjection) {
+ if (!disableMediaProjection) {
traceBeginAndSlog("StartMediaProjectionManager");
mSystemServiceManager.startService(MediaProjectionManagerService.class);
traceEnd();
@@ -1530,17 +1494,15 @@
mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS);
traceEnd();
- if (!disableNonCoreServices) {
- traceBeginAndSlog("StartWearTimeService");
- mSystemServiceManager.startService(WEAR_DISPLAY_SERVICE_CLASS);
- mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS);
- traceEnd();
+ traceBeginAndSlog("StartWearTimeService");
+ mSystemServiceManager.startService(WEAR_DISPLAY_SERVICE_CLASS);
+ mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS);
+ traceEnd();
- if (enableLeftyService) {
- traceBeginAndSlog("StartWearLeftyService");
- mSystemServiceManager.startService(WEAR_LEFTY_SERVICE_CLASS);
- traceEnd();
- }
+ if (enableLeftyService) {
+ traceBeginAndSlog("StartWearLeftyService");
+ mSystemServiceManager.startService(WEAR_LEFTY_SERVICE_CLASS);
+ traceEnd();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index 9ad7add..ea4f4b9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -193,7 +193,7 @@
token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
token.setFillsParent(false);
- // Can specify orientation if app doesn't fill parent. Allowed for SDK <= 25.
+ // Can specify orientation if app doesn't fill parent.
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
token.setFillsParent(true);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
new file mode 100644
index 0000000..ad9aea7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.wm.proto.WindowManagerTraceProto;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Test class for {@link WindowTracing}.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.WindowTracingTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class WindowTracingTest extends WindowTestsBase {
+
+ private static final byte[] MAGIC_HEADER = new byte[] {
+ 0x9, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45,
+ };
+
+ private Context mTestContext;
+ private WindowTracing mWindowTracing;
+ private WindowManagerService mWmMock;
+ private File mFile;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mWmMock = mock(WindowManagerService.class);
+
+ mTestContext = InstrumentationRegistry.getContext();
+
+ mFile = mTestContext.getFileStreamPath("tracing_test.dat");
+ mFile.delete();
+
+ mWindowTracing = new WindowTracing(mFile);
+ }
+
+ @Test
+ public void isEnabled_returnsFalseByDefault() throws Exception {
+ assertFalse(mWindowTracing.isEnabled());
+ }
+
+ @Test
+ public void isEnabled_returnsTrueAfterStart() throws Exception {
+ mWindowTracing.startTrace(mock(PrintWriter.class));
+ assertTrue(mWindowTracing.isEnabled());
+ }
+
+ @Test
+ public void isEnabled_returnsFalseAfterStop() throws Exception {
+ mWindowTracing.startTrace(mock(PrintWriter.class));
+ mWindowTracing.stopTrace(mock(PrintWriter.class));
+ assertFalse(mWindowTracing.isEnabled());
+ }
+
+ @Test
+ public void trace_discared_whenNotTracing() throws Exception {
+ mWindowTracing.traceStateLocked("where", mWmMock);
+ verifyZeroInteractions(mWmMock);
+ }
+
+ @Test
+ public void trace_dumpsWindowManagerState_whenTracing() throws Exception {
+ mWindowTracing.startTrace(mock(PrintWriter.class));
+ mWindowTracing.traceStateLocked("where", mWmMock);
+
+ verify(mWmMock).writeToProtoLocked(any(), eq(true));
+ }
+
+ @Test
+ public void traceFile_startsWithMagicHeader() throws Exception {
+ mWindowTracing.startTrace(mock(PrintWriter.class));
+ mWindowTracing.stopTrace(mock(PrintWriter.class));
+
+ byte[] header = new byte[MAGIC_HEADER.length];
+ try (InputStream is = new FileInputStream(mFile)) {
+ assertEquals(MAGIC_HEADER.length, is.read(header));
+ assertArrayEquals(MAGIC_HEADER, header);
+ }
+ }
+
+ @Test
+ @Ignore("Figure out why this test is crashing when setting up mWmMock.")
+ public void tracing_endsUpInFile() throws Exception {
+ mWindowTracing.startTrace(mock(PrintWriter.class));
+
+ doAnswer((inv) -> {
+ inv.<ProtoOutputStream>getArgument(0).write(
+ WindowManagerTraceProto.WHERE, "TEST_WM_PROTO");
+ return null;
+ }).when(mWmMock).writeToProtoLocked(any(), any());
+ mWindowTracing.traceStateLocked("TEST_WHERE", mWmMock);
+
+ mWindowTracing.stopTrace(mock(PrintWriter.class));
+
+ byte[] file = new byte[1000];
+ int fileLength;
+ try (InputStream is = new FileInputStream(mFile)) {
+ fileLength = is.read(file);
+ assertTrue(containsBytes(file, fileLength,
+ "TEST_WHERE".getBytes(StandardCharsets.UTF_8)));
+ assertTrue(containsBytes(file, fileLength,
+ "TEST_WM_PROTO".getBytes(StandardCharsets.UTF_8)));
+ }
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ mFile.delete();
+ }
+
+ /** Return true if {@code needle} appears anywhere in {@code haystack[0..length]} */
+ boolean containsBytes(byte[] haystack, int haystackLenght, byte[] needle) {
+ Preconditions.checkArgument(haystackLenght > 0);
+ Preconditions.checkArgument(needle.length > 0);
+
+ outer: for (int i = 0; i <= haystackLenght - needle.length; i++) {
+ for (int j = 0; j < needle.length; j++) {
+ if (haystack[i+j] != needle[j]) {
+ continue outer;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Test
+ public void test_containsBytes() {
+ byte[] haystack = "hello_world".getBytes(StandardCharsets.UTF_8);
+ assertTrue(containsBytes(haystack, haystack.length,
+ "hello".getBytes(StandardCharsets.UTF_8)));
+ assertTrue(containsBytes(haystack, haystack.length,
+ "world".getBytes(StandardCharsets.UTF_8)));
+ assertFalse(containsBytes(haystack, 6,
+ "world".getBytes(StandardCharsets.UTF_8)));
+ assertFalse(containsBytes(haystack, haystack.length,
+ "world_".getBytes(StandardCharsets.UTF_8)));
+ assertFalse(containsBytes(haystack, haystack.length,
+ "absent".getBytes(StandardCharsets.UTF_8)));
+ }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 03d5f9d..3fc2208 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1543,6 +1543,13 @@
"boosted_lte_earfcns_string_array";
/**
+ * Determine whether to use only RSRP for the number of LTE signal bars.
+ * @hide
+ */
+ public static final String KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL =
+ "use_only_rsrp_for_lte_signal_bar_bool";
+
+ /**
* Key identifying if voice call barring notification is required to be shown to the user.
* @hide
*/
@@ -1902,6 +1909,7 @@
null);
sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
sDefaults.putInt(IMSI_KEY_AVAILABILITY_INT, 0);
sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null);
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index c8b4776..de02de7 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -68,6 +68,7 @@
private int mTdScdmaRscp;
private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult
+ private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
/**
* Create a new SignalStrength from a intent notifier Bundle
@@ -108,6 +109,7 @@
mLteRsrpBoost = 0;
mTdScdmaRscp = INVALID;
isGsm = true;
+ mUseOnlyRsrpForLteLevel = false;
}
/**
@@ -134,6 +136,7 @@
mLteRsrpBoost = 0;
mTdScdmaRscp = INVALID;
isGsm = gsmFlag;
+ mUseOnlyRsrpForLteLevel = false;
}
/**
@@ -145,10 +148,10 @@
int cdmaDbm, int cdmaEcio,
int evdoDbm, int evdoEcio, int evdoSnr,
int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag) {
+ int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag, boolean lteLevelBaseOnRsrp) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag);
+ lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag, lteLevelBaseOnRsrp);
mTdScdmaRscp = tdScdmaRscp;
}
@@ -164,7 +167,7 @@
int tdScdmaRscp, boolean gsmFlag) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, 0, gsmFlag);
+ lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
mTdScdmaRscp = tdScdmaRscp;
}
@@ -180,7 +183,7 @@
boolean gsmFlag) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, 0, gsmFlag);
+ lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
}
/**
@@ -194,7 +197,7 @@
boolean gsmFlag) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
- INVALID, INVALID, INVALID, 0, gsmFlag);
+ INVALID, INVALID, INVALID, 0, gsmFlag, false);
}
/**
@@ -228,7 +231,7 @@
boolean gsm) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
- INVALID, INVALID, INVALID, 0, gsm);
+ INVALID, INVALID, INVALID, 0, gsm, false);
}
/**
@@ -248,6 +251,7 @@
* @param lteCqi
* @param lteRsrpBoost
* @param gsm
+ * @param useOnlyRsrpForLteLevel
*
* @hide
*/
@@ -255,7 +259,7 @@
int cdmaDbm, int cdmaEcio,
int evdoDbm, int evdoEcio, int evdoSnr,
int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- int lteRsrpBoost, boolean gsm) {
+ int lteRsrpBoost, boolean gsm, boolean useOnlyRsrpForLteLevel) {
mGsmSignalStrength = gsmSignalStrength;
mGsmBitErrorRate = gsmBitErrorRate;
mCdmaDbm = cdmaDbm;
@@ -271,6 +275,7 @@
mLteRsrpBoost = lteRsrpBoost;
mTdScdmaRscp = INVALID;
isGsm = gsm;
+ mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
if (DBG) log("initialize: " + toString());
}
@@ -293,6 +298,7 @@
mLteRsrpBoost = s.mLteRsrpBoost;
mTdScdmaRscp = s.mTdScdmaRscp;
isGsm = s.isGsm;
+ mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel;
}
/**
@@ -318,6 +324,7 @@
mLteRsrpBoost = in.readInt();
mTdScdmaRscp = in.readInt();
isGsm = (in.readInt() != 0);
+ mUseOnlyRsrpForLteLevel = (in.readInt() != 0);
}
/**
@@ -366,6 +373,7 @@
out.writeInt(mLteRsrpBoost);
out.writeInt(mTdScdmaRscp);
out.writeInt(isGsm ? 1 : 0);
+ out.writeInt(mUseOnlyRsrpForLteLevel ? 1 : 0);
}
/**
@@ -449,6 +457,17 @@
}
/**
+ * @param useOnlyRsrpForLteLevel true if it uses only RSRP for the number of LTE signal bar,
+ * otherwise false.
+ *
+ * Used by phone to use only RSRP or not for the number of LTE signal bar.
+ * @hide
+ */
+ public void setUseOnlyRsrpForLteLevel(boolean useOnlyRsrpForLteLevel) {
+ mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
+ }
+
+ /**
* @param lteRsrpBoost - signal strength offset
*
* Used by phone to set the lte signal strength offset which will be
@@ -835,6 +854,13 @@
}
}
+ if (useOnlyRsrpForLteLevel()) {
+ log("getLTELevel - rsrp = " + rsrpIconLevel);
+ if (rsrpIconLevel != -1) {
+ return rsrpIconLevel;
+ }
+ }
+
/*
* Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
* dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
@@ -915,6 +941,15 @@
}
/**
+ * @return true if it uses only RSRP for the number of LTE signal bar, otherwise false.
+ *
+ * @hide
+ */
+ public boolean useOnlyRsrpForLteLevel() {
+ return this.mUseOnlyRsrpForLteLevel;
+ }
+
+ /**
* @return get TD_SCDMA dbm
*
* @hide
@@ -974,7 +1009,8 @@
+ (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
+ (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
+ (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
- + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0));
+ + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0)
+ + (mUseOnlyRsrpForLteLevel ? 1 : 0));
}
/**
@@ -1008,7 +1044,8 @@
&& mLteCqi == s.mLteCqi
&& mLteRsrpBoost == s.mLteRsrpBoost
&& mTdScdmaRscp == s.mTdScdmaRscp
- && isGsm == s.isGsm);
+ && isGsm == s.isGsm
+ && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel);
}
/**
@@ -1031,7 +1068,9 @@
+ " " + mLteCqi
+ " " + mLteRsrpBoost
+ " " + mTdScdmaRscp
- + " " + (isGsm ? "gsm|lte" : "cdma"));
+ + " " + (isGsm ? "gsm|lte" : "cdma")
+ + " " + (mUseOnlyRsrpForLteLevel ? "use_only_rsrp_for_lte_level" :
+ "use_rsrp_and_rssnr_for_lte_level"));
}
/** Returns the signal strength related to GSM. */
@@ -1086,6 +1125,7 @@
mLteRsrpBoost = m.getInt("lteRsrpBoost");
mTdScdmaRscp = m.getInt("TdScdma");
isGsm = m.getBoolean("isGsm");
+ mUseOnlyRsrpForLteLevel = m.getBoolean("useOnlyRsrpForLteLevel");
}
/**
@@ -1110,6 +1150,7 @@
m.putInt("lteRsrpBoost", mLteRsrpBoost);
m.putInt("TdScdma", mTdScdmaRscp);
m.putBoolean("isGsm", isGsm);
+ m.putBoolean("useOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 67f6849..42c3de5 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2166,13 +2166,16 @@
* @hide
*/
public String getSimOperatorNumeric() {
- int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ int subId = mSubId;
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+ subId = SubscriptionManager.getDefaultDataSubscriptionId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+ subId = SubscriptionManager.getDefaultSmsSubscriptionId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultSubscriptionId();
+ subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+ if (!SubscriptionManager.isUsableSubIdValue(subId)) {
+ subId = SubscriptionManager.getDefaultSubscriptionId();
+ }
}
}
}
diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java
index d8d7f48..b30a3af 100644
--- a/telephony/java/android/telephony/mbms/FileServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,6 +36,7 @@
/** @hide */
@SystemApi
+ @TestApi
public FileServiceInfo(Map<Locale, String> newNames, String newClassName,
List<Locale> newLocales, String newServiceId, Date start, Date end,
List<FileInfo> newFiles) {
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 9ccdd56..4fee3df 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
@@ -42,6 +43,7 @@
* @hide
*/
@SystemApi
+@TestApi
public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>();
private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>();
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 62d570c..99a82ad 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -567,4 +567,12 @@
} while (valueIndex < endIndex);
return result;
}
+
+ public static String getDecimalSubstring(String iccId) {
+ int position;
+ for (position = 0; position < iccId.length(); position ++) {
+ if (!Character.isDigit(iccId.charAt(position))) break;
+ }
+ return iccId.substring( 0, position );
+ }
}
diff --git a/tests/testables/tests/AndroidManifest.xml b/tests/testables/tests/AndroidManifest.xml
index f6006b0..6435ad9 100644
--- a/tests/testables/tests/AndroidManifest.xml
+++ b/tests/testables/tests/AndroidManifest.xml
@@ -15,9 +15,11 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.testables">
+ package="com.android.testables" android:sharedUserId="android.uid.system">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 756549c..96e060d 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -25,6 +25,25 @@
],
}
+cc_library {
+ name: "streamingflags",
+ host_supported: true,
+ proto: {
+ export_proto_headers: true,
+ include_dirs: ["external/protobuf/src"],
+ },
+
+ target: {
+ host: {
+ proto: {
+ type: "full",
+ },
+ srcs: [
+ "stream.proto",
+ ],
+ },
+ },
+}
cc_binary_host {
name: "protoc-gen-javastream",
@@ -44,4 +63,5 @@
defaults: ["protoc-gen-stream-defaults"],
shared_libs: ["libprotoc"],
+ static_libs: ["streamingflags"],
}
diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
index d4e1b7a..dc96d5c 100644
--- a/tools/streaming_proto/cpp/main.cpp
+++ b/tools/streaming_proto/cpp/main.cpp
@@ -1,6 +1,8 @@
#include "Errors.h"
#include "string_utils.h"
+#include <frameworks/base/tools/streaming_proto/stream.pb.h>
+
#include "google/protobuf/compiler/plugin.pb.h"
#include "google/protobuf/io/zero_copy_stream_impl.h"
#include "google/protobuf/text_format.h"
@@ -160,6 +162,12 @@
text << endl;
}
+static inline bool
+should_generate_fields_mapping(const DescriptorProto& message)
+{
+ return message.options().GetExtension(stream).enable_fields_mapping();
+}
+
static void
write_message(stringstream& text, const DescriptorProto& message, const string& indent)
{
@@ -167,8 +175,7 @@
const string indented = indent + INDENT;
text << indent << "// message " << message.name() << endl;
- text << indent << "class " << message.name() << " {" << endl;
- text << indent << "public:" << endl;
+ text << indent << "namespace " << message.name() << " {" << endl;
// Enums
N = message.enum_type_size();
@@ -188,12 +195,27 @@
write_field(text, message.field(i), indented);
}
- text << indent << "};" << endl;
+ if (should_generate_fields_mapping(message)) {
+ N = message.field_size();
+ text << indented << "const int _FIELD_COUNT = " << N << ";" << endl;
+ text << indented << "const char* _FIELD_NAMES[" << N << "] = {" << endl;
+ for (int i=0; i<N; i++) {
+ text << indented << INDENT << "\"" << message.field(i).name() << "\"," << endl;
+ }
+ text << indented << "};" << endl;
+ text << indented << "const uint64_t _FIELD_IDS[" << N << "] = {" << endl;
+ for (int i=0; i<N; i++) {
+ text << indented << INDENT << make_constant_name(message.field(i).name()) << "," << endl;
+ }
+ text << indented << "};" << endl << endl;
+ }
+
+ text << indent << "} //" << message.name() << endl;
text << endl;
}
static void
-write_cpp_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
+write_header_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
{
stringstream text;
@@ -255,7 +277,7 @@
for (int i=0; i<N; i++) {
const FileDescriptorProto& file_descriptor = request.proto_file(i);
if (should_generate_for_file(request, file_descriptor.name())) {
- write_cpp_file(&response, file_descriptor);
+ write_header_file(&response, file_descriptor);
}
}
@@ -270,4 +292,4 @@
/* code */
return 0;
-}
\ No newline at end of file
+}
diff --git a/tools/streaming_proto/stream.proto b/tools/streaming_proto/stream.proto
new file mode 100644
index 0000000..123506c
--- /dev/null
+++ b/tools/streaming_proto/stream.proto
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+
+import "google/protobuf/descriptor.proto";
+
+package android.stream_proto;
+
+// This option tells streaming proto plugin to compile .proto files with extra features.
+message StreamFlags {
+ // creates a mapping of field names of the message to its field ids
+ optional bool enable_fields_mapping = 1;
+}
+
+extend google.protobuf.MessageOptions {
+ // Flags used by streaming proto plugins
+ optional StreamFlags stream = 126856794;
+}