diff options
195 files changed, 6094 insertions, 2537 deletions
diff --git a/api/current.txt b/api/current.txt index 5b986bccf017..f5f4bf4d7697 100644 --- a/api/current.txt +++ b/api/current.txt @@ -484,6 +484,7 @@ package android { field public static final int detailSocialSummary = 16843428; // 0x10102a4 field public static final int detailsElementBackground = 16843598; // 0x101034e field public static final int dial = 16843010; // 0x1010102 + field public static final int dialogCornerRadius = 16844145; // 0x1010571 field public static final int dialogIcon = 16843252; // 0x10101f4 field public static final int dialogLayout = 16843255; // 0x10101f7 field public static final int dialogMessage = 16843251; // 0x10101f3 @@ -12016,6 +12017,7 @@ package android.database.sqlite { 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 +18272,11 @@ package android.icu.text { 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 +18284,8 @@ package android.icu.text { 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 +18296,13 @@ package android.icu.text { 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 +18311,10 @@ package android.icu.text { 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 +18322,12 @@ package android.icu.text { 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 +25816,6 @@ package android.net { 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 df8e0346e531..d50fb4ecbbf9 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -616,6 +616,7 @@ package android { field public static final int detailSocialSummary = 16843428; // 0x10102a4 field public static final int detailsElementBackground = 16843598; // 0x101034e field public static final int dial = 16843010; // 0x1010102 + field public static final int dialogCornerRadius = 16844145; // 0x1010571 field public static final int dialogIcon = 16843252; // 0x10101f4 field public static final int dialogLayout = 16843255; // 0x10101f7 field public static final int dialogMessage = 16843251; // 0x10101f3 @@ -10234,6 +10235,7 @@ package android.content { field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY"; field public static final java.lang.String EXTRA_QUICK_VIEW_FEATURES = "android.intent.extra.QUICK_VIEW_FEATURES"; field public static final java.lang.String EXTRA_QUIET_MODE = "android.intent.extra.QUIET_MODE"; + field public static final java.lang.String EXTRA_REASON = "android.intent.extra.REASON"; field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER"; field public static final java.lang.String EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME"; field public static final java.lang.String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK"; @@ -12760,6 +12762,7 @@ package android.database.sqlite { 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 +19829,11 @@ package android.icu.text { 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 +19841,8 @@ package android.icu.text { 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 +19853,13 @@ package android.icu.text { 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 +19868,10 @@ package android.icu.text { 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 +19879,12 @@ package android.icu.text { 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 +28059,6 @@ package android.net { 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 6f251e965173..cee4789aaea4 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -484,6 +484,7 @@ package android { field public static final int detailSocialSummary = 16843428; // 0x10102a4 field public static final int detailsElementBackground = 16843598; // 0x101034e field public static final int dial = 16843010; // 0x1010102 + field public static final int dialogCornerRadius = 16844145; // 0x1010571 field public static final int dialogIcon = 16843252; // 0x10101f4 field public static final int dialogLayout = 16843255; // 0x10101f7 field public static final int dialogMessage = 16843251; // 0x10101f3 @@ -3873,6 +3874,7 @@ package android.app { method public void resizeStack(int, android.graphics.Rect) throws java.lang.SecurityException; method public deprecated void restartPackage(java.lang.String); method public void setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException; + method public void setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect) throws java.lang.SecurityException; method public static void setVrThread(int); method public void setWatchHeapLimit(long); method public static boolean supportsMultiWindow(android.content.Context); @@ -3886,6 +3888,8 @@ package android.app { field public static final int MOVE_TASK_WITH_HOME = 1; // 0x1 field public static final int RECENT_IGNORE_UNAVAILABLE = 2; // 0x2 field public static final int RECENT_WITH_EXCLUDED = 1; // 0x1 + field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1 + field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0 } public static class ActivityManager.AppTask { @@ -12103,6 +12107,7 @@ package android.database.sqlite { 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 +18417,11 @@ package android.icu.text { 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 +18429,8 @@ package android.icu.text { 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 +18441,13 @@ package android.icu.text { 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 +18456,10 @@ package android.icu.text { 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 +18467,12 @@ package android.icu.text { 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 +26019,6 @@ package android.net { public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable { method public void close(); - method protected void finalize(); method public int getSpi(); } @@ -41342,6 +41326,7 @@ package android.telephony.mbms { } 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 +41435,22 @@ package android.telephony.mbms { 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; @@ -44980,25 +44981,25 @@ package android.util.proto { field public static final long FIELD_COUNT_UNKNOWN = 0L; // 0x0L field public static final int FIELD_ID_MASK = -8; // 0xfffffff8 field public static final int FIELD_ID_SHIFT = 3; // 0x3 - field public static final long FIELD_TYPE_BOOL = 55834574848L; // 0xd00000000L - field public static final long FIELD_TYPE_BYTES = 64424509440L; // 0xf00000000L + field public static final long FIELD_TYPE_BOOL = 34359738368L; // 0x800000000L + field public static final long FIELD_TYPE_BYTES = 51539607552L; // 0xc00000000L field public static final long FIELD_TYPE_DOUBLE = 4294967296L; // 0x100000000L - field public static final long FIELD_TYPE_ENUM = 68719476736L; // 0x1000000000L - field public static final long FIELD_TYPE_FIXED32 = 38654705664L; // 0x900000000L - field public static final long FIELD_TYPE_FIXED64 = 42949672960L; // 0xa00000000L + field public static final long FIELD_TYPE_ENUM = 60129542144L; // 0xe00000000L + field public static final long FIELD_TYPE_FIXED32 = 30064771072L; // 0x700000000L + field public static final long FIELD_TYPE_FIXED64 = 25769803776L; // 0x600000000L field public static final long FIELD_TYPE_FLOAT = 8589934592L; // 0x200000000L - field public static final long FIELD_TYPE_INT32 = 12884901888L; // 0x300000000L - field public static final long FIELD_TYPE_INT64 = 17179869184L; // 0x400000000L + field public static final long FIELD_TYPE_INT32 = 21474836480L; // 0x500000000L + field public static final long FIELD_TYPE_INT64 = 12884901888L; // 0x300000000L field public static final long FIELD_TYPE_MASK = 1095216660480L; // 0xff00000000L - field public static final long FIELD_TYPE_OBJECT = 73014444032L; // 0x1100000000L - field public static final long FIELD_TYPE_SFIXED32 = 47244640256L; // 0xb00000000L - field public static final long FIELD_TYPE_SFIXED64 = 51539607552L; // 0xc00000000L + field public static final long FIELD_TYPE_MESSAGE = 47244640256L; // 0xb00000000L + field public static final long FIELD_TYPE_SFIXED32 = 64424509440L; // 0xf00000000L + field public static final long FIELD_TYPE_SFIXED64 = 68719476736L; // 0x1000000000L field public static final int FIELD_TYPE_SHIFT = 32; // 0x20 - field public static final long FIELD_TYPE_SINT32 = 30064771072L; // 0x700000000L - field public static final long FIELD_TYPE_SINT64 = 34359738368L; // 0x800000000L - field public static final long FIELD_TYPE_STRING = 60129542144L; // 0xe00000000L - field public static final long FIELD_TYPE_UINT32 = 21474836480L; // 0x500000000L - field public static final long FIELD_TYPE_UINT64 = 25769803776L; // 0x600000000L + field public static final long FIELD_TYPE_SINT32 = 73014444032L; // 0x1100000000L + field public static final long FIELD_TYPE_SINT64 = 77309411328L; // 0x1200000000L + field public static final long FIELD_TYPE_STRING = 38654705664L; // 0x900000000L + field public static final long FIELD_TYPE_UINT32 = 55834574848L; // 0xd00000000L + field public static final long FIELD_TYPE_UINT64 = 17179869184L; // 0x400000000L field public static final long FIELD_TYPE_UNKNOWN = 0L; // 0x0L field public static final java.lang.String TAG = "ProtoOutputStream"; field public static final int WIRE_TYPE_END_GROUP = 4; // 0x4 diff --git a/cmds/am/proto/instrumentation_data.proto b/cmds/am/proto/instrumentation_data.proto index 12a18a2a035f..8e29f9645568 100644 --- a/cmds/am/proto/instrumentation_data.proto +++ b/cmds/am/proto/instrumentation_data.proto @@ -28,6 +28,7 @@ message ResultsBundleEntry { 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 79e7fac11bb1..813335a688ab 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 @@ public class Am extends BaseCommand { } 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 b69ef1c2fca5..93b9f58d51d3 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.IPackageManager; 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 class Instrument { 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 @@ public class Instrument { * 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 @@ public class Instrument { 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 @@ public class Instrument { 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 @@ public class Instrument { 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/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp index d926ea7a9c87..77ae1a7ec730 100644 --- a/cmds/incidentd/src/PrivacyBuffer.cpp +++ b/cmds/incidentd/src/PrivacyBuffer.cpp @@ -87,12 +87,12 @@ PrivacyBuffer::stripField(const Privacy* parentPolicy, const PrivacySpec& spec) // current field is message type and its sub-fields have extra privacy policies uint32_t msgSize = mData.readRawVarint(); EncodedBuffer::Pointer start = mData.rp()->copy(); + long long token = mProto.start(policy->EncodedFieldId()); while (mData.rp()->pos() - start.pos() != msgSize) { - long long token = mProto.start(policy->EncodedFieldId()); status_t err = stripField(policy, spec); if (err != NO_ERROR) return err; - mProto.end(token); } + mProto.end(token); return NO_ERROR; } diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h index da82b008fc9b..dfd2312df668 100644 --- a/cmds/incidentd/src/section_list.h +++ b/cmds/incidentd/src/section_list.h @@ -30,7 +30,7 @@ extern const Section* SECTION_LIST[]; * This is the mapping of section IDs to each section's privacy policy. * The section IDs are guaranteed in ascending order, not NULL-terminated since size is provided. */ -extern const Privacy* PRIVACY_POLICY_LIST[]; +extern const Privacy** PRIVACY_POLICY_LIST; extern const int PRIVACY_POLICY_COUNT; diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp index 8f6e35548e78..84a2a825b4c3 100644 --- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp +++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp @@ -260,3 +260,13 @@ TEST_F(PrivacyBufferTest, BadDataInNestedMessage) { PrivacySpec spec; ASSERT_EQ(privacyBuf.strip(spec), BAD_VALUE); } + +TEST_F(PrivacyBufferTest, SelfRecursionMessage) { + string input = "\x2a\"" + VARINT_FIELD_1 + STRING_FIELD_2 + MESSAGE_FIELD_5; + writeToFdBuffer(input); + Privacy* field5 = create_message_privacy(5, NULL); + Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), field5, NULL }; + field5->children = list; + string expected = "\x2a\x1c" + STRING_FIELD_2 + "\x2a\xd" + STRING_FIELD_2; + assertStrip(EXPLICIT, expected, field5); +} diff --git a/cmds/incidentd/tests/section_list.cpp b/cmds/incidentd/tests/section_list.cpp index e47b61cf3854..4acc4298ab82 100644 --- a/cmds/incidentd/tests/section_list.cpp +++ b/cmds/incidentd/tests/section_list.cpp @@ -20,9 +20,11 @@ Privacy* list[] = { Privacy field_0 { 0, 11, list, EXPLICIT, NULL }; Privacy field_1 { 1, 9, NULL, AUTOMATIC, NULL }; -const Privacy* PRIVACY_POLICY_LIST[] = { +Privacy* final_list[] = { &field_0, &field_1 }; -const int PRIVACY_POLICY_COUNT = 2;
\ No newline at end of file +const Privacy** PRIVACY_POLICY_LIST = const_cast<const Privacy**>(final_list); + +const int PRIVACY_POLICY_COUNT = 2; diff --git a/cmds/statsd/.clang-format b/cmds/statsd/.clang-format index 3d64beeecda6..cead3a079435 100644 --- a/cmds/statsd/.clang-format +++ b/cmds/statsd/.clang-format @@ -12,3 +12,6 @@ IndentWidth: 4 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 db634d4a6601..54ade3563851 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -42,6 +42,9 @@ statsd_common_src := \ 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_PROTOC_OPTIMIZE_TYPE := lite-static 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 @@ LOCAL_SRC_FILES := \ 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 cdaca1bf85d7..c0cedb1b467e 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -60,6 +60,7 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig 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 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig } } -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 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { 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 6463441b7d75..0083827a347d 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -42,7 +42,7 @@ public: 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 604753ef54a0..edb1a0f94e2b 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -65,7 +65,6 @@ void CompanionDeathRecipient::binderDied(const wp<IBinder>& who) { 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 @@ void StatsService::print_cmd_help(FILE* out) { 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 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String // 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_uid_map(FILE* out) { 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 @@ Status StatsService::informPollAlarmFired() { "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 7f046584b2d0..3930d319ce8d 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 @@ private: /** * 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 f56c15a37086..953bcb3d67a4 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 fc88a88f4d63..dbdb3b7d31d7 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -46,8 +46,6 @@ public: 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 055b4784f72c..bb5ddeb4ec2b 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 @@ public: 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 4889b64de2ed..1a01afa7fbdc 100644 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ b/cmds/statsd/src/condition/ConditionWizard.h @@ -27,19 +27,23 @@ namespace statsd { // 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 aff476814c94..b691faea205d 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -74,6 +74,13 @@ SimpleConditionTracker::SimpleConditionTracker( 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 print(unordered_map<HashableDimensionKey, ConditionState>& conditions, cons } } -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 @@ bool SimpleConditionTracker::evaluateCondition(const LogEvent& event, // 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 1f357f059aab..b72157baf1e2 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -27,8 +27,6 @@ namespace statsd { 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 @@ public: 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 @@ private: // 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 c16971aeb8a8..88127194753f 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -118,9 +118,10 @@ static StatsdConfig build_fake_config() { 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 @@ static StatsdConfig build_fake_config() { 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 @@ static StatsdConfig build_fake_config() { 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 @@ static StatsdConfig build_fake_config() { 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 @@ static StatsdConfig build_fake_config() { 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 ee072f80aa4a..00259a83d3db 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 android { 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 c12806c1fc47..cc8059d80601 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 @@ public: // 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 000000000000..0d505cb49e8f --- /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 66556291e976..774e7f093087 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 @@ class StatsPuller { 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 7f554d3e398e..f45cb1ccaa74 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 <cutils/log.h> +#include <algorithm> +#include <climits> #include "KernelWakelockPuller.h" +#include "StatsPullerManager.h" #include "StatsService.h" -#include "external/StatsPullerManager.h" #include "logd/LogEvent.h" -#include <cutils/log.h> -#include <algorithm> #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 @@ vector<std::shared_ptr<LogEvent>> StatsPullerManager::Pull(int pullCode) { } } +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 e46aec1c060b..e599b6904ed4 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 1a039f6d61c5..8220fcb95a8c 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> @@ -23,13 +24,14 @@ namespace android { namespace os { namespace statsd { +using namespace android::util; using std::ostringstream; using std::string; using android::util::ProtoOutputStream; // 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); } @@ -206,20 +208,20 @@ string LogEvent::ToString() const { } void LogEvent::ToProto(ProtoOutputStream& proto) const { - long long atomToken = proto.start(TYPE_MESSAGE + mTagId); + long long atomToken = proto.start(FIELD_TYPE_MESSAGE | mTagId); const size_t N = mElements.size(); for (size_t i=0; i<N; i++) { const int key = i + 1; const android_log_list_element& elem = mElements[i]; if (elem.type == EVENT_TYPE_INT) { - proto.write(TYPE_INT32 + key, elem.data.int32); + proto.write(FIELD_TYPE_INT32 | key, elem.data.int32); } else if (elem.type == EVENT_TYPE_LONG) { - proto.write(TYPE_INT64 + key, (long long)elem.data.int64); + proto.write(FIELD_TYPE_INT64 | key, (long long)elem.data.int64); } else if (elem.type == EVENT_TYPE_FLOAT) { - proto.write(TYPE_FLOAT + key, elem.data.float32); + proto.write(FIELD_TYPE_FLOAT | key, elem.data.float32); } else if (elem.type == EVENT_TYPE_STRING) { - proto.write(TYPE_STRING + key, elem.data.string); + proto.write(FIELD_TYPE_STRING | key, elem.data.string); } } proto.end(atomToken); diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 9ef20ea6968c..df75d9f2e0b1 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -42,7 +42,7 @@ public: /** * 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 69f336f420fc..71cb7717d2d7 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -21,10 +21,11 @@ #include "CountMetricProducer.h" #include "stats_util.h" -#include <cutils/log.h> #include <limits.h> #include <stdlib.h> +using namespace android::util; +using android::util::ProtoOutputStream; using std::map; using std::string; using std::unordered_map; @@ -34,6 +35,27 @@ namespace android { namespace os { namespace statsd { +// for StatsLogReport +const int FIELD_ID_METRIC_ID = 1; +const int FIELD_ID_START_REPORT_NANOS = 2; +const int FIELD_ID_END_REPORT_NANOS = 3; +const int FIELD_ID_COUNT_METRICS = 5; +// for CountMetricDataWrapper +const int FIELD_ID_DATA = 1; +// for CountMetricData +const int FIELD_ID_DIMENSION = 1; +const int FIELD_ID_BUCKET_INFO = 2; +// for KeyValuePair +const int FIELD_ID_KEY = 1; +const int FIELD_ID_VALUE_STR = 2; +const int FIELD_ID_VALUE_INT = 3; +const int FIELD_ID_VALUE_BOOL = 4; +const int FIELD_ID_VALUE_FLOAT = 5; +// for CountBucketInfo +const int FIELD_ID_START_BUCKET_NANOS = 1; +const int FIELD_ID_END_BUCKET_NANOS = 2; +const int FIELD_ID_COUNT = 3; + // TODO: add back AnomalyTracker. CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex, const sp<ConditionWizard>& wizard) @@ -67,6 +89,8 @@ CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int co mConditionSliced = true; } + startNewProtoOutputStream(mStartTimeNs); + VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); } @@ -75,60 +99,99 @@ CountMetricProducer::~CountMetricProducer() { VLOG("~CountMetricProducer() called"); } -void CountMetricProducer::finish() { - // TODO: write the StatsLogReport to dropbox using - // DropboxWriter. +void CountMetricProducer::startNewProtoOutputStream(long long startTime) { + mProto = std::make_unique<ProtoOutputStream>(); + mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id()); + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime); + mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS); } -static void addSlicedCounterToReport(StatsLogReport_CountMetricDataWrapper& wrapper, - const vector<KeyValuePair>& key, - const vector<CountBucketInfo>& buckets) { - CountMetricData* 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] count: %lld", bucket.start_bucket_nanos(), - bucket.end_bucket_nanos(), bucket.count()); - } +void CountMetricProducer::finish() { } -void CountMetricProducer::onSlicedConditionMayChange() { +void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id()); } StatsLogReport CountMetricProducer::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); + long long endTime = time(nullptr) * NANO_SECONDS_IN_A_SECOND; // 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. - flushCounterIfNeeded(time(nullptr) * NANO_SECONDS_IN_A_SECOND); - report.set_end_report_nanos(mCurrentBucketStartTimeNs); - - StatsLogReport_CountMetricDataWrapper* wrapper = report.mutable_count_metrics(); + flushCounterIfNeeded(endTime); - for (const auto& pair : mPastBuckets) { - const HashableDimensionKey& hashableKey = pair.first; + for (const auto& counter : mPastBucketProtos) { + const HashableDimensionKey& hashableKey = counter.first; auto it = mDimensionKeyMap.find(hashableKey); if (it == mDimensionKeyMap.end()) { ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str()); continue; } + long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA); + + // First fill dimension (KeyValuePairs). + for (const auto& kv : it->second) { + long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION); + mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key()); + if (kv.has_value_str()) { + mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str()); + } else if (kv.has_value_int()) { + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int()); + } else if (kv.has_value_bool()) { + mProto->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool()); + } else if (kv.has_value_float()) { + mProto->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float()); + } + mProto->end(dimensionToken); + } - VLOG(" dimension key %s", hashableKey.c_str()); - addSlicedCounterToReport(*wrapper, it->second, pair.second); + // Then fill bucket_info (CountBucketInfo). + for (const auto& proto : counter.second) { + size_t bufferSize = proto->size(); + char* buffer(new char[bufferSize]); + size_t pos = 0; + auto it = proto->data(); + while (it.readBuffer() != NULL) { + size_t toRead = it.currentToRead(); + std::memcpy(&buffer[pos], it.readBuffer(), toRead); + pos += toRead; + it.rp()->move(toRead); + } + mProto->write(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION, buffer, bufferSize); + } + + mProto->end(wrapperToken); + } + + mProto->end(mProtoToken); + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, + (long long)mCurrentBucketStartTimeNs); + + size_t bufferSize = mProto->size(); + VLOG("metric %lld dump report now...", mMetric.metric_id()); + std::unique_ptr<uint8_t[]> buffer(new uint8_t[bufferSize]); + size_t pos = 0; + auto it = mProto->data(); + while (it.readBuffer() != NULL) { + size_t toRead = it.currentToRead(); + std::memcpy(&buffer[pos], it.readBuffer(), toRead); + pos += toRead; + it.rp()->move(toRead); } - return report; - // TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped. + + startNewProtoOutputStream(endTime); + mPastBucketProtos.clear(); + mByteSize = 0; + + // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this: + // return std::move(buffer); + return StatsLogReport(); + + // TODO: Clear 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 +199,7 @@ void CountMetricProducer::onConditionChanged(const bool conditionMet) { 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); @@ -176,15 +239,17 @@ void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) { // adjust the bucket start time int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs; - CountBucketInfo info; - info.set_start_bucket_nanos(mCurrentBucketStartTimeNs); - info.set_end_bucket_nanos(mCurrentBucketStartTimeNs + mBucketSizeNs); - for (const auto& counter : mCurrentSlicedCounter) { - info.set_count(counter.second); - // it will auto create new vector of CountbucketInfo if the key is not found. - auto& bucketList = mPastBuckets[counter.first]; - bucketList.push_back(info); + unique_ptr<ProtoOutputStream> proto = make_unique<ProtoOutputStream>(); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS, + (long long)mCurrentBucketStartTimeNs); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS, + (long long)mCurrentBucketStartTimeNs + mBucketSizeNs); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)counter.second); + + auto& bucketList = mPastBucketProtos[counter.first]; + bucketList.push_back(std::move(proto)); + mByteSize += proto->size(); VLOG("metric %lld, dump key value: %s -> %d", mMetric.metric_id(), counter.first.c_str(), counter.second); @@ -203,11 +268,11 @@ void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) { (long long)mCurrentBucketStartTimeNs); } +// Rough estimate of CountMetricProducer buffer stored. This number will be +// greater than actual data size as it contains each dimension of +// CountMetricData is duplicated. size_t CountMetricProducer::byteSize() { -// TODO: return actual proto size when ProtoOutputStream is ready for use for -// CountMetricsProducer. -// return mProto->size(); - return 0; + return mByteSize; } } // namespace statsd diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index be77e4739e8e..473a4ba4f428 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -19,6 +19,7 @@ #include <unordered_map> +#include <android/util/ProtoOutputStream.h> #include "../condition/ConditionTracker.h" #include "../matchers/matcher_util.h" #include "CountAnomalyTracker.h" @@ -41,29 +42,34 @@ public: 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; - // Save the past buckets and we can clear when the StatsLogReport is dumped. - std::unordered_map<HashableDimensionKey, std::vector<CountBucketInfo>> mPastBuckets; + std::unordered_map<HashableDimensionKey, + std::vector<unique_ptr<android::util::ProtoOutputStream>>> mPastBucketProtos; + + size_t mByteSize; // The current bucket. std::unordered_map<HashableDimensionKey, int> mCurrentSlicedCounter; @@ -71,6 +77,12 @@ private: vector<unique_ptr<CountAnomalyTracker>> mAnomalyTrackers; void flushCounterIfNeeded(const uint64_t newEventTime); + + std::unique_ptr<android::util::ProtoOutputStream> mProto; + + long long mProtoToken; + + void startNewProtoOutputStream(long long timestamp); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index a590bc856966..340f503fd651 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 @@ namespace statsd { 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 @@ DurationMetricProducer::~DurationMetricProducer() { 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 @@ static void addDurationBucketsToReport(StatsLogReport_DurationMetricDataWrapper& } 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 @@ StatsLogReport DurationMetricProducer::onDumpReport() { // 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,215 +148,49 @@ StatsLogReport DurationMetricProducer::onDumpReport() { 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. +void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) { + if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) { 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; + VLOG("flushing..........."); + for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end(); ++it) { + if (it->second->flushIfNeeded(eventTime)) { + VLOG("erase bucket for key %s", it->first.c_str()); + mCurrentSlicedDuration.erase(it); } } -} -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; + int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs; + mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs; } -void DurationMetricProducer::noteStopAll(const uint64_t eventTime) { - for (auto& duration : mCurrentSlicedDuration) { - noteStop(duration.first, eventTime); - } -} +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()); -// 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) { - if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) { + if (matcherIndex == mStopAllIndex) { + for (auto& pair : mCurrentSlicedDuration) { + pair.second->noteStopAll(event.GetTimestampNs()); + } 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); - - 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()); - } + HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension)); - VLOG(" final duration for last bucket: %lld", (long long)finalDuration); + if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) { + mCurrentSlicedDuration[eventKey] = createDurationTracker(mPastBuckets[eventKey]); + } - // 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); - } + auto it = mCurrentSlicedDuration.find(eventKey); - // 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 (matcherIndex == mStartIndex) { + it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys); - 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. - 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; - } + } else if (matcherIndex == mStopIndex) { + it->second->noteStop(atomKey, event.GetTimestampNs()); } } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 8820403a32fb..febf25d45cc2 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 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 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 @@ private: // 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); - - 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); + std::unique_ptr<DurationTracker> createDurationTracker(std::vector<DurationBucketInfo>& bucket); - 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 7e0610514679..cbae1d343f03 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -20,10 +20,10 @@ #include "EventMetricProducer.h" #include "stats_util.h" -#include <cutils/log.h> #include <limits.h> #include <stdlib.h> +using namespace android::util; using android::util::ProtoOutputStream; using std::map; using std::string; @@ -37,13 +37,13 @@ namespace statsd { // for StatsLogReport const int FIELD_ID_METRIC_ID = 1; const int FIELD_ID_START_REPORT_NANOS = 2; -const int FIELD_ID_END_REPORT_NANOS = 2; +const int FIELD_ID_END_REPORT_NANOS = 3; const int FIELD_ID_EVENT_METRICS = 4; +// for EventMetricDataWrapper +const int FIELD_ID_DATA = 1; // for EventMetricData const int FIELD_ID_TIMESTAMP_NANOS = 1; const int FIELD_ID_STATS_EVENTS = 2; -// for CountMetricDataWrapper -const int FIELD_ID_DATA = 1; EventMetricProducer::EventMetricProducer(const EventMetric& metric, const int conditionIndex, const sp<ConditionWizard>& wizard) @@ -70,21 +70,21 @@ void EventMetricProducer::startNewProtoOutputStream(long long startTime) { mProto = std::make_unique<ProtoOutputStream>(); // TODO: We need to auto-generate the field IDs for StatsLogReport, EventMetricData, // and StatsEvent. - mProto->write(TYPE_INT32 + FIELD_ID_METRIC_ID, mMetric.metric_id()); - mProto->write(TYPE_INT64 + FIELD_ID_START_REPORT_NANOS, startTime); - mProtoToken = mProto->start(TYPE_MESSAGE + FIELD_ID_EVENT_METRICS); + mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id()); + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime); + mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS); } void EventMetricProducer::finish() { } -void EventMetricProducer::onSlicedConditionMayChange() { +void EventMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { } StatsLogReport EventMetricProducer::onDumpReport() { long long endTime = time(nullptr) * NANO_SECONDS_IN_A_SECOND; mProto->end(mProtoToken); - mProto->write(TYPE_INT64 + FIELD_ID_END_REPORT_NANOS, endTime); + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime); size_t bufferSize = mProto->size(); VLOG("metric %lld dump report now... proto size: %zu ", mMetric.metric_id(), bufferSize); @@ -105,7 +105,7 @@ StatsLogReport EventMetricProducer::onDumpReport() { 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,15 +113,15 @@ void EventMetricProducer::onConditionChanged(const bool conditionMet) { 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; } - long long wrapperToken = mProto->start(TYPE_MESSAGE + FIELD_ID_DATA); - mProto->write(TYPE_INT64 + FIELD_ID_TIMESTAMP_NANOS, (long long)event.GetTimestampNs()); - long long eventToken = mProto->start(TYPE_MESSAGE + FIELD_ID_STATS_EVENTS); + long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA); + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_TIMESTAMP_NANOS, (long long)event.GetTimestampNs()); + long long eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_STATS_EVENTS); event.ToProto(*mProto); mProto->end(eventToken); mProto->end(wrapperToken); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 14fa31cce145..7dd0e38a0533 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -41,20 +41,22 @@ public: 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 3c8ce6e7dd1c..535f4a276fe1 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -21,7 +21,8 @@ namespace statsd { 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 @@ void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent 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 80eb527f3bdf..3b117ec4e307 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -48,11 +48,11 @@ public: 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 @@ protected: 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 4fa3965a9dd2..521fcc33ac47 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -74,6 +74,7 @@ void MetricsManager::onLogEvent(const LogEvent& event) { } int tagId = event.GetTagId(); + uint64_t eventTime = event.GetTimestampNs(); if (mTagIds.find(tagId) == mTagIds.end()) { // not interesting... return; @@ -124,13 +125,14 @@ void MetricsManager::onLogEvent(const LogEvent& event) { // 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 @@ void MetricsManager::onLogEvent(const LogEvent& event) { 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 000000000000..cb6166dafa3a --- /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 000000000000..4f1791351f87 --- /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 000000000000..0d0d9a4e2647 --- /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 000000000000..856ca9d979f5 --- /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 000000000000..c74d070a3297 --- /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 000000000000..e045fb4fb012 --- /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 000000000000..542525153fb0 --- /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 e90f998a7179..3d4036e8d176 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 os { 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 @@ bool handleMetricWithLogTrackers(const string what, const int metricIndex, 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 @@ bool handleMetricWithConditions( } 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 initConditions(const StatsdConfig& config, const unordered_map<string, int> 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 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l 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 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l 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 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l 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 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l 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 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l 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 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l 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 @@ bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds, 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 6722eb3cfe72..e089d0653238 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 @@ bool initMetrics( 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 8b948dee887e..13e776fb1f80 100644 --- a/cmds/statsd/src/packages/PackageInfoListener.h +++ b/cmds/statsd/src/packages/PackageInfoListener.h @@ -29,6 +29,9 @@ public: // 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 f4621ee15dbe..d83c3a426685 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -52,25 +52,35 @@ int UidMap::getAppVersion(int uid, const string& packageName) const { 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 @@ void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t 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::updateApp(const String16& app_16, const int32_t& uid, const int32_t } 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 @@ void UidMap::removeListener(sp<PackageInfoListener> producer) { 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::printUidMap(FILE* out) { } } +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 d550372f2e9f..bf120e04d7c8 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 @@ public: * 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 @@ public: // 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 @@ private: // 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 74ee3325a943..51244c6bdf4b 100644 --- a/cmds/statsd/src/stats_events.proto +++ b/cmds/statsd/src/stats_events.proto @@ -35,7 +35,8 @@ option java_outer_classname = "StatsEventProto"; * 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 @@ message StatsEvent { 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 @@ message ScreenBrightnessChanged { * 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 @@ message BatteryLevelChanged { * 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 @@ message ChargingStateChanged { * 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 @@ message PluggedStateChanged { * 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 @@ message DeviceTemperatureReported { * 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 @@ message DeviceOnStatusChanged { * 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 @@ message KernelWakeupReported { * 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 @@ message WifiLockStateChanged { * 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 @@ message WifiSignalStrengthChanged { * 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 @@ message WifiScanStateChanged { * 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 @@ message PhoneSignalStrengthChanged { 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 5e8ef245119a..9470372005f8 100644 --- a/cmds/statsd/src/stats_events_copy.proto +++ b/cmds/statsd/src/stats_events_copy.proto @@ -40,9 +40,28 @@ option java_outer_classname = "StatsEventProto"; */ 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 @@ message WorkSource { * 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 @@ message ScreenStateChanged { } /** - * 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 ec91509aaf40..1e37ff8c0aef 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -98,15 +98,19 @@ message GaugeMetricData { } 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 AppInfo initial = 1; + repeated PackageInfo package_info = 2; + } + repeated PackageInfoSnapshot snapshots = 1; message Change { optional bool deletion = 1; @@ -139,9 +143,11 @@ message StatsLogReport { 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 39c1d59c88b7..d3d7e3709644 100644 --- a/cmds/statsd/src/stats_util.h +++ b/cmds/statsd/src/stats_util.h @@ -30,16 +30,10 @@ namespace statsd { #define MATCHER_NOT_FOUND -2 #define NANO_SECONDS_IN_A_SECOND (1000 * 1000 * 1000) -// TODO: Remove the following constants once they are exposed in ProtOutputStream.h -const uint64_t FIELD_TYPE_SHIFT = 32; -const uint64_t TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; -const uint64_t TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; -const uint64_t TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; -const uint64_t TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; -const uint64_t TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; - 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 a4d2421b16d3..87884b33cef0 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -82,6 +82,8 @@ message SimpleCondition { optional bool count_nesting = 3 [default = true]; optional string stop_all = 4; + + repeated KeyMatcher dimension = 5; } message Condition { @@ -148,29 +150,24 @@ message CountMetric { 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 @@ message ValueMetric { 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 000000000000..ae8bf4265235 --- /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 b000e1388632..e8e4d8be1cd6 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 @@ StatsdConfig buildMissingMatchers() { 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 @@ TEST(MetricsManagerTest, TestGoodConfig) { 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 000000000000..0b7981935ab1 --- /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 f9a90e4ee29b..671f6d4ac472 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 @@ TEST(UidMapTest, TestAddAndRemove) { 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/ActivityManager.java b/core/java/android/app/ActivityManager.java index 23059576fdc4..064e97828f06 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -687,6 +687,7 @@ public class ActivityManager { * in portrait mode or at the left half of the screen if in landscape mode. * @hide */ + @TestApi public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; /** @@ -696,6 +697,7 @@ public class ActivityManager { * in portrait mode or at the right half of the screen if in landscape mode. * @hide */ + @TestApi public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; /** @@ -1926,6 +1928,33 @@ public class ActivityManager { } /** + * Moves the input task to the primary-split-screen stack. + * @param taskId Id of task to move. + * @param createMode The mode the primary split screen stack should be created in if it doesn't + * exist already. See + * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT} + * and + * {@link android.app.ActivityManager + * #SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT} + * @param toTop If the task and stack should be moved to the top. + * @param animate Whether we should play an animation for the moving the task + * @param initialBounds If the primary stack gets created, it will use these bounds for the + * docked stack. Pass {@code null} to use default bounds. + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public void setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop, + boolean animate, Rect initialBounds) throws SecurityException { + try { + getService().setTaskWindowingModeSplitScreenPrimary(taskId, createMode, toTop, animate, + initialBounds); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Resizes the input stack id to the given bounds. * @param stackId Id of the stack to resize. * @param bounds Bounds to resize the stack to or {@code null} for fullscreen. diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java index 5c6ffa39e6f7..392387a99897 100644 --- a/core/java/android/app/VrManager.java +++ b/core/java/android/app/VrManager.java @@ -198,4 +198,20 @@ public class VrManager { e.rethrowFromSystemServer(); } } + + /** + * Sets the current standby status of the VR device. Standby mode is only used on standalone vr + * devices. Standby mode is a deep sleep state where it's appropriate to turn off vr mode. + * + * @param standby True if the device is entering standby, false if it's exiting standby. + * @hide + */ + @RequiresPermission(android.Manifest.permission.ACCESS_VR_MANAGER) + public void setStandbyEnabled(boolean standby) { + try { + mService.setStandbyEnabled(standby); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index 33825b4b5f41..da718dc9a2a2 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -19,15 +19,19 @@ import android.Manifest.permission; 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 @@ public abstract class SliceProvider extends ContentProvider { @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/Intent.java b/core/java/android/content/Intent.java index e47de752ec70..dd729a36875d 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3444,11 +3444,12 @@ public class Intent implements Parcelable, Cloneable { /** * A broadcast action to trigger a factory reset. * - * <p> The sender must hold the {@link android.Manifest.permission#MASTER_CLEAR} permission. + * <p>The sender must hold the {@link android.Manifest.permission#MASTER_CLEAR} permission. The + * reason for the factory reset should be specified as {@link #EXTRA_REASON}. * * <p>Not for use by third-party applications. * - * @see #EXTRA_FORCE_MASTER_CLEAR + * @see #EXTRA_FORCE_FACTORY_RESET * * {@hide} */ @@ -4827,7 +4828,13 @@ public class Intent implements Parcelable, Cloneable { /** @hide */ public static final int EXTRA_TIME_PREF_VALUE_USE_LOCALE_DEFAULT = 2; - /** {@hide} */ + /** + * Intent extra: the reason that the operation associated with this intent is being performed. + * + * <p>Type: String + * @hide + */ + @SystemApi public static final String EXTRA_REASON = "android.intent.extra.REASON"; /** diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index b94a410b0ba0..05c555685384 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -671,9 +671,13 @@ public class LauncherApps { } /** - * Returns whether the caller can access the shortcut information. + * Returns whether the caller can access the shortcut information. Access is currently + * available to: * - * <p>Only the default launcher can access the shortcut information. + * <ul> + * <li>The current launcher (or default launcher if there is no set current launcher).</li> + * <li>The currently active voice interaction service.</li> + * </ul> * * <p>Note when this method returns {@code false}, it may be a temporary situation because * the user is trying a new launcher application. The user may decide to change the default diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index 7fc25d82870c..dadfaa9f1a66 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -73,6 +73,9 @@ public abstract class ShortcutServiceInternal { public abstract boolean hasShortcutHostPermission(int launcherUserId, @NonNull String callingPackage, int callingPid, int callingUid); + public abstract void setShortcutHostPackage(@NonNull String type, @Nullable String packageName, + int userId); + public abstract boolean requestPinAppWidget(@NonNull String callingPackage, @NonNull AppWidgetProviderInfo appWidget, @Nullable Bundle extras, @Nullable IntentSender resultIntent, int userId); diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 386239cf4f93..3239212adf66 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -49,6 +49,8 @@ import android.util.TypedValue; 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 @@ public class ResourcesImpl { 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 @@ public class ResourcesImpl { 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 @@ public class ResourcesImpl { } } } + + 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 c28583ea867a..361b81b77f1d 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -289,7 +289,9 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen 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 2dc5ca43e7a1..13e6f7182e8a 100644 --- a/core/java/android/database/sqlite/SQLiteCursor.java +++ b/core/java/android/database/sqlite/SQLiteCursor.java @@ -22,6 +22,8 @@ import android.database.DatabaseUtils; 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 @@ public class SQLiteCursor extends AbstractWindowedCursor { /** 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 @@ public class SQLiteCursor extends AbstractWindowedCursor { 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 @@ public class SQLiteCursor extends AbstractWindowedCursor { } /** + * 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 df0e262b712f..83b8dc76bfc3 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -285,6 +285,7 @@ public final class SQLiteDatabase extends SQLiteClosable { } } mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs; + mConfigurationLocked.useCompatibilityWal = SQLiteGlobal.isCompatibilityWalSupported(); } @Override @@ -2070,15 +2071,21 @@ public final class SQLiteDatabase extends SQLiteClosable { 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 34c9b3395d1a..905da7247308 100644 --- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java +++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java @@ -111,6 +111,15 @@ public final class SQLiteDatabaseConfiguration { 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 @@ public final class SQLiteDatabaseConfiguration { 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 94d5555c4c24..bb2a51706767 100644 --- a/core/java/android/database/sqlite/SQLiteGlobal.java +++ b/core/java/android/database/sqlite/SQLiteGlobal.java @@ -81,6 +81,17 @@ public final class SQLiteGlobal { } /** + * 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 000000000000..b0436eb5f8af --- /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 017c2134f288..2e62eb6a5f97 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -23,7 +23,6 @@ import android.util.Log; 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.HashMap; import java.util.Map; - - /** * Provides various debugging methods for Android applications, including * tracing and allocation counts. @@ -1959,13 +1956,7 @@ public final class Debug */ @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 @@ public final class Debug * @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 @@ public final class Debug * counting process. */ public boolean collect() { - try { - VMDebug.stopInstructionCounting(); - VMDebug.getInstructionCount(mCounts); - } catch (UnsupportedOperationException uoe) { - return false; - } - return true; + return false; } /** @@ -2003,13 +1982,7 @@ public final class Debug * 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 @@ public final class Debug * 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 5b0e5bbce2f7..f977c1dea438 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -836,7 +836,6 @@ public class Environment { * physically removed. */ public static boolean isExternalStorageRemovable() { - if (isStorageDisabled()) return false; final File externalDir = sCurrentUser.getExternalDirs()[0]; return isExternalStorageRemovable(externalDir); } @@ -875,7 +874,6 @@ public class Environment { * boolean) */ public static boolean isExternalStorageEmulated() { - if (isStorageDisabled()) return false; final File externalDir = sCurrentUser.getExternalDirs()[0]; return isExternalStorageEmulated(externalDir); } @@ -951,9 +949,6 @@ public class Environment { 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 7dec4d724f15..3544ea1e03e3 100644 --- a/core/java/android/os/HidlSupport.java +++ b/core/java/android/os/HidlSupport.java @@ -156,4 +156,27 @@ public class HidlSupport { // 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 5e2a0815b60d..dd9e774141e1 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -63,6 +63,13 @@ public abstract class HwBinder implements IHwBinder { 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 2f89ce6270be..a07e42c720c5 100644 --- a/core/java/android/os/HwRemoteBinder.java +++ b/core/java/android/os/HwRemoteBinder.java @@ -63,4 +63,9 @@ public class HwRemoteBinder implements IHwBinder { } 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 20f6c8ecd495..c0a95cc08dec 100644 --- a/core/java/android/os/IStatsCompanionService.aidl +++ b/core/java/android/os/IStatsCompanionService.aidl @@ -46,10 +46,10 @@ interface IStatsCompanionService { * 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 9b3a2d689693..00333dad1a17 100644 --- a/core/java/android/os/TokenWatcher.java +++ b/core/java/android/os/TokenWatcher.java @@ -16,17 +16,23 @@ package android.os; +import android.util.Log; + import java.io.PrintWriter; import java.util.ArrayList; -import java.util.WeakHashMap; import java.util.Set; -import android.util.Log; +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 @@ public abstract class TokenWatcher * 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 62f4bf5833d3..433878e9511a 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -72,6 +72,7 @@ import android.util.ArrayMap; 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 @@ public final class Settings { 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; @@ -8007,28 +8012,40 @@ public final class Settings { public static final String HDMI_SYSTEM_AUDIO_CONTROL_ENABLED = "hdmi_system_audio_control_enabled"; - /** - * Whether TV will automatically turn on upon reception of the CEC command - * <Text View On> or <Image View On>. (0 = false, 1 = true) - * @hide - */ - public static final String HDMI_CONTROL_AUTO_WAKEUP_ENABLED = - "hdmi_control_auto_wakeup_enabled"; + /** + * Whether TV will automatically turn on upon reception of the CEC command + * <Text View On> or <Image View On>. (0 = false, 1 = true) + * + * @hide + */ + public static final String HDMI_CONTROL_AUTO_WAKEUP_ENABLED = + "hdmi_control_auto_wakeup_enabled"; - /** - * Whether TV will also turn off other CEC devices when it goes to standby mode. - * (0 = false, 1 = true) - * @hide - */ - public static final String HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED = - "hdmi_control_auto_device_off_enabled"; + /** + * Whether TV will also turn off other CEC devices when it goes to standby mode. + * (0 = false, 1 = true) + * + * @hide + */ + public static final String HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED = + "hdmi_control_auto_device_off_enabled"; - /** - * The interval in milliseconds at which location requests will be throttled when they are - * coming from the background. - * @hide - */ - public static final String LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS = + /** + * If <b>true</b>, enables out-of-the-box execution for priv apps. + * Default: false + * Values: 0 = false, 1 = true + * + * @hide + */ + public static final String PRIV_APP_OOB_ENABLED = "priv_app_oob_enabled"; + + /** + * The interval in milliseconds at which location requests will be throttled when they are + * coming from the background. + * + * @hide + */ + public static final String LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS = "location_background_throttle_interval_ms"; /** diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl index fef92230e7b8..7285fb40ae02 100644 --- a/core/java/android/service/vr/IVrManager.aidl +++ b/core/java/android/service/vr/IVrManager.aidl @@ -101,5 +101,13 @@ interface IVrManager { * application's compositor process to bind to, or null to clear the current binding. */ void setAndBindCompositor(in String componentName); + + /** + * Sets the current standby status of the VR device. Standby mode is only used on standalone vr + * devices. Standby mode is a deep sleep state where it's appropriate to turn off vr mode. + * + * @param standy True if the device is entering standby, false if it's exiting standby. + */ + void setStandbyEnabled(boolean standby); } diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java index 43a978974fe3..a94806a02fbb 100644 --- a/core/java/android/util/proto/ProtoOutputStream.java +++ b/core/java/android/util/proto/ProtoOutputStream.java @@ -127,42 +127,48 @@ public final class ProtoOutputStream { public static final long FIELD_TYPE_UNKNOWN = 0; + /** + * The types are copied from external/protobuf/src/google/protobuf/descriptor.h directly, + * so no extra mapping needs to be maintained in this case. + */ public static final long FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT; public static final long FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_INT32 = 3L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_INT64 = 4L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_UINT32 = 5L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_UINT64 = 6L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_SINT32 = 7L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_SINT64 = 8L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_FIXED32 = 9L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_FIXED64 = 10L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_SFIXED32 = 11L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_SFIXED64 = 12L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_BOOL = 13L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_STRING = 14L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_BYTES = 15L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_ENUM = 16L << FIELD_TYPE_SHIFT; - public static final long FIELD_TYPE_OBJECT = 17L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_INT64 = 3L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_UINT64 = 4L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_INT32 = 5L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_FIXED64 = 6L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_FIXED32 = 7L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_BOOL = 8L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_STRING = 9L << FIELD_TYPE_SHIFT; +// public static final long FIELD_TYPE_GROUP = 10L << FIELD_TYPE_SHIFT; // Deprecated. + public static final long FIELD_TYPE_MESSAGE = 11L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_BYTES = 12L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_UINT32 = 13L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_ENUM = 14L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_SFIXED32 = 15L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_SFIXED64 = 16L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_SINT32 = 17L << FIELD_TYPE_SHIFT; + public static final long FIELD_TYPE_SINT64 = 18L << FIELD_TYPE_SHIFT; private static final String[] FIELD_TYPE_NAMES = new String[] { "Double", "Float", - "Int32", "Int64", - "UInt32", "UInt64", - "SInt32", - "SInt64", - "Fixed32", + "Int32", "Fixed64", - "SFixed32", - "SFixed64", + "Fixed32", "Bool", "String", + "Group", // This field is deprecated but reserved here for indexing. + "Message", "Bytes", + "UInt32", "Enum", - "Object", + "SFixed32", + "SFixed64", + "SInt32", + "SInt64", }; // @@ -867,21 +873,21 @@ public final class ProtoOutputStream { assertNotCompacted(); final int id = (int)fieldId; - switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) { + switch ((int) ((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) { // bytes - case (int)((FIELD_TYPE_BYTES | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT): + case (int) ((FIELD_TYPE_BYTES | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT): writeBytesImpl(id, val); break; - case (int)((FIELD_TYPE_BYTES | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT): - case (int)((FIELD_TYPE_BYTES | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT): + case (int) ((FIELD_TYPE_BYTES | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT): + case (int) ((FIELD_TYPE_BYTES | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT): writeRepeatedBytesImpl(id, val); break; // Object - case (int)((FIELD_TYPE_OBJECT | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT): + case (int) ((FIELD_TYPE_MESSAGE | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT): writeObjectImpl(id, val); break; - case (int)((FIELD_TYPE_OBJECT | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT): - case (int)((FIELD_TYPE_OBJECT | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT): + case (int) ((FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT): + case (int) ((FIELD_TYPE_MESSAGE | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT): writeRepeatedObjectImpl(id, val); break; // nothing else allowed @@ -899,7 +905,7 @@ public final class ProtoOutputStream { assertNotCompacted(); final int id = (int)fieldId; - if ((fieldId & FIELD_TYPE_MASK) == FIELD_TYPE_OBJECT) { + if ((fieldId & FIELD_TYPE_MASK) == FIELD_TYPE_MESSAGE) { final long count = fieldId & FIELD_COUNT_MASK; if (count == FIELD_COUNT_SINGLE) { return startObjectImpl(id, false); @@ -2091,7 +2097,7 @@ public final class ProtoOutputStream { @Deprecated public long startObject(long fieldId) { assertNotCompacted(); - final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_OBJECT); + final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_MESSAGE); return startObjectImpl(id, false); } @@ -2119,7 +2125,7 @@ public final class ProtoOutputStream { @Deprecated public long startRepeatedObject(long fieldId) { assertNotCompacted(); - final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_OBJECT); + final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_MESSAGE); return startObjectImpl(id, true); } @@ -2217,7 +2223,7 @@ public final class ProtoOutputStream { @Deprecated public void writeObject(long fieldId, byte[] value) { assertNotCompacted(); - final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_OBJECT); + final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_MESSAGE); writeObjectImpl(id, value); } @@ -2237,7 +2243,7 @@ public final class ProtoOutputStream { @Deprecated public void writeRepeatedObject(long fieldId, byte[] value) { assertNotCompacted(); - final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_OBJECT); + final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_MESSAGE); writeRepeatedObjectImpl(id, value); } @@ -2296,7 +2302,7 @@ public final class ProtoOutputStream { final String typeString = getFieldTypeString(fieldType); if (typeString != null && countString != null) { final StringBuilder sb = new StringBuilder(); - if (expectedType == FIELD_TYPE_OBJECT) { + if (expectedType == FIELD_TYPE_MESSAGE) { sb.append("start"); } else { sb.append("write"); @@ -2306,7 +2312,7 @@ public final class ProtoOutputStream { sb.append(" called for field "); sb.append((int)fieldId); sb.append(" which should be used with "); - if (fieldType == FIELD_TYPE_OBJECT) { + if (fieldType == FIELD_TYPE_MESSAGE) { sb.append("start"); } else { sb.append("write"); @@ -2321,7 +2327,7 @@ public final class ProtoOutputStream { throw new IllegalArgumentException(sb.toString()); } else { final StringBuilder sb = new StringBuilder(); - if (expectedType == FIELD_TYPE_OBJECT) { + if (expectedType == FIELD_TYPE_MESSAGE) { sb.append("start"); } else { sb.append("write"); diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index 580456023d68..ab0b3eec8753 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Outline; import android.graphics.Rect; @@ -43,6 +44,7 @@ public class NotificationHeaderView extends ViewGroup { public static final int NO_COLOR = Notification.COLOR_INVALID; private final int mChildMinWidth; private final int mContentEndMargin; + private final int mGravity; private View mAppName; private View mHeaderText; private OnClickListener mExpandClickListener; @@ -50,7 +52,6 @@ public class NotificationHeaderView extends ViewGroup { private ImageView mExpandButton; private CachingIconView mIcon; private View mProfileBadge; - private View mInfo; private int mIconColor; private int mOriginalNotificationColor; private boolean mExpanded; @@ -61,6 +62,7 @@ public class NotificationHeaderView extends ViewGroup { private boolean mEntireHeaderClickable; private boolean mExpandOnlyOnButton; private boolean mAcceptAllTouches; + private int mTotalWidth; ViewOutlineProvider mProvider = new ViewOutlineProvider() { @Override @@ -92,6 +94,11 @@ public class NotificationHeaderView extends ViewGroup { mHeaderBackgroundHeight = res.getDimensionPixelSize( R.dimen.notification_header_background_height); mEntireHeaderClickable = res.getBoolean(R.bool.config_notificationHeaderClickableForExpand); + + int[] attrIds = { android.R.attr.gravity }; + TypedArray ta = context.obtainStyledAttributes(attrs, attrIds, defStyleAttr, defStyleRes); + mGravity = ta.getInt(0, 0); + ta.recycle(); } @Override @@ -146,6 +153,7 @@ public class NotificationHeaderView extends ViewGroup { mHeaderText.measure(childWidthSpec, wrapContentHeightSpec); } } + mTotalWidth = Math.min(totalWidth, givenWidth); setMeasuredDimension(givenWidth, givenHeight); } @@ -153,6 +161,10 @@ public class NotificationHeaderView extends ViewGroup { protected void onLayout(boolean changed, int l, int t, int r, int b) { int left = getPaddingStart(); int end = getMeasuredWidth(); + final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0; + if (centerAligned) { + left += getMeasuredWidth() / 2 - mTotalWidth / 2; + } int childCount = getChildCount(); int ownHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); for (int i = 0; i < childCount; i++) { diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 45008627e019..c44c8dda83a9 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -92,7 +92,7 @@ public class ViewConfiguration { * 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/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 19213ca06c5e..c3d6c695982d 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -187,8 +187,11 @@ public final class AccessibilityInteractionClient Log.i(LOG_TAG, "Window cache miss"); } final long identityToken = Binder.clearCallingIdentity(); - window = connection.getWindow(accessibilityWindowId); - Binder.restoreCallingIdentity(identityToken); + try { + window = connection.getWindow(accessibilityWindowId); + } finally { + Binder.restoreCallingIdentity(identityToken); + } if (window != null) { sAccessibilityCache.addWindow(window); return window; @@ -225,8 +228,11 @@ public final class AccessibilityInteractionClient Log.i(LOG_TAG, "Windows cache miss"); } final long identityToken = Binder.clearCallingIdentity(); - windows = connection.getWindows(); - Binder.restoreCallingIdentity(identityToken); + try { + windows = connection.getWindows(); + } finally { + Binder.restoreCallingIdentity(identityToken); + } if (windows != null) { sAccessibilityCache.setWindows(windows); return windows; @@ -283,10 +289,14 @@ public final class AccessibilityInteractionClient } final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId( - accessibilityWindowId, accessibilityNodeId, interactionId, this, - prefetchFlags, Thread.currentThread().getId(), arguments); - Binder.restoreCallingIdentity(identityToken); + final boolean success; + try { + success = connection.findAccessibilityNodeInfoByAccessibilityId( + accessibilityWindowId, accessibilityNodeId, interactionId, this, + prefetchFlags, Thread.currentThread().getId(), arguments); + } finally { + Binder.restoreCallingIdentity(identityToken); + } if (success) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); @@ -333,10 +343,15 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success = connection.findAccessibilityNodeInfosByViewId( - accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this, - Thread.currentThread().getId()); - Binder.restoreCallingIdentity(identityToken); + final boolean success; + try { + success = connection.findAccessibilityNodeInfosByViewId( + accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this, + Thread.currentThread().getId()); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + if (success) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); @@ -381,10 +396,15 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success = connection.findAccessibilityNodeInfosByText( - accessibilityWindowId, accessibilityNodeId, text, interactionId, this, - Thread.currentThread().getId()); - Binder.restoreCallingIdentity(identityToken); + final boolean success; + try { + success = connection.findAccessibilityNodeInfosByText( + accessibilityWindowId, accessibilityNodeId, text, interactionId, this, + Thread.currentThread().getId()); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + if (success) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); @@ -428,10 +448,15 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success = connection.findFocus(accessibilityWindowId, - accessibilityNodeId, focusType, interactionId, this, - Thread.currentThread().getId()); - Binder.restoreCallingIdentity(identityToken); + final boolean success; + try { + success = connection.findFocus(accessibilityWindowId, + accessibilityNodeId, focusType, interactionId, this, + Thread.currentThread().getId()); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + if (success) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); @@ -472,10 +497,15 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success = connection.focusSearch(accessibilityWindowId, - accessibilityNodeId, direction, interactionId, this, - Thread.currentThread().getId()); - Binder.restoreCallingIdentity(identityToken); + final boolean success; + try { + success = connection.focusSearch(accessibilityWindowId, + accessibilityNodeId, direction, interactionId, this, + Thread.currentThread().getId()); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + if (success) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); @@ -515,10 +545,15 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success = connection.performAccessibilityAction( - accessibilityWindowId, accessibilityNodeId, action, arguments, - interactionId, this, Thread.currentThread().getId()); - Binder.restoreCallingIdentity(identityToken); + final boolean success; + try { + success = connection.performAccessibilityAction( + accessibilityWindowId, accessibilityNodeId, action, arguments, + interactionId, this, Thread.currentThread().getId()); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + if (success) { return getPerformAccessibilityActionResultAndClear(interactionId); } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 0b9bc5760fa8..35f6acba04dc 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -436,8 +436,11 @@ public final class AccessibilityManager { // client using it is called through Binder from another process. Example: MMS // app adds a SMS notification and the NotificationManagerService calls this method long identityToken = Binder.clearCallingIdentity(); - service.sendAccessibilityEvent(event, userId); - Binder.restoreCallingIdentity(identityToken); + try { + service.sendAccessibilityEvent(event, userId); + } finally { + Binder.restoreCallingIdentity(identityToken); + } if (DEBUG) { Log.i(LOG_TAG, event + " sent"); } diff --git a/core/java/com/android/internal/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java index e6ef10b3b7c6..71baaf177eac 100644 --- a/core/java/com/android/internal/colorextraction/types/Tonal.java +++ b/core/java/com/android/internal/colorextraction/types/Tonal.java @@ -51,9 +51,11 @@ public class Tonal implements ExtractionType { private static final boolean DEBUG = true; + public static final int THRESHOLD_COLOR_LIGHT = 0xffe0e0e0; public static final int MAIN_COLOR_LIGHT = 0xffe0e0e0; public static final int SECONDARY_COLOR_LIGHT = 0xff9e9e9e; - public static final int MAIN_COLOR_DARK = 0xff212121; + public static final int THRESHOLD_COLOR_DARK = 0xff212121; + public static final int MAIN_COLOR_DARK = 0xff000000; public static final int SECONDARY_COLOR_DARK = 0xff000000; private final TonalPalette mGreyPalette; @@ -197,12 +199,12 @@ public class Tonal implements ExtractionType { // light fallback or darker than our dark fallback. ColorUtils.colorToHSL(mainColor, mTmpHSL); final float mainLuminosity = mTmpHSL[2]; - ColorUtils.colorToHSL(MAIN_COLOR_LIGHT, mTmpHSL); + ColorUtils.colorToHSL(THRESHOLD_COLOR_LIGHT, mTmpHSL); final float lightLuminosity = mTmpHSL[2]; if (mainLuminosity > lightLuminosity) { return false; } - ColorUtils.colorToHSL(MAIN_COLOR_DARK, mTmpHSL); + ColorUtils.colorToHSL(THRESHOLD_COLOR_DARK, mTmpHSL); final float darkLuminosity = mTmpHSL[2]; if (mainLuminosity < darkLuminosity) { return false; diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java index 3baccee049b0..05ec9e90513e 100644 --- a/core/java/com/android/internal/os/BaseCommand.java +++ b/core/java/com/android/internal/os/BaseCommand.java @@ -106,6 +106,14 @@ public abstract class BaseCommand { } /** + * 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 f0d05da2ec5e..0535ebeb7615 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -5412,6 +5412,18 @@ public class BatteryStatsImpl extends BatteryStats { } } + 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/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java index fb6b8b0b2e16..3af3e2ad2772 100644 --- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java +++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java @@ -16,6 +16,10 @@ package com.android.internal.policy; +import static android.view.WindowManager.DOCKED_INVALID; +import static android.view.WindowManager.DOCKED_LEFT; +import static android.view.WindowManager.DOCKED_RIGHT; + import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -99,11 +103,12 @@ public class DividerSnapAlgorithm { public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize, boolean isHorizontalDivision, Rect insets) { - this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets, false); + this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets, + DOCKED_INVALID, false); } public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize, - boolean isHorizontalDivision, Rect insets, boolean isMinimizedMode) { + boolean isHorizontalDivision, Rect insets, int dockSide, boolean isMinimizedMode) { mMinFlingVelocityPxPerSecond = MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density; mMinDismissVelocityPxPerSecond = @@ -121,7 +126,7 @@ public class DividerSnapAlgorithm { com.android.internal.R.dimen.default_minimal_size_resizable_task); mTaskHeightInMinimizedMode = res.getDimensionPixelSize( com.android.internal.R.dimen.task_height_of_minimized_mode); - calculateTargets(isHorizontalDivision); + calculateTargets(isHorizontalDivision, dockSide); mFirstSplitTarget = mTargets.get(1); mLastSplitTarget = mTargets.get(mTargets.size() - 2); mDismissStartTarget = mTargets.get(0); @@ -254,7 +259,7 @@ public class DividerSnapAlgorithm { return mTargets.get(minIndex); } - private void calculateTargets(boolean isHorizontalDivision) { + private void calculateTargets(boolean isHorizontalDivision, int dockedSide) { mTargets.clear(); int dividerMax = isHorizontalDivision ? mDisplayHeight @@ -273,7 +278,7 @@ public class DividerSnapAlgorithm { addMiddleTarget(isHorizontalDivision); break; case SNAP_MODE_MINIMIZED: - addMinimizedTarget(isHorizontalDivision); + addMinimizedTarget(isHorizontalDivision, dockedSide); break; } mTargets.add(new SnapTarget(dividerMax - navBarSize, dividerMax, @@ -331,12 +336,16 @@ public class DividerSnapAlgorithm { mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE)); } - private void addMinimizedTarget(boolean isHorizontalDivision) { + private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) { // In portrait offset the position by the statusbar height, in landscape add the statusbar // height as well to match portrait offset int position = mTaskHeightInMinimizedMode + mInsets.top; if (!isHorizontalDivision) { - position += mInsets.left; + if (dockedSide == DOCKED_LEFT) { + position += mInsets.left; + } else if (dockedSide == DOCKED_RIGHT) { + position = mDisplayWidth - position - mInsets.right; + } } mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE)); } diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java index 073aac542e31..26023b499919 100644 --- a/core/java/com/android/internal/widget/NotificationActionListLayout.java +++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java @@ -16,7 +16,10 @@ package com.android.internal.widget; +import android.annotation.Nullable; import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Pair; @@ -37,6 +40,7 @@ import java.util.Comparator; @RemoteViews.RemoteView public class NotificationActionListLayout extends LinearLayout { + private final int mGravity; private int mTotalWidth = 0; private ArrayList<Pair<Integer, TextView>> mMeasureOrderTextViews = new ArrayList<>(); private ArrayList<View> mMeasureOrderOther = new ArrayList<>(); @@ -45,7 +49,20 @@ public class NotificationActionListLayout extends LinearLayout { private Drawable mDefaultBackground; public NotificationActionListLayout(Context context, AttributeSet attrs) { - super(context, attrs); + this(context, attrs, 0); + } + + public NotificationActionListLayout(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public NotificationActionListLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + int[] attrIds = { android.R.attr.gravity }; + TypedArray ta = context.obtainStyledAttributes(attrs, attrIds, defStyleAttr, defStyleRes); + mGravity = ta.getInt(0, 0); + ta.recycle(); } @Override @@ -95,6 +112,7 @@ public class NotificationActionListLayout extends LinearLayout { final boolean constrained = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED; + final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0; final int innerWidth = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight; final int otherSize = mMeasureOrderOther.size(); @@ -137,7 +155,7 @@ public class NotificationActionListLayout extends LinearLayout { // Make sure to measure the last child full-width if we didn't use up the entire width, // or we didn't measure yet because there's just one child. - if (lastNotGoneChild != null && (constrained && usedWidth < innerWidth + if (lastNotGoneChild != null && !centerAligned && (constrained && usedWidth < innerWidth || notGoneChildren == 1)) { MarginLayoutParams lp = (MarginLayoutParams) lastNotGoneChild.getLayoutParams(); if (notGoneChildren > 1) { @@ -201,9 +219,10 @@ public class NotificationActionListLayout extends LinearLayout { } final boolean isLayoutRtl = isLayoutRtl(); final int paddingTop = mPaddingTop; + final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0; int childTop; - int childLeft; + int childLeft = centerAligned ? left + (right - left) / 2 - mTotalWidth / 2 : 0; // Where bottom of child should go final int height = bottom - top; @@ -216,13 +235,12 @@ public class NotificationActionListLayout extends LinearLayout { final int layoutDirection = getLayoutDirection(); switch (Gravity.getAbsoluteGravity(Gravity.START, layoutDirection)) { case Gravity.RIGHT: - // mTotalWidth contains the padding already - childLeft = mPaddingLeft + right - left - mTotalWidth; + childLeft += mPaddingLeft + right - left - mTotalWidth; break; case Gravity.LEFT: default: - childLeft = mPaddingLeft; + childLeft += mPaddingLeft; break; } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index f88db259425d..fb8b9f75e031 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -185,7 +185,6 @@ cc_library_shared { "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 08d952791ef6..f6783e15e294 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -344,6 +344,17 @@ void JHwBinder_native_joinRpcThreadpool() { 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 @@ static JNINativeMethod gMethods[] = { { "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_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp index 737ec47e8ffb..bb916d2431c5 100644 --- a/core/jni/android_os_HwBlob.cpp +++ b/core/jni/android_os_HwBlob.cpp @@ -26,6 +26,7 @@ #include <android_runtime/AndroidRuntime.h> #include <hidl/Status.h> #include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/ScopedPrimitiveArray.h> #include "core_jni_helpers.h" @@ -349,6 +350,13 @@ static void JHwBlob_native_copyTo ## Suffix ## Array( \ static_cast<const uint8_t *>(blob->data()) + offset)); \ } +DEFINE_BLOB_ARRAY_COPIER(Int8,jbyte,Byte) +DEFINE_BLOB_ARRAY_COPIER(Int16,jshort,Short) +DEFINE_BLOB_ARRAY_COPIER(Int32,jint,Int) +DEFINE_BLOB_ARRAY_COPIER(Int64,jlong,Long) +DEFINE_BLOB_ARRAY_COPIER(Float,jfloat,Float) +DEFINE_BLOB_ARRAY_COPIER(Double,jdouble,Double) + static void JHwBlob_native_copyToBoolArray( JNIEnv *env, jobject thiz, @@ -386,13 +394,6 @@ static void JHwBlob_native_copyToBoolArray( dst = nullptr; } -DEFINE_BLOB_ARRAY_COPIER(Int8,jbyte,Byte) -DEFINE_BLOB_ARRAY_COPIER(Int16,jshort,Short) -DEFINE_BLOB_ARRAY_COPIER(Int32,jint,Int) -DEFINE_BLOB_ARRAY_COPIER(Int64,jlong,Long) -DEFINE_BLOB_ARRAY_COPIER(Float,jfloat,Float) -DEFINE_BLOB_ARRAY_COPIER(Double,jdouble,Double) - #define DEFINE_BLOB_PUTTER(Suffix,Type) \ static void JHwBlob_native_put ## Suffix( \ JNIEnv *env, jobject thiz, jlong offset, Type x) { \ @@ -458,23 +459,17 @@ static void JHwBlob_native_putString( #define DEFINE_BLOB_ARRAY_PUTTER(Suffix,Type,NewType) \ static void JHwBlob_native_put ## Suffix ## Array( \ JNIEnv *env, jobject thiz, jlong offset, Type ## Array array) { \ + Scoped ## NewType ## ArrayRO autoArray(env, array); \ \ if (array == nullptr) { \ - jniThrowException(env, "java/lang/NullPointerException", nullptr); \ + /* NullpointerException already pending */ \ return; \ } \ \ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \ \ - jsize len = env->GetArrayLength(array); \ - \ - Type *src = \ - env->Get ## NewType ## ArrayElements(array, nullptr /* isCopy */); \ - \ - status_t err = blob->write(offset, src, len * sizeof(Type)); \ - \ - env->Release ## NewType ## ArrayElements(array, src, 0 /* mode */); \ - src = nullptr; \ + status_t err = blob->write( \ + offset, autoArray.get(), autoArray.size() * sizeof(Type)); \ \ if (err != OK) { \ signalExceptionForError(env, err); \ @@ -490,35 +485,28 @@ DEFINE_BLOB_ARRAY_PUTTER(Double,jdouble,Double) static void JHwBlob_native_putBoolArray( JNIEnv *env, jobject thiz, jlong offset, jbooleanArray array) { + ScopedBooleanArrayRO autoArray(env, array); if (array == nullptr) { - jniThrowException(env, "java/lang/NullPointerException", nullptr); + /* NullpointerException already pending */ return; } sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); - jsize len = env->GetArrayLength(array); - - if ((offset + len * sizeof(bool)) > blob->size()) { + if ((offset + autoArray.size() * sizeof(bool)) > blob->size()) { signalExceptionForError(env, -ERANGE); return; } - const jboolean *src = - env->GetBooleanArrayElements(array, nullptr /* isCopy */); + const jboolean *src = autoArray.get(); bool *dst = reinterpret_cast<bool *>( static_cast<uint8_t *>(blob->data()) + offset); - for (jsize i = 0; i < len; ++i) { + for (size_t i = 0; i < autoArray.size(); ++i) { dst[i] = src[i]; } - - env->ReleaseBooleanArrayElements( - array, const_cast<jboolean *>(src), 0 /* mode */); - - src = nullptr; } static void JHwBlob_native_putBlob( diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp index cf59a56a13dc..ca5e1e45dcdc 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 @@ static jboolean JHwRemoteBinder_unlinkToDeath(JNIEnv* env, jobject thiz, 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 @@ static JNINativeMethod gMethods[] = { {"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 5ef2a9e6465c..6243fadc25dc 100644 --- a/core/jni/android_os_VintfObject.cpp +++ b/core/jni/android_os_VintfObject.cpp @@ -102,8 +102,11 @@ static jint android_os_VintfObject_verify(JNIEnv* env, jclass, jobjectArray pack 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 01d565b26ddb..000000000000 --- 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/incident.proto b/core/proto/android/os/incident.proto index 50e811d79845..e998b09d18ee 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -27,6 +27,7 @@ import "frameworks/base/core/proto/android/os/pagetypeinfo.proto"; import "frameworks/base/core/proto/android/os/procrank.proto"; import "frameworks/base/core/proto/android/server/activitymanagerservice.proto"; import "frameworks/base/core/proto/android/server/alarmmanagerservice.proto"; +import "frameworks/base/core/proto/android/server/windowmanagerservice.proto"; import "frameworks/base/core/proto/android/server/fingerprint.proto"; import "frameworks/base/core/proto/android/server/powermanagerservice.proto"; import "frameworks/base/core/proto/android/service/appwidget.proto"; @@ -135,4 +136,9 @@ message IncidentProto { (section).type = SECTION_DUMPSYS, (section).args = "alarm --proto" ]; + + optional com.android.server.wm.proto.WindowManagerServiceProto window = 3017 [ + (section).type = SECTION_DUMPSYS, + (section).args = "window --proto" + ]; } diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 064523a16058..4d48a4299281 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -15,6 +15,7 @@ */ 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/view/displayinfo.proto"; diff --git a/core/proto/android/server/windowmanagertrace.proto b/core/proto/android/server/windowmanagertrace.proto new file mode 100644 index 000000000000..0c65bb273809 --- /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/drawable/dialog_background_material.xml b/core/res/res/drawable/dialog_background_material.xml index 2f8d1fa13eed..e017d3c861a7 100644 --- a/core/res/res/drawable/dialog_background_material.xml +++ b/core/res/res/drawable/dialog_background_material.xml @@ -17,7 +17,7 @@ <inset xmlns:android="http://schemas.android.com/apk/res/android" android:inset="16dp"> <shape android:shape="rectangle"> - <corners android:radius="2dp" /> + <corners android:radius="?attr/dialogCornerRadius" /> <solid android:color="?attr/colorBackground" /> </shape> </inset> diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index f0c980c703cc..3a28f4d76f7c 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -37,6 +37,7 @@ android:textAppearance="?attr/notificationHeaderTextAppearance" android:layout_marginStart="@dimen/notification_header_app_name_margin_start" android:layout_marginEnd="@dimen/notification_header_separating_margin" + android:visibility="?attr/notificationHeaderAppNameVisibility" android:singleLine="true" /> <TextView diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml index ee5c758cc130..865685ff26f5 100644 --- a/core/res/res/layout/notification_template_material_ambient.xml +++ b/core/res/res/layout/notification_template_material_ambient.xml @@ -23,8 +23,8 @@ android:paddingStart="@dimen/notification_extra_margin_ambient" android:paddingEnd="@dimen/notification_extra_margin_ambient" > - <include layout="@layout/notification_template_header" - android:theme="@style/Theme.Material.Notification.Ambient" /> + <include layout="@layout/notification_template_ambient_header" + android:theme="@style/Theme.Material.Notification.Ambient" /> <LinearLayout android:id="@+id/notification_action_list_margin_target" @@ -53,6 +53,7 @@ android:textAppearance="@style/TextAppearance.Material.Notification.Title" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:gravity="top|center_horizontal" android:singleLine="true" android:ellipsize="marquee" android:fadingEdge="horizontal" @@ -65,7 +66,7 @@ android:textAppearance="@style/TextAppearance.Material.Notification" android:singleLine="false" android:layout_weight="1" - android:gravity="top" + android:gravity="top|center_horizontal" android:visibility="gone" android:textSize="16sp" android:textColor="#eeffffff" @@ -75,5 +76,19 @@ /> </LinearLayout> </LinearLayout> - <include layout="@layout/notification_material_action_list" /> + <FrameLayout android:id="@+id/actions_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom"> + <com.android.internal.widget.NotificationActionListLayout + android:id="@+id/actions" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_action_list_height" + android:paddingEnd="4dp" + android:orientation="horizontal" + android:gravity="center" + android:visibility="gone" + android:background="@color/notification_action_list" + /> + </FrameLayout> </FrameLayout> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index db4dcd2ec1a9..0eefec91e390 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -984,6 +984,8 @@ <attr name="dialogTitleDecorLayout" format="reference" /> <!-- Preferred padding for dialog content. --> <attr name="dialogPreferredPadding" format="dimension" /> + <!-- Corner radius of dialogs. --> + <attr name="dialogCornerRadius" format="dimension" /> <!-- Theme to use for alert dialogs spawned from this theme. --> <attr name="alertDialogTheme" format="reference" /> @@ -8713,6 +8715,14 @@ <attr name="notificationHeaderStyle" format="reference" /> <attr name="notificationHeaderTextAppearance" format="reference" /> <attr name="notificationHeaderIconSize" format="dimension" /> + <attr name="notificationHeaderAppNameVisibility" format="enum"> + <!-- Visible on screen; the default value. --> + <enum name="visible" value="0" /> + <!-- Not displayed, but taken into account during layout (space is left for it). --> + <enum name="invisible" value="1" /> + <!-- Completely hidden, as if the view had not been added. --> + <enum name="gone" value="2" /> + </attr> </declare-styleable> <attr name="lockPatternStyle" format="reference" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 4a8bc13f7669..1c2e5a406772 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. --> @@ -2519,7 +2524,13 @@ <bool name="config_networkSamplingWakesDevice">true</bool> - <string-array translatable="false" name="config_cdma_home_system" /> + <!-- Home (non-roaming) values for CDMA roaming indicator. + Carriers can override this table by resource overlay. If not, + the default values come from 3GPP2 C.R1001 table + 8.1-1. Enhanced Roaming Indicator Number Assignments --> + <string-array translatable="false" name="config_cdma_home_system"> + <item>1</item> + </string-array> <!--From SmsMessage--> <!--Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet @@ -3147,4 +3158,7 @@ <!-- Component name of media projection permission dialog --> <string name="config_mediaProjectionPermissionDialogComponent" translateable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string> + + <!-- Corner radius of system dialogs --> + <dimen name="config_dialogCornerRadius">2dp</dimen> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index dc75ba6077e7..947fcf127b9c 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -624,4 +624,7 @@ <dimen name="slice_icon_size">24dp</dimen> <!-- Standard padding used in a slice view --> <dimen name="slice_padding">16dp</dimen> + + <!-- Default dialog corner radius --> + <dimen name="dialog_corner_radius">2dp</dimen> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 083bf90210a5..fdd56c410ed2 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2847,6 +2847,7 @@ <public name="cantSaveState" /> <public name="ttcIndex" /> <public name="fontVariationSettings" /> + <public name="dialogCornerRadius" /> </public-group> <public-group type="style" first-id="0x010302e0"> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 470ac522aa33..cddf99a2df23 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -1296,6 +1296,11 @@ please see styles_device_defaults.xml. <item name="layout_marginBottom">@dimen/notification_header_margin_bottom</item> <item name="paddingStart">@dimen/notification_content_margin_start</item> <item name="paddingEnd">16dp</item> + <item name="gravity">top</item> + </style> + + <style name="Notification.Header.Ambient"> + <item name="gravity">top|center_horizontal</item> </style> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e513816e6743..32758e83f9b7 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/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index bf0c90660392..68d552367423 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -88,6 +88,7 @@ easier. <!-- Dialog attributes --> <item name="dialogTheme">@style/Theme.DeviceDefault.Dialog</item> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> <!-- AlertDialog attributes --> <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> @@ -214,6 +215,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme @@ -223,6 +228,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and @@ -234,6 +243,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent @@ -244,6 +257,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- DeviceDefault theme for dialog windows and activities. This changes the window to be @@ -263,6 +280,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a @@ -272,6 +293,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar --> @@ -280,6 +305,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width @@ -289,6 +318,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. --> @@ -314,6 +347,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- DeviceDefault theme for a window without an action bar that will be displayed either @@ -324,6 +361,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- DeviceDefault theme for a presentation window on a secondary display. --> @@ -332,6 +373,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- DeviceDefault theme for panel windows. This removes all extraneous window @@ -342,6 +387,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear @@ -351,6 +400,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear @@ -360,6 +413,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- DeviceDefault style for input methods, which is used by the @@ -369,6 +426,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- DeviceDefault style for input methods, which is used by the @@ -378,11 +439,19 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert"> <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault</item> + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> + <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> @@ -394,6 +463,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame"> @@ -401,6 +474,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style --> @@ -441,6 +518,7 @@ easier. <!-- Dialog attributes --> <item name="dialogTheme">@style/Theme.DeviceDefault.Light.Dialog</item> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> <!-- AlertDialog attributes --> <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> @@ -562,6 +640,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar --> @@ -570,6 +652,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar. @@ -579,6 +665,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar @@ -590,6 +680,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent @@ -600,6 +694,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be @@ -609,6 +707,10 @@ easier. <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item> <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item> + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> + <item name="buttonBarStyle">@style/DeviceDefault.Light.ButtonBar.AlertDialog</item> <item name="borderlessButtonStyle">@style/Widget.DeviceDefault.Light.Button.Borderless.Small</item> @@ -628,6 +730,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar --> @@ -636,6 +742,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum @@ -645,6 +755,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. --> @@ -680,6 +794,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- DeviceDefault light theme for a window without an action bar that will be displayed either @@ -690,6 +808,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- DeviceDefault light theme for a presentation window on a secondary display. --> @@ -698,6 +820,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- DeviceDefault light theme for panel windows. This removes all extraneous window @@ -708,6 +834,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert"> @@ -717,6 +847,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar"> @@ -724,6 +858,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice"> @@ -731,6 +869,10 @@ easier. <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- DeviceDefault theme for a window that should look like the Settings app. --> @@ -750,6 +892,10 @@ easier. <item name="navigationBarDividerColor">#1f000000</item> <item name="navigationBarColor">@android:color/white</item> <item name="windowLightNavigationBar">true</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- @hide DeviceDefault theme for a window that should use Settings theme colors @@ -761,6 +907,7 @@ easier. <item name="colorSecondary">@color/secondary_device_default_settings_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorControlNormal">?attr/textColorPrimary</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Light.Dialog"> @@ -769,6 +916,7 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item> <item name="colorSecondary">@color/secondary_device_default_settings_light</item> <item name="colorAccent">@color/accent_device_default_light</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar --> @@ -778,6 +926,10 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item> <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_dark</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> </style> <style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.Material.Settings.Dialog"> @@ -786,6 +938,10 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item> <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <style name="Theme.DeviceDefault.Settings.DialogWhenLarge" parent="Theme.Material.Settings.DialogWhenLarge"> @@ -794,6 +950,10 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item> <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert"> @@ -802,9 +962,13 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item> <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_light</item> + + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> </style> - <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar"/> + <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" /> <!-- Theme used for the intent picker activity. --> <style name="Theme.DeviceDefault.Resolver" parent="Theme.Material.Light"> @@ -820,6 +984,10 @@ easier. <item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item> <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item> + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item> + <!-- Color palette --> <item name="colorPrimary">@color/primary_device_default_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> @@ -831,7 +999,7 @@ easier. <style name="ThemeOverlay.DeviceDefault" /> - <!-- @hide Theme overlay that inherits from material actionbar, and use accent color for + <!-- @hide Theme overlay that inherits from material actionbar, and use accent color for primary text --> <style name="ThemeOverlay.DeviceDefault.ActionBar.Accent" parent="ThemeOverlay.Material.ActionBar"> <item name="textColorPrimary">@color/btn_colored_borderless_text_material</item> diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml index 9bea3eeeb9ac..c31712136eed 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -186,6 +186,7 @@ please see themes_device_defaults.xml. <item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_material</item> <item name="dialogTitleDecorLayout">@layout/dialog_title_material</item> <item name="dialogPreferredPadding">@dimen/dialog_padding_material</item> + <item name="dialogCornerRadius">@dimen/dialog_corner_radius</item> <!-- AlertDialog attributes --> <item name="alertDialogTheme">@style/ThemeOverlay.Material.Dialog.Alert</item> @@ -554,6 +555,7 @@ please see themes_device_defaults.xml. <item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_material</item> <item name="dialogTitleDecorLayout">@layout/dialog_title_material</item> <item name="dialogPreferredPadding">@dimen/dialog_padding_material</item> + <item name="dialogCornerRadius">@dimen/dialog_corner_radius</item> <!-- AlertDialog attributes --> <item name="alertDialogTheme">@style/ThemeOverlay.Material.Dialog.Alert</item> @@ -1325,12 +1327,15 @@ please see themes_device_defaults.xml. <style name="Theme.Material.Notification" parent=""> <item name="notificationHeaderStyle">@style/Notification.Header</item> <item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info</item> + <item name="notificationHeaderAppNameVisibility">visible</item> <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size</item> </style> <!-- Theme for inflating ambient notification --> <style name="Theme.Material.Notification.Ambient"> + <item name="notificationHeaderStyle">@style/Notification.Header.Ambient</item> <item name="notificationHeaderTextAppearance">@style/TextAppearance.Material.Notification.Info.Ambient</item> + <item name="notificationHeaderAppNameVisibility">gone</item> <item name="notificationHeaderIconSize">@dimen/notification_header_icon_size_ambient</item> </style> diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp index 115dcb656f66..0919e82a003b 100644 --- a/libs/hwui/Extensions.cpp +++ b/libs/hwui/Extensions.cpp @@ -66,6 +66,7 @@ Extensions::Extensions() { 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 0ecfdb1b3e0a..7af7f7944ac9 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -38,6 +38,9 @@ public: 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 @@ private: 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 2687410897ac..751e2037db91 100644 --- a/libs/hwui/OpenGLReadback.cpp +++ b/libs/hwui/OpenGLReadback.cpp @@ -128,7 +128,8 @@ inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, 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 608e6941fe67..049018cc321c 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp @@ -73,7 +73,7 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4 * 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/hwui/tests/scripts/prep_taieye.sh b/libs/hwui/tests/scripts/prep_taieye.sh new file mode 100755 index 000000000000..503f6d5d5239 --- /dev/null +++ b/libs/hwui/tests/scripts/prep_taieye.sh @@ -0,0 +1,50 @@ +nr=$(adb shell cat /proc/cpuinfo | grep processor | wc -l) +cpubase=/sys/devices/system/cpu + +adb root +adb wait-for-device +adb shell stop vendor.perfd +adb shell stop thermal-engine + +S=1036800 +cpu=0 +# Changing governor and frequency in one core will be automatically applied +# to other cores in the cluster +while [ $((cpu < 4)) -eq 1 ]; do + echo "Setting cpu ${cpu} to $S hz" + adb shell "echo userspace > $cpubase/cpu${cpu}/cpufreq/scaling_governor" + adb shell "echo 1 > $cpubase/cpu${cpu}/online" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_max_freq" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_min_freq" + cpu=$(($cpu + 1)) +done + +while [ $((cpu < $nr)) -eq 1 ]; do + echo "disable cpu $cpu" + adb shell "echo 0 > $cpubase/cpu${cpu}/online" + cpu=$(($cpu + 1)) +done + +echo "setting GPU bus and idle timer" +adb shell "echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split" +adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on" +adb shell "echo 10000 > /sys/class/kgsl/kgsl-3d0/idle_timer" + +#0 762 1144 1525 2288 3143 4173 5195 5859 7759 9887 11863 13763 +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,gpubw/min_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,gpubw/max_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,cpubw/min_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,cpubw/max_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,mincpubw/min_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,mincpubw/max_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,memlat-cpu0/min_freq" +adb shell "echo 7759 > /sys/class/devfreq/soc\:qcom,memlat-cpu0/max_freq" + +# 180000000 257000000 342000000 414000000 515000000 596000000 670000000 710000000 +echo "performance mode, 342 MHz" +adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor" +adb shell "echo 342000000 > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq" +adb shell "echo 342000000 > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq" + +adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/min_pwrlevel" +adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/max_pwrlevel" diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h index 2155084db534..ce4184967026 100644 --- a/libs/protoutil/include/android/util/ProtoOutputStream.h +++ b/libs/protoutil/include/android/util/ProtoOutputStream.h @@ -35,6 +35,10 @@ const uint64_t FIELD_TYPE_SHIFT = 32; */ const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT; +/** + * The types are copied from external/protobuf/src/google/protobuf/descriptor.h directly, + * so no extra mapping needs to be maintained in this case. + */ 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. @@ -49,7 +53,7 @@ const uint64_t FIELD_TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exac 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_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. @@ -69,7 +73,7 @@ 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; +const uint64_t FIELD_COUNT_PACKED = 5ULL << FIELD_COUNT_SHIFT; /** * Class to write to a protobuf stream. diff --git a/packages/SettingsLib/res/layout/preference_two_target.xml b/packages/SettingsLib/res/layout/preference_two_target.xml index c2167f3e608d..4658924477a0 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 b7ea1d498ce1..3f312f4efda3 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/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java index 32e6389dc34f..c3a36e97be3a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java @@ -28,6 +28,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.os.RemoteException; @@ -343,7 +344,8 @@ public class RestrictedLockUtils { } DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); - if (dpm == null) { + PackageManager pm = context.getPackageManager(); + if (!pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) || dpm == null) { return null; } boolean isAccountTypeDisabled = false; diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java index 6aae226bb9a3..3c02f6a234f4 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 @@ public abstract class AbstractEnableAdbPreferenceController extends @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 @@ public abstract class AbstractEnableAdbPreferenceController extends 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/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java index ca366ea4b6cc..64de63520f87 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.UserManager; @@ -56,6 +57,8 @@ public class RestrictedLockUtilsTest { private DevicePolicyManager mDevicePolicyManager; @Mock private UserManager mUserManager; + @Mock + private PackageManager mPackageManager; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private RestrictedLockUtils.Proxy mProxy; @@ -72,11 +75,32 @@ public class RestrictedLockUtilsTest { .thenReturn(mDevicePolicyManager); when(mContext.getSystemService(Context.USER_SERVICE)) .thenReturn(mUserManager); + when(mContext.getPackageManager()) + .thenReturn(mPackageManager); RestrictedLockUtils.sProxy = mProxy; } @Test + public void checkIfDevicePolicyServiceDisabled_noEnforceAdminForManagedProfile() { + when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(null); + final EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfAccountManagementDisabled( + mContext, "account_type", mUserId); + + assertThat(enforcedAdmin).isEqualTo(null); + } + + @Test + public void checkIfDeviceAdminFeatureDisabled_noEnforceAdminForManagedProfile() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) + .thenReturn(false); + final EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfAccountManagementDisabled( + mContext, "account_type", mUserId); + + assertThat(enforcedAdmin).isEqualTo(null); + } + + @Test public void checkIfKeyguardFeaturesDisabled_noEnforcedAdminForManagedProfile() { setUpManagedProfile(mUserId, new ComponentName[] {mAdmin1, mAdmin2}); 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 bcabff30f345..32fa01c52f21 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 com.google.common.truth.Truth.assertThat; 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 @@ public class EnableAdbPreferenceControllerTest { } @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 @@ public class EnableAdbPreferenceControllerTest { } @Override - public void dismissConfirmationDialog() {} + public void dismissConfirmationDialog() { + } } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java index 806a07387b69..f92509d8ab5b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java @@ -70,21 +70,6 @@ public class RecentsTaskLoadPlan { } /** - * An optimization to preload the raw list of tasks. The raw tasks are saved in least-recent - * to most-recent order. - * - * Note: Do not lock, callers should synchronize on the loader before making this call. - */ - void preloadRawTasks() { - int currentUserId = ActivityManagerWrapper.getInstance().getCurrentUserId(); - mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks( - ActivityManager.getMaxRecentTasksStatic(), currentUserId); - - // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it - Collections.reverse(mRawTasks); - } - - /** * Preloads the list of recent tasks from the system. After this call, the TaskStack will * have a list of all the recent tasks with their metadata, not including icons or * thumbnails which were not cached and have to be loaded. @@ -95,11 +80,15 @@ public class RecentsTaskLoadPlan { * Note: Do not lock, since this can be calling back to the loader, which separately also drives * this call (callers should synchronize on the loader before making this call). */ - void preloadPlan(RecentsTaskLoader loader, int runningTaskId) { + public void preloadPlan(RecentsTaskLoader loader, int runningTaskId, int currentUserId) { Resources res = mContext.getResources(); ArrayList<Task> allTasks = new ArrayList<>(); if (mRawTasks == null) { - preloadRawTasks(); + mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks( + ActivityManager.getMaxRecentTasksStatic(), currentUserId); + + // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it + Collections.reverse(mRawTasks); } int taskCount = mRawTasks.size(); @@ -160,7 +149,7 @@ public class RecentsTaskLoadPlan { * Note: Do not lock, since this can be calling back to the loader, which separately also drives * this call (callers should synchronize on the loader before making this call). */ - void executePlan(Options opts, RecentsTaskLoader loader) { + public void executePlan(Options opts, RecentsTaskLoader loader) { Resources res = mContext.getResources(); // Iterate through each of the tasks and load them according to the load conditions. diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java index de4c72c2ee6a..9a991cfa0699 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java @@ -147,9 +147,15 @@ public class RecentsTaskLoader { /** Preloads recents tasks using the specified plan to store the output. */ public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId) { + preloadTasks(plan, runningTaskId, ActivityManagerWrapper.getInstance().getCurrentUserId()); + } + + /** Preloads recents tasks using the specified plan to store the output. */ + public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId, + int currentUserId) { try { Trace.beginSection("preloadPlan"); - plan.preloadPlan(this, runningTaskId); + plan.preloadPlan(this, runningTaskId, currentUserId); } finally { Trace.endSection(); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java index 9a1ff544800d..5f3dcd16e074 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java @@ -21,7 +21,7 @@ import android.util.SparseArray; /** * An interface for a task filter to query whether a particular task should show in a stack. */ -interface TaskFilter { +public interface TaskFilter { /** Returns whether the filter accepts the specified task */ boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index 090617dc89c5..db1583ab132a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -17,10 +17,17 @@ package com.android.systemui.shared.system; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManager.RecentTaskInfo; +import android.app.ActivityOptions; import android.app.AppGlobals; import android.content.Context; import android.content.pm.ActivityInfo; @@ -32,15 +39,19 @@ import android.content.res.Resources.NotFoundException; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.util.IconDrawableFactory; import android.util.Log; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.recents.model.ThumbnailData; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; public class ActivityManagerWrapper { @@ -77,6 +88,25 @@ public class ActivityManagerWrapper { } /** + * @return the top running task (can be {@code null}). + */ + public ActivityManager.RunningTaskInfo getRunningTask() { + // Note: The set of running tasks from the system is ordered by recency + try { + List<ActivityManager.RunningTaskInfo> tasks = + ActivityManager.getService().getFilteredTasks(1, + ACTIVITY_TYPE_RECENTS /* ignoreActivityType */, + WINDOWING_MODE_PINNED /* ignoreWindowingMode */); + if (tasks.isEmpty()) { + return null; + } + return tasks.get(0); + } catch (RemoteException e) { + return null; + } + } + + /** * @return a list of the recents tasks. */ public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) { @@ -202,6 +232,60 @@ public class ActivityManagerWrapper { } /** + * Starts a task from Recents. + * + * @see {@link #startActivityFromRecents(TaskKey, ActivityOptions, int, int, Consumer, Handler)} + */ + public void startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options, + Consumer<Boolean> resultCallback, Handler resultCallbackHandler) { + startActivityFromRecents(taskKey, options, WINDOWING_MODE_UNDEFINED, + ACTIVITY_TYPE_UNDEFINED, resultCallback, resultCallbackHandler); + } + + /** + * Starts a task from Recents. + * + * @param resultCallback The result success callback + * @param resultCallbackHandler The handler to receive the result callback + */ + public void startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options, + int windowingMode, int activityType, Consumer<Boolean> resultCallback, + Handler resultCallbackHandler) { + if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + // We show non-visible docked tasks in Recents, but we always want to launch + // them in the fullscreen stack. + if (options == null) { + options = ActivityOptions.makeBasic(); + } + options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + } else if (windowingMode != WINDOWING_MODE_UNDEFINED + || activityType != ACTIVITY_TYPE_UNDEFINED) { + if (options == null) { + options = ActivityOptions.makeBasic(); + } + options.setLaunchWindowingMode(windowingMode); + options.setLaunchActivityType(activityType); + } + final ActivityOptions finalOptions = options; + + // Execute this from another thread such that we can do other things (like caching the + // bitmap for the thumbnail) while AM is busy starting our activity. + mBackgroundExecutor.submit(() -> { + try { + ActivityManager.getService().startActivityFromRecents(taskKey.id, + finalOptions == null ? null : finalOptions.toBundle()); + if (resultCallback != null) { + resultCallbackHandler.post(() -> resultCallback.accept(true)); + } + } catch (Exception e) { + if (resultCallback != null) { + resultCallbackHandler.post(() -> resultCallback.accept(false)); + } + } + }); + } + + /** * Requests that the system close any open system windows (including other SystemUI). */ public void closeSystemWindows(String reason) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 61c5167c8c02..5b62c7d3c002 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -411,12 +411,12 @@ public class Recents extends SystemUI } int currentUser = sSystemServicesProxy.getCurrentUser(); - SystemServicesProxy ssp = Recents.getSystemServices(); - ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask(); + ActivityManager.RunningTaskInfo runningTask = + ActivityManagerWrapper.getInstance().getRunningTask(); final int activityType = runningTask != null ? runningTask.configuration.windowConfiguration.getActivityType() : ACTIVITY_TYPE_UNDEFINED; - boolean screenPinningActive = ssp.isScreenPinningActive(); + boolean screenPinningActive = sSystemServicesProxy.isScreenPinningActive(); boolean isRunningTaskInHomeOrRecentsStack = activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS; if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 96fae35f5d2a..0b816b5ff820 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -138,8 +138,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } // Load the next task only if we aren't svelte - SystemServicesProxy ssp = Recents.getSystemServices(); - ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask(); + ActivityManager.RunningTaskInfo runningTaskInfo = + ActivityManagerWrapper.getInstance().getRunningTask(); RecentsTaskLoader loader = Recents.getTaskLoader(); RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext); loader.preloadTasks(plan, -1); @@ -353,7 +353,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener boolean forceVisible = launchedWhileDockingTask || draggingInRecents; MutableBoolean isHomeStackVisible = new MutableBoolean(forceVisible); if (forceVisible || !ssp.isRecentsActivityVisible(isHomeStackVisible)) { - ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask(); + ActivityManager.RunningTaskInfo runningTask = + ActivityManagerWrapper.getInstance().getRunningTask(); startRecentsActivity(runningTask, isHomeStackVisible.value || fromHome, animate, growTarget); } @@ -444,7 +445,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } // Otherwise, start the recents activity - ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask(); + ActivityManager.RunningTaskInfo runningTask = + ActivityManagerWrapper.getInstance().getRunningTask(); startRecentsActivity(runningTask, isHomeStackVisible.value, true /* animate */, growTarget); @@ -470,7 +472,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // don't block the touch feedback on the nav bar button which triggers this. mHandler.post(() -> { if (!ssp.isRecentsActivityVisible(null)) { - ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask(); + ActivityManager.RunningTaskInfo runningTask = + ActivityManagerWrapper.getInstance().getRunningTask(); if (runningTask == null) { return; } @@ -524,7 +527,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener if (focusedStack == null || focusedStack.getTaskCount() == 0) return; // Return early if there is no running task - ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask(); + ActivityManager.RunningTaskInfo runningTask = + ActivityManagerWrapper.getInstance().getRunningTask(); if (runningTask == null) return; // Find the task in the recents list @@ -561,8 +565,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } // Launch the task - ssp.startActivityFromRecents( - mContext, toTask.key, toTask.title, launchOpts, null /* resultListener */); + ActivityManagerWrapper.getInstance().startActivityFromRecents(toTask.key, launchOpts, + null /* resultCallback */, null /* resultCallbackHandler */); } /** @@ -579,7 +583,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener if (focusedStack == null || focusedStack.getTaskCount() == 0) return; // Return early if there is no running task (can't determine affiliated tasks in this case) - ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask(); + ActivityManager.RunningTaskInfo runningTask = + ActivityManagerWrapper.getInstance().getRunningTask(); final int activityType = runningTask.configuration.windowConfiguration.getActivityType(); if (runningTask == null) return; // Return early if the running task is in the home/recents stack (optimization) @@ -630,8 +635,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1); // Launch the task - ssp.startActivityFromRecents( - mContext, toTask.key, toTask.title, launchOpts, null /* resultListener */); + ActivityManagerWrapper.getInstance().startActivityFromRecents(toTask.key, launchOpts, + null /* resultListener */, null /* resultCallbackHandler */); } public void showNextAffiliatedTask() { diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 14b91f7aeaa6..a436e177cb51 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -83,6 +83,7 @@ import com.android.systemui.shared.system.TaskStackChangeListeners; import com.android.systemui.statusbar.policy.UserInfoController; import java.util.List; +import java.util.function.Consumer; /** * Acts as a shim around the real system services that we need to access data from, and provides @@ -123,7 +124,6 @@ public class SystemServicesProxy { Paint mBgProtectionPaint; Canvas mBgProtectionCanvas; - private final Handler mHandler = new Handler(); private final Runnable mGcRunnable = new Runnable() { @Override public void run() { @@ -197,24 +197,6 @@ public class SystemServicesProxy { } /** - * Returns the top running task. - */ - public ActivityManager.RunningTaskInfo getRunningTask() { - // Note: The set of running tasks from the system is ordered by recency - try { - List<ActivityManager.RunningTaskInfo> tasks = mIam.getFilteredTasks(1, - ACTIVITY_TYPE_RECENTS /* ignoreActivityType */, - WINDOWING_MODE_PINNED /* ignoreWindowingMode */); - if (tasks.isEmpty()) { - return null; - } - return tasks.get(0); - } catch (RemoteException e) { - return null; - } - } - - /** * Returns whether the recents activity is currently visible. */ public boolean isRecentsActivityVisible() { @@ -376,28 +358,6 @@ public class SystemServicesProxy { } } - /** Removes the task */ - public void removeTask(final int taskId) { - if (mAm == null) return; - - // Remove the task. - mUiOffloadThread.submit(() -> { - try { - mIam.removeTask(taskId); - } catch (RemoteException e) { - e.printStackTrace(); - } - }); - } - - public ActivityManager.TaskDescription getTaskDescription(int taskId) { - try { - return mIam.getTaskDescription(taskId); - } catch (RemoteException e) { - return null; - } - } - /** * Returns whether the provided {@param userId} represents the system user. */ @@ -520,56 +480,6 @@ public class SystemServicesProxy { opts != null ? opts.toBundle() : null, UserHandle.CURRENT)); } - public void startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName, - ActivityOptions options, - @Nullable final StartActivityFromRecentsResultListener resultListener) { - startActivityFromRecents(context, taskKey, taskName, options, - WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED, resultListener); - } - - /** Starts an activity from recents. */ - public void startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName, - ActivityOptions options, int windowingMode, int activityType, - @Nullable final StartActivityFromRecentsResultListener resultListener) { - if (mIam == null) { - return; - } - if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - // We show non-visible docked tasks in Recents, but we always want to launch - // them in the fullscreen stack. - if (options == null) { - options = ActivityOptions.makeBasic(); - } - options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - } else if (windowingMode != WINDOWING_MODE_UNDEFINED - || activityType != ACTIVITY_TYPE_UNDEFINED) { - if (options == null) { - options = ActivityOptions.makeBasic(); - } - options.setLaunchWindowingMode(windowingMode); - options.setLaunchActivityType(activityType); - } - final ActivityOptions finalOptions = options; - - // Execute this from another thread such that we can do other things (like caching the - // bitmap for the thumbnail) while AM is busy starting our activity. - mUiOffloadThread.submit(() -> { - try { - mIam.startActivityFromRecents( - taskKey.id, finalOptions == null ? null : finalOptions.toBundle()); - if (resultListener != null) { - mHandler.post(() -> resultListener.onStartActivityResult(true)); - } - } catch (Exception e) { - Log.e(TAG, context.getString( - R.string.recents_launch_error_message, taskName), e); - if (resultListener != null) { - mHandler.post(() -> resultListener.onStartActivityResult(false)); - } - } - }); - } - /** Starts an in-place animation on the front most application windows. */ public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) { if (mIam == null) return; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index f4973d023e76..b82f15eff4b8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -36,6 +36,7 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.util.ArraySet; import android.util.AttributeSet; +import android.util.Log; import android.util.MathUtils; import android.view.AppTransitionAnimationSpec; import android.view.LayoutInflater; @@ -1026,29 +1027,30 @@ public class RecentsView extends FrameLayout { private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView, ActivityOptions opts, AppTransitionAnimationSpecsFuture transitionFuture, int windowingMode, int activityType) { - SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.startActivityFromRecents(mContext, task.key, task.title, opts, windowingMode, - activityType, - succeeded -> { - if (succeeded) { - // Keep track of the index of the task launch - int taskIndexFromFront = 0; - int taskIndex = stack.indexOfStackTask(task); - if (taskIndex > -1) { - taskIndexFromFront = stack.getTaskCount() - taskIndex - 1; - } - EventBus.getDefault().send(new LaunchTaskSucceededEvent( - taskIndexFromFront)); - } else { - // Dismiss the task if we fail to launch it - if (taskView != null) { - taskView.dismissTask(); - } + ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key, opts, windowingMode, + activityType, succeeded -> { + if (succeeded) { + // Keep track of the index of the task launch + int taskIndexFromFront = 0; + int taskIndex = stack.indexOfStackTask(task); + if (taskIndex > -1) { + taskIndexFromFront = stack.getTaskCount() - taskIndex - 1; + } + EventBus.getDefault().send(new LaunchTaskSucceededEvent( + taskIndexFromFront)); + } else { + Log.e(TAG, mContext.getString(R.string.recents_launch_error_message, + task.title)); - // Keep track of failed launches - EventBus.getDefault().send(new LaunchTaskFailedEvent()); - } - }); + // Dismiss the task if we fail to launch it + if (taskView != null) { + taskView.dismissTask(); + } + + // Keep track of failed launches + EventBus.getDefault().send(new LaunchTaskFailedEvent()); + } + }, getHandler()); if (transitionFuture != null) { mHandler.post(transitionFuture::composeSpecsSynchronous); } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 7bcef574cfd6..1596d120c16f 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -439,7 +439,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, if (mMinimizedSnapAlgorithm == null) { mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth, mDisplayHeight, mDividerSize, isHorizontalDivision(), - mStableInsets, mDockedStackMinimized && mHomeStackResizable); + mStableInsets, mDockSide, mDockedStackMinimized && mHomeStackResizable); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 7022c479eae8..41cae6b0be57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -47,6 +47,7 @@ import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.recents.Recents; import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.StatusBarState; @@ -311,8 +312,8 @@ public class CarStatusBar extends StatusBar implements private class TaskStackListenerImpl extends SysUiTaskStackChangeListener { @Override public void onTaskStackChanged() { - SystemServicesProxy ssp = Recents.getSystemServices(); - ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask(); + ActivityManager.RunningTaskInfo runningTaskInfo = + ActivityManagerWrapper.getInstance().getRunningTask(); if (runningTaskInfo != null && runningTaskInfo.baseActivity != null) { mController.taskChanged(runningTaskInfo.baseActivity.getPackageName(), runningTaskInfo); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index d8a9c12f008e..fa34d4a62d1b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -5266,7 +5266,8 @@ public class StatusBar extends SystemUI implements DemoMode, boolean isCameraAllowedByAdmin() { if (mDevicePolicyManager.getCameraDisabled(null, mCurrentUserId)) { return false; - } else if (isKeyguardShowing() && isKeyguardSecure()) { + } else if (mStatusBarKeyguardViewManager == null || + (isKeyguardShowing() && isKeyguardSecure())) { // Check if the admin has disabled the camera specifically for the keyguard return (mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUserId) & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0; diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 2e0f394a53cc..fad6bd1b3f83 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4754,6 +4754,16 @@ message MetricsEvent { // OS: P DIALOG_OEM_LOCK_INFO = 1238; + // ACTION: Settings > Wi-Fi > Click one network > Auto sign in + // CATEGORY: SETTINGS + // OS: P + ACTION_WIFI_AUTO_SIGN_IN = 1239; + + // Open: Settings > System > About phone > IMEI + // CATEGORY: SETTINGS + // OS: P + DIALOG_IMEI_INFO = 1240; + // Add new aosp constants above this line. // END OF AOSP CONSTANTS } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 8d46d1e27235..35f83e41071c 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -60,9 +60,6 @@ public class Watchdog extends Thread { // 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 @@ public class Watchdog extends Thread { // 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 @@ public class Watchdog extends Thread { } } - 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 e98bb1a9a8cd..d2d9aab84065 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 d6bd2b317a97..7eb922c9cd80 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 43816c6a5564..a035bd026fea 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -24,6 +24,7 @@ import android.net.wifi.WifiActivityEnergyInfo; import android.os.PowerManager.ServiceType; 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 final class BatteryStatsService extends IBatteryStats.Stub } 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 400b03a986ed..c860df89f29f 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 @@ class RunningTasks { 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 6e1c21eea15a..c4e6ff6bc906 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 @@ public final class ContentService extends IContentService.Stub { }; 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 2a21135e544b..beb7486d7bc2 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 @@ public class GnssLocationProvider implements LocationProviderInterface { } }; 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 @@ public class GnssLocationProvider implements LocationProviderInterface { * 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 000000000000..791ee821b357 --- /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 110f26d5eddc..000000000000 --- 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 3c9e1d456c6f..1cfd5f02e810 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 @@ package com.android.server.media; 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 @@ public final class MediaRouterService extends IMediaRouterService.Stub 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 @@ public final class MediaRouterService extends IMediaRouterService.Stub 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 @@ public final class MediaRouterService extends IMediaRouterService.Stub final long token = Binder.clearCallingIdentity(); try { - ClientRecord clientRecord; synchronized (mLock) { - clientRecord = mAllClientRecords.get(client.asBinder()); - } - if (clientRecord != null) { - return mAudioPlayerStateMonitor.isPlaybackActive(clientRecord.mUid); + return isPlaybackActiveLocked(client); } - return false; } finally { Binder.restoreCallingIdentity(token); } @@ -514,6 +480,14 @@ public final class MediaRouterService extends IMediaRouterService.Stub 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 f6a81d07277a..aa652445c45a 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.PackageManager; 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.KeyEvent; 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 @@ public class MediaSessionService extends SystemService implements Monitor { private KeyguardManager mKeyguardManager; private IAudioService mAudioService; + private AudioManagerInternal mAudioManagerInternal; private ContentResolver mContentResolver; private SettingsObserver mSettingsObserver; private INotificationManager mNotificationManager; @@ -113,7 +114,7 @@ public class MediaSessionService extends SystemService implements Monitor { // 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { } } } - }, null /* handler */); - mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService); + }); + mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); mContentResolver = getContext().getContentResolver(); mSettingsObserver = new SettingsObserver(); mSettingsObserver.observe(); @@ -654,7 +650,7 @@ public class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 719ec362e6e8..d9fe72e0c8bb 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 @@ class MediaSessionStack { */ private final List<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); - private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; + private final AudioPlaybackMonitor mAudioPlaybackMonitor; private final OnMediaButtonSessionChangedListener mOnMediaButtonSessionChangedListener; /** @@ -84,6 +84,7 @@ class MediaSessionStack { */ private MediaSessionRecord mMediaButtonSession; + private MediaSessionRecord mCachedDefault; private MediaSessionRecord mCachedVolumeDefault; /** @@ -92,8 +93,8 @@ class MediaSessionStack { 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 @@ class MediaSessionStack { 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 @@ class MediaSessionStack { 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 @@ class MediaSessionStack { } 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 86a1c03d2829..50ac409f31c9 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 @@ public class PackageDexOptimizer { // 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 @@ public class PackageDexOptimizer { 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 @@ public class PackageDexOptimizer { * 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 @@ public class PackageDexOptimizer { (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 19b0d9bc4b90..781216c3c43f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java @@ -52,7 +52,18 @@ public class PackageManagerServiceCompilerMapping { // 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 if ("pm.dexopt.shared".equals(sysPropName)) { + sysPropValue = "speed"; + } 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/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 1c002aa43512..25e923994789 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -2262,6 +2262,10 @@ public class ShortcutService extends IShortcutService.Stub { final ShortcutUser user = getUserShortcutsLocked(userId); + if (user.hasHostPackage(packageName)) { + return true; + } + // Always trust the cached component. final ComponentName cached = user.getCachedLauncher(); if (cached != null) { @@ -2361,6 +2365,16 @@ public class ShortcutService extends IShortcutService.Stub { } } + public void setShortcutHostPackage(@NonNull String type, @Nullable String packageName, + int userId) { + synchronized (mLock) { + throwIfUserLockedL(userId); + + final ShortcutUser user = getUserShortcutsLocked(userId); + user.setShortcutHostPackage(type, packageName); + } + } + // === House keeping === private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId, @@ -2697,6 +2711,12 @@ public class ShortcutService extends IShortcutService.Stub { } @Override + public void setShortcutHostPackage(@NonNull String type, @Nullable String packageName, + int userId) { + ShortcutService.this.setShortcutHostPackage(type, packageName, userId); + } + + @Override public boolean requestPinAppWidget(@NonNull String callingPackage, @NonNull AppWidgetProviderInfo appWidget, @Nullable Bundle extras, @Nullable IntentSender resultIntent, int userId) { diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java index 48eccd02db64..1efd765b18fa 100644 --- a/services/core/java/com/android/server/pm/ShortcutUser.java +++ b/services/core/java/com/android/server/pm/ShortcutUser.java @@ -23,6 +23,7 @@ import android.content.pm.ShortcutManager; import android.text.TextUtils; import android.text.format.Formatter; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.Slog; @@ -123,6 +124,20 @@ class ShortcutUser { /** In-memory-cached default launcher. */ private ComponentName mCachedLauncher; + /** + * Keep track of additional packages that other parts of the system have said are + * allowed to access shortcuts. The key is the part of the system it came from, + * the value is the package name that has access. We don't persist these because + * at boot all relevant system services will push this data back to us they do their + * normal evaluation of the state of the world. + */ + private final ArrayMap<String, String> mHostPackages = new ArrayMap<>(); + + /** + * Set of package name values from above. + */ + private final ArraySet<String> mHostPackageSet = new ArraySet<>(); + private String mKnownLocales; private long mLastAppScanTime; @@ -467,6 +482,23 @@ class ShortcutUser { return mCachedLauncher; } + public void setShortcutHostPackage(@NonNull String type, @Nullable String packageName) { + if (packageName != null) { + mHostPackages.put(type, packageName); + } else { + mHostPackages.remove(type); + } + + mHostPackageSet.clear(); + for (int i = 0; i < mHostPackages.size(); i++) { + mHostPackageSet.add(mHostPackages.valueAt(i)); + } + } + + public boolean hasHostPackage(@NonNull String packageName) { + return mHostPackageSet.contains(packageName); + } + public void resetThrottling() { for (int i = mPackages.size() - 1; i >= 0; i--) { mPackages.valueAt(i).resetThrottling(); @@ -555,6 +587,18 @@ class ShortcutUser { pw.print("Last known launcher: "); pw.print(mLastKnownLauncher); pw.println(); + + if (mHostPackages.size() > 0) { + pw.print(prefix); + pw.println("Host packages:"); + for (int i = 0; i < mHostPackages.size(); i++) { + pw.print(prefix); + pw.print(" "); + pw.print(mHostPackages.keyAt(i)); + pw.print(": "); + pw.println(mHostPackages.valueAt(i)); + } + } } for (int i = 0; i < mLaunchers.size(); i++) { diff --git a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java index edd2fdb1b33a..c6ec287d9c6a 100644 --- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java +++ b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java @@ -52,6 +52,7 @@ import android.widget.Button; import android.widget.FrameLayout; import com.android.internal.R; +import com.android.server.vr.VrManagerService; /** * Helper to manage showing/hiding a confirmation prompt when the navigation bar is hidden @@ -147,7 +148,6 @@ public class ImmersiveModeConfirmation { && userSetupComplete && !mVrModeEnabled && !navBarEmpty - && !isLockTaskModeLocked() && !UserManager.isDeviceInDemoMode(mContext)) { mHandler.sendEmptyMessageDelayed(H.SHOW, mShowDelayMs); } @@ -156,20 +156,6 @@ public class ImmersiveModeConfirmation { } } - /** - * @return {@code true} if and only if the device is currently in LockTask mode managed by - * {@link android.app.admin.DevicePolicyManager}. Note that this differs from the screen pinning - * mode which is initiated by the user. - */ - private boolean isLockTaskModeLocked() { - try { - return ActivityManager.getService().getLockTaskModeState() - == ActivityManager.LOCK_TASK_MODE_LOCKED; - } catch (RemoteException e) { - return false; - } - } - public boolean onPowerKeyDown(boolean isScreenOn, long time, boolean inImmersiveMode, boolean navBarEmpty) { if (!isScreenOn && (time - mPanicTime < mPanicThresholdMs)) { diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 22d2bcfc3e7e..41534cbb641f 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.IntentFilter; 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.StatsLogEventWrapper; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; - -import java.util.ArrayList; -import java.util.List; +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 java.util.Map; /** @@ -65,7 +68,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 class StatsCompanionService extends IStatsCompanionService.Stub { 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 class StatsCompanionService extends IStatsCompanionService.Stub { } } - 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } @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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { mContext.unregisterReceiver(mAppUpdateReceiver); mContext.unregisterReceiver(mUserUpdateReceiver); cancelAnomalyAlarm(); - cancelPollingAlarms(); + cancelPullingAlarms(); } } diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index e7e4efccea2f..e8ebbe4dd805 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -166,6 +166,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC private boolean mUserUnlocked; private Vr2dDisplay mVr2dDisplay; private boolean mBootsToVr; + private boolean mStandby; + private boolean mUseStandbyToExitVrMode; // Handles events from the managed services (e.g. VrListenerService and any bound VR compositor // service). @@ -203,7 +205,10 @@ public class VrManagerService extends SystemService implements EnabledComponentC * */ private void updateVrModeAllowedLocked() { - boolean allowed = mSystemSleepFlags == FLAG_ALL && mUserUnlocked; + boolean ignoreSleepFlags = mBootsToVr && mUseStandbyToExitVrMode; + boolean disallowedByStandby = mStandby && mUseStandbyToExitVrMode; + boolean allowed = (mSystemSleepFlags == FLAG_ALL || ignoreSleepFlags) && mUserUnlocked + && !disallowedByStandby; if (mVrModeAllowed != allowed) { mVrModeAllowed = allowed; if (DBG) Slog.d(TAG, "VR mode is " + ((allowed) ? "allowed" : "disallowed")); @@ -273,6 +278,17 @@ public class VrManagerService extends SystemService implements EnabledComponentC } } + private void setStandbyEnabled(boolean standby) { + synchronized(mLock) { + if (!mBootsToVr) { + Slog.e(TAG, "Attempting to set standby mode on a non-standalone device"); + return; + } + mStandby = standby; + updateVrModeAllowedLocked(); + } + } + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -587,6 +603,12 @@ public class VrManagerService extends SystemService implements EnabledComponentC } @Override + public void setStandbyEnabled(boolean standby) { + enforceCallerPermissionAnyOf(Manifest.permission.ACCESS_VR_MANAGER); + VrManagerService.this.setStandbyEnabled(standby); + } + + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; @@ -733,6 +755,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC } mBootsToVr = SystemProperties.getBoolean("ro.boot.vr", false); + mUseStandbyToExitVrMode = mBootsToVr + && SystemProperties.getBoolean("persist.vr.use_standby_to_exit_vr_mode", false); publishLocalService(VrManagerInternal.class, new LocalService()); publishBinderService(Context.VR_SERVICE, mVrManager.asBinder()); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index fd71d2faafea..98db80ef0a3b 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1100,52 +1100,54 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree + " 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; + + 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); - // 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); - - // 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; + mService.updateFocusedWindowLocked( + UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/); + getDisplayContent().setLayoutNeeded(); + mService.mWindowPlacerLocked.performSurfacePlacement(); + } finally { + Binder.restoreCallingIdentity(origId); } - 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 diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java index 401547e6eebb..8fb2be8c256f 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 @@ public class DimLayer { } catch (Exception e) { Slog.e(TAG_WM, "Exception creating Dim surface", e); } finally { - service.closeSurfaceTransaction(); + service.closeSurfaceTransaction("DimLayer.constructSurface"); } } @@ -235,7 +235,7 @@ public class DimLayer { } 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 fe34834a8bed..2f54e0e18b8b 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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 69f557b1a2a8..5ea0e1d041ba 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 @@ public class DockedStackDividerController implements DimLayerUser { mDimLayer.setBounds(mTmpRect); mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */); } finally { - mService.closeSurfaceTransaction(); + mService.closeSurfaceTransaction("setResizeDimLayer"); } } mLastDimLayerRect.set(mTmpRect); @@ -558,7 +558,7 @@ public class DockedStackDividerController implements DimLayerUser { 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 860ff387d668..79d46ce51326 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 @@ class DragDropController { 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 9a955de9706c..861fb443b6d0 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 @@ class DragState { + 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 a7104410f4d3..bcb6e6736d04 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { try { forAllWindows(sRemoveReplacedWindowsConsumer, true /* traverseTopToBottom */); } finally { - mService.closeSurfaceTransaction(); + mService.closeSurfaceTransaction("removeReplacedWindows"); if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION removeReplacedWindows"); } } @@ -599,7 +599,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { } 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 c25b19ccc6ff..3350feae1bc0 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 @@ class ScreenRotationAnimation { 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 @@ class ScreenRotationAnimation { } 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 @@ class ScreenRotationAnimation { } 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 @@ class ScreenRotationAnimation { } 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 d83630dced93..13435d76cd07 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 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU 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 a11b33317f64..12f6b5a20b47 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 @@ class TaskPositioner implements DimLayer.DimLayerUser { } else { showDimLayer(); } - mService.closeSurfaceTransaction(); + mService.closeSurfaceTransaction("updateDimLayerVisibility"); } /** diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 0ac2cff42be9..6e89e3ea37bb 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -523,7 +523,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm( mService.mContext.getResources(), displayWidth, displayHeight, dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds, - isMinimizedDockAndHomeStackResizable()); + getDockSide(), isMinimizedDockAndHomeStackResizable()); final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition); // Recalculate the bounds based on the position of the target. diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index e409a68f2dfe..1912095caa1d 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 @@ public class WindowAnimator { } 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 69c7c7ecb7bb..b28bb719188c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -162,7 +162,9 @@ import android.os.PowerManager.ServiceType; 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub /** * 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 @@ public class WindowManagerService extends IWindowManager.Stub }, 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 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy = policy; mTaskSnapshotController = new TaskSnapshotController(this); + mWindowTracing = WindowTracing.createDefaultAndStartLooper(context); + LocalServices.addService(WindowManagerPolicy.class, mPolicy); if(mInputManager != null) { @@ -1068,7 +1085,7 @@ public class WindowManagerService extends IWindowManager.Stub try { createWatermarkInTransaction(); } finally { - closeSurfaceTransaction(); + closeSurfaceTransaction("createWatermarkInTransaction"); } showEmulatorDisplayOverlayIfNeeded(); @@ -3572,7 +3589,7 @@ public class WindowManagerService extends IWindowManager.Stub mCircularDisplayMask = null; } } finally { - closeSurfaceTransaction(); + closeSurfaceTransaction("showCircularMask"); if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, "<<< CLOSE TRANSACTION showCircularMask(visible=" + visible + ")"); } @@ -3597,7 +3614,7 @@ public class WindowManagerService extends IWindowManager.Stub } mEmulatorDisplayOverlay.setVisibility(true); } finally { - closeSurfaceTransaction(); + closeSurfaceTransaction("showEmulatorDisplayOverlay"); if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, "<<< CLOSE TRANSACTION showEmulatorDisplayOverlay"); } @@ -6320,7 +6337,7 @@ public class WindowManagerService extends IWindowManager.Stub * @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 @@ public class WindowManagerService extends IWindowManager.Stub 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 000000000000..4b98d9d9921d --- /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 6bed30c96328..6b1932d7b6ae 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP 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)); + + // Visibility of the removed window. Will be used later to update orientation later on. + boolean wasVisible = false; - 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; - - 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; - } + final int displayId = getDisplayId(); - // If we are not currently running the exit animation, we need to see about starting one - wasVisible = isWinVisibleLw(); + // 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; + } - if (keepVisibleDeadWindow) { - if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, - "Not removing " + this + " because app died while it's visible"); + // If we are not currently running the exit animation, we need to see about starting one + wasVisible = isWinVisibleLw(); - mAppDied = true; - setDisplayLayoutNeeded(); - mService.mWindowPlacerLocked.performSurfacePlacement(); + if (keepVisibleDeadWindow) { + if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, + "Not removing " + this + " because app died while it's visible"); - // 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*/); + mAppDied = true; + setDisplayLayoutNeeded(); + mService.mWindowPlacerLocked.performSurfacePlacement(); - Binder.restoreCallingIdentity(origId); - return; - } + // 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; + 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); + // 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(); + 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; } - 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(); + 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 5266903185d4..86397aea6a17 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 @@ class WindowStateAnimator { mSurfaceController.setLayerStackInTransaction(getLayerStack()); mSurfaceController.setLayer(mAnimLayer); } finally { - mService.closeSurfaceTransaction(); + mService.closeSurfaceTransaction("createSurfaceLocked"); } mLastHidden = true; @@ -1711,7 +1711,7 @@ class WindowStateAnimator { 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 edd650a42107..a2145230f956 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 @@ class WindowSurfaceController { mSurfaceControl.setLayer(layer); } } finally { - mService.closeSurfaceTransaction(); + mService.closeSurfaceTransaction("setLayer"); } } } @@ -385,7 +385,7 @@ class WindowSurfaceController { try { mSurfaceControl.setTransparentRegionHint(region); } finally { - mService.closeSurfaceTransaction(); + mService.closeSurfaceTransaction("setTransparentRegion"); if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setTransparentRegion"); } @@ -403,7 +403,7 @@ class WindowSurfaceController { try { mSurfaceControl.setOpaque(isOpaque); } finally { - mService.closeSurfaceTransaction(); + mService.closeSurfaceTransaction("setOpaqueLocked"); if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaqueLocked"); } } @@ -420,7 +420,7 @@ class WindowSurfaceController { 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 d57fdd26d250..cd5e4750554c 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 @@ class WindowSurfacePlacer { 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 000000000000..5657f6c4f9c5 --- /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 3901cebcc787..d4ffa70417e1 100644 --- a/services/core/jni/com_android_server_SystemServer.cpp +++ b/services/core/jni/com_android_server_SystemServer.cpp @@ -48,8 +48,6 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject / 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 e8ef1686e1b4..2f45181e6f5c 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -92,7 +92,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) 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 de5d879a05af..a2c2aeb0fa78 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -545,11 +545,9 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { } 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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(); - - 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("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("StartDeviceIdleController"); - mSystemServiceManager.startService(DeviceIdleController.class); + final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals(""); + if (hasPdb) { + traceBeginAndSlog("StartPersistentDataBlock"); + mSystemServiceManager.startService(PersistentDataBlockService.class); traceEnd(); + } - // Always start the Device Policy Manager, so that the API is compatible with - // API8. - traceBeginAndSlog("StartDevicePolicyManager"); - mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class); + 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,154 +1015,146 @@ public final class SystemServer { traceEnd(); } - if (!disableNonCoreServices) { - traceBeginAndSlog("StartClipboardService"); - mSystemServiceManager.startService(ClipboardService.class); - traceEnd(); - } + traceBeginAndSlog("StartClipboardService"); + mSystemServiceManager.startService(ClipboardService.class); + 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("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("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(); - - // 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(); + traceBeginAndSlog("StartNetworkScoreService"); + try { + networkScore = new NetworkScoreService(context); + ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore); + } catch (Throwable e) { + reportWtf("starting Network Score Service", e); + } + traceEnd(); - if (!disableRtt) { - traceBeginAndSlog("StartWifiRtt"); - mSystemServiceManager.startService("com.android.server.wifi.RttService"); - 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(); - if (context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI_RTT)) { - traceBeginAndSlog("StartRttService"); - mSystemServiceManager.startService( - "com.android.server.wifi.rtt.RttService"); - 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(); - if (context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI_AWARE)) { - traceBeginAndSlog("StartWifiAware"); - mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS); - 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 (context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI_DIRECT)) { - traceBeginAndSlog("StartWifiP2P"); - mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS); - traceEnd(); - } + if (!disableRtt) { + traceBeginAndSlog("StartWifiRtt"); + mSystemServiceManager.startService("com.android.server.wifi.RttService"); + traceEnd(); if (context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_LOWPAN)) { - traceBeginAndSlog("StartLowpan"); - mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS); + PackageManager.FEATURE_WIFI_RTT)) { + traceBeginAndSlog("StartRttService"); + mSystemServiceManager.startService( + "com.android.server.wifi.rtt.RttService"); 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_WIFI_AWARE)) { + traceBeginAndSlog("StartWifiAware"); + mSystemServiceManager.startService(WIFI_AWARE_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); - } + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_DIRECT)) { + traceBeginAndSlog("StartWifiP2P"); + mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS); traceEnd(); + } - traceBeginAndSlog("StartNsdService"); - try { - serviceDiscovery = NsdService.create(context); - ServiceManager.addService( - Context.NSD_SERVICE, serviceDiscovery); - } catch (Throwable e) { - reportWtf("starting Service Discovery Service", e); - } + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_LOWPAN)) { + traceBeginAndSlog("StartLowpan"); + mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS); traceEnd(); } - if (!disableNonCoreServices) { - traceBeginAndSlog("StartUpdateLockService"); - try { - ServiceManager.addService(Context.UPDATE_LOCK_SERVICE, - new UpdateLockService(context)); - } catch (Throwable e) { - reportWtf("starting UpdateLockService", e); - } + 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(); + + 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); SystemNotificationChannels.createAll(context); @@ -1185,27 +1167,25 @@ public final class SystemServer { 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("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("StartCountryDetectorService"); + try { + countryDetector = new CountryDetectorService(context); + ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector); + } catch (Throwable e) { + reportWtf("starting Country Detector", e); } + traceEnd(); - if (!disableNonCoreServices && !disableSearchManager) { + if (!disableSearchManager) { traceBeginAndSlog("StartSearchManagerService"); try { mSystemServiceManager.startService(SEARCH_MANAGER_SERVICE_CLASS); @@ -1215,8 +1195,7 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { } 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_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_MIDI)) { + // Start MIDI Manager service + traceBeginAndSlog("StartMidiManager"); + mSystemServiceManager.startService(MIDI_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(); - } + 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(); + } - 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,47 +1287,45 @@ public final class SystemServer { 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(); - } + if (!disableTrustManager) { + traceBeginAndSlog("StartTrustManager"); + mSystemServiceManager.startService(TrustManagerService.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); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) { + traceBeginAndSlog("StartBackupManager"); + mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS); traceEnd(); + } - if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) { - traceBeginAndSlog("StartGestureLauncher"); - mSystemServiceManager.startService(GestureLauncherService.class); - traceEnd(); - } - traceBeginAndSlog("StartSensorNotification"); - mSystemServiceManager.startService(SensorNotificationService.class); + 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(); - traceBeginAndSlog("StartContextHubSystemService"); - mSystemServiceManager.startService(ContextHubSystemService.class); + 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 { @@ -1375,16 +1348,14 @@ public final class SystemServer { 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 @@ public final class SystemServer { } 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 @@ public final class SystemServer { 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(); - - if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { - traceBeginAndSlog("StartFingerprintSensor"); - mSystemServiceManager.startService(FingerprintService.class); - traceEnd(); - } + traceBeginAndSlog("StartMediaRouterService"); + try { + mediaRouter = new MediaRouterService(context); + ServiceManager.addService(Context.MEDIA_ROUTER_SERVICE, mediaRouter); + } catch (Throwable e) { + reportWtf("starting MediaRouterService", e); + } + traceEnd(); - traceBeginAndSlog("StartBackgroundDexOptService"); - try { - BackgroundDexOptService.schedule(context); - } catch (Throwable e) { - reportWtf("starting StartBackgroundDexOptService", e); - } + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { + traceBeginAndSlog("StartFingerprintSensor"); + mSystemServiceManager.startService(FingerprintService.class); traceEnd(); + } - traceBeginAndSlog("StartPruneInstantAppsJobService"); - try { - PruneInstantAppsJobService.schedule(context); - } catch (Throwable e) { - reportWtf("StartPruneInstantAppsJobService", e); - } - 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 @@ public final class SystemServer { traceEnd(); } - if (!disableNonCoreServices && !disableMediaProjection) { + if (!disableMediaProjection) { traceBeginAndSlog("StartMediaProjectionManager"); mSystemServiceManager.startService(MediaProjectionManagerService.class); traceEnd(); @@ -1530,17 +1494,15 @@ public final class SystemServer { 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/WindowTracingTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java new file mode 100644 index 000000000000..ad9aea748ebc --- /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/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 1569ac32128b..44e5314ff7dd 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.pm.ShortcutServiceInternal; import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.soundtrigger.IRecognitionStatusCallback; @@ -44,6 +45,7 @@ import android.os.Parcel; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.service.voice.IVoiceInteractionService; import android.service.voice.IVoiceInteractionSession; @@ -53,6 +55,7 @@ import android.service.voice.VoiceInteractionServiceInfo; import android.service.voice.VoiceInteractionSession; import android.speech.RecognitionService; import android.text.TextUtils; +import android.util.ArraySet; import android.util.Log; import android.util.Slog; @@ -63,6 +66,7 @@ import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; +import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; @@ -84,7 +88,9 @@ public class VoiceInteractionManagerService extends SystemService { final ContentResolver mResolver; final DatabaseHelper mDbHelper; final ActivityManagerInternal mAmInternal; - final TreeSet<Integer> mLoadedKeyphraseIds; + final UserManager mUserManager; + final ArraySet<Integer> mLoadedKeyphraseIds = new ArraySet<>(); + ShortcutServiceInternal mShortcutServiceInternal; SoundTriggerInternal mSoundTriggerInternal; private final RemoteCallbackList<IVoiceInteractionSessionListener> @@ -96,8 +102,10 @@ public class VoiceInteractionManagerService extends SystemService { mResolver = context.getContentResolver(); mDbHelper = new DatabaseHelper(context); mServiceStub = new VoiceInteractionManagerServiceStub(); - mAmInternal = LocalServices.getService(ActivityManagerInternal.class); - mLoadedKeyphraseIds = new TreeSet<Integer>(); + mAmInternal = Preconditions.checkNotNull( + LocalServices.getService(ActivityManagerInternal.class)); + mUserManager = Preconditions.checkNotNull( + context.getSystemService(UserManager.class)); PackageManagerInternal packageManagerInternal = LocalServices.getService( PackageManagerInternal.class); @@ -124,6 +132,8 @@ public class VoiceInteractionManagerService extends SystemService { @Override public void onBootPhase(int phase) { if (PHASE_SYSTEM_SERVICES_READY == phase) { + mShortcutServiceInternal = Preconditions.checkNotNull( + LocalServices.getService(ShortcutServiceInternal.class)); mSoundTriggerInternal = LocalServices.getService(SoundTriggerInternal.class); } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { mServiceStub.systemRunning(isSafeMode()); @@ -180,6 +190,7 @@ public class VoiceInteractionManagerService extends SystemService { private boolean mSafeMode; private int mCurUser; + private boolean mCurUserUnlocked; private final boolean mEnableService; VoiceInteractionManagerServiceStub() { @@ -381,6 +392,7 @@ public class VoiceInteractionManagerService extends SystemService { public void switchUser(int userHandle) { synchronized (this) { mCurUser = userHandle; + mCurUserUnlocked = false; switchImplementationIfNeededLocked(false); } } @@ -409,13 +421,24 @@ public class VoiceInteractionManagerService extends SystemService { } } + final boolean hasComponent = serviceComponent != null && serviceInfo != null; + + if (mUserManager.isUserUnlockingOrUnlocked(mCurUser)) { + if (hasComponent) { + mShortcutServiceInternal.setShortcutHostPackage(TAG, + serviceComponent.getPackageName(), mCurUser); + } else { + mShortcutServiceInternal.setShortcutHostPackage(TAG, null, mCurUser); + } + } + if (force || mImpl == null || mImpl.mUser != mCurUser || !mImpl.mComponent.equals(serviceComponent)) { unloadAllKeyphraseModels(); if (mImpl != null) { mImpl.shutdownLocked(); } - if (serviceComponent != null && serviceInfo != null) { + if (hasComponent) { mImpl = new VoiceInteractionManagerServiceImpl(mContext, UiThread.getHandler(), this, mCurUser, serviceComponent); mImpl.startLocked(); @@ -953,12 +976,14 @@ public class VoiceInteractionManagerService extends SystemService { } private synchronized void unloadAllKeyphraseModels() { - for (int keyphraseId : mLoadedKeyphraseIds) { + for (int i = 0; i < mLoadedKeyphraseIds.size(); i++) { final long caller = Binder.clearCallingIdentity(); try { - int status = mSoundTriggerInternal.unloadKeyphraseModel(keyphraseId); + int status = mSoundTriggerInternal.unloadKeyphraseModel( + mLoadedKeyphraseIds.valueAt(i)); if (status != SoundTriggerInternal.STATUS_OK) { - Slog.w(TAG, "Failed to unload keyphrase " + keyphraseId + ":" + status); + Slog.w(TAG, "Failed to unload keyphrase " + mLoadedKeyphraseIds.valueAt(i) + + ":" + status); } } finally { Binder.restoreCallingIdentity(caller); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 03d5f9d9a3ff..3fc220857676 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1543,6 +1543,13 @@ public class CarrierConfigManager { "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 @@ public class CarrierConfigManager { 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 c8b4776522c6..de02de7ba17d 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -68,6 +68,7 @@ public class SignalStrength implements Parcelable { 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 @@ public class SignalStrength implements Parcelable { mLteRsrpBoost = 0; mTdScdmaRscp = INVALID; isGsm = true; + mUseOnlyRsrpForLteLevel = false; } /** @@ -134,6 +136,7 @@ public class SignalStrength implements Parcelable { mLteRsrpBoost = 0; mTdScdmaRscp = INVALID; isGsm = gsmFlag; + mUseOnlyRsrpForLteLevel = false; } /** @@ -145,10 +148,10 @@ public class SignalStrength implements Parcelable { 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 @@ public class SignalStrength implements Parcelable { 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 @@ public class SignalStrength implements Parcelable { 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 @@ public class SignalStrength implements Parcelable { 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 @@ public class SignalStrength implements Parcelable { 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 @@ public class SignalStrength implements Parcelable { * @param lteCqi * @param lteRsrpBoost * @param gsm + * @param useOnlyRsrpForLteLevel * * @hide */ @@ -255,7 +259,7 @@ public class SignalStrength implements Parcelable { 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 @@ public class SignalStrength implements Parcelable { mLteRsrpBoost = lteRsrpBoost; mTdScdmaRscp = INVALID; isGsm = gsm; + mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel; if (DBG) log("initialize: " + toString()); } @@ -293,6 +298,7 @@ public class SignalStrength implements Parcelable { mLteRsrpBoost = s.mLteRsrpBoost; mTdScdmaRscp = s.mTdScdmaRscp; isGsm = s.isGsm; + mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel; } /** @@ -318,6 +324,7 @@ public class SignalStrength implements Parcelable { mLteRsrpBoost = in.readInt(); mTdScdmaRscp = in.readInt(); isGsm = (in.readInt() != 0); + mUseOnlyRsrpForLteLevel = (in.readInt() != 0); } /** @@ -366,6 +373,7 @@ public class SignalStrength implements Parcelable { out.writeInt(mLteRsrpBoost); out.writeInt(mTdScdmaRscp); out.writeInt(isGsm ? 1 : 0); + out.writeInt(mUseOnlyRsrpForLteLevel ? 1 : 0); } /** @@ -449,6 +457,17 @@ public class SignalStrength implements Parcelable { } /** + * @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 @@ public class SignalStrength implements Parcelable { } } + 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 @@ public class SignalStrength implements Parcelable { } /** + * @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 @@ public class SignalStrength implements Parcelable { + (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 @@ public class SignalStrength implements Parcelable { && mLteCqi == s.mLteCqi && mLteRsrpBoost == s.mLteRsrpBoost && mTdScdmaRscp == s.mTdScdmaRscp - && isGsm == s.isGsm); + && isGsm == s.isGsm + && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel); } /** @@ -1031,7 +1068,9 @@ public class SignalStrength implements Parcelable { + " " + 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 @@ public class SignalStrength implements Parcelable { mLteRsrpBoost = m.getInt("lteRsrpBoost"); mTdScdmaRscp = m.getInt("TdScdma"); isGsm = m.getBoolean("isGsm"); + mUseOnlyRsrpForLteLevel = m.getBoolean("useOnlyRsrpForLteLevel"); } /** @@ -1110,6 +1150,7 @@ public class SignalStrength implements Parcelable { 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 67f6849edb69..42c3de563492 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2166,13 +2166,16 @@ public class TelephonyManager { * @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 d8d7f48a56be..b30a3af72c3e 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 @@ public final class FileServiceInfo extends ServiceInfo implements Parcelable { /** @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 9ccdd56fd82d..4fee3df83c9a 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java @@ -18,6 +18,7 @@ package android.telephony.mbms.vendor; 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 @@ import java.util.Map; * @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 62d570c80043..99a82ad00d25 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 @@ public class IccUtils { } 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 f6006b0a7fa9..6435ad971476 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/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp index 3654901e3e02..afa155f46eb9 100644 --- a/tools/aapt2/configuration/ConfigurationParser_test.cpp +++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp @@ -423,11 +423,11 @@ TEST_F(ConfigurationParserTest, AndroidSdkGroupAction) { TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) { static constexpr const char* xml = R"xml( - <android-sdk-group label="O"> + <android-sdk-group label="P"> <android-sdk minSdkVersion="M" - targetSdkVersion="O" - maxSdkVersion="O"> + targetSdkVersion="P" + maxSdkVersion="P"> </android-sdk> </android-sdk-group>)xml"; @@ -438,14 +438,14 @@ TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) { ASSERT_TRUE(ok); ASSERT_EQ(1ul, config.android_sdk_groups.size()); - ASSERT_EQ(1u, config.android_sdk_groups.count("O")); + ASSERT_EQ(1u, config.android_sdk_groups.count("P")); - auto& out = config.android_sdk_groups["O"]; + auto& out = config.android_sdk_groups["P"]; AndroidSdk sdk; sdk.min_sdk_version = {}; // Only the latest development version is supported. - sdk.target_sdk_version = 26; - sdk.max_sdk_version = 26; + sdk.target_sdk_version = 28; + sdk.max_sdk_version = 28; ASSERT_EQ(sdk, out); } diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp index 135df405c747..674bee139186 100644 --- a/tools/incident_section_gen/main.cpp +++ b/tools/incident_section_gen/main.cpp @@ -18,6 +18,7 @@ #include <frameworks/base/core/proto/android/os/incident.pb.h> #include <map> +#include <set> #include <string> using namespace android; @@ -27,6 +28,60 @@ using namespace google::protobuf::io; using namespace google::protobuf::internal; using namespace std; +/** + * Implementation details: + * This binary auto generates .cpp files for incident and incidentd. + * + * When argument "incident" is specified, it generates incident_section.cpp file. + * + * When argument "incidentd" is specified, it generates section_list.cpp file. + * + * In section_list.cpp file, it generates a SECTION_LIST array and a PRIVACY_POLICY_LIST array. + * For SECTION_LIST, it generates Section.h classes only for proto fields with section option enabled. + * For PRIVACY_POLICY_LIST, it generates Privacy.h classes only for proto fields with privacy option enabled. + * + * For Privacy struct, it is possible to have self recursion definitions since protobuf is defining "classes" + * So the logic to handle it becomes very complicated when Privacy tag of a message contains a list of Privacies + * of its sub-messages. The code also handles multiple depth of self recursion fields. + * + * For example here is a one level self recursion message WindowManager: + * message WindowState { + * string state = 1 [(privacy).dest = LOCAL]; + * int32 display_id = 2; + * repeated WindowState child_windows = 3; + * } + * + * message WindowManager { + * WindowState my_window = 1; + * } + * + * When generating Privacy options for WindowManager, this tool will generate cpp syntax source code: + * + * #include "section_list.h" + * ... + * Privacy WindowState_state { 1, 9, NULL, LOCAL, NULL }; // first two integers are values for field id and proto type. + * Privacy WindowState_child_windows { 3, 11, NULL, DEFAULT, NULL }; // reserved for WindowState_LIST + * Privacy* WindowState_MSG_[] = { + * &WindowState_state, + * // display id is default, nothing is generated. + * &WindowState_child_windows, + * NULL // terminator of the array + * }; + * Privacy WindowState_my_window { 1, 11, WindowState_my_window_LIST, DEFAULT, NULL }; + * + * createList() { + * ... + * WindowState_child_windows.children = WindowState_my_window_LIST; // point to its own definition after the list is defined. + * ... + * } + * + * const Privacy** PRIVACY_POLICY_LIST = createList(); + * const int PRIVACY_POLICY_COUNT = 1; + */ + +// The assignments will be called when constructs PRIVACY_POLICY_LIST, has to be global variable +vector<string> gSelfRecursionAssignments; + static inline void emptyline() { printf("\n"); } @@ -38,7 +93,7 @@ static void generateHead(const char* header) { emptyline(); } -// ================================================================================ +// ======================== incident_sections ============================= static bool generateIncidentSectionsCpp(Descriptor const* descriptor) { generateHead("incident_sections"); @@ -73,7 +128,7 @@ static bool generateIncidentSectionsCpp(Descriptor const* descriptor) return true; } -// ================================================================================ +// ========================= section_list =================================== static void splitAndPrint(const string& args) { size_t base = 0; size_t found; @@ -88,12 +143,12 @@ static void splitAndPrint(const string& args) { } } -static const std::string replaceAll(const string& field_name, const char oldC, const string& newS) { - if (field_name.find_first_of(oldC) == field_name.npos) return field_name.c_str(); +static string replaceAll(const string& fieldName, const char oldC, const string& newS) { + if (fieldName.find_first_of(oldC) == fieldName.npos) return fieldName.c_str(); size_t pos = 0, idx = 0; - char* res = new char[field_name.size() * newS.size() + 1]; // assign a larger buffer - while (pos != field_name.size()) { - char cur = field_name[pos++]; + char* res = new char[fieldName.size() * newS.size() + 1]; // assign a larger buffer + while (pos != fieldName.size()) { + char cur = fieldName[pos++]; if (cur != oldC) { res[idx++] = cur; continue; @@ -104,92 +159,162 @@ static const std::string replaceAll(const string& field_name, const char oldC, c } } res[idx] = '\0'; - std::string result(res); + string result(res); delete [] res; return result; } -static inline bool isDefaultDest(const FieldDescriptor* field) { - return field->options().GetExtension(privacy).dest() == PrivacyFlags::default_instance().dest(); +static string getFieldName(const FieldDescriptor* field) { + return replaceAll(field->full_name(), '.', "__"); +} + +static string getMessageTypeName(const Descriptor* descriptor) { + return replaceAll(descriptor->full_name(), '.', "_") + "_MSG_"; +} + +static inline SectionFlags getSectionFlags(const FieldDescriptor* field) { + return field->options().GetExtension(section); } +static inline PrivacyFlags getPrivacyFlags(const FieldDescriptor* field) { + return field->options().GetExtension(privacy); +} + +static inline bool isDefaultField(const FieldDescriptor* field) { + return getPrivacyFlags(field).dest() == PrivacyFlags::default_instance().dest(); +} + +static bool isDefaultMessageImpl(const Descriptor* descriptor, set<string>* parents) { + int N = descriptor->field_count(); + parents->insert(descriptor->full_name()); + for (int i=0; i<N; ++i) { + const FieldDescriptor* field = descriptor->field(i); + // look at if the current field is default or not, return false immediately + if (!isDefaultField(field)) return false; + + switch (field->type()) { + case FieldDescriptor::TYPE_MESSAGE: + // if self recursion, don't go deep. + if (parents->find(field->message_type()->full_name()) != parents->end()) break; + // if is a default message, just continue + if (isDefaultMessageImpl(field->message_type(), parents)) break; + // sub message is not default, so this message is always not default + return false; + case FieldDescriptor::TYPE_STRING: + if (getPrivacyFlags(field).patterns_size() != 0) return false; + default: + continue; + } + } + parents->erase(descriptor->full_name()); + return true; +} + +static bool isDefaultMessage(const Descriptor* descriptor) { + set<string> parents; + return isDefaultMessageImpl(descriptor, &parents); +} + +// This function is called for looking at privacy tags for a message type and recursively its sub-messages +// It prints out each fields's privacy tags and a List of Privacy of the message itself (don't print default values) // Returns false if the descriptor doesn't have any non default privacy flags set, including its submessages -static bool generatePrivacyFlags(const Descriptor* descriptor, const char* alias, map<string, bool> &msgNames) { +static bool generatePrivacyFlags(const Descriptor* descriptor, map<string, bool> &msgNames, set<string>* parents) { bool hasDefaultFlags[descriptor->field_count()]; + + string messageTypeName = getMessageTypeName(descriptor); + // if the message is already defined, skip it. + if (msgNames.find(messageTypeName) != msgNames.end()) { + bool hasDefault = msgNames[messageTypeName]; + return !hasDefault; // don't generate if it has default privacy. + } + // insert the message type name so sub-message will figure out if self-recursion occurs + parents->insert(messageTypeName); + // iterate though its field and generate sub flags first for (int i=0; i<descriptor->field_count(); i++) { hasDefaultFlags[i] = true; // set default to true + const FieldDescriptor* field = descriptor->field(i); - const std::string field_name_str = replaceAll(field->full_name(), '.', "__"); - const char* field_name = field_name_str.c_str(); - // check if the same name is already defined - if (msgNames.find(field_name) != msgNames.end()) { - hasDefaultFlags[i] = msgNames[field_name]; + const string fieldName = getFieldName(field); + // check if the same field name is already defined. + if (msgNames.find(fieldName) != msgNames.end()) { + hasDefaultFlags[i] = msgNames[fieldName]; continue; }; - PrivacyFlags p = field->options().GetExtension(privacy); + PrivacyFlags p = getPrivacyFlags(field); + string fieldMessageName; switch (field->type()) { case FieldDescriptor::TYPE_MESSAGE: - if (generatePrivacyFlags(field->message_type(), field_name, msgNames)) { - printf("Privacy %s { %d, %d, %s_LIST, %d, NULL };\n", field_name, field->number(), - field->type(), field_name, p.dest()); - } else if (isDefaultDest(field)) { + fieldMessageName = getMessageTypeName(field->message_type()); + if (parents->find(fieldMessageName) != parents->end()) { // Self-Recursion proto definition + if (isDefaultField(field)) { + hasDefaultFlags[i] = isDefaultMessage(field->message_type()); + } else { + hasDefaultFlags[i] = false; + } + if (!hasDefaultFlags[i]) { + printf("Privacy %s = { %d, %d, NULL, %d, NULL }; // self recursion field of %s\n", + fieldName.c_str(), field->number(), field->type(), p.dest(), fieldMessageName.c_str()); + // generate the assignment and used to construct createList function later on. + gSelfRecursionAssignments.push_back(fieldName + ".children = " + fieldMessageName); + } + break; + } else if (generatePrivacyFlags(field->message_type(), msgNames, parents)) { + printf("Privacy %s = { %d, %d, %s, %d, NULL };\n", fieldName.c_str(), field->number(), + field->type(), fieldMessageName.c_str(), p.dest()); + } else if (isDefaultField(field)) { // don't create a new privacy if the value is default. break; - } else{ - printf("Privacy %s { %d, %d, NULL, %d, NULL };\n", field_name, field->number(), + } else { + printf("Privacy %s = { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(), field->type(), p.dest()); } hasDefaultFlags[i] = false; break; case FieldDescriptor::TYPE_STRING: - if (isDefaultDest(field) && p.patterns_size() == 0) break; + if (isDefaultField(field) && p.patterns_size() == 0) break; - printf("const char* %s_patterns[] = {\n", field_name); + printf("const char* %s_patterns[] = {\n", fieldName.c_str()); for (int i=0; i<p.patterns_size(); i++) { // the generated string need to escape backslash as well, need to dup it here printf(" \"%s\",\n", replaceAll(p.patterns(i), '\\', "\\\\").c_str()); } printf(" NULL };\n"); - printf("Privacy %s { %d, %d, NULL, %d, %s_patterns };\n", field_name, field->number(), - field->type(), p.dest(), field_name); + printf("Privacy %s = { %d, %d, NULL, %d, %s_patterns };\n", fieldName.c_str(), field->number(), + field->type(), p.dest(), fieldName.c_str()); hasDefaultFlags[i] = false; break; default: - if (isDefaultDest(field)) break; - printf("Privacy %s { %d, %d, NULL, %d, NULL };\n", field_name, field->number(), + if (isDefaultField(field)) break; + printf("Privacy %s = { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(), field->type(), p.dest()); hasDefaultFlags[i] = false; } // add the field name to message map, true means it has default flags - msgNames[field_name] = hasDefaultFlags[i]; + msgNames[fieldName] = hasDefaultFlags[i]; } bool allDefaults = true; for (int i=0; i<descriptor->field_count(); i++) { allDefaults &= hasDefaultFlags[i]; } + + parents->erase(messageTypeName); // erase the message type name when exit the message. + msgNames[messageTypeName] = allDefaults; // store the privacy tags of the message here to avoid overhead. + if (allDefaults) return false; emptyline(); - - bool needConst = strcmp(alias, "PRIVACY_POLICY") == 0; int policyCount = 0; - - printf("%sPrivacy* %s_LIST[] = {\n", needConst ? "const " : "", alias); + printf("Privacy* %s[] = {\n", messageTypeName.c_str()); for (int i=0; i<descriptor->field_count(); i++) { const FieldDescriptor* field = descriptor->field(i); if (hasDefaultFlags[i]) continue; - printf(" &%s,\n", replaceAll(field->full_name(), '.', "__").c_str()); + printf(" &%s,\n", getFieldName(field).c_str()); policyCount++; } - if (needConst) { - printf("};\n\n"); - printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount); - } else { - printf(" NULL };\n"); - } + printf(" NULL };\n"); emptyline(); return true; } @@ -198,6 +323,8 @@ static bool generateSectionListCpp(Descriptor const* descriptor) { generateHead("section_list"); // generates SECTION_LIST + printf("// Generate SECTION_LIST.\n\n"); + printf("const Section* SECTION_LIST[] = {\n"); for (int i=0; i<descriptor->field_count(); i++) { const FieldDescriptor* field = descriptor->field(i); @@ -205,7 +332,7 @@ static bool generateSectionListCpp(Descriptor const* descriptor) { if (field->type() != FieldDescriptor::TYPE_MESSAGE) { continue; } - const SectionFlags s = field->options().GetExtension(section); + const SectionFlags s = getSectionFlags(field); switch (s.type()) { case SECTION_NONE: continue; @@ -225,16 +352,73 @@ static bool generateSectionListCpp(Descriptor const* descriptor) { } } printf(" NULL };\n"); + + emptyline(); + printf("// =============================================================================\n"); emptyline(); - // generates PRIVACY_POLICY + // generates PRIVACY_POLICY_LIST + printf("// Generate PRIVACY_POLICY_LIST.\n\n"); map<string, bool> messageNames; - if (!generatePrivacyFlags(descriptor, "PRIVACY_POLICY", messageNames)) { - // if no privacy options set at all, define an empty list - printf("const Privacy* PRIVACY_POLICY_LIST[] = {};\n"); - printf("const int PRIVACY_POLICY_COUNT = 0;\n"); + set<string> parents; + bool skip[descriptor->field_count()]; + + for (int i=0; i<descriptor->field_count(); i++) { + const FieldDescriptor* field = descriptor->field(i); + const string fieldName = getFieldName(field); + PrivacyFlags p = getPrivacyFlags(field); + + skip[i] = true; + + if (field->type() != FieldDescriptor::TYPE_MESSAGE) { + continue; + } + // generate privacy flags for each field. + if (generatePrivacyFlags(field->message_type(), messageNames, &parents)) { + printf("Privacy %s { %d, %d, %s, %d, NULL };\n", fieldName.c_str(), field->number(), + field->type(), getMessageTypeName(field->message_type()).c_str(), p.dest()); + } else if (isDefaultField(field)) { + continue; // don't create a new privacy if the value is default. + } else { + printf("Privacy %s { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(), + field->type(), p.dest()); + } + skip[i] = false; } + // generate final PRIVACY_POLICY_LIST + emptyline(); + int policyCount = 0; + if (gSelfRecursionAssignments.empty()) { + printf("Privacy* privacyArray[] = {\n"); + for (int i=0; i<descriptor->field_count(); i++) { + if (skip[i]) continue; + printf(" &%s,\n", getFieldName(descriptor->field(i)).c_str()); + policyCount++; + } + printf("};\n\n"); + printf("const Privacy** PRIVACY_POLICY_LIST = const_cast<const Privacy**>(privacyArray);\n\n"); + printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount); + } else { + for (int i=0; i<descriptor->field_count(); i++) { + if (!skip[i]) policyCount++; + } + + printf("static const Privacy** createList() {\n"); + for (size_t i=0; i<gSelfRecursionAssignments.size(); ++i) { + printf(" %s;\n", gSelfRecursionAssignments[i].c_str()); + } + printf(" Privacy** privacyArray = (Privacy**)malloc(%d * sizeof(Privacy**));\n", policyCount); + policyCount = 0; // reset + for (int i=0; i<descriptor->field_count(); i++) { + if (skip[i]) continue; + printf(" privacyArray[%d] = &%s;\n", policyCount++, getFieldName(descriptor->field(i)).c_str()); + } + printf(" return const_cast<const Privacy**>(privacyArray);\n"); + printf("}\n\n"); + printf("const Privacy** PRIVACY_POLICY_LIST = createList();\n\n"); + printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount); + } return true; } diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp index 96e060d0fc26..dc5c14e32cbd 100644 --- a/tools/streaming_proto/Android.bp +++ b/tools/streaming_proto/Android.bp @@ -21,8 +21,11 @@ cc_defaults { name: "protoc-gen-stream-defaults", srcs: [ "Errors.cpp", + "stream_proto_utils.cpp", "string_utils.cpp", ], + + shared_libs: ["libprotoc"], } cc_library { @@ -52,7 +55,6 @@ cc_binary_host { ], defaults: ["protoc-gen-stream-defaults"], - shared_libs: ["libprotoc"], } cc_binary_host { @@ -62,6 +64,5 @@ cc_binary_host { ], 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 dc96d5c54c16..481698432711 100644 --- a/tools/streaming_proto/cpp/main.cpp +++ b/tools/streaming_proto/cpp/main.cpp @@ -1,108 +1,23 @@ #include "Errors.h" +#include "stream_proto_utils.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" - #include <iomanip> #include <iostream> #include <sstream> using namespace android::stream_proto; -using namespace google::protobuf; -using namespace google::protobuf::compiler; using namespace google::protobuf::io; using namespace std; -/** - * Position of the field type in a (long long) fieldId. - */ -const uint64_t FIELD_TYPE_SHIFT = 32; - -// -// 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; - -// Indent -const string INDENT = " "; - -/** - * See if this is the file for this request, and not one of the imported ones. - */ -static bool -should_generate_for_file(const CodeGeneratorRequest& request, const string& file) -{ - const int N = request.file_to_generate_size(); - for (int i=0; i<N; i++) { - if (request.file_to_generate(i) == file) { - return true; - } - } - return false; -} - static string make_filename(const FileDescriptorProto& file_descriptor) { return file_descriptor.name() + ".h"; } -static string -get_proto_type(const FieldDescriptorProto& field) -{ - switch (field.type()) { - case FieldDescriptorProto::TYPE_DOUBLE: - return "double"; - case FieldDescriptorProto::TYPE_FLOAT: - return "float"; - case FieldDescriptorProto::TYPE_INT64: - return "int64"; - case FieldDescriptorProto::TYPE_UINT64: - return "uint64"; - case FieldDescriptorProto::TYPE_INT32: - return "int32"; - case FieldDescriptorProto::TYPE_FIXED64: - return "fixed64"; - case FieldDescriptorProto::TYPE_FIXED32: - return "fixed32"; - case FieldDescriptorProto::TYPE_BOOL: - return "bool"; - case FieldDescriptorProto::TYPE_STRING: - return "string"; - case FieldDescriptorProto::TYPE_GROUP: - return "group<unsupported!>"; - case FieldDescriptorProto::TYPE_MESSAGE: - return field.type_name(); - case FieldDescriptorProto::TYPE_BYTES: - return "bytes"; - case FieldDescriptorProto::TYPE_UINT32: - return "uint32"; - case FieldDescriptorProto::TYPE_ENUM: - return field.type_name(); - case FieldDescriptorProto::TYPE_SFIXED32: - return "sfixed32"; - case FieldDescriptorProto::TYPE_SFIXED64: - return "sfixed64"; - case FieldDescriptorProto::TYPE_SINT32: - return "sint32"; - case FieldDescriptorProto::TYPE_SINT64: - return "sint64"; - default: - // won't happen - return "void"; - } -} - static void write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent) { @@ -117,27 +32,6 @@ write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& ind text << endl; } -static uint64_t -get_field_id(const FieldDescriptorProto& field) -{ - // Number - uint64_t result = (uint64_t)field.number(); - - // Type - result |= (uint64_t)field.type() << FIELD_TYPE_SHIFT; - - // Count - if (field.options().packed()) { - result |= FIELD_COUNT_PACKED; - } else if (field.label() == FieldDescriptorProto::LABEL_REPEATED) { - result |= FIELD_COUNT_REPEATED; - } else { - result |= FIELD_COUNT_SINGLE; - } - - return result; -} - static void write_field(stringstream& text, const FieldDescriptorProto& field, const string& indent) { diff --git a/tools/streaming_proto/java/main.cpp b/tools/streaming_proto/java/main.cpp index b7d594bb9465..c9c50a561a04 100644 --- a/tools/streaming_proto/java/main.cpp +++ b/tools/streaming_proto/java/main.cpp @@ -1,11 +1,7 @@ #include "Errors.h" - +#include "stream_proto_utils.h" #include "string_utils.h" -#include "google/protobuf/compiler/plugin.pb.h" -#include "google/protobuf/io/zero_copy_stream_impl.h" -#include "google/protobuf/text_format.h" - #include <stdio.h> #include <iomanip> #include <iostream> @@ -13,51 +9,9 @@ #include <map> using namespace android::stream_proto; -using namespace google::protobuf; -using namespace google::protobuf::compiler; using namespace google::protobuf::io; using namespace std; -const int FIELD_TYPE_SHIFT = 32; -const uint64_t FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_INT32 = 3L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_INT64 = 4L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_UINT32 = 5L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_UINT64 = 6L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_SINT32 = 7L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_SINT64 = 8L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_FIXED32 = 9L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_FIXED64 = 10L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_SFIXED32 = 11L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_SFIXED64 = 12L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_BOOL = 13L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_STRING = 14L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_BYTES = 15L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_ENUM = 16L << FIELD_TYPE_SHIFT; -const uint64_t FIELD_TYPE_OBJECT = 17L << FIELD_TYPE_SHIFT; - -const int FIELD_COUNT_SHIFT = 40; -const uint64_t FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT; -const uint64_t FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT; -const uint64_t FIELD_COUNT_PACKED = 5L << FIELD_COUNT_SHIFT; - - -/** - * See if this is the file for this request, and not one of the imported ones. - */ -static bool -should_generate_for_file(const CodeGeneratorRequest& request, const string& file) -{ - const int N = request.file_to_generate_size(); - for (int i=0; i<N; i++) { - if (request.file_to_generate(i) == file) { - return true; - } - } - return false; -} - /** * If the descriptor gives us a class name, use that. Otherwise make one up from * the filename of the .proto file. @@ -112,7 +66,7 @@ make_file_name(const FileDescriptorProto& file_descriptor, const string& class_n static string indent_more(const string& indent) { - return indent + " "; + return indent + INDENT; } /** @@ -133,130 +87,6 @@ write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& ind } /** - * Get the string name for a field. - */ -static string -get_proto_type(const FieldDescriptorProto& field) -{ - switch (field.type()) { - case FieldDescriptorProto::TYPE_DOUBLE: - return "double"; - case FieldDescriptorProto::TYPE_FLOAT: - return "float"; - case FieldDescriptorProto::TYPE_INT64: - return "int64"; - case FieldDescriptorProto::TYPE_UINT64: - return "uint64"; - case FieldDescriptorProto::TYPE_INT32: - return "int32"; - case FieldDescriptorProto::TYPE_FIXED64: - return "fixed64"; - case FieldDescriptorProto::TYPE_FIXED32: - return "fixed32"; - case FieldDescriptorProto::TYPE_BOOL: - return "bool"; - case FieldDescriptorProto::TYPE_STRING: - return "string"; - case FieldDescriptorProto::TYPE_GROUP: - return "group<unsupported!>"; - case FieldDescriptorProto::TYPE_MESSAGE: - return field.type_name(); - case FieldDescriptorProto::TYPE_BYTES: - return "bytes"; - case FieldDescriptorProto::TYPE_UINT32: - return "uint32"; - case FieldDescriptorProto::TYPE_ENUM: - return field.type_name(); - case FieldDescriptorProto::TYPE_SFIXED32: - return "sfixed32"; - case FieldDescriptorProto::TYPE_SFIXED64: - return "sfixed64"; - case FieldDescriptorProto::TYPE_SINT32: - return "sint32"; - case FieldDescriptorProto::TYPE_SINT64: - return "sint64"; - default: - // won't happen - return "void"; - } -} - -static uint64_t -get_field_id(const FieldDescriptorProto& field) -{ - // Number - uint64_t result = (uint32_t)field.number(); - - // Type - switch (field.type()) { - case FieldDescriptorProto::TYPE_DOUBLE: - result |= FIELD_TYPE_DOUBLE; - break; - case FieldDescriptorProto::TYPE_FLOAT: - result |= FIELD_TYPE_FLOAT; - break; - case FieldDescriptorProto::TYPE_INT64: - result |= FIELD_TYPE_INT64; - break; - case FieldDescriptorProto::TYPE_UINT64: - result |= FIELD_TYPE_UINT64; - break; - case FieldDescriptorProto::TYPE_INT32: - result |= FIELD_TYPE_INT32; - break; - case FieldDescriptorProto::TYPE_FIXED64: - result |= FIELD_TYPE_FIXED64; - break; - case FieldDescriptorProto::TYPE_FIXED32: - result |= FIELD_TYPE_FIXED32; - break; - case FieldDescriptorProto::TYPE_BOOL: - result |= FIELD_TYPE_BOOL; - break; - case FieldDescriptorProto::TYPE_STRING: - result |= FIELD_TYPE_STRING; - break; - case FieldDescriptorProto::TYPE_MESSAGE: - result |= FIELD_TYPE_OBJECT; - break; - case FieldDescriptorProto::TYPE_BYTES: - result |= FIELD_TYPE_BYTES; - break; - case FieldDescriptorProto::TYPE_UINT32: - result |= FIELD_TYPE_UINT32; - break; - case FieldDescriptorProto::TYPE_ENUM: - result |= FIELD_TYPE_ENUM; - break; - case FieldDescriptorProto::TYPE_SFIXED32: - result |= FIELD_TYPE_SFIXED32; - break; - case FieldDescriptorProto::TYPE_SFIXED64: - result |= FIELD_TYPE_SFIXED64; - break; - case FieldDescriptorProto::TYPE_SINT32: - result |= FIELD_TYPE_SINT32; - break; - case FieldDescriptorProto::TYPE_SINT64: - result |= FIELD_TYPE_SINT64; - break; - default: - ; - } - - // Count - if (field.options().packed()) { - result |= FIELD_COUNT_PACKED; - } else if (field.label() == FieldDescriptorProto::LABEL_REPEATED) { - result |= FIELD_COUNT_REPEATED; - } else { - result |= FIELD_COUNT_SINGLE; - } - - return result; -} - -/** * Write a field. */ static void diff --git a/tools/streaming_proto/stream_proto_utils.cpp b/tools/streaming_proto/stream_proto_utils.cpp new file mode 100644 index 000000000000..e8f86bc0be83 --- /dev/null +++ b/tools/streaming_proto/stream_proto_utils.cpp @@ -0,0 +1,102 @@ +#include "stream_proto_utils.h" + +namespace android { +namespace stream_proto { + +/** + * Position of the field type in a (long long) fieldId. + */ +const uint64_t FIELD_TYPE_SHIFT = 32; + +// +// 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 = 5ULL << FIELD_COUNT_SHIFT; + +uint64_t +get_field_id(const FieldDescriptorProto& field) +{ + // Number + uint64_t result = (uint32_t)field.number(); + + // Type + result |= (uint64_t)field.type() << FIELD_TYPE_SHIFT; + + // Count + if (field.options().packed()) { + result |= FIELD_COUNT_PACKED; + } else if (field.label() == FieldDescriptorProto::LABEL_REPEATED) { + result |= FIELD_COUNT_REPEATED; + } else { + result |= FIELD_COUNT_SINGLE; + } + + return result; +} + +string +get_proto_type(const FieldDescriptorProto& field) +{ + switch (field.type()) { + case FieldDescriptorProto::TYPE_DOUBLE: + return "double"; + case FieldDescriptorProto::TYPE_FLOAT: + return "float"; + case FieldDescriptorProto::TYPE_INT64: + return "int64"; + case FieldDescriptorProto::TYPE_UINT64: + return "uint64"; + case FieldDescriptorProto::TYPE_INT32: + return "int32"; + case FieldDescriptorProto::TYPE_FIXED64: + return "fixed64"; + case FieldDescriptorProto::TYPE_FIXED32: + return "fixed32"; + case FieldDescriptorProto::TYPE_BOOL: + return "bool"; + case FieldDescriptorProto::TYPE_STRING: + return "string"; + case FieldDescriptorProto::TYPE_GROUP: + return "group<unsupported!>"; + case FieldDescriptorProto::TYPE_MESSAGE: + return field.type_name(); + case FieldDescriptorProto::TYPE_BYTES: + return "bytes"; + case FieldDescriptorProto::TYPE_UINT32: + return "uint32"; + case FieldDescriptorProto::TYPE_ENUM: + return field.type_name(); + case FieldDescriptorProto::TYPE_SFIXED32: + return "sfixed32"; + case FieldDescriptorProto::TYPE_SFIXED64: + return "sfixed64"; + case FieldDescriptorProto::TYPE_SINT32: + return "sint32"; + case FieldDescriptorProto::TYPE_SINT64: + return "sint64"; + default: + // won't happen + return "void"; + } +} + +bool +should_generate_for_file(const CodeGeneratorRequest& request, const string& file) +{ + const int N = request.file_to_generate_size(); + for (int i=0; i<N; i++) { + if (request.file_to_generate(i) == file) { + return true; + } + } + return false; +} + +} // stream_proto +} // android diff --git a/tools/streaming_proto/stream_proto_utils.h b/tools/streaming_proto/stream_proto_utils.h new file mode 100644 index 000000000000..5297eccba849 --- /dev/null +++ b/tools/streaming_proto/stream_proto_utils.h @@ -0,0 +1,29 @@ +#include <stdint.h> + +#include "google/protobuf/compiler/plugin.pb.h" +#include "google/protobuf/io/zero_copy_stream_impl.h" + +namespace android { +namespace stream_proto { + +using namespace google::protobuf; +using namespace google::protobuf::compiler; +using namespace std; + +/** + * Get encoded field id from a field. + */ +uint64_t get_field_id(const FieldDescriptorProto& field); + +/** + * Get the string name for a field. + */ +string get_proto_type(const FieldDescriptorProto& field); + +/** + * See if this is the file for this request, and not one of the imported ones. + */ +bool should_generate_for_file(const CodeGeneratorRequest& request, const string& file); + +} // stream_proto +} // android diff --git a/tools/streaming_proto/string_utils.h b/tools/streaming_proto/string_utils.h index 03284d16e1be..d6f195f67188 100644 --- a/tools/streaming_proto/string_utils.h +++ b/tools/streaming_proto/string_utils.h @@ -6,6 +6,9 @@ namespace stream_proto { using namespace std; +// Indent +const string INDENT = " "; + /** * Capitalizes the string, removes underscores and makes the next letter * capitalized, and makes the letter following numbers capitalized. |