diff options
334 files changed, 10343 insertions, 4119 deletions
diff --git a/Android.bp b/Android.bp index c89cc400171d..5c1ccb7cc639 100644 --- a/Android.bp +++ b/Android.bp @@ -47,6 +47,7 @@ cc_library { srcs: [ "core/proto/**/*.proto", "libs/incident/**/*.proto", + "tools/streaming_proto/stream.proto", ], }, android: { @@ -61,7 +62,7 @@ cc_library { "core/proto/android/os/pagetypeinfo.proto", "core/proto/android/os/procrank.proto", "core/proto/android/service/graphicsstats.proto", - "libs/incident/proto/android/privacy.proto", + "tools/streaming_proto/stream.proto", ], shared: { enabled: false, @@ -70,6 +71,27 @@ cc_library { }, } +gensrcs { + name: "gen-platform-proto-constants", + depfile: true, + + tools: [ + "aprotoc", + "protoc-gen-cppstream", + ], + + srcs: [ + "core/proto/android/os/kernelwake.proto", + "core/proto/android/os/pagetypeinfo.proto", + "core/proto/android/os/procrank.proto", + ], + + // Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool + cmd: "PATH=$$PATH:$$(dirname $(location protoc-gen-cppstream)) $(location aprotoc) --plugin=protoc-gen-cpp-stream=$(location protoc-gen-cppstream) --dependency_out=$(depfile) --cppstream_out=$(genDir)/ -Iexternal/protobuf/src -I . $(in)", + + output_extension = "proto.h", +} + subdirs = [ "cmds/*", "core/*", diff --git a/Android.mk b/Android.mk index 62f750c41a8b..1ed8a2506600 100644 --- a/Android.mk +++ b/Android.mk @@ -989,7 +989,8 @@ framework_docs_LOCAL_DROIDDOC_SOURCE_PATH := \ framework_docs_LOCAL_INTERMEDIATE_SOURCES := \ $(framework_res_source_path)/android/R.java \ $(framework_res_source_path)/android/Manifest.java \ - $(framework_res_source_path)/com/android/internal/R.java + $(framework_res_source_path)/com/android/internal/R.java \ + $(patsubst $(TARGET_OUT_COMMON_INTERMEDIATES)/%,%,$(libcore_to_document_generated)) framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \ core-oj \ @@ -1060,7 +1061,7 @@ framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \ framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \ frameworks/base/docs/knowntags.txt \ - libcore/Docs.mk + $(libcore_to_document_generated) samples_dir := development/samples/browseable @@ -1552,6 +1553,7 @@ LOCAL_PROTOC_FLAGS := \ -Iexternal/protobuf/src LOCAL_SOURCE_FILES_ALL_GENERATED := true LOCAL_SRC_FILES := \ + tools/streaming_proto/stream.proto \ $(call all-proto-files-under, core/proto) \ $(call all-proto-files-under, libs/incident/proto) include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/api/current.txt b/api/current.txt index 509bb88424a4..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); @@ -22816,6 +22798,10 @@ package android.media { field public static final java.lang.String KEY_DURATION = "durationUs"; field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level"; field public static final java.lang.String KEY_FRAME_RATE = "frame-rate"; + field public static final java.lang.String KEY_GRID_COLS = "grid-cols"; + field public static final java.lang.String KEY_GRID_HEIGHT = "grid-height"; + field public static final java.lang.String KEY_GRID_ROWS = "grid-rows"; + field public static final java.lang.String KEY_GRID_WIDTH = "grid-width"; field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info"; field public static final java.lang.String KEY_HEIGHT = "height"; field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period"; @@ -22859,6 +22845,7 @@ package android.media { field public static final java.lang.String MIMETYPE_AUDIO_RAW = "audio/raw"; field public static final java.lang.String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled"; field public static final java.lang.String MIMETYPE_AUDIO_VORBIS = "audio/vorbis"; + field public static final java.lang.String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic"; field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608"; field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt"; field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc"; @@ -22951,9 +22938,13 @@ package android.media { ctor public MediaMetadataRetriever(); method public java.lang.String extractMetadata(int); method public byte[] getEmbeddedPicture(); + method public android.graphics.Bitmap getFrameAtIndex(int); method public android.graphics.Bitmap getFrameAtTime(long, int); method public android.graphics.Bitmap getFrameAtTime(long); method public android.graphics.Bitmap getFrameAtTime(); + method public android.graphics.Bitmap[] getFramesAtIndex(int, int); + method public android.graphics.Bitmap getImageAtIndex(int); + method public android.graphics.Bitmap getPrimaryImage(); method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int); method public void release(); method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException; @@ -22976,11 +22967,18 @@ package android.media { field public static final int METADATA_KEY_DURATION = 9; // 0x9 field public static final int METADATA_KEY_GENRE = 6; // 0x6 field public static final int METADATA_KEY_HAS_AUDIO = 16; // 0x10 + field public static final int METADATA_KEY_HAS_IMAGE = 26; // 0x1a field public static final int METADATA_KEY_HAS_VIDEO = 17; // 0x11 + field public static final int METADATA_KEY_IMAGE_COUNT = 27; // 0x1b + field public static final int METADATA_KEY_IMAGE_HEIGHT = 30; // 0x1e + field public static final int METADATA_KEY_IMAGE_PRIMARY = 28; // 0x1c + field public static final int METADATA_KEY_IMAGE_ROTATION = 31; // 0x1f + field public static final int METADATA_KEY_IMAGE_WIDTH = 29; // 0x1d field public static final int METADATA_KEY_LOCATION = 23; // 0x17 field public static final int METADATA_KEY_MIMETYPE = 12; // 0xc field public static final int METADATA_KEY_NUM_TRACKS = 10; // 0xa field public static final int METADATA_KEY_TITLE = 7; // 0x7 + field public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32; // 0x20 field public static final int METADATA_KEY_VIDEO_HEIGHT = 19; // 0x13 field public static final int METADATA_KEY_VIDEO_ROTATION = 24; // 0x18 field public static final int METADATA_KEY_VIDEO_WIDTH = 18; // 0x12 @@ -25818,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(); } @@ -39907,6 +39904,7 @@ package android.telephony { field public static final java.lang.String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int"; field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int"; field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array"; + field public static final java.lang.String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int"; field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array"; field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool"; field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string"; @@ -40662,6 +40660,10 @@ package android.telephony { field public static final int CALL_STATE_IDLE = 0; // 0x0 field public static final int CALL_STATE_OFFHOOK = 2; // 0x2 field public static final int CALL_STATE_RINGING = 1; // 0x1 + field public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; // 0x1 + field public static final int CDMA_ROAMING_MODE_ANY = 2; // 0x2 + field public static final int CDMA_ROAMING_MODE_HOME = 0; // 0x0 + field public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; // 0xffffffff field public static final int DATA_ACTIVITY_DORMANT = 4; // 0x4 field public static final int DATA_ACTIVITY_IN = 1; // 0x1 field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3 diff --git a/api/system-current.txt b/api/system-current.txt index c0c2311e5e3e..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); @@ -24706,6 +24689,10 @@ package android.media { field public static final java.lang.String KEY_DURATION = "durationUs"; field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level"; field public static final java.lang.String KEY_FRAME_RATE = "frame-rate"; + field public static final java.lang.String KEY_GRID_COLS = "grid-cols"; + field public static final java.lang.String KEY_GRID_HEIGHT = "grid-height"; + field public static final java.lang.String KEY_GRID_ROWS = "grid-rows"; + field public static final java.lang.String KEY_GRID_WIDTH = "grid-width"; field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info"; field public static final java.lang.String KEY_HEIGHT = "height"; field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period"; @@ -24749,6 +24736,7 @@ package android.media { field public static final java.lang.String MIMETYPE_AUDIO_RAW = "audio/raw"; field public static final java.lang.String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled"; field public static final java.lang.String MIMETYPE_AUDIO_VORBIS = "audio/vorbis"; + field public static final java.lang.String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic"; field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608"; field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt"; field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc"; @@ -24841,9 +24829,13 @@ package android.media { ctor public MediaMetadataRetriever(); method public java.lang.String extractMetadata(int); method public byte[] getEmbeddedPicture(); + method public android.graphics.Bitmap getFrameAtIndex(int); method public android.graphics.Bitmap getFrameAtTime(long, int); method public android.graphics.Bitmap getFrameAtTime(long); method public android.graphics.Bitmap getFrameAtTime(); + method public android.graphics.Bitmap[] getFramesAtIndex(int, int); + method public android.graphics.Bitmap getImageAtIndex(int); + method public android.graphics.Bitmap getPrimaryImage(); method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int); method public void release(); method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException; @@ -24866,11 +24858,18 @@ package android.media { field public static final int METADATA_KEY_DURATION = 9; // 0x9 field public static final int METADATA_KEY_GENRE = 6; // 0x6 field public static final int METADATA_KEY_HAS_AUDIO = 16; // 0x10 + field public static final int METADATA_KEY_HAS_IMAGE = 26; // 0x1a field public static final int METADATA_KEY_HAS_VIDEO = 17; // 0x11 + field public static final int METADATA_KEY_IMAGE_COUNT = 27; // 0x1b + field public static final int METADATA_KEY_IMAGE_HEIGHT = 30; // 0x1e + field public static final int METADATA_KEY_IMAGE_PRIMARY = 28; // 0x1c + field public static final int METADATA_KEY_IMAGE_ROTATION = 31; // 0x1f + field public static final int METADATA_KEY_IMAGE_WIDTH = 29; // 0x1d field public static final int METADATA_KEY_LOCATION = 23; // 0x17 field public static final int METADATA_KEY_MIMETYPE = 12; // 0xc field public static final int METADATA_KEY_NUM_TRACKS = 10; // 0xa field public static final int METADATA_KEY_TITLE = 7; // 0x7 + field public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32; // 0x20 field public static final int METADATA_KEY_VIDEO_HEIGHT = 19; // 0x13 field public static final int METADATA_KEY_VIDEO_ROTATION = 24; // 0x18 field public static final int METADATA_KEY_VIDEO_WIDTH = 18; // 0x12 @@ -28060,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(); } @@ -43422,6 +43420,7 @@ package android.telephony { field public static final java.lang.String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int"; field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int"; field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array"; + field public static final java.lang.String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int"; field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array"; field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool"; field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string"; @@ -44284,6 +44283,10 @@ package android.telephony { field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1 field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0 field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff + field public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; // 0x1 + field public static final int CDMA_ROAMING_MODE_ANY = 2; // 0x2 + field public static final int CDMA_ROAMING_MODE_HOME = 0; // 0x0 + field public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; // 0xffffffff field public static final int DATA_ACTIVITY_DORMANT = 4; // 0x4 field public static final int DATA_ACTIVITY_IN = 1; // 0x1 field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3 diff --git a/api/test-current.txt b/api/test-current.txt index 909614460b87..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); @@ -23016,6 +23001,10 @@ package android.media { field public static final java.lang.String KEY_DURATION = "durationUs"; field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level"; field public static final java.lang.String KEY_FRAME_RATE = "frame-rate"; + field public static final java.lang.String KEY_GRID_COLS = "grid-cols"; + field public static final java.lang.String KEY_GRID_HEIGHT = "grid-height"; + field public static final java.lang.String KEY_GRID_ROWS = "grid-rows"; + field public static final java.lang.String KEY_GRID_WIDTH = "grid-width"; field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info"; field public static final java.lang.String KEY_HEIGHT = "height"; field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period"; @@ -23059,6 +23048,7 @@ package android.media { field public static final java.lang.String MIMETYPE_AUDIO_RAW = "audio/raw"; field public static final java.lang.String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled"; field public static final java.lang.String MIMETYPE_AUDIO_VORBIS = "audio/vorbis"; + field public static final java.lang.String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic"; field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608"; field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt"; field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc"; @@ -23151,9 +23141,13 @@ package android.media { ctor public MediaMetadataRetriever(); method public java.lang.String extractMetadata(int); method public byte[] getEmbeddedPicture(); + method public android.graphics.Bitmap getFrameAtIndex(int); method public android.graphics.Bitmap getFrameAtTime(long, int); method public android.graphics.Bitmap getFrameAtTime(long); method public android.graphics.Bitmap getFrameAtTime(); + method public android.graphics.Bitmap[] getFramesAtIndex(int, int); + method public android.graphics.Bitmap getImageAtIndex(int); + method public android.graphics.Bitmap getPrimaryImage(); method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int); method public void release(); method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException; @@ -23176,11 +23170,18 @@ package android.media { field public static final int METADATA_KEY_DURATION = 9; // 0x9 field public static final int METADATA_KEY_GENRE = 6; // 0x6 field public static final int METADATA_KEY_HAS_AUDIO = 16; // 0x10 + field public static final int METADATA_KEY_HAS_IMAGE = 26; // 0x1a field public static final int METADATA_KEY_HAS_VIDEO = 17; // 0x11 + field public static final int METADATA_KEY_IMAGE_COUNT = 27; // 0x1b + field public static final int METADATA_KEY_IMAGE_HEIGHT = 30; // 0x1e + field public static final int METADATA_KEY_IMAGE_PRIMARY = 28; // 0x1c + field public static final int METADATA_KEY_IMAGE_ROTATION = 31; // 0x1f + field public static final int METADATA_KEY_IMAGE_WIDTH = 29; // 0x1d field public static final int METADATA_KEY_LOCATION = 23; // 0x17 field public static final int METADATA_KEY_MIMETYPE = 12; // 0xc field public static final int METADATA_KEY_NUM_TRACKS = 10; // 0xa field public static final int METADATA_KEY_TITLE = 7; // 0x7 + field public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32; // 0x20 field public static final int METADATA_KEY_VIDEO_HEIGHT = 19; // 0x13 field public static final int METADATA_KEY_VIDEO_ROTATION = 24; // 0x18 field public static final int METADATA_KEY_VIDEO_WIDTH = 18; // 0x12 @@ -26018,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(); } @@ -40301,6 +40301,7 @@ package android.telephony { field public static final java.lang.String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int"; field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int"; field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array"; + field public static final java.lang.String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int"; field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array"; field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool"; field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string"; @@ -41058,6 +41059,10 @@ package android.telephony { field public static final int CALL_STATE_IDLE = 0; // 0x0 field public static final int CALL_STATE_OFFHOOK = 2; // 0x2 field public static final int CALL_STATE_RINGING = 1; // 0x1 + field public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; // 0x1 + field public static final int CDMA_ROAMING_MODE_ANY = 2; // 0x2 + field public static final int CDMA_ROAMING_MODE_HOME = 0; // 0x0 + field public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; // 0xffffffff field public static final int DATA_ACTIVITY_DORMANT = 4; // 0x4 field public static final int DATA_ACTIVITY_IN = 1; // 0x1 field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3 @@ -41321,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); @@ -41429,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; @@ -44959,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 ab075ee0e9f2..813335a688ab 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -98,7 +98,8 @@ public class Am extends BaseCommand { static final class MyShellCallback extends ShellCallback { boolean mActive = true; - @Override public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) { + @Override public ParcelFileDescriptor onOpenFile(String path, String seLinuxContext, + String mode) { if (!mActive) { System.err.println("Open attempt after active for: " + path); return null; @@ -159,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/incident_helper/Android.bp b/cmds/incident_helper/Android.bp index 053208313b00..2ef037143f07 100644 --- a/cmds/incident_helper/Android.bp +++ b/cmds/incident_helper/Android.bp @@ -8,44 +8,53 @@ cc_defaults { "-O0" ], + local_include_dirs: [ + "src/", + "src/parsers/", + ], + srcs: [ - "IncidentHelper.cpp", - "ih_util.cpp", + "src/parsers/*.cpp", + "src/TextParserBase.cpp", + "src/ih_util.cpp", ], + generated_headers: ["gen-platform-proto-constants"], + shared_libs: [ "libbase", "liblog", - "libprotobuf-cpp-full", + "libprotoutil", "libutils", ], - - static_libs: [ - "libplatformprotos", - ], } cc_binary { name: "incident_helper", defaults: ["incident_helper_defaults"], - srcs: ["main.cpp"], + srcs: ["src/main.cpp"], } cc_test { name: "incident_helper_test", defaults: ["incident_helper_defaults"], + local_include_dirs: ["src/"], srcs: [ - "tests/IncidentHelper_test.cpp", - "tests/ih_util_test.cpp", + "tests/*.cpp", ], data: [ "testdata/*", ], + shared_libs: [ + "libprotobuf-cpp-full", + ], + static_libs: [ "libgmock", + "libplatformprotos" ], -}
\ No newline at end of file +} diff --git a/cmds/incident_helper/IncidentHelper.cpp b/cmds/incident_helper/IncidentHelper.cpp deleted file mode 100644 index 7b06d42cbb55..000000000000 --- a/cmds/incident_helper/IncidentHelper.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "incident_helper" - -#include "IncidentHelper.h" -#include "ih_util.h" - -#include "frameworks/base/core/proto/android/os/kernelwake.pb.h" -#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h" -#include "frameworks/base/core/proto/android/os/procrank.pb.h" - -#include <android-base/file.h> -#include <unistd.h> -#include <string> -#include <vector> - -using namespace android::base; -using namespace android::os; -using namespace google::protobuf; -using namespace std; - - -static const string TAB_DELIMITER = "\t"; -static const string COMMA_DELIMITER = ","; - -static inline int toInt(const string& s) { - return atoi(s.c_str()); -} - -static inline long toLong(const string& s) { - return atol(s.c_str()); -} - -/** - * Sets the given protobuf message when the field name matches one of the - * fields. It is useful to set values to proto from table-like plain texts. - */ -static bool -SetTableField(::google::protobuf::Message* message, string field_name, string field_value) { - const Descriptor* descriptor = message->GetDescriptor(); - const Reflection* reflection = message->GetReflection(); - - const FieldDescriptor* field = descriptor->FindFieldByName(field_name); - switch (field->type()) { - case FieldDescriptor::TYPE_STRING: - reflection->SetString(message, field, field_value); - return true; - case FieldDescriptor::TYPE_INT64: - reflection->SetInt64(message, field, toLong(field_value)); - return true; - case FieldDescriptor::TYPE_UINT64: - reflection->SetUInt64(message, field, toLong(field_value)); - return true; - case FieldDescriptor::TYPE_INT32: - reflection->SetInt32(message, field, toInt(field_value)); - return true; - case FieldDescriptor::TYPE_UINT32: - reflection->SetUInt32(message, field, toInt(field_value)); - return true; - default: - // Add new scalar types - return false; - } -} - -// ================================================================================ -status_t NoopParser::Parse(const int in, const int out) const -{ - string content; - if (!ReadFdToString(in, &content)) { - fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string()); - return -1; - } - if (!WriteStringToFd(content, out)) { - fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string()); - return -1; - } - return NO_ERROR; -} - -// ================================================================================ -status_t ReverseParser::Parse(const int in, const int out) const -{ - string content; - if (!ReadFdToString(in, &content)) { - fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string()); - return -1; - } - // reverse the content - reverse(content.begin(), content.end()); - if (!WriteStringToFd(content, out)) { - fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string()); - return -1; - } - return NO_ERROR; -} - -// ================================================================================ -status_t KernelWakesParser::Parse(const int in, const int out) const { - Reader reader(in); - string line; - header_t header; // the header of /d/wakeup_sources - record_t record; // retain each record - int nline = 0; - - KernelWakeSources proto; - - // parse line by line - while (reader.readLine(&line)) { - if (line.empty()) continue; - // parse head line - if (nline++ == 0) { - header = parseHeader(line, TAB_DELIMITER); - continue; - } - - // parse for each record, the line delimiter is \t only! - record = parseRecord(line, TAB_DELIMITER); - - if (record.size() != header.size()) { - // TODO: log this to incident report! - fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str()); - continue; - } - - WakeupSourceProto* source = proto.add_wakeup_sources(); - for (int i=0; i<(int)record.size(); i++) { - if (!SetTableField(source, header[i], record[i])) { - fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", - this->name.string(), nline, header[i].c_str(), record[i].c_str()); - } - } - } - - if (!reader.ok(&line)) { - fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); - return -1; - } - - if (!proto.SerializeToFileDescriptor(out)) { - fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); - return -1; - } - fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize()); - return NO_ERROR; -} - -// ================================================================================ -status_t ProcrankParser::Parse(const int in, const int out) const { - Reader reader(in); - string line; - header_t header; // the header of /d/wakeup_sources - record_t record; // retain each record - int nline = 0; - - Procrank proto; - - // parse line by line - while (reader.readLine(&line)) { - if (line.empty()) continue; - - // parse head line - if (nline++ == 0) { - header = parseHeader(line); - continue; - } - - if (hasPrefix(&line, "ZRAM:")) { - proto.mutable_summary()->mutable_zram()->set_raw_text(line); - continue; - } - if (hasPrefix(&line, "RAM:")) { - proto.mutable_summary()->mutable_ram()->set_raw_text(line); - continue; - } - - record = parseRecord(line); - if (record.size() != header.size()) { - if (record[record.size() - 1] == "TOTAL") { // TOTAL record - ProcessProto* total = proto.mutable_summary()->mutable_total(); - for (int i=1; i<=(int)record.size(); i++) { - SetTableField(total, header[header.size() - i], record[record.size() - i]); - } - } else { - fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, - line.c_str()); - } - continue; - } - - ProcessProto* process = proto.add_processes(); - for (int i=0; i<(int)record.size(); i++) { - if (!SetTableField(process, header[i], record[i])) { - fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", - this->name.string(), nline, header[i].c_str(), record[i].c_str()); - } - } - } - - if (!reader.ok(&line)) { - fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); - return -1; - } - - if (!proto.SerializeToFileDescriptor(out)) { - fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); - return -1; - } - fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize()); - return NO_ERROR; -} - -// ================================================================================ -status_t PageTypeInfoParser::Parse(const int in, const int out) const { - Reader reader(in); - string line; - bool migrateTypeSession = false; - int pageBlockOrder; - header_t blockHeader; - - PageTypeInfo pageTypeInfo; - - while (reader.readLine(&line)) { - if (line.empty()) { - migrateTypeSession = false; - blockHeader.clear(); - continue; - } - - if (hasPrefix(&line, "Page block order:")) { - pageBlockOrder = toInt(line); - pageTypeInfo.set_page_block_order(pageBlockOrder); - continue; - } - if (hasPrefix(&line, "Pages per block:")) { - pageTypeInfo.set_pages_per_block(toInt(line)); - continue; - } - if (hasPrefix(&line, "Free pages count per migrate type at order")) { - migrateTypeSession = true; - continue; - } - if (hasPrefix(&line, "Number of blocks type")) { - blockHeader = parseHeader(line); - continue; - } - - record_t record = parseRecord(line, COMMA_DELIMITER); - if (migrateTypeSession && record.size() == 3) { - MigrateTypeProto* migrateType = pageTypeInfo.add_migrate_types(); - // expect part 0 starts with "Node" - if (hasPrefix(&record[0], "Node")) { - migrateType->set_node(toInt(record[0])); - } else goto ERROR; - // expect part 1 starts with "zone" - if (hasPrefix(&record[1], "zone")) { - migrateType->set_zone(record[1]); - } else goto ERROR; - // expect part 2 starts with "type" - if (hasPrefix(&record[2], "type")) { - // expect the rest of part 2 has number of (pageBlockOrder + 2) parts - // An example looks like: - // header line: type 0 1 2 3 4 5 6 7 8 9 10 - // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0 - // The pageBlockOrder = 10 and it's zero-indexed. so total parts - // are 10 + 1(zero-indexed) + 1(the type part) = 12. - record_t pageCounts = parseRecord(record[2]); - int pageCountsSize = pageBlockOrder + 2; - if ((int)pageCounts.size() != pageCountsSize) goto ERROR; - - migrateType->set_type(pageCounts[0]); - for (auto i=1; i<pageCountsSize; i++) { - migrateType->add_free_pages_count(toInt(pageCounts[i])); - } - } else goto ERROR; - continue; - } - - if (!blockHeader.empty() && record.size() == 2) { - BlockProto* block = pageTypeInfo.add_blocks(); - - if (hasPrefix(&record[0], "Node")) { - block->set_node(toInt(record[0])); - } else goto ERROR; - - if (hasPrefix(&record[1], "zone")) { - record_t blockCounts = parseRecord(record[1]); - block->set_zone(blockCounts[0]); - for (size_t i=0; i<blockHeader.size(); i++) { - if (!SetTableField(block, blockHeader[i], blockCounts[i+1])) goto ERROR; - } - } else goto ERROR; - - continue; - } - -ERROR: // print out error for this single line and continue parsing - fprintf(stderr, "[%s]Bad line: %s\n", this->name.string(), line.c_str()); - } - - if (!reader.ok(&line)) { - fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); - return -1; - } - - if (!pageTypeInfo.SerializeToFileDescriptor(out)) { - fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); - return -1; - } - - fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), pageTypeInfo.ByteSize()); - return NO_ERROR; -} diff --git a/cmds/incident_helper/src/TextParserBase.cpp b/cmds/incident_helper/src/TextParserBase.cpp new file mode 100644 index 000000000000..a8f9968ee8f6 --- /dev/null +++ b/cmds/incident_helper/src/TextParserBase.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "incident_helper" + +#include "TextParserBase.h" + +#include <android-base/file.h> + +using namespace android::base; +using namespace std; + +// ================================================================================ +status_t NoopParser::Parse(const int in, const int out) const +{ + string content; + if (!ReadFdToString(in, &content)) { + fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string()); + return -1; + } + if (!WriteStringToFd(content, out)) { + fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string()); + return -1; + } + return NO_ERROR; +} + +// ================================================================================ +status_t ReverseParser::Parse(const int in, const int out) const +{ + string content; + if (!ReadFdToString(in, &content)) { + fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string()); + return -1; + } + // reverse the content + reverse(content.begin(), content.end()); + if (!WriteStringToFd(content, out)) { + fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string()); + return -1; + } + return NO_ERROR; +}
\ No newline at end of file diff --git a/cmds/incident_helper/IncidentHelper.h b/cmds/incident_helper/src/TextParserBase.h index d24d7173aa26..c41612de4eb3 100644 --- a/cmds/incident_helper/IncidentHelper.h +++ b/cmds/incident_helper/src/TextParserBase.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef INCIDENT_HELPER_H -#define INCIDENT_HELPER_H +#ifndef TEXT_PARSER_BASE_H +#define TEXT_PARSER_BASE_H #include <utils/Errors.h> #include <utils/String8.h> @@ -68,37 +68,4 @@ public: virtual status_t Parse(const int in, const int out) const; }; -/** - * Kernel wakeup sources parser, parses text to protobuf in /d/wakeup_sources - */ -class KernelWakesParser : public TextParserBase { -public: - KernelWakesParser() : TextParserBase(String8("KernelWakeSources")) {}; - ~KernelWakesParser() {}; - - virtual status_t Parse(const int in, const int out) const; -}; - -/** - * PageTypeInfo parser, parses text to protobuf in /proc/pageinfotype - */ -class PageTypeInfoParser : public TextParserBase { -public: - PageTypeInfoParser() : TextParserBase(String8("PageTypeInfo")) {}; - ~PageTypeInfoParser() {}; - - virtual status_t Parse(const int in, const int out) const; -}; - -/** - * Procrank parser, parses text produced by command procrank - */ -class ProcrankParser : public TextParserBase { -public: - ProcrankParser() : TextParserBase(String8("ProcrankParser")) {}; - ~ProcrankParser() {}; - - virtual status_t Parse(const int in, const int out) const; -}; - -#endif // INCIDENT_HELPER_H +#endif // TEXT_PARSER_BASE_H
\ No newline at end of file diff --git a/cmds/incident_helper/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp index 2ab4b54e193f..c7d1ca231a03 100644 --- a/cmds/incident_helper/ih_util.cpp +++ b/cmds/incident_helper/src/ih_util.cpp @@ -87,6 +87,15 @@ bool hasPrefix(std::string* line, const char* key) { return true; } +int toInt(const std::string& s) { + return atoi(s.c_str()); +} + +long long toLongLong(const std::string& s) { + return atoll(s.c_str()); +} + +// ============================================================================== Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {}; Reader::Reader(const int fd, const size_t capacity) @@ -151,3 +160,57 @@ bool Reader::ok(std::string* error) { error->assign(mStatus); return mStatus.empty(); } + +// ============================================================================== +Table::Table(const char* names[], const uint64_t ids[], const int count) + :mFieldNames(names), + mFieldIds(ids), + mFieldCount(count) +{ +} + +Table::~Table() +{ +} + +bool +Table::insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value) +{ + uint64_t found = 0; + for (int i=0; i<mFieldCount; i++) { + if (strcmp(name.c_str(), mFieldNames[i]) == 0) { + found = mFieldIds[i]; + break; + } + } + + switch (found & FIELD_TYPE_MASK) { + case FIELD_TYPE_DOUBLE: + case FIELD_TYPE_FLOAT: + // TODO: support parse string to float/double + return false; + case FIELD_TYPE_STRING: + case FIELD_TYPE_BYTES: + proto.write(found, value); + break; + case FIELD_TYPE_INT64: + case FIELD_TYPE_SINT64: + case FIELD_TYPE_UINT64: + case FIELD_TYPE_FIXED64: + case FIELD_TYPE_SFIXED64: + proto.write(found, toLongLong(value)); + break; + case FIELD_TYPE_BOOL: + case FIELD_TYPE_ENUM: + case FIELD_TYPE_INT32: + case FIELD_TYPE_SINT32: + case FIELD_TYPE_UINT32: + case FIELD_TYPE_FIXED32: + case FIELD_TYPE_SFIXED32: + proto.write(found, toInt(value)); + break; + default: + return false; + } + return true; +} diff --git a/cmds/incident_helper/ih_util.h b/cmds/incident_helper/src/ih_util.h index ce5baeef0dc3..86761e93f49c 100644 --- a/cmds/incident_helper/ih_util.h +++ b/cmds/incident_helper/src/ih_util.h @@ -21,6 +21,10 @@ #include <vector> #include <sstream> +#include <android/util/ProtoOutputStream.h> + +using namespace android::util; + typedef std::vector<std::string> header_t; typedef std::vector<std::string> record_t; typedef std::string (*trans_func) (const std::string&); @@ -52,6 +56,12 @@ record_t parseRecord(const std::string& line, const std::string& delimiters = DE bool hasPrefix(std::string* line, const char* key); /** + * Converts string to the desired type + */ +int toInt(const std::string& s); +long long toLongLong(const std::string& s); + +/** * Reader class reads data from given fd in streaming fashion. * The buffer size is controlled by capacity parameter. */ @@ -78,4 +88,22 @@ private: inline bool EOR() { return mFd == -1 && mBufSize == 0; }; }; +/** + * The class contains a mapping between table headers to its field ids. + * And allow users to insert the field values to proto based on its header name. + */ +class Table +{ +public: + Table(const char* names[], const uint64_t ids[], const int count); + ~Table(); + + bool insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value); + +private: + const char** mFieldNames; + const uint64_t* mFieldIds; + const int mFieldCount; +}; + #endif // INCIDENT_HELPER_UTIL_H diff --git a/cmds/incident_helper/main.cpp b/cmds/incident_helper/src/main.cpp index 52ff77720d70..3da87b9c801b 100644 --- a/cmds/incident_helper/main.cpp +++ b/cmds/incident_helper/src/main.cpp @@ -16,7 +16,9 @@ #define LOG_TAG "incident_helper" -#include "IncidentHelper.h" +#include "parsers/KernelWakesParser.h" +#include "parsers/PageTypeInfoParser.h" +#include "parsers/ProcrankParser.h" #include <android-base/file.h> #include <getopt.h> diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp new file mode 100644 index 000000000000..cc4a1e1ecfa2 --- /dev/null +++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "incident_helper" + +#include <android/util/ProtoOutputStream.h> + +#include "frameworks/base/core/proto/android/os/kernelwake.proto.h" +#include "ih_util.h" +#include "KernelWakesParser.h" + +using namespace android::os; + +const std::string LINE_DELIMITER = "\t"; + +status_t +KernelWakesParser::Parse(const int in, const int out) const +{ + Reader reader(in); + string line; + header_t header; // the header of /d/wakeup_sources + record_t record; // retain each record + int nline = 0; + + ProtoOutputStream proto; + Table table(WakeupSourceProto::_FIELD_NAMES, WakeupSourceProto::_FIELD_IDS, WakeupSourceProto::_FIELD_COUNT); + + // parse line by line + while (reader.readLine(&line)) { + if (line.empty()) continue; + // parse head line + if (nline++ == 0) { + header = parseHeader(line, LINE_DELIMITER); + continue; + } + + // parse for each record, the line delimiter is \t only! + record = parseRecord(line, LINE_DELIMITER); + + if (record.size() != header.size()) { + // TODO: log this to incident report! + fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str()); + continue; + } + + long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES); + for (int i=0; i<(int)record.size(); i++) { + if (!table.insertField(proto, header[i], record[i])) { + fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", + this->name.string(), nline, header[i].c_str(), record[i].c_str()); + } + } + proto.end(token); + } + + if (!reader.ok(&line)) { + fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); + return -1; + } + + if (!proto.flush(out)) { + fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); + return -1; + } + fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size()); + return NO_ERROR; +} diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.h b/cmds/incident_helper/src/parsers/KernelWakesParser.h new file mode 100644 index 000000000000..aabab7c64a4f --- /dev/null +++ b/cmds/incident_helper/src/parsers/KernelWakesParser.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KERNEL_WAKES_PARSER_H +#define KERNEL_WAKES_PARSER_H + +#include "TextParserBase.h" + +/** + * Kernel wakeup sources parser, parses text to protobuf in /d/wakeup_sources + */ +class KernelWakesParser : public TextParserBase { +public: + KernelWakesParser() : TextParserBase(String8("KernelWakeSources")) {}; + ~KernelWakesParser() {}; + + virtual status_t Parse(const int in, const int out) const; +}; + +#endif // KERNEL_WAKES_PARSER_H diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp new file mode 100644 index 000000000000..6047bd189b95 --- /dev/null +++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "incident_helper" + +#include <android/util/ProtoOutputStream.h> + +#include "frameworks/base/core/proto/android/os/pagetypeinfo.proto.h" +#include "ih_util.h" +#include "PageTypeInfoParser.h" + +using namespace android::os; + +const std::string LINE_DELIMITER = ","; + +status_t +PageTypeInfoParser::Parse(const int in, const int out) const +{ + Reader reader(in); + string line; + bool migrateTypeSession = false; + int pageBlockOrder; + header_t blockHeader; + + ProtoOutputStream proto; + Table table(BlockProto::_FIELD_NAMES, BlockProto::_FIELD_IDS, BlockProto::_FIELD_COUNT); + + while (reader.readLine(&line)) { + if (line.empty()) { + migrateTypeSession = false; + blockHeader.clear(); + continue; + } + + if (hasPrefix(&line, "Page block order:")) { + pageBlockOrder = toInt(line); + proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder); + continue; + } + if (hasPrefix(&line, "Pages per block:")) { + proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line)); + continue; + } + if (hasPrefix(&line, "Free pages count per migrate type at order")) { + migrateTypeSession = true; + continue; + } + if (hasPrefix(&line, "Number of blocks type")) { + blockHeader = parseHeader(line); + continue; + } + + record_t record = parseRecord(line, LINE_DELIMITER); + if (migrateTypeSession && record.size() == 3) { + long long token = proto.start(PageTypeInfo::MIGRATE_TYPES); + // expect part 0 starts with "Node" + if (hasPrefix(&record[0], "Node")) { + proto.write(MigrateTypeProto::NODE, toInt(record[0])); + } else return BAD_VALUE; + // expect part 1 starts with "zone" + if (hasPrefix(&record[1], "zone")) { + proto.write(MigrateTypeProto::ZONE, record[1]); + } else return BAD_VALUE; + // expect part 2 starts with "type" + if (hasPrefix(&record[2], "type")) { + // expect the rest of part 2 has number of (pageBlockOrder + 2) parts + // An example looks like: + // header line: type 0 1 2 3 4 5 6 7 8 9 10 + // record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0 + // The pageBlockOrder = 10 and it's zero-indexed. so total parts + // are 10 + 1(zero-indexed) + 1(the type part) = 12. + record_t pageCounts = parseRecord(record[2]); + int pageCountsSize = pageBlockOrder + 2; + if ((int)pageCounts.size() != pageCountsSize) return BAD_VALUE; + + proto.write(MigrateTypeProto::TYPE, pageCounts[0]); + for (auto i=1; i<pageCountsSize; i++) { + proto.write(MigrateTypeProto::FREE_PAGES_COUNT, toInt(pageCounts[i])); + } + } else return BAD_VALUE; + + proto.end(token); + } else if (!blockHeader.empty() && record.size() == 2) { + long long token = proto.start(PageTypeInfo::BLOCKS); + if (hasPrefix(&record[0], "Node")) { + proto.write(BlockProto::NODE, toInt(record[0])); + } else return BAD_VALUE; + + if (hasPrefix(&record[1], "zone")) { + record_t blockCounts = parseRecord(record[1]); + proto.write(BlockProto::ZONE, blockCounts[0]); + + for (size_t i=0; i<blockHeader.size(); i++) { + if (!table.insertField(proto, blockHeader[i], blockCounts[i+1])) { + return BAD_VALUE; + } + } + } else return BAD_VALUE; + proto.end(token); + } + } + + if (!reader.ok(&line)) { + fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); + return -1; + } + + if (!proto.flush(out)) { + fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); + return -1; + } + + fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size()); + return NO_ERROR; +}
\ No newline at end of file diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.h b/cmds/incident_helper/src/parsers/PageTypeInfoParser.h new file mode 100644 index 000000000000..fb84d912a5f2 --- /dev/null +++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PAGE_TYPE_INFO_PARSER_H +#define PAGE_TYPE_INFO_PARSER_H + +#include "TextParserBase.h" + +using namespace android; + +/** + * PageTypeInfo parser, parses text to protobuf in /proc/pageinfotype + */ +class PageTypeInfoParser : public TextParserBase { +public: + PageTypeInfoParser() : TextParserBase(String8("PageTypeInfo")) {}; + ~PageTypeInfoParser() {}; + + virtual status_t Parse(const int in, const int out) const; +}; + +#endif // PAGE_TYPE_INFO_PARSER_H diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.cpp b/cmds/incident_helper/src/parsers/ProcrankParser.cpp new file mode 100644 index 000000000000..93f970f820d9 --- /dev/null +++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "incident_helper" + +#include <android/util/ProtoOutputStream.h> + +#include "frameworks/base/core/proto/android/os/procrank.proto.h" +#include "ih_util.h" +#include "ProcrankParser.h" + +using namespace android::os; + +status_t +ProcrankParser::Parse(const int in, const int out) const +{ + Reader reader(in); + string line; + header_t header; // the header of /d/wakeup_sources + record_t record; // retain each record + int nline = 0; + + ProtoOutputStream proto; + Table table(ProcessProto::_FIELD_NAMES, ProcessProto::_FIELD_IDS, ProcessProto::_FIELD_COUNT); + string zram, ram, total; + + // parse line by line + while (reader.readLine(&line)) { + if (line.empty()) continue; + + // parse head line + if (nline++ == 0) { + header = parseHeader(line); + continue; + } + + if (hasPrefix(&line, "ZRAM:")) { + zram = line; + continue; + } + if (hasPrefix(&line, "RAM:")) { + ram = line; + continue; + } + + record = parseRecord(line); + if (record.size() != header.size()) { + if (record[record.size() - 1] == "TOTAL") { // TOTAL record + total = line; + } else { + fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, + line.c_str()); + } + continue; + } + + long long token = proto.start(Procrank::PROCESSES); + for (int i=0; i<(int)record.size(); i++) { + if (!table.insertField(proto, header[i], record[i])) { + fprintf(stderr, "[%s]Line %d has bad value %s of %s\n", + this->name.string(), nline, header[i].c_str(), record[i].c_str()); + } + } + proto.end(token); + } + + // add summary + long long token = proto.start(Procrank::SUMMARY); + if (!total.empty()) { + record = parseRecord(total); + long long token = proto.start(SummaryProto::TOTAL); + for (int i=(int)record.size(); i>0; i--) { + table.insertField(proto, header[header.size() - i].c_str(), record[record.size() - i].c_str()); + } + proto.end(token); + } + if (!zram.empty()) { + long long token = proto.start(SummaryProto::ZRAM); + proto.write(ZramProto::RAW_TEXT, zram); + proto.end(token); + } + if (!ram.empty()) { + long long token = proto.start(SummaryProto::RAM); + proto.write(RamProto::RAW_TEXT, ram); + proto.end(token); + } + proto.end(token); + + if (!reader.ok(&line)) { + fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); + return -1; + } + + if (!proto.flush(out)) { + fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); + return -1; + } + fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size()); + return NO_ERROR; +} diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.h b/cmds/incident_helper/src/parsers/ProcrankParser.h new file mode 100644 index 000000000000..5d0ee48aa5b1 --- /dev/null +++ b/cmds/incident_helper/src/parsers/ProcrankParser.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PROCRANK_PARSER_H +#define PROCRANK_PARSER_H + +#include "TextParserBase.h" + +using namespace android; + +/** + * Procrank parser, parses text produced by command procrank + */ +class ProcrankParser : public TextParserBase { +public: + ProcrankParser() : TextParserBase(String8("ProcrankParser")) {}; + ~ProcrankParser() {}; + + virtual status_t Parse(const int in, const int out) const; +}; + +#endif // PROCRANK_PARSER_H diff --git a/cmds/incident_helper/testdata/kernel_wakeups_short.txt b/cmds/incident_helper/testdata/kernel_wakeups_short.txt new file mode 100644 index 000000000000..a51926e70def --- /dev/null +++ b/cmds/incident_helper/testdata/kernel_wakeups_short.txt @@ -0,0 +1,3 @@ +name active_count last_change +ab 8 123456123456 +df 143 0 diff --git a/cmds/incident_helper/tests/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp new file mode 100644 index 000000000000..a8fa62088450 --- /dev/null +++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "KernelWakesParser.h" + +#include "frameworks/base/core/proto/android/os/kernelwake.pb.h" + +#include <android-base/file.h> +#include <android-base/test_utils.h> +#include <gmock/gmock.h> +#include <google/protobuf/message.h> +#include <gtest/gtest.h> +#include <string.h> +#include <fcntl.h> + +using namespace android::base; +using namespace android::os; +using namespace std; +using ::testing::StrEq; +using ::testing::Test; +using ::testing::internal::CaptureStderr; +using ::testing::internal::CaptureStdout; +using ::testing::internal::GetCapturedStderr; +using ::testing::internal::GetCapturedStdout; + +class KernelWakesParserTest : public Test { +public: + virtual void SetUp() override { + ASSERT_TRUE(tf.fd != -1); + } + + string getSerializedString(::google::protobuf::Message& message) { + string expectedStr; + message.SerializeToFileDescriptor(tf.fd); + ReadFileToString(tf.path, &expectedStr); + return expectedStr; + } + +protected: + TemporaryFile tf; + + const string kTestPath = GetExecutableDirectory(); + const string kTestDataPath = kTestPath + "/testdata/"; +}; + +TEST_F(KernelWakesParserTest, Short) { + const string testFile = kTestDataPath + "kernel_wakeups_short.txt"; + KernelWakesParser parser; + KernelWakeSources expected; + + WakeupSourceProto* record1 = expected.add_wakeup_sources(); + record1->set_name("ab"); + record1->set_active_count(8); + record1->set_last_change(123456123456LL); + + WakeupSourceProto* record2 = expected.add_wakeup_sources(); + record2->set_name("df"); + record2->set_active_count(143); + record2->set_last_change(0LL); + + int fd = open(testFile.c_str(), O_RDONLY); + ASSERT_TRUE(fd != -1); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); + EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); + close(fd); +} + +TEST_F(KernelWakesParserTest, Normal) { + const string testFile = kTestDataPath + "kernel_wakeups.txt"; + KernelWakesParser parser; + KernelWakeSources expected; + + WakeupSourceProto* record1 = expected.add_wakeup_sources(); + record1->set_name("ipc000000ab_ATFWD-daemon"); + record1->set_active_count(8); + record1->set_event_count(8); + record1->set_wakeup_count(0); + record1->set_expire_count(0); + record1->set_active_since(0l); + record1->set_total_time(0l); + record1->set_max_time(0l); + record1->set_last_change(131348LL); + record1->set_prevent_suspend_time(0LL); + + WakeupSourceProto* record2 = expected.add_wakeup_sources(); + record2->set_name("ipc000000aa_ATFWD-daemon"); + record2->set_active_count(143); + record2->set_event_count(143); + record2->set_wakeup_count(0); + record2->set_expire_count(0); + record2->set_active_since(0l); + record2->set_total_time(123l); + record2->set_max_time(3l); + record2->set_last_change(2067286206LL); + record2->set_prevent_suspend_time(0LL); + + int fd = open(testFile.c_str(), O_RDONLY); + ASSERT_TRUE(fd != -1); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); + EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); + close(fd); +} diff --git a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp new file mode 100644 index 000000000000..de64e70c80c7 --- /dev/null +++ b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PageTypeInfoParser.h" + +#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h" + +#include <android-base/file.h> +#include <android-base/test_utils.h> +#include <gmock/gmock.h> +#include <google/protobuf/message.h> +#include <gtest/gtest.h> +#include <string.h> +#include <fcntl.h> + +using namespace android::base; +using namespace android::os; +using namespace std; +using ::testing::StrEq; +using ::testing::Test; +using ::testing::internal::CaptureStderr; +using ::testing::internal::CaptureStdout; +using ::testing::internal::GetCapturedStderr; +using ::testing::internal::GetCapturedStdout; + +class PageTypeInfoParserTest : public Test { +public: + virtual void SetUp() override { + ASSERT_TRUE(tf.fd != -1); + } + + string getSerializedString(::google::protobuf::Message& message) { + string expectedStr; + message.SerializeToFileDescriptor(tf.fd); + ReadFileToString(tf.path, &expectedStr); + return expectedStr; + } + +protected: + TemporaryFile tf; + + const string kTestPath = GetExecutableDirectory(); + const string kTestDataPath = kTestPath + "/testdata/"; +}; + +TEST_F(PageTypeInfoParserTest, Success) { + const string testFile = kTestDataPath + "pagetypeinfo.txt"; + PageTypeInfoParser parser; + PageTypeInfo expected; + + expected.set_page_block_order(10); + expected.set_pages_per_block(1024); + + MigrateTypeProto* mt1 = expected.add_migrate_types(); + mt1->set_node(0); + mt1->set_zone("DMA"); + mt1->set_type("Unmovable"); + int arr1[] = { 426, 279, 226, 1, 1, 1, 0, 0, 2, 2, 0}; + for (auto i=0; i<11; i++) { + mt1->add_free_pages_count(arr1[i]); + } + + MigrateTypeProto* mt2 = expected.add_migrate_types(); + mt2->set_node(0); + mt2->set_zone("Normal"); + mt2->set_type("Reclaimable"); + int arr2[] = { 953, 773, 437, 154, 92, 26, 15, 14, 12, 7, 0}; + for (auto i=0; i<11; i++) { + mt2->add_free_pages_count(arr2[i]); + } + + BlockProto* block1 = expected.add_blocks(); + block1->set_node(0); + block1->set_zone("DMA"); + block1->set_unmovable(74); + block1->set_reclaimable(9); + block1->set_movable(337); + block1->set_cma(41); + block1->set_reserve(1); + block1->set_isolate(0); + + + BlockProto* block2 = expected.add_blocks(); + block2->set_node(0); + block2->set_zone("Normal"); + block2->set_unmovable(70); + block2->set_reclaimable(12); + block2->set_movable(423); + block2->set_cma(0); + block2->set_reserve(1); + block2->set_isolate(0); + + int fd = open(testFile.c_str(), O_RDONLY); + ASSERT_TRUE(fd != -1); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); + EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); + close(fd); +}
\ No newline at end of file diff --git a/cmds/incident_helper/tests/IncidentHelper_test.cpp b/cmds/incident_helper/tests/ProcrankParser_test.cpp index c44a163efa11..e86647ad479b 100644 --- a/cmds/incident_helper/tests/IncidentHelper_test.cpp +++ b/cmds/incident_helper/tests/ProcrankParser_test.cpp @@ -14,10 +14,8 @@ * limitations under the License. */ -#include "IncidentHelper.h" +#include "ProcrankParser.h" -#include "frameworks/base/core/proto/android/os/kernelwake.pb.h" -#include "frameworks/base/core/proto/android/os/pagetypeinfo.pb.h" #include "frameworks/base/core/proto/android/os/procrank.pb.h" #include <android-base/file.h> @@ -38,7 +36,7 @@ using ::testing::internal::CaptureStdout; using ::testing::internal::GetCapturedStderr; using ::testing::internal::GetCapturedStdout; -class IncidentHelperTest : public Test { +class ProcrankParserTest : public Test { public: virtual void SetUp() override { ASSERT_TRUE(tf.fd != -1); @@ -58,57 +56,7 @@ protected: const string kTestDataPath = kTestPath + "/testdata/"; }; -TEST_F(IncidentHelperTest, ReverseParser) { - ReverseParser parser; - TemporaryFile tf; - - ASSERT_TRUE(tf.fd != -1); - ASSERT_TRUE(WriteStringToFile("TestData", tf.path, false)); - - CaptureStdout(); - ASSERT_EQ(NO_ERROR, parser.Parse(tf.fd, STDOUT_FILENO)); - EXPECT_THAT(GetCapturedStdout(), StrEq("ataDtseT")); -} - -TEST_F(IncidentHelperTest, KernelWakesParser) { - const string testFile = kTestDataPath + "kernel_wakeups.txt"; - KernelWakesParser parser; - KernelWakeSources expected; - - WakeupSourceProto* record1 = expected.add_wakeup_sources(); - record1->set_name("ipc000000ab_ATFWD-daemon"); - record1->set_active_count(8); - record1->set_event_count(8); - record1->set_wakeup_count(0); - record1->set_expire_count(0); - record1->set_active_since(0l); - record1->set_total_time(0l); - record1->set_max_time(0l); - record1->set_last_change(131348l); - record1->set_prevent_suspend_time(0l); - - WakeupSourceProto* record2 = expected.add_wakeup_sources(); - record2->set_name("ipc000000aa_ATFWD-daemon"); - record2->set_active_count(143); - record2->set_event_count(143); - record2->set_wakeup_count(0); - record2->set_expire_count(0); - record2->set_active_since(0l); - record2->set_total_time(123l); - record2->set_max_time(3l); - record2->set_last_change(2067286206l); - record2->set_prevent_suspend_time(0l); - - int fd = open(testFile.c_str(), O_RDONLY); - ASSERT_TRUE(fd != -1); - - CaptureStdout(); - ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); - EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); - close(fd); -} - -TEST_F(IncidentHelperTest, ProcrankParser) { +TEST_F(ProcrankParserTest, HasSwapInfo) { const string testFile = kTestDataPath + "procrank.txt"; ProcrankParser parser; Procrank expected; @@ -160,7 +108,7 @@ TEST_F(IncidentHelperTest, ProcrankParser) { close(fd); } -TEST_F(IncidentHelperTest, ProcrankParserShortHeader) { +TEST_F(ProcrankParserTest, NoSwapInfo) { const string testFile = kTestDataPath + "procrank_short.txt"; ProcrankParser parser; Procrank expected; @@ -197,59 +145,3 @@ TEST_F(IncidentHelperTest, ProcrankParserShortHeader) { EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); close(fd); } - -TEST_F(IncidentHelperTest, PageTypeInfoParser) { - const string testFile = kTestDataPath + "pagetypeinfo.txt"; - PageTypeInfoParser parser; - PageTypeInfo expected; - - expected.set_page_block_order(10); - expected.set_pages_per_block(1024); - - MigrateTypeProto* mt1 = expected.add_migrate_types(); - mt1->set_node(0); - mt1->set_zone("DMA"); - mt1->set_type("Unmovable"); - int arr1[] = { 426, 279, 226, 1, 1, 1, 0, 0, 2, 2, 0}; - for (auto i=0; i<11; i++) { - mt1->add_free_pages_count(arr1[i]); - } - - MigrateTypeProto* mt2 = expected.add_migrate_types(); - mt2->set_node(0); - mt2->set_zone("Normal"); - mt2->set_type("Reclaimable"); - int arr2[] = { 953, 773, 437, 154, 92, 26, 15, 14, 12, 7, 0}; - for (auto i=0; i<11; i++) { - mt2->add_free_pages_count(arr2[i]); - } - - BlockProto* block1 = expected.add_blocks(); - block1->set_node(0); - block1->set_zone("DMA"); - block1->set_unmovable(74); - block1->set_reclaimable(9); - block1->set_movable(337); - block1->set_cma(41); - block1->set_reserve(1); - block1->set_isolate(0); - - - BlockProto* block2 = expected.add_blocks(); - block2->set_node(0); - block2->set_zone("Normal"); - block2->set_unmovable(70); - block2->set_reclaimable(12); - block2->set_movable(423); - block2->set_cma(0); - block2->set_reserve(1); - block2->set_isolate(0); - - int fd = open(testFile.c_str(), O_RDONLY); - ASSERT_TRUE(fd != -1); - - CaptureStdout(); - ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); - EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); - close(fd); -}
\ No newline at end of file diff --git a/cmds/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/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 29433f3fc14f..9490880afe0f 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -157,7 +157,8 @@ public final class Pm { } static final class MyShellCallback extends ShellCallback { - @Override public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) { + @Override public ParcelFileDescriptor onOpenFile(String path, String seLinuxContext, + String mode) { File file = new File(path); final ParcelFileDescriptor fd; try { 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 56d4e4d940c2..c0cedb1b467e 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -46,6 +46,7 @@ void StatsLogProcessor::OnLogEvent(const LogEvent& msg) { // pass the event to metrics managers. for (auto& pair : mMetricsManagers) { pair.second->onLogEvent(msg); + flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second); } } @@ -59,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"); @@ -68,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) { @@ -83,42 +98,42 @@ 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()) { + mLastFlushTimes.erase(flushTime); } } -void StatsLogProcessor::addEventMetricData(const EventMetricData& eventMetricData) { - // TODO: Replace this code when MetricsManager.onDumpReport() is ready to - // get a list of byte arrays. - flushIfNecessary(eventMetricData); - const int numBytes = eventMetricData.ByteSize(); - char buffer[numBytes]; - eventMetricData.SerializeToArray(&buffer[0], numBytes); - string bufferString(buffer, numBytes); - mEvents.push_back(bufferString); - mBufferSize += eventMetricData.ByteSize(); -} +void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs, + const ConfigKey& key, + const unique_ptr<MetricsManager>& metricsManager) { + auto lastFlushNs = mLastFlushTimes.find(key); + if (lastFlushNs != mLastFlushTimes.end()) { + if (timestampNs - lastFlushNs->second < kMinFlushPeriod) { + return; + } + } -void StatsLogProcessor::flushIfNecessary(const EventMetricData& eventMetricData) { - if (eventMetricData.ByteSize() + mBufferSize > kMaxSerializedBytes) { - flush(); + size_t totalBytes = metricsManager->byteSize(); + if (totalBytes > kMaxSerializedBytes) { + flush(); + mLastFlushTimes[key] = std::move(timestampNs); } } void StatsLogProcessor::flush() { + // TODO: Take ConfigKey as an argument and flush metrics related to the + // ConfigKey. Also, create a wrapper that holds a repeated field of + // StatsLogReport's. + /* StatsLogReport logReport; - for (string eventBuffer : mEvents) { - EventMetricData eventFromBuffer; - eventFromBuffer.ParseFromString(eventBuffer); - EventMetricData* newEntry = logReport.mutable_event_metrics()->add_data(); - newEntry->CopyFrom(eventFromBuffer); - } - const int numBytes = logReport.ByteSize(); vector<uint8_t> logReportBuffer(numBytes); logReport.SerializeToArray(&logReportBuffer[0], numBytes); mPushLog(logReportBuffer); - mEvents.clear(); - mBufferSize = 0; + */ } } // namespace statsd diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 9cd74caf095e..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(); @@ -50,6 +50,8 @@ public: private: std::unordered_map<ConfigKey, std::unique_ptr<MetricsManager>> mMetricsManagers; + std::unordered_map<ConfigKey, long> mLastFlushTimes; + sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid. /* Max *serialized* size of the logs kept in memory before flushing through binder call. @@ -59,25 +61,17 @@ private: */ static const size_t kMaxSerializedBytes = 16 * 1024; - /* List of data that was captured for a single metric over a given interval of time. */ - vector<string> mEvents; - - /* Current *serialized* size of the logs kept in memory. - To save computation, we will not calculate the size of the StatsLogReport every time when a - new entry is added, which would recursively call ByteSize() on every log entry. Instead, we - keep the sum of all individual stats log entry sizes. The size of a proto is approximately - the sum of the size of all member protos. - */ - size_t mBufferSize = 0; - /* Check if the buffer size exceeds the max buffer size when the new entry is added, and flush the logs to callback clients if true. */ - void flushIfNecessary(const EventMetricData& eventMetricData); - - /* Append event metric data to StatsLogReport. */ - void addEventMetricData(const EventMetricData& eventMetricData); + void flushIfNecessary(uint64_t timestampNs, + const ConfigKey& key, + const unique_ptr<MetricsManager>& metricsManager); std::function<void(const vector<uint8_t>&)> mPushLog; + + /* Minimum period between two flushes in nanoseconds. Currently set to 10 + * minutes. */ + static const unsigned long long kMinFlushPeriod = 600 * NS_PER_SEC; }; } // namespace statsd 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 7bb9c8a502c1..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); + } + + // 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); + } - VLOG(" dimension key %s", hashableKey.c_str()); - addSlicedCounterToReport(*wrapper, it->second, pair.second); + mProto->end(wrapperToken); } - return report; - // TODO: Clear mPastBuckets, mDimensionKeyMap once the report is dumped. + + 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); + } + + 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,6 +268,13 @@ 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() { + return mByteSize; +} + } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 340c8309b7fa..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,27 +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; @@ -69,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 38e55fde40d5..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,216 +148,57 @@ 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()); +void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) { + if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) { return; } - if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end() && mConditionSliced) { - // add the durationInfo for the current bucket. - auto& durationInfo = mCurrentSlicedDuration[eventKey]; - durationInfo.conditionKeys = conditionKeys; + 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); + } } - 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()); - } + int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs; + mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs; } -void DurationMetricProducer::noteConditionChanged(const HashableDimensionKey& key, - const bool conditionMet, - const uint64_t eventTime) { - flushDurationIfNeeded(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()); - auto it = mCurrentSlicedDuration.find(key); - if (it == mCurrentSlicedDuration.end()) { + if (matcherIndex == mStopAllIndex) { + for (auto& pair : mCurrentSlicedDuration) { + pair.second->noteStopAll(event.GetTimestampNs()); + } 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; - } -} + HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension)); -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; + if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) { + mCurrentSlicedDuration[eventKey] = createDurationTracker(mPastBuckets[eventKey]); } -} -void DurationMetricProducer::noteStop(const HashableDimensionKey& key, const uint64_t eventTime) { - if (mCurrentSlicedDuration.find(key) == mCurrentSlicedDuration.end()) { - // we didn't see a start event before. do nothing. - return; - } - DurationInfo& duration = mCurrentSlicedDuration[key]; - - switch (duration.state) { - case DurationState::kStopped: - // already stopped, do nothing. - break; - case DurationState::kStarted: { - duration.state = DurationState::kStopped; - int64_t durationTime = eventTime - duration.lastStartTime; - VLOG("Metric %lld, key %s, Stop %lld %lld %lld", mMetric.metric_id(), key.c_str(), - (long long)duration.lastStartTime, (long long)eventTime, (long long)durationTime); - duration.lastDuration = - updateDuration(duration.lastDuration, durationTime, mMetric.type()); - VLOG(" record duration: %lld ", (long long)duration.lastDuration); - break; - } - case DurationState::kPaused: { - duration.state = DurationState::kStopped; - break; - } - } -} + auto it = mCurrentSlicedDuration.find(eventKey); -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; -} + if (matcherIndex == mStartIndex) { + it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys); -void DurationMetricProducer::noteStopAll(const uint64_t eventTime) { - for (auto& duration : mCurrentSlicedDuration) { - noteStop(duration.first, eventTime); + } else if (matcherIndex == mStopIndex) { + it->second->noteStop(atomKey, 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) { - 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()); - } - - VLOG(" final duration for last bucket: %lld", (long long)finalDuration); - - // Don't record empty bucket. - if (finalDuration != 0) { - info.set_duration_nanos(finalDuration); - // it will auto create new vector of CountbucketInfo if the key is not found. - auto& bucketList = mPastBuckets[it->first]; - bucketList.push_back(info); - } - - // if the event is still on-going, add the buckets between previous bucket and now. Because - // the event has been going on across all the buckets in between. - // |prev_bucket|...|..|...|now_bucket| - if (it->second.state == kStarted) { - for (int i = 1; i < numBucketsForward; i++) { - DurationBucketInfo info; - info.set_start_bucket_nanos(oldBucketStartTimeNs + mBucketSizeNs * i); - info.set_end_bucket_nanos(endTime + mBucketSizeNs * i); - info.set_duration_nanos(mBucketSizeNs); - auto& bucketList = mPastBuckets[it->first]; - bucketList.push_back(info); - VLOG(" add filling bucket with duration %lld", (long long)mBucketSizeNs); - } - } - - if (it->second.state == DurationState::kStopped) { - // No need to keep buckets for events that were stopped before. If the event starts - // again, we will add it back. - 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; - } - } +size_t DurationMetricProducer::byteSize() { +// TODO: return actual proto size when ProtoOutputStream is ready for use for +// DurationMetricsProducer. +// return mProto->size(); + return 0; } } // namespace statsd diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 19e2437ca538..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,50 +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; @@ -89,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 8b3f405eec80..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,20 +113,24 @@ 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); } +size_t EventMetricProducer::byteSize() { + return mProto->size(); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 879175cc25ff..7dd0e38a0533 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -41,18 +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 496b1453e46c..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. @@ -64,6 +64,8 @@ public: return mConditionSliced; }; + virtual size_t byteSize() = 0; + protected: const uint64_t mStartTimeNs; @@ -105,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 1ffa58b8c862..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,13 +145,23 @@ 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); } } } } } +// Returns the total byte size of all metrics managed by a single config source. +size_t MetricsManager::byteSize() { + size_t totalSize = 0; + for (auto metricProducer : mAllMetricProducers) { + totalSize += metricProducer->byteSize(); + } + return totalSize; +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 2f91460061fa..44cd6371199e 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -46,6 +46,8 @@ public: // Config source owner can call onDumpReport() to get all the metrics collected. std::vector<StatsLogReport> onDumpReport(); + size_t byteSize(); + private: // All event tags that are interesting to my metrics. std::set<int> mTagIds; 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 29cd94b21b1b..1e37ff8c0aef 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -69,21 +69,53 @@ message DurationMetricData { repeated DurationBucketInfo bucket_info = 2; } +message ValueBucketInfo { + optional int64 start_bucket_nanos = 1; + + optional int64 end_bucket_nanos = 2; + + optional int64 value = 3; +} + +message ValueMetricData { + repeated KeyValuePair dimension = 1; + + repeated ValueBucketInfo bucket_info = 2; +} + +message GaugeBucketInfo { + optional int64 start_bucket_nanos = 1; + + optional int64 end_bucket_nanos = 2; + + optional int64 gauge = 3; +} + +message GaugeMetricData { + repeated KeyValuePair dimension = 1; + + repeated GaugeBucketInfo bucket_info = 2; +} + 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; - optional int64 timestamp = 2; + optional int64 timestamp_nanos = 2; optional string app = 3; optional int32 uid = 4; @@ -108,9 +140,31 @@ message StatsLogReport { message DurationMetricDataWrapper { repeated DurationMetricData data = 1; } + message ValueMetricDataWrapper { + repeated ValueMetricData data = 1; + } + + message GaugeMetricDataWrapper { + repeated GaugeMetricData data = 1; + } + oneof data { EventMetricDataWrapper event_metrics = 4; CountMetricDataWrapper count_metrics = 5; DurationMetricDataWrapper duration_metrics = 6; + ValueMetricDataWrapper value_metrics = 7; + GaugeMetricDataWrapper gauge_metrics = 8; } } + +message ConfigMetricsReport { + message ConfigKey { + optional int32 uid = 1; + optional string name = 2; + } + optional ConfigKey config_key = 1; + + repeated StatsLogReport metrics = 2; + + optional UidMapping uid_map = 3; +} 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 113ac62699d2..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,27 +150,43 @@ 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 = 7; + + repeated EventConditionLink links = 8; - repeated EventConditionLink links = 9; +} + +message GaugeMetric { + optional int64 metric_id = 1; + + optional string what = 2; + + optional int32 gauge_field = 3; + + optional string condition = 4; + + repeated KeyMatcher dimension = 5; + + optional Bucket bucket = 6; + + repeated Alert alerts = 7; + + repeated EventConditionLink links = 8; } message ValueMetric { @@ -184,24 +202,21 @@ message ValueMetric { optional Bucket bucket = 6; + repeated Alert alerts = 7; + + repeated EventConditionLink links = 8; + enum Operation { - SUM_DIFF = 1; - MIN_DIFF = 2; - MAX_DIFF = 3; - SUM = 4; - MIN = 5; - MAX = 6; - FIRST = 7; - LAST = 8; + SUM = 1; } - optional Operation operation = 7; + optional Operation operation = 9 [default = SUM]; } message EventConditionLink { - optional string condition = 1; + optional string condition = 1; - repeated KeyMatcher key_in_main = 2; - repeated KeyMatcher key_in_condition = 3; + repeated KeyMatcher key_in_main = 2; + repeated KeyMatcher key_in_condition = 3; }; message StatsdConfig { 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/Activity.java b/core/java/android/app/Activity.java index 9d331a02e392..a8863bf99445 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -16,8 +16,6 @@ package android.app; -import static android.os.Build.VERSION_CODES.O_MR1; - import static java.lang.Character.MIN_VALUE; import android.annotation.CallSuper; @@ -136,6 +134,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; + /** * An activity is a single, focused thing that the user can do. Almost all * activities interact with the user, so the Activity class takes care of @@ -991,17 +990,6 @@ public class Activity extends ContextThemeWrapper protected void onCreate(@Nullable Bundle savedInstanceState) { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState); - if (getApplicationInfo().targetSdkVersion >= O_MR1 && mActivityInfo.isFixedOrientation()) { - final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window); - final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta); - ta.recycle(); - - if (isTranslucentOrFloating) { - throw new IllegalStateException( - "Only fullscreen opaque activities can request orientation"); - } - } - if (mLastNonConfigurationInstances != null) { mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders); } @@ -7304,24 +7292,25 @@ public class Activity extends ContextThemeWrapper } /** - * Request to put this Activity in a mode where the user is locked to the - * current task. + * Request to put this activity in a mode where the user is locked to a restricted set of + * applications. * - * This will prevent the user from launching other apps, going to settings, or reaching the - * home screen. This does not include those apps whose {@link android.R.attr#lockTaskMode} - * values permit launching while locked. + * <p>If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns {@code true} + * for this component, the current task will be launched directly into LockTask mode. Only apps + * whitelisted by {@link DevicePolicyManager#setLockTaskPackages(ComponentName, String[])} can + * be launched while LockTask mode is active. The user will not be able to leave this mode + * until this activity calls {@link #stopLockTask()}. Calling this method while the device is + * already in LockTask mode has no effect. * - * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns true or - * lockTaskMode=lockTaskModeAlways for this component then the app will go directly into - * Lock Task mode. The user will not be able to exit this mode until - * {@link Activity#stopLockTask()} is called. + * <p>Otherwise, the current task will be launched into screen pinning mode. In this case, the + * system will prompt the user with a dialog requesting permission to use this mode. + * The user can exit at any time through instructions shown on the request dialog. Calling + * {@link #stopLockTask()} will also terminate this mode. * - * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns false - * then the system will prompt the user with a dialog requesting permission to enter - * this mode. When entered through this method the user can exit at any time through - * an action described by the request dialog. Calling stopLockTask will also exit the - * mode. + * <p><strong>Note:</strong> this method can only be called when the activity is foreground. + * That is, between {@link #onResume()} and {@link #onPause()}. * + * @see #stopLockTask() * @see android.R.attr#lockTaskMode */ public void startLockTask() { @@ -7332,25 +7321,24 @@ public class Activity extends ContextThemeWrapper } /** - * Allow the user to switch away from the current task. + * Stop the current task from being locked. * - * Called to end the mode started by {@link Activity#startLockTask}. This - * can only be called by activities that have successfully called - * startLockTask previously. + * <p>Called to end the LockTask or screen pinning mode started by {@link #startLockTask()}. + * This can only be called by activities that have called {@link #startLockTask()} previously. * - * This will allow the user to exit this app and move onto other activities. - * <p>Note: This method should only be called when the activity is user-facing. That is, - * between onResume() and onPause(). - * <p>Note: If there are other tasks below this one that are also locked then calling this - * method will immediately finish this task and resume the previous locked one, remaining in - * lockTask mode. + * <p><strong>Note:</strong> If the device is in LockTask mode that is not initially started + * by this activity, then calling this method will not terminate the LockTask mode, but only + * finish its own task. The device will remain in LockTask mode, until the activity which + * started the LockTask mode calls this method, or until its whitelist authorization is revoked + * by {@link DevicePolicyManager#setLockTaskPackages(ComponentName, String[])}. * + * @see #startLockTask() * @see android.R.attr#lockTaskMode * @see ActivityManager#getLockTaskModeState() */ public void stopLockTask() { try { - ActivityManager.getService().stopLockTaskMode(); + ActivityManager.getService().stopLockTaskModeByToken(mToken); } catch (RemoteException e) { } } 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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 4bd85ae9ee5c..b6fb12018201 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -254,8 +254,10 @@ public class AppOpsManager { public static final int OP_ANSWER_PHONE_CALLS = 69; /** @hide Run jobs when in background */ public static final int OP_RUN_ANY_IN_BACKGROUND = 70; + /** @hide Change Wi-Fi connectivity state */ + public static final int OP_CHANGE_WIFI_STATE = 71; /** @hide */ - public static final int _NUM_OP = 71; + public static final int _NUM_OP = 72; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -496,6 +498,7 @@ public class AppOpsManager { OP_INSTANT_APP_START_FOREGROUND, OP_ANSWER_PHONE_CALLS, OP_RUN_ANY_IN_BACKGROUND, + OP_CHANGE_WIFI_STATE, }; /** @@ -574,6 +577,7 @@ public class AppOpsManager { OPSTR_INSTANT_APP_START_FOREGROUND, OPSTR_ANSWER_PHONE_CALLS, null, // OP_RUN_ANY_IN_BACKGROUND + null, // OP_CHANGE_WIFI_STATE }; /** @@ -652,6 +656,7 @@ public class AppOpsManager { "INSTANT_APP_START_FOREGROUND", "ANSWER_PHONE_CALLS", "RUN_ANY_IN_BACKGROUND", + "CHANGE_WIFI_STATE", }; /** @@ -730,6 +735,7 @@ public class AppOpsManager { Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE, Manifest.permission.ANSWER_PHONE_CALLS, null, // no permission for OP_RUN_ANY_IN_BACKGROUND + Manifest.permission.CHANGE_WIFI_STATE, }; /** @@ -809,6 +815,7 @@ public class AppOpsManager { null, // INSTANT_APP_START_FOREGROUND null, // ANSWER_PHONE_CALLS null, // OP_RUN_ANY_IN_BACKGROUND + null, // OP_CHANGE_WIFI_STATE }; /** @@ -887,6 +894,7 @@ public class AppOpsManager { false, // INSTANT_APP_START_FOREGROUND false, // ANSWER_PHONE_CALLS false, // OP_RUN_ANY_IN_BACKGROUND + false, // OP_CHANGE_WIFI_STATE }; /** @@ -964,6 +972,7 @@ public class AppOpsManager { AppOpsManager.MODE_DEFAULT, // OP_INSTANT_APP_START_FOREGROUND AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS AppOpsManager.MODE_ALLOWED, // OP_RUN_ANY_IN_BACKGROUND + AppOpsManager.MODE_ALLOWED, // OP_CHANGE_WIFI_STATE }; /** @@ -1045,6 +1054,7 @@ public class AppOpsManager { false, false, // ANSWER_PHONE_CALLS false, // OP_RUN_ANY_IN_BACKGROUND + false, // OP_CHANGE_WIFI_STATE }; /** diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index e8cce0f3a0de..388459fbbc6e 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -232,7 +232,7 @@ interface IActivityManager { boolean requireFull, in String name, in String callerPackage); void addPackageDependency(in String packageName); void killApplication(in String pkg, int appId, int userId, in String reason); - void closeSystemDialogs(in String reason); + oneway void closeSystemDialogs(in String reason); Debug.MemoryInfo[] getProcessMemoryInfo(in int[] pids); void killApplicationProcess(in String processName, int uid); int startActivityIntentSender(in IApplicationThread caller, @@ -413,7 +413,7 @@ interface IActivityManager { String getTagForIntentSender(in IIntentSender sender, in String prefix); boolean startUserInBackground(int userid); void startLockTaskModeByToken(in IBinder token); - void stopLockTaskMode(); + void stopLockTaskModeByToken(in IBinder token); boolean isInLockTaskMode(); void setTaskDescription(in IBinder token, in ActivityManager.TaskDescription values); int startVoiceActivity(in String callingPackage, int callingPid, int callingUid, @@ -450,9 +450,8 @@ interface IActivityManager { int checkPermissionWithToken(in String permission, int pid, int uid, in IBinder callerToken); void registerTaskStackListener(in ITaskStackListener listener); + void unregisterTaskStackListener(in ITaskStackListener listener); - - // Start of M transactions void notifyCleartextNetwork(int uid, in byte[] firstPacket); int createStackOnDisplay(int displayId); void setTaskResizeable(int taskId, int resizeableMode); @@ -618,7 +617,6 @@ interface IActivityManager { * @return Returns true if the configuration was updated. */ boolean updateDisplayOverrideConfiguration(in Configuration values, int displayId); - void unregisterTaskStackListener(ITaskStackListener listener); void moveStackToDisplay(int stackId, int displayId); boolean requestAutofillData(in IAssistDataReceiver receiver, in Bundle receiverExtras, in IBinder activityToken, int flags); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index d4752a771492..9e926bd63a6a 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -138,6 +138,7 @@ interface INotificationManager void setNotificationPolicy(String pkg, in NotificationManager.Policy policy); boolean isNotificationPolicyAccessGrantedForPackage(String pkg); void setNotificationPolicyAccessGranted(String pkg, boolean granted); + void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted); AutomaticZenRule getAutomaticZenRule(String id); List<ZenModeConfig.ZenRule> getZenRules(); String addAutomaticZenRule(in AutomaticZenRule automaticZenRule); 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/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 41667c4c3ce3..837c00a72784 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -455,7 +455,6 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { */ public static final int FLAG_TURN_SCREEN_ON = 0x1000000; - /** * @hide Bit in {@link #flags}: If set, this component will only be seen * by the system user. Only works with broadcast receivers. Set from the @@ -1001,20 +1000,12 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * Returns true if the activity's orientation is fixed. * @hide */ - public boolean isFixedOrientation() { + boolean isFixedOrientation() { return isFixedOrientationLandscape() || isFixedOrientationPortrait() || screenOrientation == SCREEN_ORIENTATION_LOCKED; } /** - * Returns true if the specified orientation is considered fixed. - * @hide - */ - static public boolean isFixedOrientation(int orientation) { - return isFixedOrientationLandscape(orientation) || isFixedOrientationPortrait(orientation); - } - - /** * Returns true if the activity's orientation is fixed to landscape. * @hide */ diff --git a/core/java/android/content/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/PackageParser.java b/core/java/android/content/pm/PackageParser.java index b48829cfc87d..1c5cf15da062 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -102,6 +102,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.File; +import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -1708,13 +1709,33 @@ public class PackageParser { */ public static ApkLite parseApkLite(File apkFile, int flags) throws PackageParserException { - final String apkPath = apkFile.getAbsolutePath(); + return parseApkLiteInner(apkFile, null, null, flags); + } + + /** + * Utility method that retrieves lightweight details about a single APK + * file, including package name, split name, and install location. + * + * @param fd already open file descriptor of an apk file + * @param debugPathName arbitrary text name for this file, for debug output + * @param flags optional parse flags, such as + * {@link #PARSE_COLLECT_CERTIFICATES} + */ + public static ApkLite parseApkLite(FileDescriptor fd, String debugPathName, int flags) + throws PackageParserException { + return parseApkLiteInner(null, fd, debugPathName, flags); + } + + private static ApkLite parseApkLiteInner(File apkFile, FileDescriptor fd, String debugPathName, + int flags) throws PackageParserException { + final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); AssetManager assets = null; XmlResourceParser parser = null; try { assets = newConfiguredAssetManager(); - int cookie = assets.addAssetPath(apkPath); + int cookie = fd != null + ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); if (cookie == 0) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); 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/AssetManager.java b/core/java/android/content/res/AssetManager.java index f0adcd6cfb3e..78665609bdd4 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -28,8 +28,7 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; -import dalvik.annotation.optimization.FastNative; - +import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -694,7 +693,35 @@ public final class AssetManager implements AutoCloseable { private native final int addAssetPathNative(String path, boolean appAsLib); - /** + /** + * Add an additional set of assets to the asset manager from an already open + * FileDescriptor. Not for use by applications. + * This does not give full AssetManager functionality for these assets, + * since the origin of the file is not known for purposes of sharing, + * overlay resolution, and other features. However it does allow you + * to do simple access to the contents of the given fd as an apk file. + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * Returns the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public int addAssetFd(FileDescriptor fd, String debugPathName) { + return addAssetFdInternal(fd, debugPathName, false); + } + + private int addAssetFdInternal(FileDescriptor fd, String debugPathName, + boolean appAsLib) { + synchronized (this) { + int res = addAssetFdNative(fd, debugPathName, appAsLib); + makeStringBlocks(mStringBlocks); + return res; + } + } + + private native int addAssetFdNative(FileDescriptor fd, String debugPathName, + boolean appAsLib); + + /** * Add a set of assets to overlay an already added set of assets. * * This is only intended for application resources. System wide resources 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/net/OWNERS b/core/java/android/net/OWNERS index 0f1e2597f39e..d1ce60e9ff08 100644 --- a/core/java/android/net/OWNERS +++ b/core/java/android/net/OWNERS @@ -1,6 +1,6 @@ ek@google.com hugobenichi@google.com -jsharkey@google.com +jsharkey@android.com lorenzo@google.com satk@google.com silberst@google.com 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 270e63f408a7..dd9e774141e1 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -16,10 +16,10 @@ package android.os; -import java.util.ArrayList; -import java.util.NoSuchElementException; import libcore.util.NativeAllocationRegistry; +import java.util.NoSuchElementException; + /** @hide */ public abstract class HwBinder implements IHwBinder { private static final String TAG = "HwBinder"; @@ -46,9 +46,16 @@ public abstract class HwBinder implements IHwBinder { public native final void registerService(String serviceName) throws RemoteException; - public static native final IHwBinder getService( + public static final IHwBinder getService( String iface, String serviceName) + throws RemoteException, NoSuchElementException { + return getService(iface, serviceName, false /* retry */); + } + public static native final IHwBinder getService( + String iface, + String serviceName, + boolean retry) throws RemoteException, NoSuchElementException; public static native final void configureRpcThreadpool( @@ -56,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/HwBlob.java b/core/java/android/os/HwBlob.java index 88226f0a1665..5e9b9ae3d49a 100644 --- a/core/java/android/os/HwBlob.java +++ b/core/java/android/os/HwBlob.java @@ -43,6 +43,18 @@ public class HwBlob { public native final double getDouble(long offset); public native final String getString(long offset); + /** + The copyTo... methods copy the blob's data, starting from the given + byte offset, into the array. A total of "size" _elements_ are copied. + */ + public native final void copyToBoolArray(long offset, boolean[] array, int size); + public native final void copyToInt8Array(long offset, byte[] array, int size); + public native final void copyToInt16Array(long offset, short[] array, int size); + public native final void copyToInt32Array(long offset, int[] array, int size); + public native final void copyToInt64Array(long offset, long[] array, int size); + public native final void copyToFloatArray(long offset, float[] array, int size); + public native final void copyToDoubleArray(long offset, double[] array, int size); + public native final void putBool(long offset, boolean x); public native final void putInt8(long offset, byte x); public native final void putInt16(long offset, short x); @@ -52,6 +64,14 @@ public class HwBlob { public native final void putDouble(long offset, double x); public native final void putString(long offset, String x); + public native final void putBoolArray(long offset, boolean[] x); + public native final void putInt8Array(long offset, byte[] x); + public native final void putInt16Array(long offset, short[] x); + public native final void putInt32Array(long offset, int[] x); + public native final void putInt64Array(long offset, long[] x); + public native final void putFloatArray(long offset, float[] x); + public native final void putDoubleArray(long offset, double[] x); + public native final void putBlob(long offset, HwBlob blob); public native final long handle(); 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/LocaleList.java b/core/java/android/os/LocaleList.java index 2dc3bebb2d10..ca9cbec99cde 100644 --- a/core/java/android/os/LocaleList.java +++ b/core/java/android/os/LocaleList.java @@ -295,7 +295,11 @@ public final class LocaleList implements Parcelable { return STRING_EN_XA.equals(locale) || STRING_AR_XB.equals(locale); } - private static boolean isPseudoLocale(Locale locale) { + /** + * Returns true if locale is a pseudo-locale, false otherwise. + * {@hide} + */ + public static boolean isPseudoLocale(Locale locale) { return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale); } diff --git a/core/java/android/os/ShellCallback.java b/core/java/android/os/ShellCallback.java index e7fe697f9c54..ad9fbfbfae40 100644 --- a/core/java/android/os/ShellCallback.java +++ b/core/java/android/os/ShellCallback.java @@ -35,8 +35,9 @@ public class ShellCallback implements Parcelable { IShellCallback mShellCallback; class MyShellCallback extends IShellCallback.Stub { - public ParcelFileDescriptor openOutputFile(String path, String seLinuxContext) { - return onOpenOutputFile(path, seLinuxContext); + public ParcelFileDescriptor openFile(String path, String seLinuxContext, + String mode) { + return onOpenFile(path, seLinuxContext, mode); } } @@ -48,23 +49,27 @@ public class ShellCallback implements Parcelable { } /** - * Ask the shell to open a file for writing. This will truncate the file if it - * already exists. It will create the file if it doesn't exist. + * Ask the shell to open a file. If opening for writing, will truncate the file if it + * already exists and will create the file if it doesn't exist. * @param path Path of the file to be opened/created. * @param seLinuxContext Optional SELinux context that must be allowed to have * access to the file; if null, nothing is required. + * @param mode Mode to open file in: "r" for input/reading an existing file, + * "r+" for reading/writing an existing file, "w" for output/writing a new file (either + * creating or truncating an existing one), "w+" for reading/writing a new file (either + * creating or truncating an existing one). */ - public ParcelFileDescriptor openOutputFile(String path, String seLinuxContext) { - if (DEBUG) Log.d(TAG, "openOutputFile " + this + ": mLocal=" + mLocal + public ParcelFileDescriptor openFile(String path, String seLinuxContext, String mode) { + if (DEBUG) Log.d(TAG, "openFile " + this + " mode=" + mode + ": mLocal=" + mLocal + " mShellCallback=" + mShellCallback); if (mLocal) { - return onOpenOutputFile(path, seLinuxContext); + return onOpenFile(path, seLinuxContext, mode); } if (mShellCallback != null) { try { - return mShellCallback.openOutputFile(path, seLinuxContext); + return mShellCallback.openFile(path, seLinuxContext, mode); } catch (RemoteException e) { Log.w(TAG, "Failure opening " + path, e); } @@ -72,7 +77,7 @@ public class ShellCallback implements Parcelable { return null; } - public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) { + public ParcelFileDescriptor onOpenFile(String path, String seLinuxContext, String mode) { return null; } diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java index 6223235e628f..d75219fdfd11 100644 --- a/core/java/android/os/ShellCommand.java +++ b/core/java/android/os/ShellCommand.java @@ -226,10 +226,10 @@ public abstract class ShellCommand { * Helper for just system services to ask the shell to open an output file. * @hide */ - public ParcelFileDescriptor openOutputFileForSystem(String path) { + public ParcelFileDescriptor openFileForSystem(String path, String mode) { try { - ParcelFileDescriptor pfd = getShellCallback().openOutputFile(path, - "u:r:system_server:s0"); + ParcelFileDescriptor pfd = getShellCallback().openFile(path, + "u:r:system_server:s0", mode); if (pfd != null) { return pfd; } 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..2d6a7b0133aa 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; 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/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 3d38dc41aa9a..65bde49520d3 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -385,7 +385,7 @@ interface IWindowManager /** * Create an input consumer by name. */ - void createInputConsumer(String name, out InputChannel inputChannel); + void createInputConsumer(IBinder token, String name, out InputChannel inputChannel); /** * Destroy an input consumer by name. This method will also dispose the input channels 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/android/webkit/ServiceWorkerClient.java b/core/java/android/webkit/ServiceWorkerClient.java index d6e8f36ce101..9124c8554a26 100644 --- a/core/java/android/webkit/ServiceWorkerClient.java +++ b/core/java/android/webkit/ServiceWorkerClient.java @@ -29,9 +29,9 @@ public class ServiceWorkerClient { * application to return the data. If the return value is {@code null}, the * Service Worker will continue to load the resource as usual. * Otherwise, the return response and data will be used. - * NOTE: This method is called on a thread other than the UI thread - * so clients should exercise caution when accessing private data - * or the view system. + * + * <p class="note"><b>Note:</b> This method is called on a thread other than the UI thread so + * clients should exercise caution when accessing private data or the view system. * * @param request Object containing the details of the request. * @return A {@link android.webkit.WebResourceResponse} containing the diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 259bf60a3059..9295f5cb228a 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -191,7 +191,7 @@ import java.util.Map; * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)} * (introduced in API level {@link android.os.Build.VERSION_CODES#CUPCAKE}). * - * <p>NOTE: Using zoom if either the height or width is set to + * <p class="note"><b>Note:</b> Using zoom if either the height or width is set to * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} may lead to undefined behavior * and should be avoided. * @@ -536,9 +536,13 @@ public class WebView extends AbsoluteLayout } /** - * Constructs a new WebView with a Context object. + * Constructs a new WebView with an Activity Context object. * - * @param context a Context object used to access application assets + * <p class="note"><b>Note:</b> WebView should always be instantiated with an Activity Context. + * If instantiated with an Application Context, WebView will be unable to provide several + * features, such as JavaScript dialogs and autofill. + * + * @param context an Activity Context to access application assets */ public WebView(Context context) { this(context, null); @@ -547,7 +551,7 @@ public class WebView extends AbsoluteLayout /** * Constructs a new WebView with layout parameters. * - * @param context a Context object used to access application assets + * @param context an Activity Context to access application assets * @param attrs an AttributeSet passed to our parent */ public WebView(Context context, AttributeSet attrs) { @@ -557,7 +561,7 @@ public class WebView extends AbsoluteLayout /** * Constructs a new WebView with layout parameters and a default style. * - * @param context a Context object used to access application assets + * @param context an Activity Context to access application assets * @param attrs an AttributeSet passed to our parent * @param defStyleAttr an attribute in the current theme that contains a * reference to a style resource that supplies default values for @@ -570,7 +574,7 @@ public class WebView extends AbsoluteLayout /** * Constructs a new WebView with layout parameters and a default style. * - * @param context a Context object used to access application assets + * @param context an Activity Context to access application assets * @param attrs an AttributeSet passed to our parent * @param defStyleAttr an attribute in the current theme that contains a * reference to a style resource that supplies default values for @@ -587,7 +591,7 @@ public class WebView extends AbsoluteLayout /** * Constructs a new WebView with layout parameters and a default style. * - * @param context a Context object used to access application assets + * @param context an Activity Context to access application assets * @param attrs an AttributeSet passed to our parent * @param defStyleAttr an attribute in the current theme that contains a * reference to a style resource that supplies default values for @@ -612,7 +616,7 @@ public class WebView extends AbsoluteLayout * time. This guarantees that these interfaces will be available when the JS * context is initialized. * - * @param context a Context object used to access application assets + * @param context an Activity Context to access application assets * @param attrs an AttributeSet passed to our parent * @param defStyleAttr an attribute in the current theme that contains a * reference to a style resource that supplies default values for diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index c5b64eb89f44..517ad07c8323 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -130,7 +130,7 @@ public class WebViewClient { * <p>This method is called when the body of the HTTP response has started loading, is reflected * in the DOM, and will be visible in subsequent draws. This callback occurs early in the * document loading process, and as such you should expect that linked resources (for example, - * css and images) may not be available. + * CSS and images) may not be available. * * <p>For more fine-grained notification of visual state updates, see {@link * WebView#postVisualStateCallback}. @@ -150,13 +150,15 @@ public class WebViewClient { * Notify the host application of a resource request and allow the * application to return the data. If the return value is {@code null}, the WebView * will continue to load the resource as usual. Otherwise, the return - * response and data will be used. NOTE: This method is called on a thread + * response and data will be used. + * + * <p class="note"><b>Note:</b> This method is called on a thread * other than the UI thread so clients should exercise caution * when accessing private data or the view system. * - * <p>Note: when Safe Browsing is enabled, these URLs still undergo Safe Browsing checks. If - * this is undesired, whitelist the URL with {@link WebView#setSafeBrowsingWhitelist} or ignore - * the warning with {@link #onSafeBrowsingHit}. + * <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe + * Browsing checks. If this is undesired, whitelist the URL with {@link + * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}. * * @param view The {@link android.webkit.WebView} that is requesting the * resource. @@ -178,13 +180,15 @@ public class WebViewClient { * Notify the host application of a resource request and allow the * application to return the data. If the return value is {@code null}, the WebView * will continue to load the resource as usual. Otherwise, the return - * response and data will be used. NOTE: This method is called on a thread + * response and data will be used. + * + * <p class="note"><b>Note:</b> This method is called on a thread * other than the UI thread so clients should exercise caution * when accessing private data or the view system. * - * <p>Note: when Safe Browsing is enabled, these URLs still undergo Safe Browsing checks. If - * this is undesired, whitelist the URL with {@link WebView#setSafeBrowsingWhitelist} or ignore - * the warning with {@link #onSafeBrowsingHit}. + * <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe + * Browsing checks. If this is undesired, whitelist the URL with {@link + * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}. * * @param view The {@link android.webkit.WebView} that is requesting the * resource. @@ -248,7 +252,7 @@ public class WebViewClient { public static final int ERROR_FILE_NOT_FOUND = -14; /** Too many requests during this load */ public static final int ERROR_TOO_MANY_REQUESTS = -15; - /** Resource load was cancelled by Safe Browsing */ + /** Resource load was canceled by Safe Browsing */ public static final int ERROR_UNSAFE_RESOURCE = -16; /** @hide */ @@ -272,8 +276,8 @@ public class WebViewClient { /** * Report an error to the host application. These errors are unrecoverable - * (i.e. the main resource is unavailable). The errorCode parameter - * corresponds to one of the ERROR_* constants. + * (i.e. the main resource is unavailable). The {@code errorCode} parameter + * corresponds to one of the {@code ERROR_*} constants. * @param view The WebView that is initiating the callback. * @param errorCode The error code corresponding to an ERROR_* value. * @param description A String describing the error. @@ -289,11 +293,11 @@ public class WebViewClient { /** * Report web resource loading error to the host application. These errors usually indicate * inability to connect to the server. Note that unlike the deprecated version of the callback, - * the new version will be called for any resource (iframe, image, etc), not just for the main + * the new version will be called for any resource (iframe, image, etc.), not just for the main * page. Thus, it is recommended to perform minimum required work in this callback. * @param view The WebView that is initiating the callback. * @param request The originating request. - * @param error Information about the error occured. + * @param error Information about the error occurred. */ public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { if (request.isForMainFrame()) { @@ -306,12 +310,12 @@ public class WebViewClient { /** * Notify the host application that an HTTP error has been received from the server while * loading a resource. HTTP errors have status codes >= 400. This callback will be called - * for any resource (iframe, image, etc), not just for the main page. Thus, it is recommended to - * perform minimum required work in this callback. Note that the content of the server - * response may not be provided within the <b>errorResponse</b> parameter. + * for any resource (iframe, image, etc.), not just for the main page. Thus, it is recommended + * to perform minimum required work in this callback. Note that the content of the server + * response may not be provided within the {@code errorResponse} parameter. * @param view The WebView that is initiating the callback. * @param request The originating request. - * @param errorResponse Information about the error occured. + * @param errorResponse Information about the error occurred. */ public void onReceivedHttpError( WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { @@ -365,7 +369,7 @@ public class WebViewClient { * if desired and providing the keys. There are three ways to * respond: proceed(), cancel() or ignore(). Webview stores the response * in memory (for the life of the application) if proceed() or cancel() is - * called and does not call onReceivedClientCertRequest() again for the + * called and does not call {@code onReceivedClientCertRequest()} again for the * same host and port pair. Webview does not store the response if ignore() * is called. Note that, multiple layers in chromium network stack might be * caching the responses, so the behavior for ignore is only a best case @@ -432,7 +436,7 @@ public class WebViewClient { /** * Notify the host application that a key was not handled by the WebView. * Except system keys, WebView always consumes the keys in the normal flow - * or if shouldOverrideKeyEvent returns {@code true}. This is called asynchronously + * or if {@link #shouldOverrideKeyEvent} returns {@code true}. This is called asynchronously * from where the key is dispatched. It gives the host application a chance * to handle the unhandled key events. * @@ -446,7 +450,7 @@ public class WebViewClient { /** * Notify the host application that a input event was not handled by the WebView. * Except system keys, WebView always consumes input events in the normal flow - * or if shouldOverrideKeyEvent returns {@code true}. This is called asynchronously + * or if {@link #shouldOverrideKeyEvent} returns {@code true}. This is called asynchronously * from where the event is dispatched. It gives the host application a chance * to handle the unhandled input events. * @@ -503,7 +507,7 @@ public class WebViewClient { } /** - * Notify host application that the given webview's render process has exited. + * Notify host application that the given WebView's render process has exited. * * Multiple WebView instances may be associated with a single render process; * onRenderProcessGone will be called for each WebView that was affected. @@ -513,10 +517,10 @@ public class WebViewClient { * * The given WebView can't be used, and should be removed from the view hierarchy, * all references to it should be cleaned up, e.g any references in the Activity - * or other classes saved using findViewById and similar calls, etc + * or other classes saved using {@link android.view.View#findViewById} and similar calls, etc. * * To cause an render process crash for test purpose, the application can - * call loadUrl("chrome://crash") on the WebView. Note that multiple WebView + * call {@code loadUrl("chrome://crash")} on the WebView. Note that multiple WebView * instances may be affected if they share a render process, not just the * specific WebView which loaded chrome://crash. * @@ -537,12 +541,13 @@ public class WebViewClient { * behavior is to show an interstitial to the user, with the reporting checkbox visible. * * If the application needs to show its own custom interstitial UI, the callback can be invoked - * asynchronously with backToSafety() or proceed(), depending on user response. + * asynchronously with {@link SafeBrowsingResponse#backToSafety} or {@link + * SafeBrowsingResponse#proceed}, depending on user response. * * @param view The WebView that hit the malicious resource. * @param request Object containing the details of the request. * @param threatType The reason the resource was caught by Safe Browsing, corresponding to a - * SAFE_BROWSING_THREAT_* value. + * {@code SAFE_BROWSING_THREAT_*} value. * @param callback Applications must invoke one of the callback methods. */ public void onSafeBrowsingHit(WebView view, WebResourceRequest request, diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java index 341c69fd2eba..de0b97d15e23 100644 --- a/core/java/android/webkit/WebViewLibraryLoader.java +++ b/core/java/android/webkit/WebViewLibraryLoader.java @@ -229,7 +229,9 @@ public class WebViewLibraryLoader { /** * Load WebView's native library into the current process. - * Note: assumes that we have waited for relro creation. + * + * <p class="note"><b>Note:</b> Assumes that we have waited for relro creation. + * * @param clazzLoader class loader used to find the linker namespace to load the library into. * @param packageInfo the package from which WebView is loaded. */ diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index c46c681c3285..a8969252ff2e 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -316,7 +316,7 @@ public interface WebViewProvider { /** * Provides mechanism for the name-sake methods declared in View and ViewGroup to be delegated * into the WebViewProvider instance. - * NOTE For many of these methods, the WebView will provide a super.Foo() call before or after + * NOTE: For many of these methods, the WebView will provide a super.Foo() call before or after * making the call into the provider instance. This is done for convenience in the common case * of maintaining backward compatibility. For remaining super class calls (e.g. where the * provider may need to only conditionally make the call based on some internal state) see the diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java index 386aa84bfdbe..0a230a90a735 100644 --- a/core/java/com/android/internal/app/LocaleHelper.java +++ b/core/java/com/android/internal/app/LocaleHelper.java @@ -136,7 +136,16 @@ public class LocaleHelper { * @return the localized country name. */ public static String getDisplayCountry(Locale locale, Locale displayLocale) { - return ULocale.getDisplayCountry(locale.toLanguageTag(), ULocale.forLocale(displayLocale)); + final String languageTag = locale.toLanguageTag(); + final ULocale uDisplayLocale = ULocale.forLocale(displayLocale); + final String country = ULocale.getDisplayCountry(languageTag, uDisplayLocale); + final String numberingSystem = locale.getUnicodeLocaleType("nu"); + if (numberingSystem != null) { + return String.format("%s (%s)", country, + ULocale.getDisplayKeywordValue(languageTag, "numbers", uDisplayLocale)); + } else { + return country; + } } /** diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java index 9936ed5c6491..c8c2fcf60d1f 100644 --- a/core/java/com/android/internal/app/LocalePicker.java +++ b/core/java/com/android/internal/app/LocalePicker.java @@ -93,10 +93,6 @@ public class LocalePicker extends ListFragment { return context.getResources().getStringArray(R.array.supported_locales); } - public static String[] getPseudoLocales() { - return pseudoLocales; - } - public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) { final Resources resources = context.getResources(); @@ -104,13 +100,6 @@ public class LocalePicker extends ListFragment { List<String> localeList = new ArrayList<String>(locales.length); Collections.addAll(localeList, locales); - // Don't show the pseudolocales unless we're in developer mode. http://b/17190407. - if (!isInDeveloperMode) { - for (String locale : pseudoLocales) { - localeList.remove(locale); - } - } - Collections.sort(localeList); final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes); final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names); @@ -122,6 +111,10 @@ public class LocalePicker extends ListFragment { || l.getLanguage().isEmpty() || l.getCountry().isEmpty()) { continue; } + // Don't show the pseudolocales unless we're in developer mode. http://b/17190407. + if (!isInDeveloperMode && LocaleList.isPseudoLocale(l)) { + continue; + } if (localeInfos.isEmpty()) { if (DEBUG) { diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java index e3fce5197dca..2b0b5eec6c56 100644 --- a/core/java/com/android/internal/app/LocaleStore.java +++ b/core/java/com/android/internal/app/LocaleStore.java @@ -17,6 +17,7 @@ package com.android.internal.app; import android.content.Context; +import android.os.LocaleList; import android.provider.Settings; import android.telephony.TelephonyManager; @@ -68,7 +69,9 @@ public class LocaleStore { return null; } return new Locale.Builder() - .setLocale(locale).setRegion("") + .setLocale(locale) + .setRegion("") + .setExtension(Locale.UNICODE_LOCALE_EXTENSION, "") .build(); } @@ -253,11 +256,25 @@ public class LocaleStore { Set<String> simCountries = getSimCountries(context); + final boolean isInDeveloperMode = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; for (String localeId : LocalePicker.getSupportedLocales(context)) { if (localeId.isEmpty()) { throw new IllformedLocaleException("Bad locale entry in locale_config.xml"); } LocaleInfo li = new LocaleInfo(localeId); + + if (LocaleList.isPseudoLocale(li.getLocale())) { + if (isInDeveloperMode) { + li.setTranslated(true); + li.mIsPseudo = true; + li.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM; + } else { + // Do not display pseudolocales unless in development mode. + continue; + } + } + if (simCountries.contains(li.getLocale().getCountry())) { li.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM; } @@ -271,19 +288,6 @@ public class LocaleStore { } } - boolean isInDeveloperMode = Settings.Global.getInt(context.getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; - for (String localeId : LocalePicker.getPseudoLocales()) { - LocaleInfo li = getLocaleInfo(Locale.forLanguageTag(localeId)); - if (isInDeveloperMode) { - li.setTranslated(true); - li.mIsPseudo = true; - li.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM; - } else { - sLocaleCache.remove(li.getId()); - } - } - // TODO: See if we can reuse what LocaleList.matchScore does final HashSet<String> localizedLocales = new HashSet<>(); for (String localeId : LocalePicker.getSystemAssetLocales()) { diff --git a/core/java/com/android/internal/app/NightDisplayController.java b/core/java/com/android/internal/app/NightDisplayController.java index b2053c004e2f..b8bfc6491f70 100644 --- a/core/java/com/android/internal/app/NightDisplayController.java +++ b/core/java/com/android/internal/app/NightDisplayController.java @@ -25,6 +25,7 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.Looper; +import android.os.SystemProperties; import android.provider.Settings.Secure; import android.provider.Settings.System; import android.util.Slog; @@ -100,6 +101,12 @@ public final class NightDisplayController { */ public static final int COLOR_MODE_SATURATED = 2; + /** + * See com.android.server.display.DisplayTransformManager. + */ + private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation"; + private static final String PERSISTENT_PROPERTY_NATIVE_MODE = "persist.sys.sf.native_mode"; + private final Context mContext; private final int mUserId; @@ -334,9 +341,15 @@ public final class NightDisplayController { */ public int getColorMode() { final int colorMode = System.getIntForUser(mContext.getContentResolver(), - System.DISPLAY_COLOR_MODE, COLOR_MODE_BOOSTED, mUserId); + System.DISPLAY_COLOR_MODE, -1, mUserId); if (colorMode < COLOR_MODE_NATURAL || colorMode > COLOR_MODE_SATURATED) { - return COLOR_MODE_BOOSTED; + // There still might be a legacy system property controlling color mode that we need to + // respect. + if ("1".equals(SystemProperties.get(PERSISTENT_PROPERTY_NATIVE_MODE))) { + return COLOR_MODE_SATURATED; + } + return "1.0".equals(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) + ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED; } return colorMode; } 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/net/OWNERS b/core/java/com/android/internal/net/OWNERS index 7cb32ff793e7..e2064a8099ae 100644 --- a/core/java/com/android/internal/net/OWNERS +++ b/core/java/com/android/internal/net/OWNERS @@ -2,5 +2,5 @@ set noparent ek@google.com hugobenichi@google.com -jsharkey@google.com +jsharkey@android.com lorenzo@google.com 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/os/IShellCallback.aidl b/core/java/com/android/internal/os/IShellCallback.aidl index 57d67890d840..57043424fc47 100644 --- a/core/java/com/android/internal/os/IShellCallback.aidl +++ b/core/java/com/android/internal/os/IShellCallback.aidl @@ -20,5 +20,5 @@ import android.os.ParcelFileDescriptor; /** @hide */ interface IShellCallback { - ParcelFileDescriptor openOutputFile(String path, String seLinuxContext); + ParcelFileDescriptor openFile(String path, String seLinuxContext, String mode); } 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 c4f22eeb8b95..f6783e15e294 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -296,7 +296,8 @@ static jobject JHwBinder_native_getService( JNIEnv *env, jclass /* clazzObj */, jstring ifaceNameObj, - jstring serviceNameObj) { + jstring serviceNameObj, + jboolean retry) { using ::android::hidl::base::V1_0::IBase; using ::android::hardware::details::getRawServiceInternal; @@ -319,8 +320,7 @@ static jobject JHwBinder_native_getService( serviceName = str.c_str(); } - // TODO(b/67981006): true /* retry */ - sp<IBase> ret = getRawServiceInternal(ifaceName, serviceName, false /* retry */, false /* getStub */); + sp<IBase> ret = getRawServiceInternal(ifaceName, serviceName, retry /* retry */, false /* getStub */); sp<hardware::IBinder> service = hardware::toBinder<hidl::base::V1_0::IBase>(ret); if (service == NULL) { @@ -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(); @@ -360,7 +371,7 @@ static JNINativeMethod gMethods[] = { { "registerService", "(Ljava/lang/String;)V", (void *)JHwBinder_native_registerService }, - { "getService", "(Ljava/lang/String;Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;", + { "getService", "(Ljava/lang/String;Ljava/lang/String;Z)L" PACKAGE_PATH "/IHwBinder;", (void *)JHwBinder_native_getService }, { "configureRpcThreadpool", "(JZ)V", @@ -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 40d49b7662d9..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" @@ -60,12 +61,12 @@ sp<JHwBlob> JHwBlob::SetNativeContext( JNIEnv *env, jobject thiz, const sp<JHwBlob> &context) { sp<JHwBlob> old = (JHwBlob *)env->GetLongField(thiz, gFields.contextID); - if (context != NULL) { - context->incStrong(NULL /* id */); + if (context != nullptr) { + context->incStrong(nullptr /* id */); } - if (old != NULL) { - old->decStrong(NULL /* id */); + if (old != nullptr) { + old->decStrong(nullptr /* id */); } env->SetLongField(thiz, gFields.contextID, (long)context.get()); @@ -150,6 +151,10 @@ const void *JHwBlob::data() const { return mBuffer; } +void *JHwBlob::data() { + return mBuffer; +} + size_t JHwBlob::size() const { return mSize; } @@ -242,8 +247,8 @@ using namespace android; static void releaseNativeContext(void *nativeContext) { sp<JHwBlob> parcel = (JHwBlob *)nativeContext; - if (parcel != NULL) { - parcel->decStrong(NULL /* id */); + if (parcel != nullptr) { + parcel->decStrong(nullptr /* id */); } } @@ -313,6 +318,82 @@ static jstring JHwBlob_native_getString( return env->NewStringUTF(s->c_str()); } +#define DEFINE_BLOB_ARRAY_COPIER(Suffix,Type,NewType) \ +static void JHwBlob_native_copyTo ## Suffix ## Array( \ + JNIEnv *env, \ + jobject thiz, \ + jlong offset, \ + Type ## Array array, \ + jint size) { \ + if (array == nullptr) { \ + jniThrowException(env, "java/lang/NullPointerException", nullptr); \ + return; \ + } \ + \ + if (env->GetArrayLength(array) < size) { \ + signalExceptionForError(env, BAD_VALUE); \ + return; \ + } \ + \ + sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \ + \ + if ((offset + size * sizeof(Type)) > blob->size()) { \ + signalExceptionForError(env, -ERANGE); \ + return; \ + } \ + \ + env->Set ## NewType ## ArrayRegion( \ + array, \ + 0 /* start */, \ + size, \ + reinterpret_cast<const Type *>( \ + 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, + jlong offset, + jbooleanArray array, + jint size) { + if (array == nullptr) { + jniThrowException(env, "java/lang/NullPointerException", nullptr); + return; + } + + if (env->GetArrayLength(array) < size) { + signalExceptionForError(env, BAD_VALUE); + return; + } + + sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); + + if ((offset + size * sizeof(bool)) > blob->size()) { + signalExceptionForError(env, -ERANGE); + return; + } + + const bool *src = + reinterpret_cast<const bool *>( + static_cast<const uint8_t *>(blob->data()) + offset); + + jboolean *dst = env->GetBooleanArrayElements(array, nullptr /* isCopy */); + + for (jint i = 0; i < size; ++i) { + dst[i] = src[i]; + } + + env->ReleaseBooleanArrayElements(array, dst, 0 /* mode */); + dst = nullptr; +} + #define DEFINE_BLOB_PUTTER(Suffix,Type) \ static void JHwBlob_native_put ## Suffix( \ JNIEnv *env, jobject thiz, jlong offset, Type x) { \ @@ -375,6 +456,59 @@ static void JHwBlob_native_putString( blob->putBlob(offset + hidl_string::kOffsetOfBuffer, subBlob); } +#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) { \ + /* NullpointerException already pending */ \ + return; \ + } \ + \ + sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \ + \ + status_t err = blob->write( \ + offset, autoArray.get(), autoArray.size() * sizeof(Type)); \ + \ + if (err != OK) { \ + signalExceptionForError(env, err); \ + } \ +} + +DEFINE_BLOB_ARRAY_PUTTER(Int8,jbyte,Byte) +DEFINE_BLOB_ARRAY_PUTTER(Int16,jshort,Short) +DEFINE_BLOB_ARRAY_PUTTER(Int32,jint,Int) +DEFINE_BLOB_ARRAY_PUTTER(Int64,jlong,Long) +DEFINE_BLOB_ARRAY_PUTTER(Float,jfloat,Float) +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) { + /* NullpointerException already pending */ + return; + } + + sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); + + if ((offset + autoArray.size() * sizeof(bool)) > blob->size()) { + signalExceptionForError(env, -ERANGE); + return; + } + + const jboolean *src = autoArray.get(); + + bool *dst = reinterpret_cast<bool *>( + static_cast<uint8_t *>(blob->data()) + offset); + + for (size_t i = 0; i < autoArray.size(); ++i) { + dst[i] = src[i]; + } +} + static void JHwBlob_native_putBlob( JNIEnv *env, jobject thiz, jlong offset, jobject blobObj) { if (blobObj == nullptr) { @@ -413,6 +547,14 @@ static JNINativeMethod gMethods[] = { { "getDouble", "(J)D", (void *)JHwBlob_native_getDouble }, { "getString", "(J)Ljava/lang/String;", (void *)JHwBlob_native_getString }, + { "copyToBoolArray", "(J[ZI)V", (void *)JHwBlob_native_copyToBoolArray }, + { "copyToInt8Array", "(J[BI)V", (void *)JHwBlob_native_copyToInt8Array }, + { "copyToInt16Array", "(J[SI)V", (void *)JHwBlob_native_copyToInt16Array }, + { "copyToInt32Array", "(J[II)V", (void *)JHwBlob_native_copyToInt32Array }, + { "copyToInt64Array", "(J[JI)V", (void *)JHwBlob_native_copyToInt64Array }, + { "copyToFloatArray", "(J[FI)V", (void *)JHwBlob_native_copyToFloatArray }, + { "copyToDoubleArray", "(J[DI)V", (void *)JHwBlob_native_copyToDoubleArray }, + { "putBool", "(JZ)V", (void *)JHwBlob_native_putBool }, { "putInt8", "(JB)V", (void *)JHwBlob_native_putInt8 }, { "putInt16", "(JS)V", (void *)JHwBlob_native_putInt16 }, @@ -422,6 +564,14 @@ static JNINativeMethod gMethods[] = { { "putDouble", "(JD)V", (void *)JHwBlob_native_putDouble }, { "putString", "(JLjava/lang/String;)V", (void *)JHwBlob_native_putString }, + { "putBoolArray", "(J[Z)V", (void *)JHwBlob_native_putBoolArray }, + { "putInt8Array", "(J[B)V", (void *)JHwBlob_native_putInt8Array }, + { "putInt16Array", "(J[S)V", (void *)JHwBlob_native_putInt16Array }, + { "putInt32Array", "(J[I)V", (void *)JHwBlob_native_putInt32Array }, + { "putInt64Array", "(J[J)V", (void *)JHwBlob_native_putInt64Array }, + { "putFloatArray", "(J[F)V", (void *)JHwBlob_native_putFloatArray }, + { "putDoubleArray", "(J[D)V", (void *)JHwBlob_native_putDoubleArray }, + { "putBlob", "(JL" PACKAGE_PATH "/HwBlob;)V", (void *)JHwBlob_native_putBlob }, diff --git a/core/jni/android_os_HwBlob.h b/core/jni/android_os_HwBlob.h index 39393cb76c23..6b1db639ac19 100644 --- a/core/jni/android_os_HwBlob.h +++ b/core/jni/android_os_HwBlob.h @@ -50,6 +50,8 @@ struct JHwBlob : public RefBase { size_t offset, const android::hardware::hidl_string **s) const; const void *data() const; + void *data(); + size_t size() const; status_t putBlob(size_t offset, const sp<JHwBlob> &blob); 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/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index b137da3ef792..c6828c4f60de 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -568,6 +568,35 @@ static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject cla return (res) ? (jint)cookie : 0; } +static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, + jobject fileDescriptor, jstring debugPathName, + jboolean appAsLib) +{ + ScopedUtfChars debugPathName8(env, debugPathName); + + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (fd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + int dupfd = ::dup(fd); + if (dupfd < 0) { + jniThrowIOException(env, errno); + return 0; + } + + int32_t cookie; + bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); + + return (res) ? static_cast<jint>(cookie) : 0; +} + static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) { AssetManager* am = assetManagerForJavaObject(env, clazz); @@ -1673,6 +1702,8 @@ static const JNINativeMethod gAssetManagerMethods[] = { (void*) android_content_AssetManager_getAssetRemainingLength }, { "addAssetPathNative", "(Ljava/lang/String;Z)I", (void*) android_content_AssetManager_addAssetPath }, + { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", + (void*) android_content_AssetManager_addAssetFd }, { "addOverlayPathNative", "(Ljava/lang/String;)I", (void*) android_content_AssetManager_addOverlayPath }, { "isUpToDate", "()Z", 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/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto index 12649e1f0ab2..d032a452c14e 100644 --- a/core/proto/android/os/kernelwake.proto +++ b/core/proto/android/os/kernelwake.proto @@ -13,11 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - syntax = "proto2"; + option java_multiple_files = true; option java_outer_classname = "WakeupSourcesProto"; +import "frameworks/base/tools/streaming_proto/stream.proto"; + package android.os; message KernelWakeSources { @@ -27,6 +29,8 @@ message KernelWakeSources { // Next Tag: 11 message WakeupSourceProto { + option (stream_proto.stream).enable_fields_mapping = true; + // Name of the event which triggers application processor optional string name = 1; diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto index f82ea7672879..22b3d730f1a1 100644 --- a/core/proto/android/os/pagetypeinfo.proto +++ b/core/proto/android/os/pagetypeinfo.proto @@ -18,6 +18,8 @@ syntax = "proto2"; option java_multiple_files = true; option java_outer_classname = "PageTypeInfoProto"; +import "frameworks/base/tools/streaming_proto/stream.proto"; + package android.os; /* @@ -61,6 +63,7 @@ message MigrateTypeProto { // Next tag: 9 message BlockProto { + option (stream_proto.stream).enable_fields_mapping = true; optional int32 node = 1; diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto index ab6a6a32f698..4d62a60c8345 100644 --- a/core/proto/android/os/procrank.proto +++ b/core/proto/android/os/procrank.proto @@ -18,6 +18,8 @@ syntax = "proto2"; option java_multiple_files = true; option java_outer_classname = "ProcrankProto"; +import "frameworks/base/tools/streaming_proto/stream.proto"; + package android.os; //Memory usage of running processes @@ -31,6 +33,8 @@ message Procrank { // Next Tag: 11 message ProcessProto { + option (stream_proto.stream).enable_fields_mapping = true; + // ID of the process optional int32 pid = 1; diff --git a/core/proto/android/server/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-bs/strings.xml b/core/res/res/values-bs/strings.xml index 57dd73f5a192..aa83d1d52b55 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -1653,7 +1653,6 @@ <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2. poslovni <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3. poslovni <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="lock_to_app_toast" msgid="6820571533009838261">"Da otkačite ovaj ekran, dodirnite i držite dugmad Nazad i Pregled."</string> - <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"Ova aplikacija se ne može otkačiti"</string> <string name="lock_to_app_start" msgid="6643342070839862795">"Ekran je zakačen"</string> <string name="lock_to_app_exit" msgid="8598219838213787430">"Ekran je otkačen"</string> <string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Traži PIN prije nego se otkači"</string> @@ -1819,14 +1818,10 @@ <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Test poruka za hitne slučajeve"</string> <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Odgovori"</string> <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string> - <!-- no translation found for mmcc_authentication_reject (5767701075994754356) --> - <skip /> - <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) --> - <skip /> - <!-- no translation found for mmcc_illegal_ms (807334478177362062) --> - <skip /> - <!-- no translation found for mmcc_illegal_me (1950705155760872972) --> - <skip /> + <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM kartica nije dozvoljena za govor"</string> + <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM kartica nije dodijeljena za govor"</string> + <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM kartica nije dozvoljena za govor"</string> + <string name="mmcc_illegal_me" msgid="1950705155760872972">"Telefon nije dozvoljen za govor"</string> <string name="popup_window_default_title" msgid="4874318849712115433">"Iskočni prozor"</string> <string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string> <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Za ovu prečicu potrebna je najnovija aplikacija"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index cb7500429edd..57312e7444e0 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1635,8 +1635,8 @@ <string name="package_updated_device_owner" msgid="1847154566357862089">"Diupdate oleh admin Anda"</string> <string name="package_deleted_device_owner" msgid="2307122077550236438">"Dihapus oleh admin Anda"</string> <string name="battery_saver_description" msgid="1960431123816253034">"Untuk membantu meningkatkan masa pakai baterai, penghemat baterai mengurangi kinerja perangkat dan membatasi getaran, layanan lokasi, dan sebagian besar data latar belakang. Email, pesan, dan aplikasi lain yang mengandalkan sinkronisasi mungkin tidak diperbarui kecuali jika dibuka.\n\nPenghemat baterai otomatis nonaktif jika perangkat diisi dayanya."</string> - <string name="data_saver_description" msgid="6015391409098303235">"Untuk membantu mengurangi penggunaan data, Penghemat Data mencegah beberapa aplikasi mengirim atau menerima data di latar belakang. Aplikasi yang sedang digunakan dapat mengakses data, tetapi frekuensinya agak lebih jarang. Misalnya saja, gambar hanya akan ditampilkan setelah disentuh."</string> - <string name="data_saver_enable_title" msgid="4674073932722787417">"Aktifkan Penghemat Data?"</string> + <string name="data_saver_description" msgid="6015391409098303235">"Untuk membantu mengurangi penggunaan data, Penghemat Kuota Internet mencegah beberapa aplikasi mengirim atau menerima data di latar belakang. Aplikasi yang sedang digunakan dapat mengakses data, tetapi frekuensinya agak lebih jarang. Misalnya saja, gambar hanya akan ditampilkan setelah disentuh."</string> + <string name="data_saver_enable_title" msgid="4674073932722787417">"Aktifkan Penghemat Kuota Internet?"</string> <string name="data_saver_enable_button" msgid="7147735965247211818">"Aktifkan"</string> <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848"> <item quantity="other">Selama %1$d menit (hingga <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index aa2cc3d1ed44..1976bbe6bfe0 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1231,7 +1231,7 @@ <string name="ext_media_unmounting_notification_title" msgid="640674168454809372">"아직 <xliff:g id="NAME">%s</xliff:g> 꺼내는 중…"</string> <string name="ext_media_unmounting_notification_message" msgid="4182843895023357756">"삭제하지 마세요."</string> <string name="ext_media_init_action" msgid="7952885510091978278">"설정"</string> - <string name="ext_media_unmount_action" msgid="1121883233103278199">"꺼내기"</string> + <string name="ext_media_unmount_action" msgid="1121883233103278199">"마운트 해제"</string> <string name="ext_media_browse_action" msgid="8322172381028546087">"둘러보기"</string> <string name="ext_media_missing_title" msgid="620980315821543904">"<xliff:g id="NAME">%s</xliff:g> 없음"</string> <string name="ext_media_missing_message" msgid="5761133583368750174">"기기 다시 삽입"</string> @@ -1626,7 +1626,6 @@ <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"두 번째 업무용 <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"세 번째 업무용<xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="lock_to_app_toast" msgid="6820571533009838261">"이 화면을 고정 해제하려면 \'뒤로\' 및 \'최근 사용\'을 길게 터치하세요."</string> - <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"이 앱은 고정 해제할 수 없습니다."</string> <string name="lock_to_app_start" msgid="6643342070839862795">"화면 고정됨"</string> <string name="lock_to_app_exit" msgid="8598219838213787430">"화면 고정 해제됨"</string> <string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"고정 해제 이전에 PIN 요청"</string> @@ -1782,14 +1781,10 @@ <string name="etws_primary_default_message_test" msgid="2709597093560037455">"긴급 메시지 테스트"</string> <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"답장"</string> <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string> - <!-- no translation found for mmcc_authentication_reject (5767701075994754356) --> - <skip /> - <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) --> - <skip /> - <!-- no translation found for mmcc_illegal_ms (807334478177362062) --> - <skip /> - <!-- no translation found for mmcc_illegal_me (1950705155760872972) --> - <skip /> + <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM에서 음성이 허용되지 않음"</string> + <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM이 음성으로 프로비저닝되지 않음"</string> + <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM에서 음성이 허용되지 않음"</string> + <string name="mmcc_illegal_me" msgid="1950705155760872972">"휴대전화에서 음성이 허용되지 않음"</string> <string name="popup_window_default_title" msgid="4874318849712115433">"팝업 창"</string> <string name="slice_more_content" msgid="8504342889413274608">"<xliff:g id="NUMBER">%1$d</xliff:g>개 더보기"</string> <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"바로가기를 사용하려면 최신 앱이 필요합니다"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index cc6cdc93b76c..d7c10ecb76cb 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -1676,7 +1676,6 @@ <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"<xliff:g id="LABEL">%1$s</xliff:g> – praca 2"</string> <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"<xliff:g id="LABEL">%1$s</xliff:g> – praca 3"</string> <string name="lock_to_app_toast" msgid="6820571533009838261">"Aby odpiąć ten ekran, naciśnij i przytrzymaj Wstecz oraz Przegląd"</string> - <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"Tej aplikacji nie można odpiąć"</string> <string name="lock_to_app_start" msgid="6643342070839862795">"Ekran przypięty"</string> <string name="lock_to_app_exit" msgid="8598219838213787430">"Ekran odpięty"</string> <string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Podaj PIN, aby odpiąć"</string> @@ -1852,14 +1851,10 @@ <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Test komunikatów alarmowych"</string> <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Odpowiedz"</string> <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string> - <!-- no translation found for mmcc_authentication_reject (5767701075994754356) --> - <skip /> - <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) --> - <skip /> - <!-- no translation found for mmcc_illegal_ms (807334478177362062) --> - <skip /> - <!-- no translation found for mmcc_illegal_me (1950705155760872972) --> - <skip /> + <string name="mmcc_authentication_reject" msgid="5767701075994754356">"Karta SIM jest niedozwolona w przypadku usług głosowych"</string> + <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"Karta SIM nie jest obsługiwana w przypadku usług głosowych"</string> + <string name="mmcc_illegal_ms" msgid="807334478177362062">"Karta SIM jest niedozwolona w przypadku usług głosowych"</string> + <string name="mmcc_illegal_me" msgid="1950705155760872972">"Telefon jest niedozwolony w przypadku usług głosowych"</string> <string name="popup_window_default_title" msgid="4874318849712115433">"Wyskakujące okienko"</string> <string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string> <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Ten skrót wymaga zainstalowania najnowszej wersji aplikacji"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index af5cfeca6004..2de83c6106d5 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -610,7 +610,7 @@ <item msgid="3145118944639869809">"Personalizado"</item> </string-array> <string-array name="organizationTypes"> - <item msgid="7546335612189115615">"Emprego"</item> + <item msgid="7546335612189115615">"Trabalho"</item> <item msgid="4378074129049520373">"Outro"</item> <item msgid="3455047468583965104">"Personalizado"</item> </string-array> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 62b54d2b5c61..429ecf109bfd 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -1626,7 +1626,6 @@ <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2வது பணி <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3வது பணி <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="lock_to_app_toast" msgid="6820571533009838261">"இந்தத் திரையை அகற்ற, முந்தையது, மேலோட்டப் பார்வை ஆகிய இரண்டு பொத்தானையும் தொட்டுப் பிடித்திருக்கவும்"</string> - <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"இந்தப் பயன்பாட்டை அகற்ற முடியாது"</string> <string name="lock_to_app_start" msgid="6643342070839862795">"திரை பின் செய்யப்பட்டது"</string> <string name="lock_to_app_exit" msgid="8598219838213787430">"திரையின் பின் அகற்றப்பட்டது"</string> <string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"அகற்றும் முன் PINஐக் கேள்"</string> @@ -1782,14 +1781,10 @@ <string name="etws_primary_default_message_test" msgid="2709597093560037455">"அவசரக் காலச் செய்திகளுக்கான சோதனை"</string> <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"பதிலளிக்கும்"</string> <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string> - <!-- no translation found for mmcc_authentication_reject (5767701075994754356) --> - <skip /> - <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) --> - <skip /> - <!-- no translation found for mmcc_illegal_ms (807334478177362062) --> - <skip /> - <!-- no translation found for mmcc_illegal_me (1950705155760872972) --> - <skip /> + <string name="mmcc_authentication_reject" msgid="5767701075994754356">"குரல் அழைப்பை மேற்கொள்ள இந்த சிம்மிற்கு அனுமதி இல்லை"</string> + <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"குரல் அழைப்பை மேற்கொள்ளும் வசதி இந்த சிம்மிற்கு இல்லை"</string> + <string name="mmcc_illegal_ms" msgid="807334478177362062">"குரல் அழைப்பை மேற்கொள்ள இந்த சிம்மிற்கு அனுமதி இல்லை"</string> + <string name="mmcc_illegal_me" msgid="1950705155760872972">"குரல் அழைப்பை மேற்கொள்ள இந்த ஃபோனுக்கு அனுமதி இல்லை"</string> <string name="popup_window_default_title" msgid="4874318849712115433">"பாப்அப் சாளரம்"</string> <string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string> <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"இந்த ஷார்ட்கட்டைப் பயன்படுத்த, சமீபத்திய பயன்பாடு வேண்டும்"</string> 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 face9cd45017..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. --> @@ -3153,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/locale_config.xml b/core/res/res/values/locale_config.xml index 04ea0771f8e9..2c4058acc972 100644 --- a/core/res/res/values/locale_config.xml +++ b/core/res/res/values/locale_config.xml @@ -27,6 +27,7 @@ <item>ar-DJ</item> <!-- Arabic (Djibouti) --> <item>ar-DZ</item> <!-- Arabic (Algeria) --> <item>ar-EG</item> <!-- Arabic (Egypt) --> + <item>ar-EG-u-nu-latn</item> <!-- Arabic (Egypt,Western Digits) --> <item>ar-EH</item> <!-- Arabic (Western Sahara) --> <item>ar-ER</item> <!-- Arabic (Eritrea) --> <item>ar-IL</item> <!-- Arabic (Israel) --> @@ -48,6 +49,7 @@ <item>ar-SY</item> <!-- Arabic (Syria) --> <item>ar-TD</item> <!-- Arabic (Chad) --> <item>ar-TN</item> <!-- Arabic (Tunisia) --> + <item>ar-TN-u-nu-arab</item> <!-- Arabic (Tunisia,Arabic-Indic Digits) --> <item>ar-XB</item> <!-- Right-to-left pseudolocale --> <item>ar-YE</item> <!-- Arabic (Yemen) --> <item>as-IN</item> <!-- Assamese (India) --> @@ -366,6 +368,7 @@ <item>ms-SG</item> <!-- Malay (Singapore) --> <item>mt-MT</item> <!-- Maltese (Malta) --> <item>my-MM</item> <!-- Burmese (Myanmar (Burma)) --> + <item>my-MM-u-nu-latn</item> <!-- Burmese (Myanmar (Burma), Western Digits) --> <item>mzn-IR</item> <!-- Mazanderani (Iran) --> <item>naq-NA</item> <!-- Nama (Namibia) --> <item>nb-NO</item> <!-- Norwegian Bokmål (Norway) --> 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/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index 3e0348123618..bbdbdb13b3f7 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -26,12 +26,10 @@ import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates; -import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles; import static android.widget.espresso.DragHandleUtils.onHandleView; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed; -import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsNotDisplayed; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex; import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem; import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup; @@ -61,6 +59,7 @@ import android.support.test.espresso.action.EspressoKey; import android.support.test.filters.MediumTest; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.Suppress; import android.text.InputType; import android.text.Selection; import android.text.Spannable; @@ -216,7 +215,6 @@ public class TextViewActivityTest { onView(withId(R.id.textview)).check(matches(withText("abc ghi.def"))); onView(withId(R.id.textview)).check(hasSelection("")); - assertNoSelectionHandles(); onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length())); // Test undo returns to the original state. @@ -269,18 +267,12 @@ public class TextViewActivityTest { @Test public void testToolbarAppearsAfterSelection() { final String text = "Toolbar appears after selection."; - assertFloatingToolbarIsNotDisplayed(); onView(withId(R.id.textview)).perform(replaceText(text)); onView(withId(R.id.textview)).perform( longPressOnTextAtIndex(text.indexOf("appears"))); sleepForFloatingToolbarPopup(); assertFloatingToolbarIsDisplayed(); - - final String text2 = "Toolbar disappears after typing text."; - onView(withId(R.id.textview)).perform(replaceText(text2)); - sleepForFloatingToolbarPopup(); - assertFloatingToolbarIsNotDisplayed(); } @Test @@ -310,7 +302,6 @@ public class TextViewActivityTest { @Test public void testToolbarAndInsertionHandle() { final String text = "text"; - assertFloatingToolbarIsNotDisplayed(); onView(withId(R.id.textview)).perform(replaceText(text)); onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length())); @@ -404,8 +395,6 @@ public class TextViewActivityTest { final String text = "abcd efg hijk lmn"; onView(withId(R.id.textview)).perform(replaceText(text)); - assertNoSelectionHandles(); - onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f'))); onHandleView(com.android.internal.R.id.selection_start_handle) @@ -428,8 +417,6 @@ public class TextViewActivityTest { final String text = "abc \u0621\u0622\u0623 def"; onView(withId(R.id.textview)).perform(replaceText(text)); - assertNoSelectionHandles(); - onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('\u0622'))); onHandleView(com.android.internal.R.id.selection_start_handle) @@ -491,6 +478,7 @@ public class TextViewActivityTest { onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk\nlmn\nopqr")); } + @Suppress // Consistently failing. @Test public void testSelectionHandles_multiLine_rtl() { // Arabic text. @@ -649,13 +637,11 @@ public class TextViewActivityTest { onView(withId(R.id.textview)).perform(replaceText(text)); final TextView textView = mActivity.findViewById(R.id.textview); - assertFloatingToolbarIsNotDisplayed(); mActivityRule.runOnUiThread( () -> Selection.setSelection((Spannable) textView.getText(), 0, 3)); mInstrumentation.waitForIdleSync(); - sleepForFloatingToolbarPopup(); // Don't automatically start action mode. - assertFloatingToolbarIsNotDisplayed(); + // TODO: Implement assertActionModeNotStarted() // Make sure that "Select All" is included in the selection action mode when the entire text // is not selected. onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e'))); @@ -685,8 +671,6 @@ public class TextViewActivityTest { () -> Selection.setSelection((Spannable) textView.getText(), 0)); mInstrumentation.waitForIdleSync(); - sleepForFloatingToolbarPopup(); - assertFloatingToolbarIsNotDisplayed(); // Make sure that user click can trigger the insertion action mode. onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length())); onHandleView(com.android.internal.R.id.insertion_handle).perform(click()); diff --git a/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java index 6a2233ba126e..1693e548f7bd 100644 --- a/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java +++ b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java @@ -36,6 +36,10 @@ public final class DragHandleUtils { private DragHandleUtils() {} + /** + * @deprecated Negative assertions are taking too long to timeout in Espresso. + */ + @Deprecated public static void assertNoSelectionHandles() { try { onView(isAssignableFrom(Editor.SelectionHandleView.class)) diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java index f7069b31710f..b6986d5c5a68 100644 --- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java +++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java @@ -87,7 +87,9 @@ public class FloatingToolbarEspressoUtils { * Asserts that the floating toolbar is not displayed on screen. * * @throws AssertionError if the assertion fails + * @deprecated Negative assertions are taking too long to timeout in Espresso. */ + @Deprecated public static void assertFloatingToolbarIsNotDisplayed() { try { onFloatingToolBar().check(matches(isDisplayed())); @@ -173,12 +175,31 @@ public class FloatingToolbarEspressoUtils { * @throws AssertionError if the assertion fails */ public static void assertFloatingToolbarDoesNotContainItem(String itemLabel) { - try{ - assertFloatingToolbarContainsItem(itemLabel); - } catch (AssertionError e) { - return; - } - throw new AssertionError("Floating toolbar contains " + itemLabel); + onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() { + @Override + public boolean matchesSafely(View view) { + return doesNotContainItem(view); + } + + @Override + public void describeTo(Description description) {} + + private boolean doesNotContainItem(View view) { + if (view.getTag() instanceof MenuItem) { + if (itemLabel.equals(((MenuItem) view.getTag()).getTitle().toString())) { + return false; + } + } else if (view instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) view; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + if (doesNotContainItem(viewGroup.getChildAt(i))) { + return false; + } + } + } + return true; + } + })); } /** diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 5603508eaf09..3c8736ea0c4c 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -150,6 +150,14 @@ AssetManager::~AssetManager() { ALOGI("Destroying AssetManager in %p #%d\n", this, count); } + // Manually close any fd paths for which we have not yet opened their zip (which + // will take ownership of the fd and close it when done). + for (size_t i=0; i<mAssetPaths.size(); i++) { + if (mAssetPaths[i].rawFd >= 0 && mAssetPaths[i].zip == NULL) { + close(mAssetPaths[i].rawFd); + } + } + delete mConfig; delete mResources; @@ -280,7 +288,35 @@ bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie) } return true; - } +} + +bool AssetManager::addAssetFd( + int fd, const String8& debugPathName, int32_t* cookie, bool appAsLib, + bool assume_ownership) { + AutoMutex _l(mLock); + + asset_path ap; + + ap.path = debugPathName; + ap.rawFd = fd; + ap.type = kFileTypeRegular; + ap.assumeOwnership = assume_ownership; + + ALOGV("In %p Asset fd %d name: %s", this, fd, ap.path.string()); + + mAssetPaths.add(ap); + + // new paths are always added at the end + if (cookie) { + *cookie = static_cast<int32_t>(mAssetPaths.size()); + } + + if (mResources != NULL) { + appendPathToResTable(ap, appAsLib); + } + + return true; +} bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApkPath, uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize) @@ -505,7 +541,7 @@ bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) con Asset* idmap = openIdmapLocked(ap); size_t nextEntryIdx = mResources->getTableCount(); ALOGV("Looking for resource asset in '%s'\n", ap.path.string()); - if (ap.type != kFileTypeDirectory) { + if (ap.type != kFileTypeDirectory && ap.rawFd < 0) { if (nextEntryIdx == 0) { // The first item is typically the framework resources, // which we want to avoid parsing every time. @@ -738,6 +774,8 @@ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode m { Asset* pAsset = NULL; + ALOGV("openNonAssetInPath: name=%s type=%d fd=%d", fileName, ap.type, ap.rawFd); + /* look at the filesystem on disk */ if (ap.type == kFileTypeDirectory) { String8 path(ap.path); @@ -752,7 +790,7 @@ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode m } if (pAsset != NULL) { - //printf("FOUND NA '%s' on disk\n", fileName); + ALOGV("FOUND NA '%s' on disk", fileName); pAsset->setAssetSource(path); } @@ -763,10 +801,10 @@ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode m /* check the appropriate Zip file */ ZipFileRO* pZip = getZipFileLocked(ap); if (pZip != NULL) { - //printf("GOT zip, checking NA '%s'\n", (const char*) path); + ALOGV("GOT zip, checking NA '%s'", (const char*) path); ZipEntryRO entry = pZip->findEntryByName(path.string()); if (entry != NULL) { - //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon); + ALOGV("FOUND NA in Zip file for %s", (const char*) path); pAsset = openAssetFromZipLocked(pZip, entry, mode, path); pZip->releaseEntry(entry); } @@ -817,7 +855,17 @@ ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap) { ALOGV("getZipFileLocked() in %p\n", this); - return mZipSet.getZip(ap.path); + if (ap.zip != NULL) { + return ap.zip->getZip(); + } + + if (ap.rawFd < 0) { + ap.zip = mZipSet.getSharedZip(ap.path); + } else { + ap.zip = SharedZip::create(ap.rawFd, ap.path); + + } + return ap.zip != NULL ? ap.zip->getZip() : NULL; } /* @@ -1374,6 +1422,21 @@ AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) } } +AssetManager::SharedZip::SharedZip(int fd, const String8& path) + : mPath(path), mZipFile(NULL), mModWhen(0), + mResourceTableAsset(NULL), mResourceTable(NULL) +{ + if (kIsDebug) { + ALOGI("Creating SharedZip %p fd=%d %s\n", this, fd, (const char*)mPath); + } + ALOGV("+++ opening zip fd=%d '%s'\n", fd, mPath.string()); + mZipFile = ZipFileRO::openFd(fd, mPath.string()); + if (mZipFile == NULL) { + ::close(fd); + ALOGD("failed to open Zip archive fd=%d '%s'\n", fd, mPath.string()); + } +} + sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path, bool createIfNotPresent) { @@ -1389,7 +1452,11 @@ sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path, zip = new SharedZip(path, modWhen); gOpen.add(path, zip); return zip; +} +sp<AssetManager::SharedZip> AssetManager::SharedZip::create(int fd, const String8& path) +{ + return new SharedZip(fd, path); } ZipFileRO* AssetManager::SharedZip::getZip() @@ -1500,19 +1567,23 @@ void AssetManager::ZipSet::closeZip(int idx) mZipFile.editItemAt(idx) = NULL; } - /* * Retrieve the appropriate Zip file from the set. */ ZipFileRO* AssetManager::ZipSet::getZip(const String8& path) { + return getSharedZip(path)->getZip(); +} + +const sp<AssetManager::SharedZip> AssetManager::ZipSet::getSharedZip(const String8& path) +{ int idx = getIndex(path); sp<SharedZip> zip = mZipFile[idx]; if (zip == NULL) { zip = SharedZip::get(path); mZipFile.editItemAt(idx) = zip; } - return zip->getZip(); + return zip; } Asset* AssetManager::ZipSet::getZipResourceTableAsset(const String8& path) diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 87999c353a90..17de8fa1e7a5 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -2847,14 +2847,111 @@ void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool can } memcpy(str + charsWritten, localeVariant, sizeof(localeVariant)); } -} -/* static */ inline bool assignLocaleComponent(ResTable_config* config, - const char* start, size_t size) { + /* TODO: Add BCP47 extension. It requires RESTABLE_MAX_LOCALE_LEN + * increase from 28 to 42 bytes (-u-nu-xxxxxxxx) */ +} + +struct LocaleParserState { + enum State : uint8_t { + BASE, UNICODE_EXTENSION, IGNORE_THE_REST + } parserState; + enum UnicodeState : uint8_t { + /* Initial state after the Unicode singleton is detected. Either a keyword + * or an attribute is expected. */ + NO_KEY, + /* Unicode extension key (but not attribute) is expected. Next states: + * NO_KEY, IGNORE_KEY or NUMBERING_SYSTEM. */ + EXPECT_KEY, + /* A key is detected, however it is not supported for now. Ignore its + * value. Next states: IGNORE_KEY or NUMBERING_SYSTEM. */ + IGNORE_KEY, + /* Numbering system key was detected. Store its value in the configuration + * localeNumberingSystem field. Next state: EXPECT_KEY */ + NUMBERING_SYSTEM + } unicodeState; + + LocaleParserState(): parserState(BASE), unicodeState(NO_KEY) {} +}; + +/* static */ inline LocaleParserState assignLocaleComponent(ResTable_config* config, + const char* start, size_t size, LocaleParserState state) { + + /* It is assumed that this function is not invoked with state.parserState + * set to IGNORE_THE_REST. The condition is checked by setBcp47Locale + * function. */ + + if (state.parserState == LocaleParserState::UNICODE_EXTENSION) { + switch (size) { + case 1: + /* Other BCP 47 extensions are not supported at the moment */ + state.parserState = LocaleParserState::IGNORE_THE_REST; + break; + case 2: + if (state.unicodeState == LocaleParserState::NO_KEY || + state.unicodeState == LocaleParserState::EXPECT_KEY) { + /* Analyze Unicode extension key. Currently only 'nu' + * (numbering system) is supported.*/ + if ((start[0] == 'n' || start[0] == 'N') && + (start[1] == 'u' || start[1] == 'U')) { + state.unicodeState = LocaleParserState::NUMBERING_SYSTEM; + } else { + state.unicodeState = LocaleParserState::IGNORE_KEY; + } + } else { + /* Keys are not allowed in other state allowed, ignore the rest. */ + state.parserState = LocaleParserState::IGNORE_THE_REST; + } + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + switch (state.unicodeState) { + case LocaleParserState::NUMBERING_SYSTEM: + /* Accept only the first occurrence of the numbering system. */ + if (config->localeNumberingSystem[0] == '\0') { + for (size_t i = 0; i < size; ++i) { + config->localeNumberingSystem[i] = tolower(start[i]); + } + state.unicodeState = LocaleParserState::EXPECT_KEY; + } else { + state.parserState = LocaleParserState::IGNORE_THE_REST; + } + break; + case LocaleParserState::IGNORE_KEY: + /* Unsupported Unicode keyword. Ignore. */ + state.unicodeState = LocaleParserState::EXPECT_KEY; + break; + case LocaleParserState::EXPECT_KEY: + /* A keyword followed by an attribute is not allowed. */ + state.parserState = LocaleParserState::IGNORE_THE_REST; + break; + case LocaleParserState::NO_KEY: + /* Extension attribute. Do nothing. */ + break; + default: + break; + } + break; + default: + /* Unexpected field length - ignore the rest and treat as an error */ + state.parserState = LocaleParserState::IGNORE_THE_REST; + } + return state; + } switch (size) { case 0: - return false; + state.parserState = LocaleParserState::IGNORE_THE_REST; + break; + case 1: + state.parserState = (start[0] == 'u' || start[0] == 'U') + ? LocaleParserState::UNICODE_EXTENSION + : LocaleParserState::IGNORE_THE_REST; + break; case 2: case 3: config->language[0] ? config->packRegion(start) : config->packLanguage(start); @@ -2878,30 +2975,35 @@ void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool can } break; default: - return false; + state.parserState = LocaleParserState::IGNORE_THE_REST; } - return true; + return state; } void ResTable_config::setBcp47Locale(const char* in) { locale = 0; memset(localeScript, 0, sizeof(localeScript)); memset(localeVariant, 0, sizeof(localeVariant)); + memset(localeNumberingSystem, 0, sizeof(localeNumberingSystem)); - const char* separator = in; const char* start = in; - while ((separator = strchr(start, '-')) != NULL) { + LocaleParserState state; + while (const char* separator = strchr(start, '-')) { const size_t size = separator - start; - if (!assignLocaleComponent(this, start, size)) { - fprintf(stderr, "Invalid BCP-47 locale string: %s", in); + state = assignLocaleComponent(this, start, size, state); + if (state.parserState == LocaleParserState::IGNORE_THE_REST) { + fprintf(stderr, "Invalid BCP-47 locale string: %s\n", in); + break; } - start = (separator + 1); } - const size_t size = in + strlen(in) - start; - assignLocaleComponent(this, start, size); + if (state.parserState != LocaleParserState::IGNORE_THE_REST) { + const size_t size = strlen(start); + assignLocaleComponent(this, start, size, state); + } + localeScriptWasComputed = (localeScript[0] == '\0'); if (localeScriptWasComputed) { computeScript(); diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index 49fe8a261178..6e2ca60cc3d3 100644 --- a/libs/androidfw/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -55,7 +55,9 @@ private: ZipFileRO::~ZipFileRO() { CloseArchive(mHandle); - free(mFileName); + if (mFileName != NULL) { + free(mFileName); + } } /* @@ -76,6 +78,20 @@ ZipFileRO::~ZipFileRO() { } +/* static */ ZipFileRO* ZipFileRO::openFd(int fd, const char* debugFileName, + bool assume_ownership) +{ + ZipArchiveHandle handle; + const int32_t error = OpenArchiveFd(fd, debugFileName, &handle, assume_ownership); + if (error) { + ALOGW("Error opening archive fd %d %s: %s", fd, debugFileName, ErrorCodeString(error)); + CloseArchive(handle); + return NULL; + } + + return new ZipFileRO(handle, strdup(debugFileName)); +} + ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const { _ZipEntryRO* data = new _ZipEntryRO; @@ -139,7 +155,8 @@ bool ZipFileRO::startIteration(void** cookie, const char* prefix, const char* su prefix ? &pe : NULL, suffix ? &se : NULL); if (error) { - ALOGW("Could not start iteration over %s: %s", mFileName, ErrorCodeString(error)); + ALOGW("Could not start iteration over %s: %s", mFileName != NULL ? mFileName : "<null>", + ErrorCodeString(error)); delete ze; return false; } @@ -154,7 +171,8 @@ ZipEntryRO ZipFileRO::nextEntry(void* cookie) int32_t error = Next(ze->cookie, &(ze->entry), &(ze->name)); if (error) { if (error != -1) { - ALOGW("Error iteration over %s: %s", mFileName, ErrorCodeString(error)); + ALOGW("Error iteration over %s: %s", mFileName != NULL ? mFileName : "<null>", + ErrorCodeString(error)); } return NULL; } diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h index 0441b9d789e2..4254614c8448 100644 --- a/libs/androidfw/include/androidfw/AssetManager.h +++ b/libs/androidfw/include/androidfw/AssetManager.h @@ -92,6 +92,20 @@ public: bool addOverlayPath(const String8& path, int32_t* cookie); /* + * Add a new source for assets from an already open file descriptor. + * This does not give full AssetManager functionality for these assets, + * since the origin of the file is not known for purposes of sharing, + * overlay resolution, and other features. However it does allow you + * to do simple access to the contents of the given fd as an apk file. + * + * Returns "true" on success, "false" on failure. If 'cookie' is non-NULL, + * then on success, *cookie is set to the value corresponding to the + * newly-added asset source. + */ + bool addAssetFd(int fd, const String8& debugPathName, int32_t* cookie, + bool appAsLib=false, bool assume_ownership=true); + + /* * Convenience for adding the standard system assets. Uses the * ANDROID_ROOT environment variable to find them. */ @@ -195,15 +209,20 @@ public: uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize); private: + class SharedZip; + struct asset_path { - asset_path() : path(""), type(kFileTypeRegular), idmap(""), - isSystemOverlay(false), isSystemAsset(false) {} + asset_path() : path(""), rawFd(-1), type(kFileTypeRegular), idmap(""), + isSystemOverlay(false), isSystemAsset(false), assumeOwnership(false) {} String8 path; + int rawFd; FileType type; String8 idmap; bool isSystemOverlay; bool isSystemAsset; + bool assumeOwnership; + mutable sp<SharedZip> zip; }; Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode, @@ -238,6 +257,7 @@ private: class SharedZip : public RefBase { public: static sp<SharedZip> get(const String8& path, bool createIfNotPresent = true); + static sp<SharedZip> create(int fd, const String8& path); ZipFileRO* getZip(); @@ -257,6 +277,7 @@ private: private: SharedZip(const String8& path, time_t modWhen); + SharedZip(int fd, const String8& path); SharedZip(); // <-- not implemented String8 mPath; @@ -290,6 +311,8 @@ private: */ ZipFileRO* getZip(const String8& path); + const sp<SharedZip> getSharedZip(const String8& path); + Asset* getZipResourceTableAsset(const String8& path); Asset* setZipResourceTableAsset(const String8& path, Asset* asset); diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 854795559750..20d017813cf7 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1182,6 +1182,10 @@ struct ResTable_config // tried but could not compute a script. bool localeScriptWasComputed; + // The value of BCP 47 Unicode extension for key 'nu' (numbering system). + // Varies in length from 3 to 8 chars. Zero-filled value. + char localeNumberingSystem[8]; + void copyFromDeviceNoSwap(const ResTable_config& o); void copyFromDtoH(const ResTable_config& o); @@ -1259,9 +1263,9 @@ struct ResTable_config // variants, it will be a modified bcp47 tag: b+en+Latn+US. void appendDirLocale(String8& str) const; - // Sets the values of language, region, script and variant to the - // well formed BCP-47 locale contained in |in|. The input locale is - // assumed to be valid and no validation is performed. + // Sets the values of language, region, script, variant and numbering + // system to the well formed BCP 47 locale contained in |in|. + // The input locale is assumed to be valid and no validation is performed. void setBcp47Locale(const char* in); inline void clearLocale() { @@ -1269,6 +1273,7 @@ struct ResTable_config localeScriptWasComputed = false; memset(localeScript, 0, sizeof(localeScript)); memset(localeVariant, 0, sizeof(localeVariant)); + memset(localeNumberingSystem, 0, sizeof(localeNumberingSystem)); } inline void computeScript() { diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h index 768034287afa..03154d04def1 100644 --- a/libs/androidfw/include/androidfw/ZipFileRO.h +++ b/libs/androidfw/include/androidfw/ZipFileRO.h @@ -80,6 +80,12 @@ public: static ZipFileRO* open(const char* zipFileName); /* + * Open an archive from an already open file descriptor. + */ + static ZipFileRO* openFd(int fd, const char* debugFileName, + bool assume_ownership = true); + + /* * Find an entry, by name. Returns the entry identifier, or NULL if * not found. */ diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp index 86a627e1485d..35007c815edd 100644 --- a/libs/androidfw/tests/ConfigLocale_test.cpp +++ b/libs/androidfw/tests/ConfigLocale_test.cpp @@ -185,6 +185,7 @@ TEST(ConfigLocaleTest, setLocale) { EXPECT_TRUE(test.localeScriptWasComputed); EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4)); EXPECT_EQ(0, test.localeVariant[0]); + EXPECT_EQ(0, test.localeNumberingSystem[0]); test.setBcp47Locale("eng-419"); char out[4] = {1, 1, 1, 1}; @@ -198,6 +199,7 @@ TEST(ConfigLocaleTest, setLocale) { EXPECT_EQ('4', out[0]); EXPECT_EQ('1', out[1]); EXPECT_EQ('9', out[2]); + EXPECT_EQ(0, test.localeNumberingSystem[0]); test.setBcp47Locale("en-Latn-419"); EXPECT_EQ('e', test.language[0]); @@ -209,6 +211,7 @@ TEST(ConfigLocaleTest, setLocale) { EXPECT_EQ('4', out[0]); EXPECT_EQ('1', out[1]); EXPECT_EQ('9', out[2]); + EXPECT_EQ(0, test.localeNumberingSystem[0]); test.setBcp47Locale("de-1901"); memset(out, 1, 4); @@ -222,6 +225,7 @@ TEST(ConfigLocaleTest, setLocale) { test.unpackRegion(out); EXPECT_EQ('\0', out[0]); EXPECT_EQ(0, strcmp("1901", test.localeVariant)); + EXPECT_EQ(0, test.localeNumberingSystem[0]); test.setBcp47Locale("de-Latn-1901"); memset(out, 1, 4); @@ -235,6 +239,44 @@ TEST(ConfigLocaleTest, setLocale) { test.unpackRegion(out); EXPECT_EQ('\0', out[0]); EXPECT_EQ(0, strcmp("1901", test.localeVariant)); + EXPECT_EQ(0, test.localeNumberingSystem[0]); + + test.setBcp47Locale("ar-EG-u-nu-latn"); + EXPECT_EQ('a', test.language[0]); + EXPECT_EQ('r', test.language[1]); + EXPECT_EQ('E', test.country[0]); + EXPECT_EQ('G', test.country[1]); + EXPECT_TRUE(test.localeScriptWasComputed); + EXPECT_EQ(0, memcmp("Arab", test.localeScript, 4)); + EXPECT_EQ(0, test.localeVariant[0]); + EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4)); + + test.setBcp47Locale("ar-EG-u"); + EXPECT_EQ(0, test.localeNumberingSystem[0]); + + test.setBcp47Locale("ar-EG-u-nu"); + EXPECT_EQ(0, test.localeNumberingSystem[0]); + + test.setBcp47Locale("ar-EG-u-attr-nu-latn"); + EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4)); + + test.setBcp47Locale("ar-EG-u-ca-gregory-nu-latn"); + EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4)); + + test.setBcp47Locale("ar-EG-u-nu-latn-ca-gregory"); + EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4)); + + test.setBcp47Locale("ar-EG-u-nu-toolongnumsys"); + EXPECT_EQ(0, test.localeNumberingSystem[0]); + + test.setBcp47Locale("ar-EG-u-nu-latn-nu-arab"); + EXPECT_EQ(0, memcmp("latn", test.localeNumberingSystem, 4)); + + test.setBcp47Locale("ar-EG-u-co-nu-latn"); + EXPECT_EQ(0, test.localeNumberingSystem[0]); + + test.setBcp47Locale("ar-u-co-abcd-attr-nu-latn"); + EXPECT_EQ(0, test.localeNumberingSystem[0]); } TEST(ConfigLocaleTest, computeScript) { diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp index 6b8006cc5a33..0919e82a003b 100644 --- a/libs/hwui/Extensions.cpp +++ b/libs/hwui/Extensions.cpp @@ -31,10 +31,11 @@ namespace android { namespace uirenderer { Extensions::Extensions() { - if (Properties::getRenderPipelineType() != RenderPipelineType::OpenGL) { - //Extensions class is used only by OpenGL pipeline + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + //Extensions class is used only by OpenGL and SkiaGL pipelines //The code below will crash for SkiaVulkan, because OpenGL is not initialized //TODO: instantiate Extensions class only for OpenGL pipeline + //TODO: remove the only usage of Extensions by SkiaGL in SkiaOpenGLReadback::copyImageInto return; } const char* version = (const char*) glGetString(GL_VERSION); @@ -65,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 75967e9f6503..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 && - !grContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) { + !DeviceInfo::get()->extensions().hasRenderableFloatTextures()) { ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported"); return CopyResult::DestinationInvalid; } @@ -84,57 +84,50 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4 sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), backendTexture, kTopLeft_GrSurfaceOrigin)); if (image) { - // Convert imgTransform matrix from right to left handed coordinate system. - // If we have a matrix transformation in right handed coordinate system - //|ScaleX, SkewX, TransX| same transform in left handed is |ScaleX, SkewX, TransX | - //|SkewY, ScaleY, TransY| |-SkewY, -ScaleY, 1-TransY| - //|0, 0, 1 | |0, 0, 1 | - SkMatrix textureMatrix; - textureMatrix.setIdentity(); - textureMatrix[SkMatrix::kMScaleX] = imgTransform[Matrix4::kScaleX]; - textureMatrix[SkMatrix::kMScaleY] = -imgTransform[Matrix4::kScaleY]; - textureMatrix[SkMatrix::kMSkewX] = imgTransform[Matrix4::kSkewX]; - textureMatrix[SkMatrix::kMSkewY] = -imgTransform[Matrix4::kSkewY]; - textureMatrix[SkMatrix::kMTransX] = imgTransform[Matrix4::kTranslateX]; - textureMatrix[SkMatrix::kMTransY] = 1-imgTransform[Matrix4::kTranslateY]; - - // textureMatrix maps 2D texture coordinates of the form (s, t, 1) with s and t in the - // inclusive range [0, 1] to the texture (see GLConsumer::getTransformMatrix comments). - // Convert textureMatrix to translate in real texture dimensions. Texture width and - // height are affected by the orientation (width and height swapped for 90/270 rotation). - if (textureMatrix[SkMatrix::kMSkewX] >= 0.5f || textureMatrix[SkMatrix::kMSkewX] <= -0.5f) { - textureMatrix[SkMatrix::kMTransX] *= imgHeight; - textureMatrix[SkMatrix::kMTransY] *= imgWidth; - } else { - textureMatrix[SkMatrix::kMTransX] *= imgWidth; - textureMatrix[SkMatrix::kMTransY] *= imgHeight; + int displayedWidth = imgWidth, displayedHeight = imgHeight; + // If this is a 90 or 270 degree rotation we need to swap width/height to get the device + // size. + if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) { + std::swap(displayedWidth, displayedHeight); } - - // convert to Skia data structures - SkRect skiaSrcRect = srcRect.toSkRect(); - SkMatrix textureMatrixInv; SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height()); - bool srcNotEmpty = false; - if (textureMatrix.invert(&textureMatrixInv)) { - if (skiaSrcRect.isEmpty()) { - skiaSrcRect = SkRect::MakeIWH(imgWidth, imgHeight); - srcNotEmpty = !skiaSrcRect.isEmpty(); - } else { - // src and dest rectangles need to be converted into texture coordinates before the - // rotation matrix is applied (because drawImageRect preconcat its matrix). - textureMatrixInv.mapRect(&skiaSrcRect); - srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(imgWidth, imgHeight)); - } - textureMatrixInv.mapRect(&skiaDestRect); + SkRect skiaSrcRect = srcRect.toSkRect(); + if (skiaSrcRect.isEmpty()) { + skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight); } + bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight)); if (srcNotEmpty) { + SkMatrix textureMatrixInv; + imgTransform.copyTo(textureMatrixInv); + //TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed + // use bottom left origin and remove flipV and invert transformations. + SkMatrix flipV; + flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1); + textureMatrixInv.preConcat(flipV); + textureMatrixInv.preScale(1.0f/displayedWidth, 1.0f/displayedHeight); + textureMatrixInv.postScale(imgWidth, imgHeight); + SkMatrix textureMatrix; + if (!textureMatrixInv.invert(&textureMatrix)) { + textureMatrix = textureMatrixInv; + } + + textureMatrixInv.mapRect(&skiaSrcRect); + textureMatrixInv.mapRect(&skiaDestRect); + // we render in an offscreen buffer to scale and to avoid an issue b/62262733 // with reading incorrect data from EGLImage backed SkImage (likely a driver bug) sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget( grContext.get(), SkBudgeted::kYes, bitmap->info()); SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); + // Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage + // is codified by tests using golden images like DecodeAccuracyTest. + if (skiaSrcRect.width() != bitmap->width() + || skiaSrcRect.height() != bitmap->height()) { + //TODO: apply filter always, but check if tests will be fine + paint.setFilterQuality(kLow_SkFilterQuality); + } scaledSurface->getCanvas()->concat(textureMatrix); scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint, SkCanvas::kFast_SrcRectConstraint); 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/Android.bp b/libs/protoutil/Android.bp new file mode 100644 index 000000000000..4f1d2d5a4fe5 --- /dev/null +++ b/libs/protoutil/Android.bp @@ -0,0 +1,39 @@ +// +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library { + name: "libprotoutil", + + cflags: [ + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-unused-variable", + "-Wunused-parameter", + ], + + srcs: [ + "src/EncodedBuffer.cpp", + "src/ProtoOutputStream.cpp", + "src/protobuf.cpp", + ], + + export_include_dirs: ["include"], + + shared_libs: [ + "libcutils", + "liblog", + ], +} diff --git a/libs/protoutil/Android.mk b/libs/protoutil/Android.mk deleted file mode 100644 index 2a2b087dc032..000000000000 --- a/libs/protoutil/Android.mk +++ /dev/null @@ -1,39 +0,0 @@ -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE := libprotoutil - -LOCAL_CFLAGS := \ - -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - liblog \ - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/include - -LOCAL_SRC_FILES := \ - src/EncodedBuffer.cpp \ - src/ProtoOutputStream.cpp \ - src/protobuf.cpp \ - -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include - -include $(BUILD_SHARED_LIBRARY) - diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h index 10be6499417f..ce4184967026 100644 --- a/libs/protoutil/include/android/util/ProtoOutputStream.h +++ b/libs/protoutil/include/android/util/ProtoOutputStream.h @@ -19,13 +19,63 @@ #include <android/util/EncodedBuffer.h> -#include <stdint.h> #include <string> namespace android { namespace util { /** + * Position of the field type in a (long long) fieldId. + */ +const uint64_t FIELD_TYPE_SHIFT = 32; + +/** + * Mask for the field types stored in a fieldId. Leaves a whole + * byte for future expansion, even though there are currently only 17 types. + */ +const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT; + +/** + * 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. +const uint64_t FIELD_TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; // int64, varint on the wire. Negative numbers + // take 10 bytes. Use TYPE_SINT64 if negative + // values are likely. +const uint64_t FIELD_TYPE_UINT64 = 4ULL << FIELD_TYPE_SHIFT; // uint64, varint on the wire. +const uint64_t FIELD_TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; // int32, varint on the wire. Negative numbers + // take 10 bytes. Use TYPE_SINT32 if negative + // values are likely. +const uint64_t FIELD_TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exactly eight bytes on the wire. +const uint64_t FIELD_TYPE_FIXED32 = 7ULL << FIELD_TYPE_SHIFT; // uint32, exactly four bytes on the wire. +const uint64_t FIELD_TYPE_BOOL = 8ULL << FIELD_TYPE_SHIFT; // bool, varint on the wire. +const uint64_t FIELD_TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; // UTF-8 text. +// const uint64_t FIELD_TYPE_GROUP = 10ULL << FIELD_TYPE_SHIFT; // Tag-delimited message. Deprecated. +const uint64_t FIELD_TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; // Length-delimited message. + +const uint64_t FIELD_TYPE_BYTES = 12ULL << FIELD_TYPE_SHIFT; // Arbitrary byte array. +const uint64_t FIELD_TYPE_UINT32 = 13ULL << FIELD_TYPE_SHIFT; // uint32, varint on the wire +const uint64_t FIELD_TYPE_ENUM = 14ULL << FIELD_TYPE_SHIFT; // Enum, varint on the wire +const uint64_t FIELD_TYPE_SFIXED32 = 15ULL << FIELD_TYPE_SHIFT; // int32, exactly four bytes on the wire +const uint64_t FIELD_TYPE_SFIXED64 = 16ULL << FIELD_TYPE_SHIFT; // int64, exactly eight bytes on the wire +const uint64_t FIELD_TYPE_SINT32 = 17ULL << FIELD_TYPE_SHIFT; // int32, ZigZag-encoded varint on the wire +const uint64_t FIELD_TYPE_SINT64 = 18ULL << FIELD_TYPE_SHIFT; // int64, ZigZag-encoded varint on the wire + +// +// FieldId flags for whether the field is single, repeated or packed. +// TODO: packed is not supported yet. +// +const uint64_t FIELD_COUNT_SHIFT = 40; +const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT; +const uint64_t FIELD_COUNT_UNKNOWN = 0; +const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT; +const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT; +const uint64_t FIELD_COUNT_PACKED = 5ULL << FIELD_COUNT_SHIFT; + +/** * Class to write to a protobuf stream. * * Each write method takes an ID code from the protoc generated classes @@ -60,13 +110,14 @@ public: void end(long long token); /** - * Flushes the protobuf data out to given fd. + * Flushes the protobuf data out to given fd. When the following functions are called, + * it is not able to write to ProtoOutputStream any more since the data is compact. */ - size_t size(); - EncodedBuffer::iterator data(); - bool flush(int fd); + size_t size(); // Get the size of the serialized protobuf. + EncodedBuffer::iterator data(); // Get the reader apis of the data. + bool flush(int fd); // Flush data directly to a file descriptor. - // Please don't use the following functions to dump protos unless you are sure about it. + // Please don't use the following functions to dump protos unless you are familiar with protobuf encoding. void writeRawVarint(uint64_t varint); void writeLengthDelimitedHeader(uint32_t id, size_t size); void writeRawByte(uint8_t byte); @@ -94,6 +145,7 @@ private: inline void writeEnumImpl(uint32_t id, int val); inline void writeBoolImpl(uint32_t id, bool val); inline void writeUtf8StringImpl(uint32_t id, const char* val, size_t size); + inline void writeMessageBytesImpl(uint32_t id, const char* val, size_t size); bool compact(); size_t editEncodedSize(size_t rawSize); diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp index 9dadf1c20510..9d8ee729a80d 100644 --- a/libs/protoutil/src/ProtoOutputStream.cpp +++ b/libs/protoutil/src/ProtoOutputStream.cpp @@ -18,58 +18,10 @@ #include <android/util/protobuf.h> #include <android/util/ProtoOutputStream.h> #include <cutils/log.h> -#include <cstring> namespace android { namespace util { -/** - * Position of the field type in a (long long) fieldId. - */ -const uint64_t FIELD_TYPE_SHIFT = 32; - -/** - * Mask for the field types stored in a fieldId. Leaves a whole - * byte for future expansion, even though there are currently only 17 types. - */ -const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT; - -const uint64_t FIELD_TYPE_UNKNOWN = 0; -const uint64_t TYPE_DOUBLE = 1ULL << FIELD_TYPE_SHIFT; // double, exactly eight bytes on the wire. -const uint64_t TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; // float, exactly four bytes on the wire. -const uint64_t TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; // int64, varint on the wire. Negative numbers - // take 10 bytes. Use TYPE_SINT64 if negative - // values are likely. -const uint64_t TYPE_UINT64 = 4ULL << FIELD_TYPE_SHIFT; // uint64, varint on the wire. -const uint64_t TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; // int32, varint on the wire. Negative numbers - // take 10 bytes. Use TYPE_SINT32 if negative - // values are likely. -const uint64_t TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exactly eight bytes on the wire. -const uint64_t TYPE_FIXED32 = 7ULL << FIELD_TYPE_SHIFT; // uint32, exactly four bytes on the wire. -const uint64_t TYPE_BOOL = 8ULL << FIELD_TYPE_SHIFT; // bool, varint on the wire. -const uint64_t TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; // UTF-8 text. -const uint64_t TYPE_GROUP = 10ULL << FIELD_TYPE_SHIFT; // Tag-delimited message. Deprecated. -const uint64_t TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; // Length-delimited message. - -const uint64_t TYPE_BYTES = 12ULL << FIELD_TYPE_SHIFT; // Arbitrary byte array. -const uint64_t TYPE_UINT32 = 13ULL << FIELD_TYPE_SHIFT; // uint32, varint on the wire -const uint64_t TYPE_ENUM = 14ULL << FIELD_TYPE_SHIFT; // Enum, varint on the wire -const uint64_t TYPE_SFIXED32 = 15ULL << FIELD_TYPE_SHIFT; // int32, exactly four bytes on the wire -const uint64_t TYPE_SFIXED64 = 16ULL << FIELD_TYPE_SHIFT; // int64, exactly eight bytes on the wire -const uint64_t TYPE_SINT32 = 17ULL << FIELD_TYPE_SHIFT; // int32, ZigZag-encoded varint on the wire -const uint64_t TYPE_SINT64 = 18ULL << FIELD_TYPE_SHIFT; // int64, ZigZag-encoded varint on the wire - -// -// FieldId flags for whether the field is single, repeated or packed. -// TODO: packed is not supported yet. -// -const uint64_t FIELD_COUNT_SHIFT = 40; -const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT; -const uint64_t FIELD_COUNT_UNKNOWN = 0; -const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT; -const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT; -const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT; - ProtoOutputStream::ProtoOutputStream() :mBuffer(), mCopyBegin(0), @@ -90,18 +42,18 @@ ProtoOutputStream::write(uint64_t fieldId, double val) if (mCompact) return false; const uint32_t id = (uint32_t)fieldId; switch (fieldId & FIELD_TYPE_MASK) { - case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; - case TYPE_FLOAT: writeFloatImpl(id, (float)val); break; - case TYPE_INT64: writeInt64Impl(id, (long long)val); break; - case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; - case TYPE_INT32: writeInt32Impl(id, (int)val); break; - case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; - case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; - case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; - case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; - case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; - case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; - case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break; + case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; default: ALOGW("Field type %d is not supported when writing double val.", (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); @@ -116,18 +68,18 @@ ProtoOutputStream::write(uint64_t fieldId, float val) if (mCompact) return false; const uint32_t id = (uint32_t)fieldId; switch (fieldId & FIELD_TYPE_MASK) { - case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; - case TYPE_FLOAT: writeFloatImpl(id, (float)val); break; - case TYPE_INT64: writeInt64Impl(id, (long long)val); break; - case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; - case TYPE_INT32: writeInt32Impl(id, (int)val); break; - case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; - case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; - case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; - case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; - case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; - case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; - case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break; + case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; default: ALOGW("Field type %d is not supported when writing float val.", (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); @@ -142,20 +94,20 @@ ProtoOutputStream::write(uint64_t fieldId, int val) if (mCompact) return false; const uint32_t id = (uint32_t)fieldId; switch (fieldId & FIELD_TYPE_MASK) { - case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; - case TYPE_FLOAT: writeFloatImpl(id, (float)val); break; - case TYPE_INT64: writeInt64Impl(id, (long long)val); break; - case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; - case TYPE_INT32: writeInt32Impl(id, (int)val); break; - case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; - case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; - case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; - case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; - case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; - case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; - case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; - case TYPE_ENUM: writeEnumImpl(id, (int)val); break; - case TYPE_BOOL: writeBoolImpl(id, val != 0); break; + case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break; + case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break; + case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break; default: ALOGW("Field type %d is not supported when writing int val.", (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); @@ -170,20 +122,20 @@ ProtoOutputStream::write(uint64_t fieldId, long long val) if (mCompact) return false; const uint32_t id = (uint32_t)fieldId; switch (fieldId & FIELD_TYPE_MASK) { - case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; - case TYPE_FLOAT: writeFloatImpl(id, (float)val); break; - case TYPE_INT64: writeInt64Impl(id, (long long)val); break; - case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; - case TYPE_INT32: writeInt32Impl(id, (int)val); break; - case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; - case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; - case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; - case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; - case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; - case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; - case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; - case TYPE_ENUM: writeEnumImpl(id, (int)val); break; - case TYPE_BOOL: writeBoolImpl(id, val != 0); break; + case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break; + case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break; + case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break; default: ALOGW("Field type %d is not supported when writing long long val.", (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); @@ -198,7 +150,7 @@ ProtoOutputStream::write(uint64_t fieldId, bool val) if (mCompact) return false; const uint32_t id = (uint32_t)fieldId; switch (fieldId & FIELD_TYPE_MASK) { - case TYPE_BOOL: + case FIELD_TYPE_BOOL: writeBoolImpl(id, val); return true; default: @@ -214,7 +166,7 @@ ProtoOutputStream::write(uint64_t fieldId, string val) if (mCompact) return false; const uint32_t id = (uint32_t)fieldId; switch (fieldId & FIELD_TYPE_MASK) { - case TYPE_STRING: + case FIELD_TYPE_STRING: writeUtf8StringImpl(id, val.c_str(), val.size()); return true; default: @@ -230,10 +182,14 @@ ProtoOutputStream::write(uint64_t fieldId, const char* val, size_t size) if (mCompact) return false; const uint32_t id = (uint32_t)fieldId; switch (fieldId & FIELD_TYPE_MASK) { - case TYPE_STRING: - case TYPE_BYTES: + case FIELD_TYPE_STRING: + case FIELD_TYPE_BYTES: writeUtf8StringImpl(id, val, size); return true; + case FIELD_TYPE_MESSAGE: + // can directly write valid format of message bytes into ProtoOutputStream without calling start/end + writeMessageBytesImpl(id, val, size); + return true; default: ALOGW("Field type %d is not supported when writing char[] val.", (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); @@ -287,7 +243,7 @@ static int getSizePosFromToken(long long token) { long long ProtoOutputStream::start(uint64_t fieldId) { - if ((fieldId & FIELD_TYPE_MASK) != TYPE_MESSAGE) { + if ((fieldId & FIELD_TYPE_MASK) != FIELD_TYPE_MESSAGE) { ALOGE("Can't call start for non-message type field: 0x%llx", (long long)fieldId); return 0; } @@ -560,7 +516,6 @@ inline To bit_cast(From const &from) { inline void ProtoOutputStream::writeDoubleImpl(uint32_t id, double val) { - if (val == 0.0) return; mBuffer.writeHeader(id, WIRE_TYPE_FIXED64); mBuffer.writeRawFixed64(bit_cast<double, uint64_t>(val)); } @@ -568,7 +523,6 @@ ProtoOutputStream::writeDoubleImpl(uint32_t id, double val) inline void ProtoOutputStream::writeFloatImpl(uint32_t id, float val) { - if (val == 0.0) return; mBuffer.writeHeader(id, WIRE_TYPE_FIXED32); mBuffer.writeRawFixed32(bit_cast<float, uint32_t>(val)); } @@ -576,7 +530,6 @@ ProtoOutputStream::writeFloatImpl(uint32_t id, float val) inline void ProtoOutputStream::writeInt64Impl(uint32_t id, long long val) { - if (val == 0) return; mBuffer.writeHeader(id, WIRE_TYPE_VARINT); mBuffer.writeRawVarint64((uint64_t)val); } @@ -584,7 +537,6 @@ ProtoOutputStream::writeInt64Impl(uint32_t id, long long val) inline void ProtoOutputStream::writeInt32Impl(uint32_t id, int val) { - if (val == 0) return; mBuffer.writeHeader(id, WIRE_TYPE_VARINT); mBuffer.writeRawVarint32((uint32_t)val); } @@ -592,7 +544,6 @@ ProtoOutputStream::writeInt32Impl(uint32_t id, int val) inline void ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val) { - if (val == 0) return; mBuffer.writeHeader(id, WIRE_TYPE_VARINT); mBuffer.writeRawVarint64(val); } @@ -600,7 +551,6 @@ ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val) inline void ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val) { - if (val == 0) return; mBuffer.writeHeader(id, WIRE_TYPE_VARINT); mBuffer.writeRawVarint32(val); } @@ -608,7 +558,6 @@ ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val) inline void ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val) { - if (val == 0) return; mBuffer.writeHeader(id, WIRE_TYPE_FIXED64); mBuffer.writeRawFixed64(val); } @@ -616,7 +565,6 @@ ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val) inline void ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val) { - if (val == 0) return; mBuffer.writeHeader(id, WIRE_TYPE_FIXED32); mBuffer.writeRawFixed32(val); } @@ -624,7 +572,6 @@ ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val) inline void ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val) { - if (val == 0) return; mBuffer.writeHeader(id, WIRE_TYPE_FIXED64); mBuffer.writeRawFixed64((uint64_t)val); } @@ -632,7 +579,6 @@ ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val) inline void ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val) { - if (val == 0) return; mBuffer.writeHeader(id, WIRE_TYPE_FIXED32); mBuffer.writeRawFixed32((uint32_t)val); } @@ -640,7 +586,6 @@ ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val) inline void ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val) { - if (val == 0) return; mBuffer.writeHeader(id, WIRE_TYPE_VARINT); mBuffer.writeRawVarint64((val << 1) ^ (val >> 63)); } @@ -648,7 +593,6 @@ ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val) inline void ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val) { - if (val == 0) return; mBuffer.writeHeader(id, WIRE_TYPE_VARINT); mBuffer.writeRawVarint32((val << 1) ^ (val >> 31)); } @@ -663,7 +607,6 @@ ProtoOutputStream::writeEnumImpl(uint32_t id, int val) inline void ProtoOutputStream::writeBoolImpl(uint32_t id, bool val) { - if (!val) return; mBuffer.writeHeader(id, WIRE_TYPE_VARINT); mBuffer.writeRawVarint32(val ? 1 : 0); } @@ -671,13 +614,23 @@ ProtoOutputStream::writeBoolImpl(uint32_t id, bool val) inline void ProtoOutputStream::writeUtf8StringImpl(uint32_t id, const char* val, size_t size) { - if (val == NULL || size == 0) return; + if (val == NULL) return; writeLengthDelimitedHeader(id, size); for (size_t i=0; i<size; i++) { mBuffer.writeRawByte((uint8_t)val[i]); } } +inline void +ProtoOutputStream::writeMessageBytesImpl(uint32_t id, const char* val, size_t size) +{ + if (val == NULL) return; + writeLengthDelimitedHeader(id, size); + for (size_t i=0; i<size; i++) { + mBuffer.writeRawByte(val[i]); + } +} + } // util } // android diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index e7f903e831ff..c9d2f7f8bfd4 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -821,7 +821,7 @@ public class Location implements Parcelable { * considered 1 standard deviation. * * <p>For example, if {@link #getAltitude()} returns 150, and - * {@link #getVerticalAccuracyMeters()} ()} returns 20 then there is a 68% probability + * {@link #getVerticalAccuracyMeters()} returns 20 then there is a 68% probability * of the true altitude being between 130 and 170 meters. * * <p>If this location does not have a vertical accuracy, then 0.0 is returned. @@ -933,7 +933,7 @@ public class Location implements Parcelable { * considered 1 standard deviation. * * <p>For example, if {@link #getBearing()} returns 60, and - * {@link #getBearingAccuracyDegrees()} ()} returns 10, then there is a 68% probability of the + * {@link #getBearingAccuracyDegrees()} returns 10, then there is a 68% probability of the * true bearing being between 50 and 70 degrees. * * <p>If this location does not have a bearing accuracy, then 0.0 is returned. diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index ba41a7bd0868..91754162180f 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -2564,51 +2564,66 @@ public class ExifInterface { }); } + String hasImage = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE); String hasVideo = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO); - final String METADATA_HAS_VIDEO_VALUE_YES = "yes"; - if (METADATA_HAS_VIDEO_VALUE_YES.equals(hasVideo)) { - String width = retriever.extractMetadata( + String width = null; + String height = null; + String rotation = null; + final String METADATA_VALUE_YES = "yes"; + // If the file has both image and video, prefer image info over video info. + // App querying ExifInterface is most likely using the bitmap path which + // picks the image first. + if (METADATA_VALUE_YES.equals(hasImage)) { + width = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH); + height = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT); + rotation = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION); + } else if (METADATA_VALUE_YES.equals(hasVideo)) { + width = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); - String height = retriever.extractMetadata( + height = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); + rotation = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION); + } - if (width != null) { - mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, - ExifAttribute.createUShort(Integer.parseInt(width), mExifByteOrder)); - } - - if (height != null) { - mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, - ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder)); - } + if (width != null) { + mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, + ExifAttribute.createUShort(Integer.parseInt(width), mExifByteOrder)); + } - String rotation = retriever.extractMetadata( - MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION); - if (rotation != null) { - int orientation = ExifInterface.ORIENTATION_NORMAL; + if (height != null) { + mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, + ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder)); + } - // all rotation angles in CW - switch (Integer.parseInt(rotation)) { - case 90: - orientation = ExifInterface.ORIENTATION_ROTATE_90; - break; - case 180: - orientation = ExifInterface.ORIENTATION_ROTATE_180; - break; - case 270: - orientation = ExifInterface.ORIENTATION_ROTATE_270; - break; - } + if (rotation != null) { + int orientation = ExifInterface.ORIENTATION_NORMAL; - mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION, - ExifAttribute.createUShort(orientation, mExifByteOrder)); + // all rotation angles in CW + switch (Integer.parseInt(rotation)) { + case 90: + orientation = ExifInterface.ORIENTATION_ROTATE_90; + break; + case 180: + orientation = ExifInterface.ORIENTATION_ROTATE_180; + break; + case 270: + orientation = ExifInterface.ORIENTATION_ROTATE_270; + break; } - if (DEBUG) { - Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation); - } + mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION, + ExifAttribute.createUShort(orientation, mExifByteOrder)); + } + + if (DEBUG) { + Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation); } } finally { retriever.release(); diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index ed5f7d848663..c475e122833a 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -96,6 +96,19 @@ import java.util.Map; * <tr><td>{@link #KEY_MIME}</td><td>String</td><td>The type of the format.</td></tr> * <tr><td>{@link #KEY_LANGUAGE}</td><td>String</td><td>The language of the content.</td></tr> * </table> + * + * Image formats have the following keys: + * <table> + * <tr><td>{@link #KEY_MIME}</td><td>String</td><td>The type of the format.</td></tr> + * <tr><td>{@link #KEY_WIDTH}</td><td>Integer</td><td></td></tr> + * <tr><td>{@link #KEY_HEIGHT}</td><td>Integer</td><td></td></tr> + * <tr><td>{@link #KEY_COLOR_FORMAT}</td><td>Integer</td><td>set by the user + * for encoders, readable in the output format of decoders</b></td></tr> + * <tr><td>{@link #KEY_GRID_WIDTH}</td><td>Integer</td><td>required if the image has grid</td></tr> + * <tr><td>{@link #KEY_GRID_HEIGHT}</td><td>Integer</td><td>required if the image has grid</td></tr> + * <tr><td>{@link #KEY_GRID_ROWS}</td><td>Integer</td><td>required if the image has grid</td></tr> + * <tr><td>{@link #KEY_GRID_COLS}</td><td>Integer</td><td>required if the image has grid</td></tr> + * </table> */ public final class MediaFormat { public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8"; @@ -126,6 +139,35 @@ public final class MediaFormat { public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled"; /** + * MIME type for HEIF still image data encoded in HEVC. + * + * To decode such an image, {@link MediaCodec} decoder for + * {@ #MIMETYPE_VIDEO_HEVC} shall be used. The client needs to form + * the correct {@link #MediaFormat} based on additional information in + * the track format, and send it to {@link MediaCodec#configure}. + * + * The track's MediaFormat will come with {@link #KEY_WIDTH} and + * {@link #KEY_HEIGHT} keys, which describes the width and height + * of the image. If the image doesn't contain grid (i.e. none of + * {@link #KEY_GRID_WIDTH}, {@link #KEY_GRID_HEIGHT}, + * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLS} are present}), the + * track will contain a single sample of coded data for the entire image, + * and the image width and height should be used to set up the decoder. + * + * If the image does come with grid, each sample from the track will + * contain one tile in the grid, of which the size is described by + * {@link #KEY_GRID_WIDTH} and {@link #KEY_GRID_HEIGHT}. This size + * (instead of {@link #KEY_WIDTH} and {@link #KEY_HEIGHT}) should be + * used to set up the decoder. The track contains {@link #KEY_GRID_ROWS} + * by {@link #KEY_GRID_COLS} samples in row-major, top-row first, + * left-to-right order. The output image should be reconstructed by + * first tiling the decoding results of the tiles in the correct order, + * then trimming (before rotation is applied) on the bottom and right + * side, if the tiled area is larger than the image width and height. + */ + public static final String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic"; + + /** * MIME type for WebVTT subtitle data. */ public static final String MIMETYPE_TEXT_VTT = "text/vtt"; @@ -232,6 +274,54 @@ public final class MediaFormat { public static final String KEY_FRAME_RATE = "frame-rate"; /** + * A key describing the grid width of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} + * track. The associated value is an integer. + * + * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. + * + * @see #KEY_GRID_HEIGHT + * @see #KEY_GRID_ROWS + * @see #KEY_GRID_COLS + */ + public static final String KEY_GRID_WIDTH = "grid-width"; + + /** + * A key describing the grid height of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} + * track. The associated value is an integer. + * + * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. + * + * @see #KEY_GRID_WIDTH + * @see #KEY_GRID_ROWS + * @see #KEY_GRID_COLS + */ + public static final String KEY_GRID_HEIGHT = "grid-height"; + + /** + * A key describing the number of grid rows in the content in a + * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer. + * + * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. + * + * @see #KEY_GRID_WIDTH + * @see #KEY_GRID_HEIGHT + * @see #KEY_GRID_COLS + */ + public static final String KEY_GRID_ROWS = "grid-rows"; + + /** + * A key describing the number of grid columns in the content in a + * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer. + * + * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. + * + * @see #KEY_GRID_WIDTH + * @see #KEY_GRID_HEIGHT + * @see #KEY_GRID_ROWS + */ + public static final String KEY_GRID_COLS = "grid-cols"; + + /** * A key describing the raw audio sample encoding/format. * * <p>The associated value is an integer, using one of the diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 760cc49bc1e2..0b8640184ff5 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -47,7 +47,7 @@ public class MediaMetadataRetriever // The field below is accessed by native methods @SuppressWarnings("unused") private long mNativeContext; - + private static final int EMBEDDED_PICTURE_TYPE_ANY = 0xFFFF; public MediaMetadataRetriever() { @@ -58,7 +58,7 @@ public class MediaMetadataRetriever * Sets the data source (file pathname) to use. Call this * method before the rest of the methods in this class. This method may be * time-consuming. - * + * * @param path The path of the input media file. * @throws IllegalArgumentException If the path is invalid. */ @@ -113,7 +113,7 @@ public class MediaMetadataRetriever * responsibility to close the file descriptor. It is safe to do so as soon * as this call returns. Call this method before the rest of the methods in * this class. This method may be time-consuming. - * + * * @param fd the FileDescriptor for the file you want to play * @param offset the offset into the file where the data to be played starts, * in bytes. It must be non-negative @@ -123,13 +123,13 @@ public class MediaMetadataRetriever */ public native void setDataSource(FileDescriptor fd, long offset, long length) throws IllegalArgumentException; - + /** * Sets the data source (FileDescriptor) to use. It is the caller's * responsibility to close the file descriptor. It is safe to do so as soon * as this call returns. Call this method before the rest of the methods in * this class. This method may be time-consuming. - * + * * @param fd the FileDescriptor for the file you want to play * @throws IllegalArgumentException if the FileDescriptor is invalid */ @@ -138,11 +138,11 @@ public class MediaMetadataRetriever // intentionally less than LONG_MAX setDataSource(fd, 0, 0x7ffffffffffffffL); } - + /** - * Sets the data source as a content Uri. Call this method before + * Sets the data source as a content Uri. Call this method before * the rest of the methods in this class. This method may be time-consuming. - * + * * @param context the Context to use when resolving the Uri * @param uri the Content URI of the data you want to play * @throws IllegalArgumentException if the Uri is invalid @@ -154,7 +154,7 @@ public class MediaMetadataRetriever if (uri == null) { throw new IllegalArgumentException(); } - + String scheme = uri.getScheme(); if(scheme == null || scheme.equals("file")) { setDataSource(uri.getPath()); @@ -213,12 +213,12 @@ public class MediaMetadataRetriever /** * Call this method after setDataSource(). This method retrieves the * meta data value associated with the keyCode. - * + * * The keyCode currently supported is listed below as METADATA_XXX * constants. With any other value, it returns a null pointer. - * + * * @param keyCode One of the constants listed below at the end of the class. - * @return The meta data value associate with the given keyCode on success; + * @return The meta data value associate with the given keyCode on success; * null on failure. */ public native String extractMetadata(int keyCode); @@ -357,6 +357,109 @@ public class MediaMetadataRetriever private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height); /** + * This method retrieves a video frame by its index. It should only be called + * after {@link #setDataSource}. + * + * @param frameIndex 0-based index of the video frame. The frame index must be that of + * a valid frame. The total number of frames available for retrieval can be queried + * via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. + * + * @throws IllegalStateException if the container doesn't contain video or image sequences. + * @throws IllegalArgumentException if the requested frame index does not exist. + * + * @return A Bitmap containing the requested video frame, or null if the retrieval fails. + * + * @see #getFramesAtIndex(int, int) + */ + public Bitmap getFrameAtIndex(int frameIndex) { + Bitmap[] bitmaps = getFramesAtIndex(frameIndex, 1); + if (bitmaps == null || bitmaps.length < 1) { + return null; + } + return bitmaps[0]; + } + + /** + * This method retrieves a consecutive set of video frames starting at the + * specified index. It should only be called after {@link #setDataSource}. + * + * If the caller intends to retrieve more than one consecutive video frames, + * this method is preferred over {@link #getFrameAtIndex(int)} for efficiency. + * + * @param frameIndex 0-based index of the first video frame to retrieve. The frame index + * must be that of a valid frame. The total number of frames available for retrieval + * can be queried via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. + * @param numFrames number of consecutive video frames to retrieve. Must be a positive + * value. The stream must contain at least numFrames frames starting at frameIndex. + * + * @throws IllegalStateException if the container doesn't contain video or image sequences. + * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the + * stream doesn't contain at least numFrames starting at frameIndex. + + * @return An array of Bitmaps containing the requested video frames. The returned + * array could contain less frames than requested if the retrieval fails. + * + * @see #getFrameAtIndex(int) + */ + public Bitmap[] getFramesAtIndex(int frameIndex, int numFrames) { + if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) { + throw new IllegalStateException("Does not contail video or image sequences"); + } + int frameCount = Integer.parseInt( + extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT)); + if (frameIndex < 0 || numFrames < 1 + || frameIndex >= frameCount + || frameIndex > frameCount - numFrames) { + throw new IllegalArgumentException("Invalid frameIndex or numFrames: " + + frameIndex + ", " + numFrames); + } + return _getFrameAtIndex(frameIndex, numFrames); + } + private native Bitmap[] _getFrameAtIndex(int frameIndex, int numFrames); + + /** + * This method retrieves a still image by its index. It should only be called + * after {@link #setDataSource}. + * + * @param imageIndex 0-based index of the image, with negative value indicating + * the primary image. + * @throws IllegalStateException if the container doesn't contain still images. + * @throws IllegalArgumentException if the requested image does not exist. + * + * @return the requested still image, or null if the image cannot be retrieved. + * + * @see #getPrimaryImage + */ + public Bitmap getImageAtIndex(int imageIndex) { + if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) { + throw new IllegalStateException("Does not contail still images"); + } + + String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT); + if (imageIndex >= Integer.parseInt(imageCount)) { + throw new IllegalArgumentException("Invalid image index: " + imageCount); + } + + return _getImageAtIndex(imageIndex); + } + + /** + * This method retrieves the primary image of the media content. It should only + * be called after {@link #setDataSource}. + * + * @return the primary image, or null if it cannot be retrieved. + * + * @throws IllegalStateException if the container doesn't contain still images. + * + * @see #getImageAtIndex(int) + */ + public Bitmap getPrimaryImage() { + return getImageAtIndex(-1); + } + + private native Bitmap _getImageAtIndex(int imageIndex); + + /** * Call this method after setDataSource(). This method finds the optional * graphic or album/cover art associated associated with the data source. If * there are more than one pictures, (any) one of them is returned. @@ -572,5 +675,40 @@ public class MediaMetadataRetriever * number. */ public static final int METADATA_KEY_CAPTURE_FRAMERATE = 25; + /** + * If this key exists the media contains still image content. + */ + public static final int METADATA_KEY_HAS_IMAGE = 26; + /** + * If the media contains still images, this key retrieves the number + * of still images. + */ + public static final int METADATA_KEY_IMAGE_COUNT = 27; + /** + * If the media contains still images, this key retrieves the image + * index of the primary image. + */ + public static final int METADATA_KEY_IMAGE_PRIMARY = 28; + /** + * If the media contains still images, this key retrieves the width + * of the primary image. + */ + public static final int METADATA_KEY_IMAGE_WIDTH = 29; + /** + * If the media contains still images, this key retrieves the height + * of the primary image. + */ + public static final int METADATA_KEY_IMAGE_HEIGHT = 30; + /** + * If the media contains still images, this key retrieves the rotation + * of the primary image. + */ + public static final int METADATA_KEY_IMAGE_ROTATION = 31; + /** + * If the media contains video and this key exists, it retrieves the + * total number of frames in the video sequence. + */ + public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32; + // Add more here... } diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 4659ae131f53..42ebf6ab8cfc 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -244,29 +244,9 @@ static void rotate(T *dst, const T *src, size_t width, size_t height, int angle) } } -static jobject android_media_MediaMetadataRetriever_getFrameAtTime( - JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height) -{ - ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d", - (long long)timeUs, option, dst_width, dst_height); - MediaMetadataRetriever* retriever = getRetriever(env, thiz); - if (retriever == 0) { - jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); - return NULL; - } - - // Call native method to retrieve a video frame - VideoFrame *videoFrame = NULL; - sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option); - if (frameMemory != 0) { // cast the shared structure to a VideoFrame object - videoFrame = static_cast<VideoFrame *>(frameMemory->pointer()); - } - if (videoFrame == NULL) { - ALOGE("getFrameAtTime: videoFrame is a NULL pointer"); - return NULL; - } - - ALOGV("Dimension = %dx%d and bytes = %d", +static jobject getBitmapFromVideoFrame( + JNIEnv *env, VideoFrame *videoFrame, jint dst_width, jint dst_height) { + ALOGV("getBitmapFromVideoFrame: dimension = %dx%d and bytes = %d", videoFrame->mDisplayWidth, videoFrame->mDisplayHeight, videoFrame->mSize); @@ -301,7 +281,7 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime( if (env->ExceptionCheck()) { env->ExceptionClear(); } - ALOGE("getFrameAtTime: create Bitmap failed!"); + ALOGE("getBitmapFromVideoFrame: create Bitmap failed!"); return NULL; } @@ -340,6 +320,93 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime( return jBitmap; } +static jobject android_media_MediaMetadataRetriever_getFrameAtTime( + JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height) +{ + ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d", + (long long)timeUs, option, dst_width, dst_height); + MediaMetadataRetriever* retriever = getRetriever(env, thiz); + if (retriever == 0) { + jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); + return NULL; + } + + // Call native method to retrieve a video frame + VideoFrame *videoFrame = NULL; + sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option); + if (frameMemory != 0) { // cast the shared structure to a VideoFrame object + videoFrame = static_cast<VideoFrame *>(frameMemory->pointer()); + } + if (videoFrame == NULL) { + ALOGE("getFrameAtTime: videoFrame is a NULL pointer"); + return NULL; + } + + return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height); +} + +static jobject android_media_MediaMetadataRetriever_getImageAtIndex( + JNIEnv *env, jobject thiz, jint index) +{ + ALOGV("getImageAtIndex: index %d", index); + MediaMetadataRetriever* retriever = getRetriever(env, thiz); + if (retriever == 0) { + jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); + return NULL; + } + + // Call native method to retrieve an image + VideoFrame *videoFrame = NULL; + sp<IMemory> frameMemory = retriever->getImageAtIndex(index); + if (frameMemory != 0) { // cast the shared structure to a VideoFrame object + videoFrame = static_cast<VideoFrame *>(frameMemory->pointer()); + } + if (videoFrame == NULL) { + ALOGE("getImageAtIndex: videoFrame is a NULL pointer"); + return NULL; + } + + return getBitmapFromVideoFrame(env, videoFrame, -1, -1); +} + +static jobjectArray android_media_MediaMetadataRetriever_getFrameAtIndex( + JNIEnv *env, jobject thiz, jint frameIndex, jint numFrames) +{ + ALOGV("getFrameAtIndex: frameIndex %d, numFrames %d", frameIndex, numFrames); + MediaMetadataRetriever* retriever = getRetriever(env, thiz); + if (retriever == 0) { + jniThrowException(env, + "java/lang/IllegalStateException", "No retriever available"); + return NULL; + } + + std::vector<sp<IMemory> > frames; + status_t err = retriever->getFrameAtIndex(&frames, frameIndex, numFrames); + if (err != OK || frames.size() == 0) { + ALOGE("failed to get frames from retriever, err=%d, size=%zu", + err, frames.size()); + return NULL; + } + + jobjectArray bitmapArrayObj = env->NewObjectArray( + frames.size(), fields.bitmapClazz, NULL); + if (bitmapArrayObj == NULL) { + ALOGE("can't create bitmap array object"); + return NULL; + } + + for (size_t i = 0; i < frames.size(); i++) { + if (frames[i] == NULL || frames[i]->pointer() == NULL) { + ALOGE("video frame at index %zu is a NULL pointer", frameIndex + i); + continue; + } + VideoFrame *videoFrame = static_cast<VideoFrame *>(frames[i]->pointer()); + jobject bitmapObj = getBitmapFromVideoFrame(env, videoFrame, -1, -1); + env->SetObjectArrayElement(bitmapArrayObj, i, bitmapObj); + } + return bitmapArrayObj; +} + static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture( JNIEnv *env, jobject thiz, jint pictureType) { @@ -485,6 +552,8 @@ static const JNINativeMethod nativeMethods[] = { {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD}, {"_setDataSource", "(Landroid/media/MediaDataSource;)V", (void *)android_media_MediaMetadataRetriever_setDataSourceCallback}, {"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime}, + {"_getImageAtIndex", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getImageAtIndex}, + {"_getFrameAtIndex", "(II)[Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtIndex}, {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata}, {"getEmbeddedPicture", "(I)[B", (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture}, {"release", "()V", (void *)android_media_MediaMetadataRetriever_release}, diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index 0da628917713..fe2a939b58a6 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -955,6 +955,7 @@ void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) outThumbSize = image_data.thumbnail.length; } else { free(result); + result = NULL; } } break; diff --git a/packages/SettingsLib/res/drawable/ic_bt_laptop.xml b/packages/SettingsLib/res/drawable/ic_bt_laptop.xml new file mode 100644 index 000000000000..029e4d913434 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_bt_laptop.xml @@ -0,0 +1,26 @@ +<!-- + Copyright (C) 2016 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> + <path + android:fillColor="#FF000000" + android:pathData="M20,18c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 + -2,0.9 -2,2v10c0,1.1 0.9,2 2,2H0v2h24v-2h-4zM4,6h16v10H4V6z"/> +</vector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/drawable/ic_settings_bluetooth.xml b/packages/SettingsLib/res/drawable/ic_settings_bluetooth.xml new file mode 100644 index 000000000000..6e32e1a7f631 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_settings_bluetooth.xml @@ -0,0 +1,25 @@ +<!-- + Copyright (C) 2016 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M13.5,12l3.8,-3.7c0.4,-0.4 0.4,-1.1 0,-1.5l-4.5,-4.5c-0.4,-0.4 -1.1,-0.4 -1.5,0.1C11.1,2.5 11,2.8 11,3v6.4L6.9,5.4C6.5,5 5.9,5 5.5,5.4s-0.4,1.1 0,1.5l5.1,5.1l-5.1,5.1c-0.4,0.4 -0.4,1.1 0,1.5s1.1,0.4 1.5,0l4.1,-4V21c0,0.6 0.5,1 1,1c0.3,0 0.5,-0.1 0.7,-0.3l0.1,0l4.5,-4.5c0.4,-0.4 0.4,-1.1 0,-1.5L13.5,12zM13,9.7V5.4l2.1,2.2L13,9.7zM13,18.6v-4.3l2.1,2.2L13,18.6z"/> +</vector> diff --git a/packages/SettingsLib/res/drawable/ic_settings_print.xml b/packages/SettingsLib/res/drawable/ic_settings_print.xml new file mode 100644 index 000000000000..0eab4021956b --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_settings_print.xml @@ -0,0 +1,28 @@ +<!-- + Copyright (C) 2016 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M19,8H5c-1.66,0-3,1.34-3,3v5c0,0.55,0.45,1,1,1h3v3c0,0.55,0.45,1,1,1h10c0.55,0,1-0.45,1-1v-3h3c0.55,0,1-0.45,1-1v-5 +C22,9.34,20.66,8,19,8z M16,19H8v-5h8V19z +M19,12c-0.55,0-1-0.45-1-1s0.45-1,1-1s1,0.45,1,1S19.55,12,19,12z M17,3H7 +C6.45,3,6,3.45,6,4v3h12V4C18,3.45,17.55,3,17,3z"/> +</vector> 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-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 9318a7f62769..f45532432214 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -210,7 +210,7 @@ <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"აირჩიეთ Bluetooth აუდიოს LDAC კოდეკის\nდაკვრის ხარისხი"</string> <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"სტრიმინგი: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string> <string name="dns_tls" msgid="6773814174391131955">"DNS-ის TLS-ის მეშვეობით გადაცემა"</string> - <string name="dns_tls_summary" msgid="3692494150251071380">"ჩართვის შემთხვევაში, ცადეთ DNS-ის TLS-ის მეშვეობით გადაცემა პორტზე 853."</string> + <string name="dns_tls_summary" msgid="3692494150251071380">"ჩართვის შემთხვევაში, DNS-ის TLS-ის მეშვეობით გადაცემის ცდა, პორტზე 853."</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"უსადენო ეკრანის სერტიფიცირების ვარიანტების ჩვენება"</string> <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi-ს აღრიცხვის დონის გაზრდა, Wi‑Fi ამომრჩეველში ყოველ SSID RSSI-ზე ჩვენება"</string> <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ჩართვის შემთხვევაში, Wi‑Fi უფრო აქტიურად შეეცდება მობილურ ინტერნეტზე გადართვას, როცა Wi‑Fi სიგნალი სუსტია"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 725665dcc09b..d88adeac21c3 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -295,7 +295,7 @@ <string name="app_process_limit_title" msgid="4280600650253107163">"Лимит фоновых процессов"</string> <string name="show_all_anrs" msgid="28462979638729082">"Все ANR"</string> <string name="show_all_anrs_summary" msgid="641908614413544127">"Уведомлять о том, что приложение не отвечает"</string> - <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Показывать предупреждения канала передачи оповещения"</string> + <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Показывать предупреждения канала передачи уведомлений"</string> <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Показывать предупреждение о новых уведомлениях приложения вне допустимого канала"</string> <string name="force_allow_on_external" msgid="3215759785081916381">"Разрешить сохранение на внешние накопители"</string> <string name="force_allow_on_external_summary" msgid="3640752408258034689">"Разрешить сохранение приложений на внешних накопителях (независимо от значений в манифесте)"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index a404759d0639..360848f72ddd 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -235,6 +235,27 @@ <!-- Message for the error dialog when BT pairing fails because the other device rejected the pairing. --> <string name="bluetooth_pairing_rejected_error_message">Pairing rejected by <xliff:g id="device_name">%1$s</xliff:g>.</string> + <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=4875089335641234463] --> + <string name="bluetooth_talkback_computer">Computer</string> + + <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=5140152177885220949] --> + <string name="bluetooth_talkback_headset">Headset</string> + + <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=4260255181240622896] --> + <string name="bluetooth_talkback_phone">Phone</string> + + <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=551146170554589119] --> + <string name="bluetooth_talkback_imaging">Imaging</string> + + <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=26580326066627664] --> + <string name="bluetooth_talkback_headphone">Headphone</string> + + <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=5165842622743212268] --> + <string name="bluetooth_talkback_input_peripheral">Input Peripheral</string> + + <!-- Message for telling the user the kind of BT device being displayed in list. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=5615463912185280812] --> + <string name="bluetooth_talkback_bluetooth">Bluetooth</string> + <!-- Content description of the WIFI signal when WIFI is disabled for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_wifi_off">Wifi off.</string> <!-- Content description of the WIFI signal when no signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> 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/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java index 7bda2314f4c4..3299cb2d1221 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java @@ -32,7 +32,7 @@ import java.util.List; /** * PanProfile handles Bluetooth PAN profile (NAP and PANU). */ -public final class PanProfile implements LocalBluetoothProfile { +public class PanProfile implements LocalBluetoothProfile { private static final String TAG = "PanProfile"; private static boolean V = true; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java index c9194268543a..0ee1dad9d744 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java @@ -1,9 +1,17 @@ package com.android.settingslib.bluetooth; +import android.bluetooth.BluetoothClass; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.annotation.DrawableRes; +import android.util.Pair; import com.android.settingslib.R; +import com.android.settingslib.graph.BluetoothDeviceLayerDrawable; + +import java.util.List; public class Utils { public static final boolean V = false; // verbose logging @@ -40,4 +48,78 @@ public class Utils { void onShowError(Context context, String name, int messageResId); } + public static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context, + CachedBluetoothDevice cachedDevice) { + return getBtClassDrawableWithDescription(context, cachedDevice, 1 /* iconScale */); + } + + public static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context, + CachedBluetoothDevice cachedDevice, float iconScale) { + BluetoothClass btClass = cachedDevice.getBtClass(); + final int level = cachedDevice.getBatteryLevel(); + if (btClass != null) { + switch (btClass.getMajorDeviceClass()) { + case BluetoothClass.Device.Major.COMPUTER: + return new Pair<>(getBluetoothDrawable(context, R.drawable.ic_bt_laptop, level, + iconScale), + context.getString(R.string.bluetooth_talkback_computer)); + + case BluetoothClass.Device.Major.PHONE: + return new Pair<>( + getBluetoothDrawable(context, R.drawable.ic_bt_cellphone, level, + iconScale), + context.getString(R.string.bluetooth_talkback_phone)); + + case BluetoothClass.Device.Major.PERIPHERAL: + return new Pair<>( + getBluetoothDrawable(context, HidProfile.getHidClassDrawable(btClass), + level, iconScale), + context.getString(R.string.bluetooth_talkback_input_peripheral)); + + case BluetoothClass.Device.Major.IMAGING: + return new Pair<>( + getBluetoothDrawable(context, R.drawable.ic_settings_print, level, + iconScale), + context.getString(R.string.bluetooth_talkback_imaging)); + + default: + // unrecognized device class; continue + } + } + + List<LocalBluetoothProfile> profiles = cachedDevice.getProfiles(); + for (LocalBluetoothProfile profile : profiles) { + int resId = profile.getDrawableResource(btClass); + if (resId != 0) { + return new Pair<>(getBluetoothDrawable(context, resId, level, iconScale), null); + } + } + if (btClass != null) { + if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) { + return new Pair<>( + getBluetoothDrawable(context, R.drawable.ic_bt_headset_hfp, level, + iconScale), + context.getString(R.string.bluetooth_talkback_headset)); + } + if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) { + return new Pair<>( + getBluetoothDrawable(context, R.drawable.ic_bt_headphones_a2dp, level, + iconScale), + context.getString(R.string.bluetooth_talkback_headphone)); + } + } + return new Pair<>( + getBluetoothDrawable(context, R.drawable.ic_settings_bluetooth, level, iconScale), + context.getString(R.string.bluetooth_talkback_bluetooth)); + } + + public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId, + int batteryLevel, float iconScale) { + if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { + return BluetoothDeviceLayerDrawable.createLayerDrawable(context, resId, batteryLevel, + iconScale); + } else { + return context.getDrawable(resId); + } + } } 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/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index b1dbb0af54a7..4091ce1f173e 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -59,7 +59,7 @@ public class CachedBluetoothDeviceTest { @Mock private A2dpProfile mA2dpProfile; @Mock - private HidProfile mHidProfile; + private PanProfile mPanProfile; @Mock private BluetoothDevice mDevice; private CachedBluetoothDevice mCachedDevice; @@ -74,7 +74,7 @@ public class CachedBluetoothDeviceTest { when(mAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON); when(mHfpProfile.isProfileReady()).thenReturn(true); when(mA2dpProfile.isProfileReady()).thenReturn(true); - when(mHidProfile.isProfileReady()).thenReturn(true); + when(mPanProfile.isProfileReady()).thenReturn(true); mCachedDevice = spy( new CachedBluetoothDevice(mContext, mAdapter, mProfileManager, mDevice)); doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel(); @@ -92,37 +92,37 @@ public class CachedBluetoothDeviceTest { @Test public void testGetConnectionSummary_testSingleProfileConnectDisconnect() { // Test without battery level - // Set HID profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED); + // Set PAN profile to be connected and test connection state summary + mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString( R.string.bluetooth_connected)); - // Set HID profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED); + // Set PAN profile to be disconnected and test connection state summary + mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); // Test with battery level mBatteryLevel = 10; - // Set HID profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED); + // Set PAN profile to be connected and test connection state summary + mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString( R.string.bluetooth_connected_battery_level, com.android.settingslib.Utils.formatPercentage(mBatteryLevel))); - // Set HID profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED); + // Set PAN profile to be disconnected and test connection state summary + mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; - // Set HID profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED); + // Set PAN profile to be connected and test connection state summary + mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString( R.string.bluetooth_connected)); - // Set HID profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED); + // Set PAN profile to be disconnected and test connection state summary + mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); } @@ -130,10 +130,10 @@ public class CachedBluetoothDeviceTest { public void testGetConnectionSummary_testMultipleProfileConnectDisconnect() { mBatteryLevel = 10; - // Set HFP, A2DP and HID profile to be connected and test connection state summary + // Set HFP, A2DP and PAN profile to be connected and test connection state summary mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); - mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED); + mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString( R.string.bluetooth_connected_battery_level, com.android.settingslib.Utils.formatPercentage(mBatteryLevel))); @@ -158,7 +158,7 @@ public class CachedBluetoothDeviceTest { com.android.settingslib.Utils.formatPercentage(mBatteryLevel))); // Disconnect all profiles and test connection state summary - mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED); + mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/UtilsTest.java new file mode 100644 index 000000000000..5eb543ba3bae --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/UtilsTest.java @@ -0,0 +1,54 @@ +/* + * 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.settingslib.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import android.bluetooth.BluetoothDevice; +import android.graphics.drawable.Drawable; + +import com.android.settingslib.R; +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.TestConfig; +import com.android.settingslib.graph.BluetoothDeviceLayerDrawable; +import com.android.settingslib.testutils.shadow.SettingsLibShadowResources; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, + shadows = SettingsLibShadowResources.class) +public class UtilsTest { + + @Test + public void testGetBluetoothDrawable_noBatteryLevel_returnSimpleDrawable() { + final Drawable drawable = Utils.getBluetoothDrawable(RuntimeEnvironment.application, + R.drawable.ic_bt_laptop, BluetoothDevice.BATTERY_LEVEL_UNKNOWN, 1 /* iconScale */); + + assertThat(drawable).isNotInstanceOf(BluetoothDeviceLayerDrawable.class); + } + + @Test + public void testGetBluetoothDrawable_hasBatteryLevel_returnLayerDrawable() { + final Drawable drawable = Utils.getBluetoothDrawable(RuntimeEnvironment.application, + R.drawable.ic_bt_laptop, 10 /* batteryLevel */, 1 /* iconScale */); + + assertThat(drawable).isInstanceOf(BluetoothDeviceLayerDrawable.class); + } +} 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/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml index d311ba87d5fb..527122873f23 100644 --- a/packages/SystemUI/res-keyguard/values-bs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml @@ -29,8 +29,7 @@ <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Upišite lozinku za otključavanje"</string> <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Upišite PIN za otključavanje"</string> <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Pogrešan PIN."</string> - <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) --> - <skip /> + <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nevažeća kartica."</string> <string name="keyguard_charged" msgid="2222329688813033109">"Napunjeno"</string> <string name="keyguard_plugged_in" msgid="89308975354638682">"Punjenje"</string> <string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Brzo punjenje"</string> diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml index c706900e21d3..9b48a4b1b564 100644 --- a/packages/SystemUI/res-keyguard/values-ko/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml @@ -29,8 +29,7 @@ <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"잠금 해제하려면 비밀번호 입력"</string> <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"잠금 해제하려면 PIN 입력"</string> <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"잘못된 PIN 코드입니다."</string> - <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) --> - <skip /> + <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"유효하지 않은 카드"</string> <string name="keyguard_charged" msgid="2222329688813033109">"충전됨"</string> <string name="keyguard_plugged_in" msgid="89308975354638682">"충전 중"</string> <string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"고속 충전 중"</string> diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml index 2c33c98b0dba..59f441290208 100644 --- a/packages/SystemUI/res-keyguard/values-pl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml @@ -29,8 +29,7 @@ <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Wpisz hasło, aby odblokować"</string> <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Wpisz kod PIN, aby odblokować"</string> <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Nieprawidłowy kod PIN."</string> - <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) --> - <skip /> + <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nieprawidłowa karta."</string> <string name="keyguard_charged" msgid="2222329688813033109">"Naładowana"</string> <string name="keyguard_plugged_in" msgid="89308975354638682">"Ładowanie"</string> <string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Szybkie ładowanie"</string> diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml index 4855161d45d3..0ba941cd59d4 100644 --- a/packages/SystemUI/res-keyguard/values-ta/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml @@ -29,8 +29,7 @@ <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"திறக்க, கடவுச்சொல்லை உள்ளிடவும்"</string> <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"திறக்க, பின்னை உள்ளிடவும்"</string> <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"தவறான பின் குறியீடு."</string> - <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) --> - <skip /> + <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"செல்லாத சிம் கார்டு."</string> <string name="keyguard_charged" msgid="2222329688813033109">"சார்ஜ் செய்யப்பட்டது"</string> <string name="keyguard_plugged_in" msgid="89308975354638682">"சார்ஜ் ஆகிறது"</string> <string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"வேகமாகச் சார்ஜாகிறது"</string> diff --git a/packages/SystemUI/res/layout/operator_name.xml b/packages/SystemUI/res/layout/operator_name.xml new file mode 100644 index 000000000000..c4f75e927604 --- /dev/null +++ b/packages/SystemUI/res/layout/operator_name.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> +<com.android.systemui.statusbar.AlphaOptimizedFrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/operator_name_frame" + android:layout_width="wrap_content" + android:layout_height="match_parent" + > + <com.android.systemui.statusbar.OperatorNameView + android:id="@+id/operator_name" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:maxLength="20" + android:gravity="center_vertical|start" + android:textAppearance="?android:attr/textAppearanceSmall" + android:singleLine="true" /> +</com.android.systemui.statusbar.AlphaOptimizedFrameLayout> diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index c6452c03459a..6de27acd741b 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -48,6 +48,11 @@ android:paddingEnd="8dp" android:orientation="horizontal" > + <ViewStub + android:id="@+id/operator_name" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout="@layout/operator_name" /> <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). --> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index f9a423527db5..5d621cb79fc4 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -701,7 +701,7 @@ <string name="accessibility_action_divider_top_70" msgid="5090779195650364522">"Gore 70%"</string> <string name="accessibility_action_divider_top_50" msgid="6385859741925078668">"Gore 50%"</string> <string name="accessibility_action_divider_top_30" msgid="6201455163864841205">"Gore 30%"</string> - <string name="accessibility_action_divider_bottom_full" msgid="301433196679548001">"Dole cijeli ekran"</string> + <string name="accessibility_action_divider_bottom_full" msgid="301433196679548001">"Donji ekran kao cijeli ekran"</string> <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Pozicija <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dvaput dodirnite za uređivanje."</string> <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g> Dvaput dodirnite za dodavanje."</string> <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Pozicija <xliff:g id="POSITION">%1$d</xliff:g>. Dvaput dodirnite za odabir."</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 296842f86fa3..27d5f1b89868 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -233,8 +233,8 @@ <string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Mode kerja aktif."</string> <string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Mode kerja dinonaktifkan."</string> <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Mode kerja diaktifkan."</string> - <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Penghemat Data nonaktif."</string> - <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Penghemat Data diaktifkan."</string> + <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Penghemat Kuota Internet nonaktif."</string> + <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Penghemat Kuota Internet diaktifkan."</string> <string name="accessibility_brightness" msgid="8003681285547803095">"Kecerahan tampilan"</string> <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"Mengisi daya"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Data 2G-3G dijeda"</string> @@ -636,9 +636,9 @@ <string name="headset" msgid="4534219457597457353">"Headset"</string> <string name="accessibility_status_bar_headphones" msgid="9156307120060559989">"Headphone terhubung"</string> <string name="accessibility_status_bar_headset" msgid="8666419213072449202">"Headset terhubung"</string> - <string name="data_saver" msgid="5037565123367048522">"Penghemat Data"</string> - <string name="accessibility_data_saver_on" msgid="8454111686783887148">"Penghemat Data aktif"</string> - <string name="accessibility_data_saver_off" msgid="8841582529453005337">"Penghemat Data nonaktif"</string> + <string name="data_saver" msgid="5037565123367048522">"Penghemat Kuota Internet"</string> + <string name="accessibility_data_saver_on" msgid="8454111686783887148">"Penghemat Kuota Internet aktif"</string> + <string name="accessibility_data_saver_off" msgid="8841582529453005337">"Penghemat Kuota Internet nonaktif"</string> <string name="switch_bar_on" msgid="1142437840752794229">"Aktif"</string> <string name="switch_bar_off" msgid="8803270596930432874">"Nonaktif"</string> <string name="nav_bar" msgid="1993221402773877607">"Bilah navigasi"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 0fe81d9dfef1..f54115b0c9e2 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -301,6 +301,9 @@ <!-- Enable the default volume dialog --> <bool name="enable_volume_ui">true</bool> + <!-- Whether to show operator name in the status bar --> + <bool name="config_showOperatorNameInStatusBar">false</bool> + <!-- Duration of the full carrier network change icon animation. --> <integer name="carrier_network_change_anim_time">3000</integer> 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/recents/view/AppTransitionAnimationSpecsFuture.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java new file mode 100644 index 000000000000..a46fab35f66b --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java @@ -0,0 +1,84 @@ +/* + * 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.systemui.shared.recents.view; + +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.view.AppTransitionAnimationSpec; +import android.view.IAppTransitionAnimationSpecsFuture; + +import java.util.List; +import java.util.concurrent.FutureTask; + +/** + * To be implemented by a particular animation to asynchronously provide the animation specs for a + * particular transition. + */ +public abstract class AppTransitionAnimationSpecsFuture { + + private final Handler mHandler; + private FutureTask<List<AppTransitionAnimationSpec>> mComposeTask = new FutureTask<>(() -> { + synchronized (AppTransitionAnimationSpecsFuture.this) { + return composeSpecs(); + } + }); + + private final IAppTransitionAnimationSpecsFuture mFuture = + new IAppTransitionAnimationSpecsFuture.Stub() { + @Override + public AppTransitionAnimationSpec[] get() throws RemoteException { + try { + if (!mComposeTask.isDone()) { + mHandler.post(mComposeTask); + } + List<AppTransitionAnimationSpec> specs = mComposeTask.get(); + if (specs == null) { + return null; + } + + AppTransitionAnimationSpec[] arr = new AppTransitionAnimationSpec[specs.size()]; + specs.toArray(arr); + return arr; + } catch (Exception e) { + return null; + } + } + }; + + public AppTransitionAnimationSpecsFuture(Handler handler) { + mHandler = handler; + } + + /** + * Returns the future to handle the call from window manager. + */ + public final IAppTransitionAnimationSpecsFuture getFuture() { + return mFuture; + } + + /** + * Called ahead of the future callback to compose the specs to be returned in the future. + */ + public final void composeSpecsSynchronous() { + if (Looper.myLooper() != mHandler.getLooper()) { + throw new RuntimeException("composeSpecsSynchronous() called from wrong looper"); + } + mComposeTask.run(); + } + + public abstract List<AppTransitionAnimationSpec> composeSpecs(); +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java new file mode 100644 index 000000000000..3095d151726d --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java @@ -0,0 +1,117 @@ +/* + * 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.systemui.shared.recents.view; + +import android.app.ActivityOptions; +import android.app.ActivityOptions.OnAnimationStartedListener; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.GraphicBuffer; +import android.os.Bundle; +import android.os.Handler; +import android.os.IRemoteCallback; +import android.os.RemoteException; +import android.view.DisplayListCanvas; +import android.view.RenderNode; +import android.view.ThreadedRenderer; +import android.view.View; + +import java.util.function.Consumer; + +/** + * A helper class to create transitions to/from an App to Recents. + */ +public class RecentsTransition { + + /** + * Creates a new transition aspect scaled transition activity options. + */ + public static ActivityOptions createAspectScaleAnimation(Context context, Handler handler, + boolean scaleUp, AppTransitionAnimationSpecsFuture animationSpecsFuture, + OnAnimationStartedListener animationStartCallback) { + final OnAnimationStartedListener animStartedListener = new OnAnimationStartedListener() { + private boolean mHandled; + + @Override + public void onAnimationStarted() { + // OnAnimationStartedListener can be called numerous times, so debounce here to + // prevent multiple callbacks + if (mHandled) { + return; + } + mHandled = true; + + if (animationStartCallback != null) { + animationStartCallback.onAnimationStarted(); + } + } + }; + final ActivityOptions opts = ActivityOptions.makeMultiThumbFutureAspectScaleAnimation( + context, handler, + animationSpecsFuture != null ? animationSpecsFuture.getFuture() : null, + animStartedListener, scaleUp); + return opts; + } + + /** + * Wraps a animation-start callback in a binder that can be called from window manager. + */ + public static IRemoteCallback wrapStartedListener(Handler handler, + OnAnimationStartedListener listener) { + if (listener == null) { + return null; + } + return new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle data) throws RemoteException { + handler.post(listener::onAnimationStarted); + } + }; + } + + /** + * @return a {@link GraphicBuffer} with the {@param view} drawn into it. Result can be null if + * we were unable to allocate a hardware bitmap. + */ + public static GraphicBuffer drawViewIntoGraphicBuffer(int width, int height, View view, + float scale, int eraseColor) { + final Bitmap hwBitmap = createHardwareBitmap(width, height, (c) -> { + c.scale(scale, scale); + if (eraseColor != 0) { + c.drawColor(eraseColor); + } + if (view != null) { + view.draw(c); + } + }); + return hwBitmap != null ? hwBitmap.createGraphicBufferHandle() : null; + } + + /** + * @return a hardware {@link Bitmap} after being drawn with the {@param consumer}. Result can be + * null if we were unable to allocate a hardware bitmap. + */ + public static Bitmap createHardwareBitmap(int width, int height, Consumer<Canvas> consumer) { + RenderNode node = RenderNode.create("RecentsTransition", null); + node.setLeftTopRightBottom(0, 0, width, height); + node.setClipToBounds(false); + DisplayListCanvas c = node.start(width, height); + consumer.accept(c); + node.end(c); + return ThreadedRenderer.createHardwareBitmap(node, width, height); + } +} 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 3f93f76af7e4..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 { @@ -50,11 +61,13 @@ public class ActivityManagerWrapper { private final PackageManager mPackageManager; private final IconDrawableFactory mDrawableFactory; + private final BackgroundExecutor mBackgroundExecutor; private ActivityManagerWrapper() { final Context context = AppGlobals.getInitialApplication(); mPackageManager = context.getPackageManager(); mDrawableFactory = IconDrawableFactory.newInstance(context); + mBackgroundExecutor = BackgroundExecutor.get(); } public static ActivityManagerWrapper getInstance() { @@ -75,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) { @@ -198,4 +230,106 @@ public class ActivityManagerWrapper { } return label; } + + /** + * 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) { + mBackgroundExecutor.submit(() -> { + try { + ActivityManager.getService().closeSystemDialogs(reason); + } catch (RemoteException e) { + Log.w(TAG, "Failed to close system windows", e); + } + }); + } + + /** + * Removes a task by id. + */ + public void removeTask(int taskId) { + mBackgroundExecutor.submit(() -> { + try { + ActivityManager.getService().removeTask(taskId); + } catch (RemoteException e) { + Log.w(TAG, "Failed to remove task=" + taskId, e); + } + }); + } + + /** + * Cancels the current window transtion to/from Recents for the given task id. + */ + public void cancelWindowTransition(int taskId) { + try { + ActivityManager.getService().cancelTaskWindowTransition(taskId); + } catch (RemoteException e) { + Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e); + } + } + + /** + * Cancels the current thumbnail transtion to/from Recents for the given task id. + */ + public void cancelThumbnailTransition(int taskId) { + try { + ActivityManager.getService().cancelTaskThumbnailTransition(taskId); + } catch (RemoteException e) { + Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e); + } + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java new file mode 100644 index 000000000000..26e1813792bd --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java @@ -0,0 +1,45 @@ +/* + * 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.systemui.shared.system; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + * Offloads work from other threads by running it in a background thread. + */ +public class BackgroundExecutor { + + private static final BackgroundExecutor sInstance = new BackgroundExecutor(); + + private final ExecutorService mExecutorService = Executors.newFixedThreadPool(2); + + /** + * @return the static instance of the background executor. + */ + public static BackgroundExecutor get() { + return sInstance; + } + + /** + * Runs the given {@param runnable} on one of the background executor threads. + */ + public Future<?> submit(Runnable runnable) { + return mExecutorService.submit(runnable); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index 6d0952abe2f8..36310f11c22d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/TaskStackChangeListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java @@ -14,24 +14,22 @@ * limitations under the License. */ -package com.android.systemui.recents.misc; +package com.android.systemui.shared.system; import android.app.ActivityManager.TaskSnapshot; -import android.content.Context; import android.os.UserHandle; import android.util.Log; /** - * An abstract class to track task stack changes. - * Classes should implement this instead of {@link android.app.ITaskStackListener} - * to reduce IPC calls from system services. These callbacks will be called on the main thread. + * An interface to track task stack changes. Classes should implement this instead of + * {@link android.app.ITaskStackListener} to reduce IPC calls from system services. */ public abstract class TaskStackChangeListener { - /** - * NOTE: This call is made of the thread that the binder call comes in on. - */ + // Binder thread callbacks public void onTaskStackChangedBackground() { } + + // Main thread callbacks public void onTaskStackChanged() { } public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { } public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { } @@ -45,17 +43,16 @@ public abstract class TaskStackChangeListener { public void onTaskProfileLocked(int taskId, int userId) { } /** - * Checks that the current user matches the user's SystemUI process. Since + * Checks that the current user matches the process. Since * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of - * TaskStackChangeListener should make this call to verify that we don't act on events from other - * user's processes. + * {@link TaskStackChangeListener} should make this call to verify that we don't act on events + * originating from another user's interactions. */ - protected final boolean checkCurrentUserId(Context context, boolean debug) { + protected final boolean checkCurrentUserId(int currentUserId, boolean debug) { int processUserId = UserHandle.myUserId(); - int currentUserId = SystemServicesProxy.getInstance(context).getCurrentUser(); if (processUserId != currentUserId) { if (debug) { - Log.d(SystemServicesProxy.TAG, "UID mismatch. SystemUI is running uid=" + processUserId + Log.d("TaskStackChangeListener", "UID mismatch. Process is uid=" + processUserId + " and the current user is uid=" + currentUserId); } return false; diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index 8eb70f04cf32..95fc96f5f228 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.recents.misc; +package com.android.systemui.shared.system; import android.app.ActivityManager.TaskSnapshot; import android.app.IActivityManager; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 2bb992c449b6..623fe53f5d7f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -76,8 +76,8 @@ import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.TaskStackChangeListener; import com.google.android.collect.Lists; @@ -1738,6 +1738,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { mFailedAttempts.delete(sCurrentUser); } + public ServiceState getServiceState(int subId) { + return mServiceStates.get(subId); + } + public int getFailedUnlockAttempts(int userId) { return mFailedAttempts.get(userId, 0); } @@ -1772,7 +1776,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } - private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { + private final SysUiTaskStackChangeListener + mTaskStackListener = new SysUiTaskStackChangeListener() { @Override public void onTaskStackChangedBackground() { try { diff --git a/packages/SystemUI/src/com/android/systemui/DemoMode.java b/packages/SystemUI/src/com/android/systemui/DemoMode.java index 11996d078bc3..5c3971571b87 100644 --- a/packages/SystemUI/src/com/android/systemui/DemoMode.java +++ b/packages/SystemUI/src/com/android/systemui/DemoMode.java @@ -37,4 +37,5 @@ public interface DemoMode { public static final String COMMAND_STATUS = "status"; public static final String COMMAND_NOTIFICATIONS = "notifications"; public static final String COMMAND_VOLUME = "volume"; + public static final String COMMAND_OPERATOR = "operator"; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java index 4c3d5badf934..4119ec076dc3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java @@ -26,14 +26,16 @@ import android.content.Intent; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.TaskStackChangeListener; public class WorkLockActivityController { + private static final String TAG = WorkLockActivityController.class.getSimpleName(); + private final Context mContext; - private final SystemServicesProxy mSsp; private final IActivityManager mIam; public WorkLockActivityController(Context context) { @@ -43,17 +45,22 @@ public class WorkLockActivityController { @VisibleForTesting WorkLockActivityController(Context context, SystemServicesProxy ssp, IActivityManager am) { mContext = context; - mSsp = ssp; mIam = am; - mSsp.registerTaskStackListener(mLockListener); + ssp.registerTaskStackListener(mLockListener); } private void startWorkChallengeInTask(int taskId, int userId) { + ActivityManager.TaskDescription taskDescription = null; + try { + taskDescription = mIam.getTaskDescription(taskId); + } catch (RemoteException e) { + Log.w(TAG, "Failed to get description for task=" + taskId); + } Intent intent = new Intent(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER) .setComponent(new ComponentName(mContext, WorkLockActivity.class)) .putExtra(Intent.EXTRA_USER_ID, userId) - .putExtra(WorkLockActivity.EXTRA_TASK_DESCRIPTION, mSsp.getTaskDescription(taskId)) + .putExtra(WorkLockActivity.EXTRA_TASK_DESCRIPTION, taskDescription) .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -67,7 +74,11 @@ public class WorkLockActivityController { } else { // Starting the activity inside the task failed. We can't be sure why, so to be // safe just remove the whole task if it still exists. - mSsp.removeTask(taskId); + try { + mIam.removeTask(taskId); + } catch (RemoteException e) { + Log.w(TAG, "Failed to get description for task=" + taskId); + } } } @@ -96,7 +107,7 @@ public class WorkLockActivityController { } } - private final TaskStackChangeListener mLockListener = new TaskStackChangeListener() { + private final SysUiTaskStackChangeListener mLockListener = new SysUiTaskStackChangeListener() { @Override public void onTaskProfileLocked(int taskId, int userId) { startWorkChallengeInTask(taskId, userId); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java index e6d6c5586ad8..db4f988a9122 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java @@ -18,6 +18,8 @@ package com.android.systemui.pip.phone; import static android.view.WindowManager.INPUT_CONSUMER_PIP; +import android.os.Binder; +import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.util.Log; @@ -77,7 +79,8 @@ public class InputConsumerController { } } - private IWindowManager mWindowManager; + private final IWindowManager mWindowManager; + private final IBinder mToken; private PipInputEventReceiver mInputEventReceiver; private TouchListener mListener; @@ -85,6 +88,7 @@ public class InputConsumerController { public InputConsumerController(IWindowManager windowManager) { mWindowManager = windowManager; + mToken = new Binder(); registerInputConsumer(); } @@ -122,7 +126,7 @@ public class InputConsumerController { final InputChannel inputChannel = new InputChannel(); try { mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP); - mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel); + mWindowManager.createInputConsumer(mToken, INPUT_CONSUMER_PIP, inputChannel); } catch (RemoteException e) { Log.e(TAG, "Failed to create PIP input consumer", e); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 7e87666a18f5..7ef0f1539dd6 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -40,8 +40,9 @@ import android.view.WindowManagerGlobal; import com.android.systemui.pip.BasePipManager; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.component.ExpandPipEvent; +import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.TaskStackChangeListener; +import com.android.systemui.shared.system.ActivityManagerWrapper; import java.io.PrintWriter; @@ -69,7 +70,7 @@ public class PipManager implements BasePipManager { /** * Handler for system task stack changes. */ - TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { + SysUiTaskStackChangeListener mTaskStackListener = new SysUiTaskStackChangeListener() { @Override public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { mTouchHandler.onActivityPinned(); @@ -202,9 +203,9 @@ public class PipManager implements BasePipManager { StackInfo stackInfo = mActivityManager.getStackInfo( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (stackInfo != null && stackInfo.taskIds != null) { - SystemServicesProxy ssp = SystemServicesProxy.getInstance(mContext); + ActivityManagerWrapper am = ActivityManagerWrapper.getInstance(); for (int taskId : stackInfo.taskIds) { - ssp.cancelThumbnailTransition(taskId); + am.cancelThumbnailTransition(taskId); } } } catch (RemoteException e) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index c92562b4436f..5a91defc42df 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -45,8 +45,8 @@ import android.view.WindowManagerGlobal; import com.android.systemui.R; import com.android.systemui.pip.BasePipManager; +import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.TaskStackChangeListener; import java.io.PrintWriter; import java.util.ArrayList; @@ -620,7 +620,7 @@ public class PipManager implements BasePipManager { return false; } - private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { + private SysUiTaskStackChangeListener mTaskStackListener = new SysUiTaskStackChangeListener() { @Override public void onTaskStackChanged() { if (DEBUG) Log.d(TAG, "onTaskStackChanged()"); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index 1aecdceb378c..0e4a9fe32911 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -134,7 +134,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { if (lastDevice != null) { int batteryLevel = lastDevice.getBatteryLevel(); if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { - state.icon = new BluetoothBatteryTileIcon(batteryLevel, + state.icon = new BluetoothBatteryTileIcon(lastDevice, mContext.getResources().getFraction( R.fraction.bt_battery_scale_fraction, 1, 1)); } @@ -213,18 +213,19 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { } private class BluetoothBatteryTileIcon extends Icon { - private int mLevel; private float mIconScale; + private CachedBluetoothDevice mDevice; - BluetoothBatteryTileIcon(int level, float iconScale) { - mLevel = level; + BluetoothBatteryTileIcon(CachedBluetoothDevice device, float iconScale) { mIconScale = iconScale; + mDevice = device; } @Override public Drawable getDrawable(Context context) { - return createLayerDrawable(context, - R.drawable.ic_qs_bluetooth_connected, mLevel, mIconScale); + // This method returns Pair<Drawable, String> while first value is the drawable + return com.android.settingslib.bluetooth.Utils.getBtClassDrawableWithDescription( + context, mDevice, mIconScale).first; } } @@ -306,7 +307,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { item.iconResId = R.drawable.ic_qs_bluetooth_connected; int batteryLevel = device.getBatteryLevel(); if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { - item.icon = new BluetoothBatteryTileIcon(batteryLevel, + item.icon = new BluetoothBatteryTileIcon(device, 1 /* iconScale */); item.line2 = mContext.getString( R.string.quick_settings_connected_battery_level, diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 2250e410c56d..5b62c7d3c002 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -63,6 +63,7 @@ import com.android.systemui.recents.events.component.ShowUserToastEvent; import com.android.systemui.recents.events.ui.RecentsDrawnEvent; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.recents.model.RecentsTaskLoader; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; @@ -246,7 +247,7 @@ public class Recents extends SystemUI return; } - sSystemServicesProxy.sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS); + ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS); int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents(); int currentUser = sSystemServicesProxy.getCurrentUser(); if (sSystemServicesProxy.isSystemUser(currentUser)) { @@ -410,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/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index b75a142864e8..c666d572e10a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -16,6 +16,9 @@ package com.android.systemui.recents; +import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY; +import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS; + import android.app.Activity; import android.app.ActivityOptions; import android.app.TaskStackBuilder; @@ -92,6 +95,7 @@ import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.TaskStack; import com.android.systemui.recents.views.RecentsView; import com.android.systemui.recents.views.SystemBarScrimViews; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.phone.StatusBar; import java.io.FileDescriptor; @@ -280,8 +284,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD new DismissRecentsToHomeAnimationStarted(animateTaskViews); dismissEvent.addPostAnimationCallback(new LaunchHomeRunnable(mHomeIntent, overrideAnimation)); - Recents.getSystemServices().sendCloseSystemWindows( - StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY); + ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY); EventBus.getDefault().send(dismissEvent); } @@ -706,9 +709,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD int launchToTaskId = launchState.launchedToTaskId; if (launchToTaskId != -1 && (event.launchTask == null || launchToTaskId != event.launchTask.key.id)) { - SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.cancelWindowTransition(launchState.launchedToTaskId); - ssp.cancelThumbnailTransition(getTaskId()); + ActivityManagerWrapper am = ActivityManagerWrapper.getInstance(); + am.cancelWindowTransition(launchState.launchedToTaskId); + am.cancelThumbnailTransition(getTaskId()); } } @@ -755,8 +758,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD loader.deleteTaskData(event.task, false); // Remove the task from activity manager - SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.removeTask(event.task.key.id); + ActivityManagerWrapper.getInstance().removeTask(event.task.key.id); } public final void onBusEvent(TaskViewDismissedEvent event) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 1533e0a1fff2..0b816b5ff820 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -20,6 +20,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.view.View.MeasureSpec; +import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS; + import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityOptions; @@ -70,26 +72,28 @@ import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent; import com.android.systemui.recents.misc.DozeTrigger; import com.android.systemui.recents.misc.ForegroundThread; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.TaskStackChangeListener; +import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan; import com.android.systemui.shared.recents.model.RecentsTaskLoader; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.recents.model.TaskStack; import com.android.systemui.shared.recents.model.ThumbnailData; -import com.android.systemui.recents.views.RecentsTransitionHelper; -import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture; import com.android.systemui.recents.views.TaskStackLayoutAlgorithm; import com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport; import com.android.systemui.recents.views.TaskStackView; import com.android.systemui.recents.views.TaskViewHeader; import com.android.systemui.recents.views.TaskViewTransform; import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm; +import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture; +import com.android.systemui.shared.recents.view.RecentsTransition; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.stackdivider.DividerView; import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; import com.android.systemui.statusbar.phone.StatusBar; import java.util.ArrayList; +import java.util.List; /** * An implementation of the Recents component for the current user. For secondary users, this can @@ -113,10 +117,10 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity"; /** - * An implementation of TaskStackChangeListener, that allows us to listen for changes to the system + * An implementation of SysUiTaskStackChangeListener, that allows us to listen for changes to the system * task stacks and update recents accordingly. */ - class TaskStackListenerImpl extends TaskStackChangeListener { + class TaskStackListenerImpl extends SysUiTaskStackChangeListener { @Override public void onTaskStackChangedBackground() { @@ -134,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); @@ -349,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); } @@ -440,12 +445,14 @@ 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); // Only close the other system windows if we are actually showing recents - ssp.sendCloseSystemWindows(StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS); + ActivityManagerWrapper.getInstance().closeSystemWindows( + SYSTEM_DIALOG_REASON_RECENT_APPS); mLastToggleTime = SystemClock.elapsedRealtime(); } } catch (ActivityNotFoundException e) { @@ -465,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; } @@ -519,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 @@ -556,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 */); } /** @@ -574,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) @@ -625,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() { @@ -864,22 +874,22 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener windowOverrideRect); RectF toTaskRect = toTransform.rect; - AppTransitionAnimationSpecsFuture future = - new RecentsTransitionHelper(mContext).getAppTransitionFuture( - () -> { - Rect rect = new Rect(); - toTaskRect.round(rect); - GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(toTask, - toTransform); - return Lists.newArrayList(new AppTransitionAnimationSpec( - toTask.key.id, thumbnail, rect)); - }); + AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(mHandler) { + @Override + public List<AppTransitionAnimationSpec> composeSpecs() { + Rect rect = new Rect(); + toTaskRect.round(rect); + GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform); + return Lists.newArrayList(new AppTransitionAnimationSpec(toTask.key.id, thumbnail, + rect)); + } + }; // For low end ram devices, wait for transition flag is reset when Recents entrance // animation is complete instead of when the transition animation starts - return new Pair<>(ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext, - mHandler, future.getFuture(), isLowRamDevice ? null : mResetToggleFlagListener, - false /* scaleUp */), future); + return new Pair<>(RecentsTransition.createAspectScaleAnimation(mContext, mHandler, + false /* scaleUp */, future, isLowRamDevice ? null : mResetToggleFlagListener), + future); } /** @@ -919,7 +929,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode(); mHeaderBar.onTaskViewSizeChanged(width, height); if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) { - return RecentsTransitionHelper.drawViewIntoGraphicBuffer(width, mTaskBarHeight, + return RecentsTransition.drawViewIntoGraphicBuffer(width, mTaskBarHeight, null, 1f, 0xFFff0000); } else { // Workaround for b/27815919, reset the callback so that we do not trigger an @@ -932,7 +942,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener disabledInSafeMode); mHeaderBar.onTaskDataLoaded(); mHeaderBar.setDimAlpha(toTransform.dimAlpha); - return RecentsTransitionHelper.drawViewIntoGraphicBuffer(width, mTaskBarHeight, + return RecentsTransition.drawViewIntoGraphicBuffer(width, mTaskBarHeight, mHeaderBar, 1f, 0); } } @@ -1047,7 +1057,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener Recents.getSystemServices().startActivityAsUserAsync(intent, opts); EventBus.getDefault().send(new RecentsActivityStartingEvent()); if (future != null) { - future.precacheSpecs(); + future.composeSpecsSynchronous(); } }); EventBus.getDefault().send(hideMenuEvent); diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java new file mode 100644 index 000000000000..5d7f1ba5eaf4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents.misc; + +import android.content.Context; + +import com.android.systemui.shared.system.TaskStackChangeListener; + +/** + * An implementation of {@link TaskStackChangeListener}. + */ +public abstract class SysUiTaskStackChangeListener extends TaskStackChangeListener { + + /** + * Checks that the current user matches the user's SystemUI process. + */ + protected final boolean checkCurrentUserId(Context context, boolean debug) { + int currentUserId = SystemServicesProxy.getInstance(context).getCurrentUser(); + return checkCurrentUserId(currentUserId, debug); + } +} 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 c4ac52b8b6d9..a436e177cb51 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -79,9 +79,11 @@ import com.android.systemui.UiOffloadThread; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImpl; import com.android.systemui.shared.recents.model.Task; +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 @@ -122,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() { @@ -196,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() { @@ -364,32 +347,6 @@ public class SystemServicesProxy { return insets.right > 0; } - /** - * Cancels the current window transtion to/from Recents for the given task id. - */ - public void cancelWindowTransition(int taskId) { - if (mIam == null) return; - - try { - mIam.cancelTaskWindowTransition(taskId); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - - /** - * Cancels the current thumbnail transtion to/from Recents for the given task id. - */ - public void cancelThumbnailTransition(int taskId) { - if (mIam == null) return; - - try { - mIam.cancelTaskThumbnailTransition(taskId); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - /** Set the task's windowing mode. */ public void setTaskWindowingMode(int taskId, int windowingMode) { if (mIam == null) return; @@ -401,40 +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(); - } - }); - } - - /** - * Sends a message to close other system windows. - */ - public void sendCloseSystemWindows(String reason) { - mUiOffloadThread.submit(() -> { - try { - mIam.closeSystemDialogs(reason); - } catch (RemoteException e) { - } - }); - } - - 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. */ @@ -557,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; @@ -623,7 +496,7 @@ public class SystemServicesProxy { * Registers a task stack listener with the system. * This should be called on the main thread. */ - public void registerTaskStackListener(TaskStackChangeListener listener) { + public void registerTaskStackListener(SysUiTaskStackChangeListener listener) { if (mIam == null) return; synchronized (mTaskStackChangeListeners) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java new file mode 100644 index 000000000000..99390ecfe08c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2014 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.systemui.recents.views; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +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.content.Context; +import android.graphics.GraphicBuffer; +import android.graphics.Rect; +import android.util.Log; +import android.view.AppTransitionAnimationSpec; + +import com.android.systemui.recents.Recents; +import com.android.systemui.recents.RecentsDebugFlags; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.view.RecentsTransition; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A helper class to create the transition app animation specs to/from Recents + */ +public class RecentsTransitionComposer { + + private static final String TAG = "RecentsTransitionComposer"; + + private Context mContext; + private TaskViewTransform mTmpTransform = new TaskViewTransform(); + + public RecentsTransitionComposer(Context context) { + mContext = context; + } + + /** + * Composes a single animation spec for the given {@link TaskView} + */ + private static AppTransitionAnimationSpec composeAnimationSpec(TaskStackView stackView, + TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) { + GraphicBuffer b = null; + if (addHeaderBitmap) { + b = composeHeaderBitmap(taskView, transform); + if (b == null) { + return null; + } + } + + Rect taskRect = new Rect(); + transform.rect.round(taskRect); + // Disable in for low ram devices because each task does in Recents does not have fullscreen + // height (stackView height) and when transitioning to fullscreen app, the code below would + // force the task thumbnail to full stackView height immediately causing the transition + // jarring. + if (!Recents.getConfiguration().isLowRamDevice && taskView.getTask() != + stackView.getStack().getStackFrontMostTask()) { + taskRect.bottom = taskRect.top + stackView.getMeasuredHeight(); + } + return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect); + } + + /** + * Composes the transition spec when docking a task, which includes a full task bitmap. + */ + public List<AppTransitionAnimationSpec> composeDockAnimationSpec(TaskView taskView, + Rect bounds) { + mTmpTransform.fillIn(taskView); + Task task = taskView.getTask(); + GraphicBuffer buffer = RecentsTransitionComposer.composeTaskBitmap(taskView, mTmpTransform); + return Collections.singletonList(new AppTransitionAnimationSpec(task.key.id, buffer, + bounds)); + } + + /** + * Composes the animation specs for all the tasks in the target stack. + */ + public List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task, + final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) { + // Calculate the offscreen task rect (for tasks that are not backed by views) + TaskView taskView = stackView.getChildViewForTask(task); + TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm(); + Rect offscreenTaskRect = new Rect(); + stackLayout.getFrontOfStackTransform().rect.round(offscreenTaskRect); + + // If this is a full screen stack, the transition will be towards the single, full screen + // task. We only need the transition spec for this task. + + // TODO: Sometimes targetStackId is not initialized after reboot, so we also have to + // check for INVALID_STACK_ID (now WINDOWING_MODE_UNDEFINED) + if (windowingMode == WINDOWING_MODE_FULLSCREEN + || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY + || activityType == ACTIVITY_TYPE_ASSISTANT + || windowingMode == WINDOWING_MODE_UNDEFINED) { + List<AppTransitionAnimationSpec> specs = new ArrayList<>(); + if (taskView == null) { + specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect)); + } else { + mTmpTransform.fillIn(taskView); + stackLayout.transformToScreenCoordinates(mTmpTransform, windowRect); + AppTransitionAnimationSpec spec = composeAnimationSpec(stackView, taskView, + mTmpTransform, true /* addHeaderBitmap */); + if (spec != null) { + specs.add(spec); + } + } + return specs; + } + return Collections.emptyList(); + } + + /** + * Composes a single animation spec for the given {@link Task} + */ + private static AppTransitionAnimationSpec composeOffscreenAnimationSpec(Task task, + Rect taskRect) { + return new AppTransitionAnimationSpec(task.key.id, null, taskRect); + } + + public static GraphicBuffer composeTaskBitmap(TaskView taskView, TaskViewTransform transform) { + float scale = transform.scale; + int fromWidth = (int) (transform.rect.width() * scale); + int fromHeight = (int) (transform.rect.height() * scale); + if (fromWidth == 0 || fromHeight == 0) { + Log.e(TAG, "Could not compose thumbnail for task: " + taskView.getTask() + + " at transform: " + transform); + + return RecentsTransition.drawViewIntoGraphicBuffer(1, 1, null, 1f, 0x00ffffff); + } else { + if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) { + return RecentsTransition.drawViewIntoGraphicBuffer(fromWidth, fromHeight, null, 1f, + 0xFFff0000); + } else { + return RecentsTransition.drawViewIntoGraphicBuffer(fromWidth, fromHeight, taskView, + scale, 0); + } + } + } + + private static GraphicBuffer composeHeaderBitmap(TaskView taskView, + TaskViewTransform transform) { + float scale = transform.scale; + int headerWidth = (int) (transform.rect.width()); + int headerHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale); + if (headerWidth == 0 || headerHeight == 0) { + return null; + } + + if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) { + return RecentsTransition.drawViewIntoGraphicBuffer(headerWidth, headerHeight, null, 1f, + 0xFFff0000); + } else { + return RecentsTransition.drawViewIntoGraphicBuffer(headerWidth, headerHeight, + taskView.mHeaderView, scale, 0); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java deleted file mode 100644 index 7442904ec1c0..000000000000 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +++ /dev/null @@ -1,458 +0,0 @@ -/* - * Copyright (C) 2014 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.systemui.recents.views; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -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.Nullable; -import android.app.ActivityOptions; -import android.app.ActivityOptions.OnAnimationStartedListener; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.GraphicBuffer; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.Handler; -import android.os.IRemoteCallback; -import android.os.RemoteException; -import android.util.Log; -import android.view.AppTransitionAnimationSpec; -import android.view.DisplayListCanvas; -import android.view.IAppTransitionAnimationSpecsFuture; -import android.view.RenderNode; -import android.view.ThreadedRenderer; -import android.view.View; - -import com.android.internal.annotations.GuardedBy; -import com.android.systemui.recents.Recents; -import com.android.systemui.recents.RecentsDebugFlags; -import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; -import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent; -import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent; -import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent; -import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent; -import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; -import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent; -import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.shared.recents.model.Task; -import com.android.systemui.shared.recents.model.TaskStack; -import com.android.systemui.statusbar.phone.StatusBar; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * A helper class to create transitions to/from Recents - */ -public class RecentsTransitionHelper { - - private static final String TAG = "RecentsTransitionHelper"; - private static final boolean DEBUG = false; - - /** - * Special value for {@link #mAppTransitionAnimationSpecs}: Indicate that we are currently - * waiting for the specs to be retrieved. - */ - private static final List<AppTransitionAnimationSpec> SPECS_WAITING = new ArrayList<>(); - - @GuardedBy("this") - private List<AppTransitionAnimationSpec> mAppTransitionAnimationSpecs = SPECS_WAITING; - - private Context mContext; - private Handler mHandler; - private TaskViewTransform mTmpTransform = new TaskViewTransform(); - - private class StartScreenPinningRunnableRunnable implements Runnable { - - private int taskId = -1; - - @Override - public void run() { - EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext, taskId)); - } - } - private StartScreenPinningRunnableRunnable mStartScreenPinningRunnable - = new StartScreenPinningRunnableRunnable(); - - public RecentsTransitionHelper(Context context) { - mContext = context; - mHandler = new Handler(); - } - - /** - * Launches the specified {@link Task}. - */ - public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task, - final TaskStackView stackView, final TaskView taskView, - final boolean screenPinningRequested, final int windowingMode, final int activityType) { - - final ActivityOptions.OnAnimationStartedListener animStartedListener; - final AppTransitionAnimationSpecsFuture transitionFuture; - if (taskView != null) { - - // Fetch window rect here already in order not to be blocked on lock contention in WM - // when the future calls it. - final Rect windowRect = Recents.getSystemServices().getWindowRect(); - transitionFuture = getAppTransitionFuture(() -> composeAnimationSpecs( - task, stackView, windowingMode, activityType, windowRect)); - animStartedListener = new OnAnimationStartedListener() { - private boolean mHandled; - - @Override - public void onAnimationStarted() { - if (mHandled) { - return; - } - mHandled = true; - - // If we are launching into another task, cancel the previous task's - // window transition - EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); - EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); - stackView.cancelAllTaskViewAnimations(); - - if (screenPinningRequested) { - // Request screen pinning after the animation runs - mStartScreenPinningRunnable.taskId = task.key.id; - mHandler.postDelayed(mStartScreenPinningRunnable, 350); - } - - if (!Recents.getConfiguration().isLowRamDevice) { - // Reset the state where we are waiting for the transition to start - EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false)); - } - } - }; - } else { - // This is only the case if the task is not on screen (scrolled offscreen for example) - transitionFuture = null; - animStartedListener = new OnAnimationStartedListener() { - private boolean mHandled; - - @Override - public void onAnimationStarted() { - if (mHandled) { - return; - } - mHandled = true; - - // If we are launching into another task, cancel the previous task's - // window transition - EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); - EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); - stackView.cancelAllTaskViewAnimations(); - - if (!Recents.getConfiguration().isLowRamDevice) { - // Reset the state where we are waiting for the transition to start - EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false)); - } - } - }; - } - - EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(true)); - final ActivityOptions opts = ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext, - mHandler, transitionFuture != null ? transitionFuture.future : null, - animStartedListener, true /* scaleUp */); - if (taskView == null) { - // If there is no task view, then we do not need to worry about animating out occluding - // task views, and we can launch immediately - startTaskActivity(stack, task, taskView, opts, transitionFuture, - windowingMode, activityType); - } else { - LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView, - screenPinningRequested); - EventBus.getDefault().send(launchStartedEvent); - startTaskActivity(stack, task, taskView, opts, transitionFuture, windowingMode, - activityType); - } - Recents.getSystemServices().sendCloseSystemWindows( - StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY); - } - - public IRemoteCallback wrapStartedListener(final OnAnimationStartedListener listener) { - if (listener == null) { - return null; - } - return new IRemoteCallback.Stub() { - @Override - public void sendResult(Bundle data) throws RemoteException { - mHandler.post(new Runnable() { - @Override - public void run() { - listener.onAnimationStarted(); - } - }); - } - }; - } - - /** - * Starts the activity for the launch task. - * - * @param taskView this is the {@link TaskView} that we are launching from. This can be null if - * we are toggling recents and the launch-to task is now offscreen. - */ - 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(); - } - - // Keep track of failed launches - EventBus.getDefault().send(new LaunchTaskFailedEvent()); - } - }); - if (transitionFuture != null) { - mHandler.post(transitionFuture::precacheSpecs); - } - } - - /** - * Creates a future which will later be queried for animation specs for this current transition. - * - * @param composer The implementation that composes the specs on the UI thread. - */ - public AppTransitionAnimationSpecsFuture getAppTransitionFuture( - final AnimationSpecComposer composer) { - synchronized (this) { - mAppTransitionAnimationSpecs = SPECS_WAITING; - } - IAppTransitionAnimationSpecsFuture future = new IAppTransitionAnimationSpecsFuture.Stub() { - @Override - public AppTransitionAnimationSpec[] get() throws RemoteException { - mHandler.post(() -> { - synchronized (RecentsTransitionHelper.this) { - mAppTransitionAnimationSpecs = composer.composeSpecs(); - RecentsTransitionHelper.this.notifyAll(); - } - }); - synchronized (RecentsTransitionHelper.this) { - while (mAppTransitionAnimationSpecs == SPECS_WAITING) { - try { - RecentsTransitionHelper.this.wait(); - } catch (InterruptedException e) {} - } - if (mAppTransitionAnimationSpecs == null) { - return null; - } - AppTransitionAnimationSpec[] specs - = new AppTransitionAnimationSpec[mAppTransitionAnimationSpecs.size()]; - mAppTransitionAnimationSpecs.toArray(specs); - mAppTransitionAnimationSpecs = SPECS_WAITING; - return specs; - } - } - }; - return new AppTransitionAnimationSpecsFuture(composer, future); - } - - /** - * Composes the transition spec when docking a task, which includes a full task bitmap. - */ - public List<AppTransitionAnimationSpec> composeDockAnimationSpec(TaskView taskView, - Rect bounds) { - mTmpTransform.fillIn(taskView); - Task task = taskView.getTask(); - GraphicBuffer buffer = RecentsTransitionHelper.composeTaskBitmap(taskView, mTmpTransform); - return Collections.singletonList(new AppTransitionAnimationSpec(task.key.id, buffer, - bounds)); - } - - /** - * Composes the animation specs for all the tasks in the target stack. - */ - private List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task, - final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) { - // Calculate the offscreen task rect (for tasks that are not backed by views) - TaskView taskView = stackView.getChildViewForTask(task); - TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm(); - Rect offscreenTaskRect = new Rect(); - stackLayout.getFrontOfStackTransform().rect.round(offscreenTaskRect); - - // If this is a full screen stack, the transition will be towards the single, full screen - // task. We only need the transition spec for this task. - - // TODO: Sometimes targetStackId is not initialized after reboot, so we also have to - // check for INVALID_STACK_ID (now WINDOWING_MODE_UNDEFINED) - if (windowingMode == WINDOWING_MODE_FULLSCREEN - || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY - || activityType == ACTIVITY_TYPE_ASSISTANT - || windowingMode == WINDOWING_MODE_UNDEFINED) { - List<AppTransitionAnimationSpec> specs = new ArrayList<>(); - if (taskView == null) { - specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect)); - } else { - mTmpTransform.fillIn(taskView); - stackLayout.transformToScreenCoordinates(mTmpTransform, windowRect); - AppTransitionAnimationSpec spec = composeAnimationSpec(stackView, taskView, - mTmpTransform, true /* addHeaderBitmap */); - if (spec != null) { - specs.add(spec); - } - } - return specs; - } - return Collections.emptyList(); - } - - /** - * Composes a single animation spec for the given {@link Task} - */ - private static AppTransitionAnimationSpec composeOffscreenAnimationSpec(Task task, - Rect taskRect) { - return new AppTransitionAnimationSpec(task.key.id, null, taskRect); - } - - public static GraphicBuffer composeTaskBitmap(TaskView taskView, TaskViewTransform transform) { - float scale = transform.scale; - int fromWidth = (int) (transform.rect.width() * scale); - int fromHeight = (int) (transform.rect.height() * scale); - if (fromWidth == 0 || fromHeight == 0) { - Log.e(TAG, "Could not compose thumbnail for task: " + taskView.getTask() + - " at transform: " + transform); - - return drawViewIntoGraphicBuffer(1, 1, null, 1f, 0x00ffffff); - } else { - if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) { - return drawViewIntoGraphicBuffer(fromWidth, fromHeight, null, 1f, 0xFFff0000); - } else { - return drawViewIntoGraphicBuffer(fromWidth, fromHeight, taskView, scale, 0); - } - } - } - - private static GraphicBuffer composeHeaderBitmap(TaskView taskView, - TaskViewTransform transform) { - float scale = transform.scale; - int headerWidth = (int) (transform.rect.width()); - int headerHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale); - if (headerWidth == 0 || headerHeight == 0) { - return null; - } - - if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) { - return drawViewIntoGraphicBuffer(headerWidth, headerHeight, null, 1f, 0xFFff0000); - } else { - return drawViewIntoGraphicBuffer(headerWidth, headerHeight, taskView.mHeaderView, - scale, 0); - } - } - - public static GraphicBuffer drawViewIntoGraphicBuffer(int bufferWidth, int bufferHeight, - View view, float scale, int eraseColor) { - RenderNode node = RenderNode.create("RecentsTransition", null); - node.setLeftTopRightBottom(0, 0, bufferWidth, bufferHeight); - node.setClipToBounds(false); - DisplayListCanvas c = node.start(bufferWidth, bufferHeight); - c.scale(scale, scale); - if (eraseColor != 0) { - c.drawColor(eraseColor); - } - if (view != null) { - view.draw(c); - } - node.end(c); - Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, bufferWidth, bufferHeight); - return hwBitmap.createGraphicBufferHandle(); - } - - /** - * Composes a single animation spec for the given {@link TaskView} - */ - private static AppTransitionAnimationSpec composeAnimationSpec(TaskStackView stackView, - TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) { - GraphicBuffer b = null; - if (addHeaderBitmap) { - b = composeHeaderBitmap(taskView, transform); - if (b == null) { - return null; - } - } - - Rect taskRect = new Rect(); - transform.rect.round(taskRect); - // Disable in for low ram devices because each task does in Recents does not have fullscreen - // height (stackView height) and when transitioning to fullscreen app, the code below would - // force the task thumbnail to full stackView height immediately causing the transition - // jarring. - if (!Recents.getConfiguration().isLowRamDevice && taskView.getTask() != - stackView.getStack().getStackFrontMostTask()) { - taskRect.bottom = taskRect.top + stackView.getMeasuredHeight(); - } - return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect); - } - - public interface AnimationSpecComposer { - List<AppTransitionAnimationSpec> composeSpecs(); - } - - /** - * Class to be returned from {@link #composeAnimationSpec} that gives access to both the future - * and the anonymous class used for composing. - */ - public class AppTransitionAnimationSpecsFuture { - - private final AnimationSpecComposer composer; - private final IAppTransitionAnimationSpecsFuture future; - - private AppTransitionAnimationSpecsFuture(AnimationSpecComposer composer, - IAppTransitionAnimationSpecsFuture future) { - this.composer = composer; - this.future = future; - } - - public IAppTransitionAnimationSpecsFuture getFuture() { - return future; - } - - /** - * Manually generates and caches the spec such that they are already available when the - * future needs. - */ - public void precacheSpecs() { - synchronized (RecentsTransitionHelper.this) { - mAppTransitionAnimationSpecs = composer.composeSpecs(); - } - } - } -} 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 5f12a04b5e4d..b82f15eff4b8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -16,8 +16,13 @@ package com.android.systemui.recents.views; +import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY; +import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS; + import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.Nullable; +import android.app.ActivityOptions; import android.app.ActivityOptions.OnAnimationStartedListener; import android.content.Context; import android.content.res.ColorStateList; @@ -28,8 +33,10 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; 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; @@ -54,15 +61,22 @@ import com.android.systemui.recents.RecentsActivity; import com.android.systemui.recents.RecentsActivityLaunchState; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent; +import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent; import com.android.systemui.recents.events.activity.HideStackActionButtonEvent; import com.android.systemui.recents.events.activity.LaunchTaskEvent; +import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent; +import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent; +import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent; import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent; import com.android.systemui.recents.events.activity.ShowEmptyViewEvent; import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent; import com.android.systemui.recents.events.component.ExpandPipEvent; +import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; +import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent; import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent; @@ -76,8 +90,9 @@ import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.TaskStack; -import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer; -import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture; +import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture; +import com.android.systemui.shared.recents.view.RecentsTransition; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.stackdivider.WindowManagerProxy; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.phone.ScrimController; @@ -101,6 +116,7 @@ public class RecentsView extends FrameLayout { private static final int BUSY_RECENTS_TASK_COUNT = 3; + private Handler mHandler; private TaskStackView mTaskStackView; private TextView mStackActionButton; private TextView mEmptyView; @@ -126,7 +142,7 @@ public class RecentsView extends FrameLayout { mMultiWindowBackgroundScrim.setAlpha(alpha); }; - private RecentsTransitionHelper mTransitionHelper; + private RecentsTransitionComposer mTransitionHelper; @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_") private RecentsViewTouchHandler mTouchHandler; private final FlingAnimationUtils mFlingAnimationUtils; @@ -148,7 +164,8 @@ public class RecentsView extends FrameLayout { setWillNotDraw(false); SystemServicesProxy ssp = Recents.getSystemServices(); - mTransitionHelper = new RecentsTransitionHelper(getContext()); + mHandler = new Handler(); + mTransitionHelper = new RecentsTransitionComposer(getContext()); mDividerSize = ssp.getDockedDividerSize(context); mTouchHandler = new RecentsViewTouchHandler(this); mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f); @@ -518,9 +535,8 @@ public class RecentsView extends FrameLayout { /**** EventBus Events ****/ public final void onBusEvent(LaunchTaskEvent event) { - mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView, - event.taskView, event.screenPinningRequested, event.targetWindowingMode, - event.targetActivityType); + launchTaskFromRecents(getStack(), event.task, mTaskStackView, event.taskView, + event.screenPinningRequested, event.targetWindowingMode, event.targetActivityType); if (Recents.getConfiguration().isLowRamDevice) { EventBus.getDefault().send(new HideStackActionButtonEvent(false /* translate */)); } @@ -607,17 +623,15 @@ public class RecentsView extends FrameLayout { }; final Rect taskRect = getTaskRect(event.taskView); - AppTransitionAnimationSpecsFuture future = - mTransitionHelper.getAppTransitionFuture( - new AnimationSpecComposer() { - @Override - public List<AppTransitionAnimationSpec> composeSpecs() { - return mTransitionHelper.composeDockAnimationSpec( - event.taskView, taskRect); - } - }); + AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture( + getHandler()) { + @Override + public List<AppTransitionAnimationSpec> composeSpecs() { + return mTransitionHelper.composeDockAnimationSpec(event.taskView, taskRect); + } + }; ssp.overridePendingAppTransitionMultiThumbFuture(future.getFuture(), - mTransitionHelper.wrapStartedListener(startedListener), + RecentsTransition.wrapStartedListener(getHandler(), startedListener), true /* scaleUp */); MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP, @@ -881,7 +895,8 @@ public class RecentsView extends FrameLayout { * @return the bounds of the stack action button. */ Rect getStackActionButtonBoundsFromStackLayout() { - Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect()); + Rect actionButtonRect = new Rect( + mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect()); int left, top; if (Recents.getConfiguration().isLowRamDevice) { Rect windowRect = Recents.getSystemServices().getWindowRect(); @@ -906,6 +921,141 @@ public class RecentsView extends FrameLayout { return mStackActionButton; } + /** + * Launches the specified {@link Task}. + */ + public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task, + final TaskStackView stackView, final TaskView taskView, + final boolean screenPinningRequested, final int windowingMode, final int activityType) { + + final ActivityOptions.OnAnimationStartedListener animStartedListener; + final AppTransitionAnimationSpecsFuture transitionFuture; + if (taskView != null) { + + // Fetch window rect here already in order not to be blocked on lock contention in WM + // when the future calls it. + final Rect windowRect = Recents.getSystemServices().getWindowRect(); + transitionFuture = new AppTransitionAnimationSpecsFuture(stackView.getHandler()) { + @Override + public List<AppTransitionAnimationSpec> composeSpecs() { + return mTransitionHelper.composeAnimationSpecs(task, stackView, windowingMode, + activityType, windowRect); + } + }; + animStartedListener = new OnAnimationStartedListener() { + private boolean mHandled; + + @Override + public void onAnimationStarted() { + if (mHandled) { + return; + } + mHandled = true; + + // If we are launching into another task, cancel the previous task's + // window transition + EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); + EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); + stackView.cancelAllTaskViewAnimations(); + + if (screenPinningRequested) { + // Request screen pinning after the animation runs + mHandler.postDelayed(() -> { + EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext, + task.key.id)); + }, 350); + } + + if (!Recents.getConfiguration().isLowRamDevice) { + // Reset the state where we are waiting for the transition to start + EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false)); + } + } + }; + } else { + // This is only the case if the task is not on screen (scrolled offscreen for example) + transitionFuture = null; + animStartedListener = new OnAnimationStartedListener() { + private boolean mHandled; + + @Override + public void onAnimationStarted() { + if (mHandled) { + return; + } + mHandled = true; + + // If we are launching into another task, cancel the previous task's + // window transition + EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); + EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); + stackView.cancelAllTaskViewAnimations(); + + if (!Recents.getConfiguration().isLowRamDevice) { + // Reset the state where we are waiting for the transition to start + EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false)); + } + } + }; + } + + EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(true)); + final ActivityOptions opts = RecentsTransition.createAspectScaleAnimation(mContext, + mHandler, true /* scaleUp */, transitionFuture != null ? transitionFuture : null, + animStartedListener); + if (taskView == null) { + // If there is no task view, then we do not need to worry about animating out occluding + // task views, and we can launch immediately + startTaskActivity(stack, task, taskView, opts, transitionFuture, + windowingMode, activityType); + } else { + LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView, + screenPinningRequested); + EventBus.getDefault().send(launchStartedEvent); + startTaskActivity(stack, task, taskView, opts, transitionFuture, windowingMode, + activityType); + } + ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS); + } + + /** + * Starts the activity for the launch task. + * + * @param taskView this is the {@link TaskView} that we are launching from. This can be null if + * we are toggling recents and the launch-to task is now offscreen. + */ + private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView, + ActivityOptions opts, AppTransitionAnimationSpecsFuture transitionFuture, + int windowingMode, int activityType) { + 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)); + + // 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); + } + } + @Override public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { super.requestDisallowInterceptTouchEvent(disallowIntercept); 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/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java index 0997983ae73f..b2bbe307d8bf 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java @@ -31,8 +31,8 @@ import com.android.systemui.R; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent; import com.android.systemui.recents.events.component.ShowUserToastEvent; +import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.TaskStackChangeListener; import com.android.systemui.stackdivider.events.StartedDragingEvent; import com.android.systemui.stackdivider.events.StoppedDragingEvent; @@ -76,7 +76,7 @@ public class ForcedResizableInfoActivityController { mContext = context; EventBus.getDefault().register(this); SystemServicesProxy.getInstance(context).registerTaskStackListener( - new TaskStackChangeListener() { + new SysUiTaskStackChangeListener() { @Override public void onActivityForcedResizable(String packageName, int taskId, int reason) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java new file mode 100644 index 000000000000..5090f74d4019 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java @@ -0,0 +1,155 @@ +/* + * 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.systemui.statusbar; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.graphics.Rect; +import android.os.Bundle; +import android.provider.Settings; +import android.telephony.ServiceState; +import android.telephony.SubscriptionInfo; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.widget.TextView; + +import com.android.internal.telephony.IccCardConstants.State; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.WirelessUtils; +import com.android.systemui.DemoMode; +import com.android.systemui.Dependency; +import com.android.systemui.statusbar.policy.DarkIconDispatcher; +import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.systemui.tuner.TunerService; +import com.android.systemui.tuner.TunerService.Tunable; + +import java.util.List; + +public class OperatorNameView extends TextView implements DemoMode, DarkReceiver, + SignalCallback, Tunable { + + private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name"; + + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private boolean mDemoMode; + + private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onRefreshCarrierInfo() { + updateText(); + } + }; + + public OperatorNameView(Context context) { + this(context, null); + } + + public OperatorNameView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public OperatorNameView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); + mKeyguardUpdateMonitor.registerCallback(mCallback); + Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this); + Dependency.get(NetworkController.class).addCallback(this); + Dependency.get(TunerService.class).addTunable(this, KEY_SHOW_OPERATOR_NAME); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mKeyguardUpdateMonitor.removeCallback(mCallback); + Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this); + Dependency.get(NetworkController.class).removeCallback(this); + Dependency.get(TunerService.class).removeTunable(this); + } + + @Override + public void onDarkChanged(Rect area, float darkIntensity, int tint) { + setTextColor(DarkIconDispatcher.getTint(area, this, tint)); + } + + @Override + public void setIsAirplaneMode(IconState icon) { + update(); + } + + @Override + public void onTuningChanged(String key, String newValue) { + update(); + } + + @Override + public void dispatchDemoCommand(String command, Bundle args) { + if (!mDemoMode && command.equals(COMMAND_ENTER)) { + mDemoMode = true; + } else if (mDemoMode && command.equals(COMMAND_EXIT)) { + mDemoMode = false; + update(); + } else if (mDemoMode && command.equals(COMMAND_OPERATOR)) { + setText(args.getString("name")); + } + } + + private void update() { + boolean showOperatorName = Dependency.get(TunerService.class) + .getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0; + setVisibility(showOperatorName ? VISIBLE : GONE); + + boolean hasMobile = ConnectivityManager.from(mContext) + .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + boolean airplaneMode = WirelessUtils.isAirplaneModeOn(mContext); + if (!hasMobile || airplaneMode) { + setText(null); + setVisibility(GONE); + return; + } + + if (!mDemoMode) { + updateText(); + } + } + + private void updateText() { + CharSequence displayText = null; + List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false); + final int N = subs.size(); + for (int i = 0; i < N; i++) { + int subId = subs.get(i).getSubscriptionId(); + State simState = mKeyguardUpdateMonitor.getSimState(subId); + CharSequence carrierName = subs.get(i).getCarrierName(); + if (!TextUtils.isEmpty(carrierName) && simState == State.READY) { + ServiceState ss = mKeyguardUpdateMonitor.getServiceState(subId); + if (ss != null && ss.getState() == ServiceState.STATE_IN_SERVICE) { + displayText = carrierName; + break; + } + } + } + + setText(displayText); + } +} 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 fed2ebe9c14f..41cae6b0be57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -45,8 +45,9 @@ import com.android.systemui.classifier.FalsingLog; import com.android.systemui.classifier.FalsingManager; 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.recents.misc.TaskStackChangeListener; +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; @@ -305,14 +306,14 @@ public class CarStatusBar extends StatusBar implements } /** - * An implementation of TaskStackChangeListener, that listens for changes in the system task + * An implementation of SysUiTaskStackChangeListener, that listens for changes in the system task * stack and notifies the navigation bar. */ - private class TaskStackListenerImpl extends TaskStackChangeListener { + 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/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index 2c3f452e8274..61f3130b9be4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -60,6 +60,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private StatusBar mStatusBarComponent; private DarkIconManager mDarkIconManager; private SignalClusterView mSignalClusterView; + private View mOperatorNameFrame; private SignalCallback mSignalCallback = new SignalCallback() { @Override @@ -97,6 +98,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue // Default to showing until we know otherwise. showSystemIconArea(false); initEmergencyCryptkeeperText(); + initOperatorName(); } @Override @@ -150,8 +152,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue if ((diff1 & DISABLE_SYSTEM_INFO) != 0) { if ((state1 & DISABLE_SYSTEM_INFO) != 0) { hideSystemIconArea(animate); + hideOperatorName(animate); } else { showSystemIconArea(animate); + showOperatorName(animate); } } if ((diff1 & DISABLE_NOTIFICATION_ICONS) != 0) { @@ -207,6 +211,18 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue animateShow(mNotificationIconAreaInner, animate); } + public void hideOperatorName(boolean animate) { + if (mOperatorNameFrame != null) { + animateHide(mOperatorNameFrame, animate); + } + } + + public void showOperatorName(boolean animate) { + if (mOperatorNameFrame != null) { + animateShow(mOperatorNameFrame, animate); + } + } + /** * Hides a view. */ @@ -268,4 +284,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue parent.removeView(emergencyViewStub); } } + + private void initOperatorName() { + if (getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar)) { + ViewStub stub = mStatusBar.findViewById(R.id.operator_name); + mOperatorNameFrame = stub.inflate(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index b876286b9ba2..ba4ff585fed7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -65,8 +65,8 @@ import com.android.systemui.SysUiServiceProvider; import com.android.systemui.UiOffloadThread; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.RotationLockTile; +import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.TaskStackChangeListener; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.policy.BluetoothController; @@ -768,7 +768,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mIconController.setIconVisibility(mSlotDataSaver, isDataSaving); } - private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() { + private final SysUiTaskStackChangeListener mTaskListener = new SysUiTaskStackChangeListener() { @Override public void onTaskStackChanged() { // Listen for changes to stacks and then check which instant apps are foreground. 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 afcf8ffe74f4..fa34d4a62d1b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE; import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.windowStateToString; @@ -4128,6 +4129,9 @@ public class StatusBar extends SystemUI implements DemoMode, } } } + if (modeChange || command.equals(COMMAND_OPERATOR)) { + dispatchDemoCommandToView(command, args, R.id.operator_name); + } } private void dispatchDemoCommandToView(String command, Bundle args, int id) { @@ -5262,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; @@ -7093,7 +7098,9 @@ public class StatusBar extends SystemUI implements DemoMode, } public boolean shouldShowOnKeyguard(StatusBarNotification sbn) { - return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey()); + return mShowLockscreenNotifications + && ((mDisabled2 & DISABLE2_NOTIFICATION_SHADE) == 0) + && !mNotificationData.isAmbient(sbn.getKey()); } // extended in StatusBar diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java index b86fc214129c..d7583149ccc6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java @@ -20,7 +20,6 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.argThat; @@ -41,10 +40,8 @@ import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; -import com.android.systemui.keyguard.WorkLockActivity; -import com.android.systemui.keyguard.WorkLockActivityController; +import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.TaskStackChangeListener; import org.junit.Before; import org.junit.Test; @@ -68,7 +65,7 @@ public class WorkLockActivityControllerTest extends SysuiTestCase { private @Mock IActivityManager mIActivityManager; private WorkLockActivityController mController; - private TaskStackChangeListener mTaskStackListener; + private SysUiTaskStackChangeListener mTaskStackListener; @Before public void setUp() throws Exception { @@ -78,8 +75,8 @@ public class WorkLockActivityControllerTest extends SysuiTestCase { doReturn("com.example.test").when(mContext).getPackageName(); // Construct controller. Save the TaskStackListener for injecting events. - final ArgumentCaptor<TaskStackChangeListener> listenerCaptor = - ArgumentCaptor.forClass(TaskStackChangeListener.class); + final ArgumentCaptor<SysUiTaskStackChangeListener> listenerCaptor = + ArgumentCaptor.forClass(SysUiTaskStackChangeListener.class); mController = new WorkLockActivityController(mContext, mSystemServicesProxy, mIActivityManager); @@ -97,7 +94,7 @@ public class WorkLockActivityControllerTest extends SysuiTestCase { // The overlay should start and the task the activity started in should not be removed. verifyStartActivity(TASK_ID, true /*taskOverlay*/); - verify(mSystemServicesProxy, never()).removeTask(anyInt() /*taskId*/); + verify(mIActivityManager, never()).removeTask(anyInt() /*taskId*/); } @Test @@ -111,7 +108,7 @@ public class WorkLockActivityControllerTest extends SysuiTestCase { // The task the activity started in should be removed to prevent the locked task from // being shown. verifyStartActivity(TASK_ID, true /*taskOverlay*/); - verify(mSystemServicesProxy).removeTask(TASK_ID); + verify(mIActivityManager).removeTask(TASK_ID); } // End of tests, start of helpers diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index f82d435dd360..fad6bd1b3f83 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4750,6 +4750,20 @@ message MetricsEvent { // OS: P ANOMALY_TYPE_UNOPTIMIZED_BT = 1237; + // Open: Settings > Dev options > Oem unlock > lock it > warning dialog. + // 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/proto/src/wifi.proto b/proto/src/wifi.proto index ee0c04331da2..9d25055d6d5a 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -673,6 +673,10 @@ message StaEvent { // Framework initiated disconnect. Sometimes generated to give an extra reason for a disconnect // Should typically be followed by a NETWORK_DISCONNECTION_EVENT with a local_gen = true TYPE_FRAMEWORK_DISCONNECT = 15; + + // The NetworkAgent score for wifi has changed in a way that may impact + // connectivity + TYPE_SCORE_BREACH = 16; } enum FrameworkDisconnectReason { @@ -784,6 +788,9 @@ message StaEvent { // Authentication failure reason, as reported by WifiManager (calculated from state & deauth code) optional AuthFailureReason auth_failure_reason = 13 [default = AUTH_FAILURE_UNKNOWN]; + + // NetworkAgent score of connected wifi + optional int32 last_score = 14 [default = -1]; } // Wi-Fi Aware metrics diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 236fbfd94f53..02a62e10e111 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -28,6 +28,7 @@ import android.view.autofill.AutofillValue; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Objects; @@ -112,4 +113,12 @@ public final class Helper { } return log; } + + public static void printlnRedactedText(@NonNull PrintWriter pw, @Nullable String text) { + if (text == null) { + pw.println("null"); + } else { + pw.print(text.length()); pw.println("_chars"); + } + } } diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 6d3d792e8d31..dac4586fbd9d 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -49,6 +49,8 @@ import android.widget.RemoteViews; import com.android.internal.R; import com.android.server.UiThread; +import com.android.server.autofill.Helper; + import libcore.util.Objects; import java.io.PrintWriter; @@ -466,7 +468,8 @@ final class FillUi { pw.print(prefix); pw.print("mCallback: "); pw.println(mCallback != null); pw.print(prefix); pw.print("mListView: "); pw.println(mListView); pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter != null); - pw.print(prefix); pw.print("mFilterText: "); pw.println(mFilterText); + pw.print(prefix); pw.print("mFilterText: "); + Helper.printlnRedactedText(pw, mFilterText); pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth); pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight); pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed); diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 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 4d90b6e77905..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); @@ -10843,7 +10847,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private void startLockTaskModeLocked(@Nullable TaskRecord task, boolean isAppPinning) { + private void startLockTaskModeLocked(@Nullable TaskRecord task, boolean isSystemCaller) { if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task); if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) { return; @@ -10857,13 +10861,16 @@ public class ActivityManagerService extends IActivityManager.Stub // When a task is locked, dismiss the pinned stack if it exists mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); - // isAppPinning is used to distinguish between locked and pinned mode, as pinned mode - // is initiated by system after the pinning request was shown and locked mode is initiated - // by an authorized app directly + // {@code isSystemCaller} is used to distinguish whether this request is initiated by the + // system or a specific app. + // * System-initiated requests will only start the pinned mode (screen pinning) + // * App-initiated requests + // - will put the device in fully locked mode (LockTask), if the app is whitelisted + // - will start the pinned mode, otherwise final int callingUid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); try { - mLockTaskController.startLockTaskMode(task, isAppPinning, callingUid); + mLockTaskController.startLockTaskMode(task, isSystemCaller, callingUid); } finally { Binder.restoreCallingIdentity(ident); } @@ -10876,7 +10883,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { return; } - startLockTaskModeLocked(r.getTask(), false /* not system initiated */); + startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */); } } @@ -10888,7 +10895,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { synchronized (this) { startLockTaskModeLocked(mStackSupervisor.anyTaskForIdLocked(taskId), - true /* system initiated */); + true /* isSystemCaller */); } } finally { Binder.restoreCallingIdentity(ident); @@ -10896,8 +10903,14 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void stopLockTaskMode() { - stopLockTaskModeInternal(false /* not system initiated */); + public void stopLockTaskModeByToken(IBinder token) { + synchronized (this) { + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r == null) { + return; + } + stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */); + } } /** @@ -10907,15 +10920,15 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void stopSystemLockTaskMode() throws RemoteException { enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode"); - stopLockTaskModeInternal(true /* system initiated */); + stopLockTaskModeInternal(null, true /* isSystemCaller */); } - private void stopLockTaskModeInternal(boolean isSystemRequest) { + private void stopLockTaskModeInternal(@Nullable TaskRecord task, boolean isSystemCaller) { final int callingUid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { - mLockTaskController.stopLockTaskMode(isSystemRequest, callingUid); + mLockTaskController.stopLockTaskMode(task, isSystemCaller, callingUid); } // Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock // task and jumping straight into a call in the case of emergency call back. @@ -17078,22 +17091,39 @@ public class ActivityManagerService extends IActivityManager.Stub return stringifySize(size * 1024, 1024); } - // Update this version number in case you change the 'compact' format + // Update this version number if you change the 'compact' format. private static final int MEMINFO_COMPACT_VERSION = 1; + private static class MemoryUsageDumpOptions { + boolean dumpDetails; + boolean dumpFullDetails; + boolean dumpDalvik; + boolean dumpSummaryOnly; + boolean dumpUnreachable; + boolean oomOnly; + boolean isCompact; + boolean localOnly; + boolean packages; + boolean isCheckinRequest; + boolean dumpSwapPss; + boolean dumpProto; + } + final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix, String[] args, boolean brief, PrintWriter categoryPw) { - boolean dumpDetails = false; - boolean dumpFullDetails = false; - boolean dumpDalvik = false; - boolean dumpSummaryOnly = false; - boolean dumpUnreachable = false; - boolean oomOnly = false; - boolean isCompact = false; - boolean localOnly = false; - boolean packages = false; - boolean isCheckinRequest = false; - boolean dumpSwapPss = false; + MemoryUsageDumpOptions opts = new MemoryUsageDumpOptions(); + opts.dumpDetails = false; + opts.dumpFullDetails = false; + opts.dumpDalvik = false; + opts.dumpSummaryOnly = false; + opts.dumpUnreachable = false; + opts.oomOnly = false; + opts.isCompact = false; + opts.localOnly = false; + opts.packages = false; + opts.isCheckinRequest = false; + opts.dumpSwapPss = false; + opts.dumpProto = false; int opti = 0; while (opti < args.length) { @@ -17103,29 +17133,31 @@ public class ActivityManagerService extends IActivityManager.Stub } opti++; if ("-a".equals(opt)) { - dumpDetails = true; - dumpFullDetails = true; - dumpDalvik = true; - dumpSwapPss = true; + opts.dumpDetails = true; + opts.dumpFullDetails = true; + opts.dumpDalvik = true; + opts.dumpSwapPss = true; } else if ("-d".equals(opt)) { - dumpDalvik = true; + opts.dumpDalvik = true; } else if ("-c".equals(opt)) { - isCompact = true; + opts.isCompact = true; } else if ("-s".equals(opt)) { - dumpDetails = true; - dumpSummaryOnly = true; + opts.dumpDetails = true; + opts.dumpSummaryOnly = true; } else if ("-S".equals(opt)) { - dumpSwapPss = true; + opts.dumpSwapPss = true; } else if ("--unreachable".equals(opt)) { - dumpUnreachable = true; + opts.dumpUnreachable = true; } else if ("--oom".equals(opt)) { - oomOnly = true; + opts.oomOnly = true; } else if ("--local".equals(opt)) { - localOnly = true; + opts.localOnly = true; } else if ("--package".equals(opt)) { - packages = true; + opts.packages = true; } else if ("--checkin".equals(opt)) { - isCheckinRequest = true; + opts.isCheckinRequest = true; + } else if ("--proto".equals(opt)) { + opts.dumpProto = true; } else if ("-h".equals(opt)) { pw.println("meminfo dump options: [-a] [-d] [-c] [-s] [--oom] [process]"); @@ -17139,6 +17171,7 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(" --package: interpret process arg as package, dumping all"); pw.println(" processes that have loaded that package."); pw.println(" --checkin: dump data for a checkin"); + pw.println(" --proto: dump data to proto"); pw.println("If [process] is specified it can be the name or "); pw.println("pid of a specific process to dump."); return; @@ -17147,21 +17180,33 @@ public class ActivityManagerService extends IActivityManager.Stub } } + String[] innerArgs = new String[args.length-opti]; + System.arraycopy(args, opti, innerArgs, 0, args.length-opti); + + ArrayList<ProcessRecord> procs = collectProcesses(pw, opti, opts.packages, args); + if (opts.dumpProto) { + dumpApplicationMemoryUsage(fd, pw, opts, innerArgs, brief, procs); + } else { + dumpApplicationMemoryUsage(fd, pw, prefix, opts, innerArgs, brief, procs, categoryPw); + } + } + + final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix, + MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief, + ArrayList<ProcessRecord> procs, PrintWriter categoryPw) { long uptime = SystemClock.uptimeMillis(); long realtime = SystemClock.elapsedRealtime(); final long[] tmpLong = new long[1]; - ArrayList<ProcessRecord> procs = collectProcesses(pw, opti, packages, args); if (procs == null) { // No Java processes. Maybe they want to print a native process. - if (args != null && args.length > opti - && args[opti].charAt(0) != '-') { + if (innerArgs.length > 0 && innerArgs[0].charAt(0) != '-') { ArrayList<ProcessCpuTracker.Stats> nativeProcs = new ArrayList<ProcessCpuTracker.Stats>(); updateCpuStatsNow(); int findPid = -1; try { - findPid = Integer.parseInt(args[opti]); + findPid = Integer.parseInt(innerArgs[0]); } catch (NumberFormatException e) { } synchronized (mProcessCpuTracker) { @@ -17169,51 +17214,48 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i=0; i<N; i++) { ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); if (st.pid == findPid || (st.baseName != null - && st.baseName.equals(args[opti]))) { + && st.baseName.equals(innerArgs[0]))) { nativeProcs.add(st); } } } if (nativeProcs.size() > 0) { - dumpApplicationMemoryUsageHeader(pw, uptime, realtime, isCheckinRequest, - isCompact); + dumpApplicationMemoryUsageHeader(pw, uptime, realtime, opts.isCheckinRequest, + opts.isCompact); Debug.MemoryInfo mi = null; for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) { final ProcessCpuTracker.Stats r = nativeProcs.get(i); final int pid = r.pid; - if (!isCheckinRequest && dumpDetails) { + if (!opts.isCheckinRequest && opts.dumpDetails) { pw.println("\n** MEMINFO in pid " + pid + " [" + r.baseName + "] **"); } if (mi == null) { mi = new Debug.MemoryInfo(); } - if (dumpDetails || (!brief && !oomOnly)) { + if (opts.dumpDetails || (!brief && !opts.oomOnly)) { Debug.getMemoryInfo(pid, mi); } else { mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null); mi.dalvikPrivateDirty = (int)tmpLong[0]; } - ActivityThread.dumpMemInfoTable(pw, mi, isCheckinRequest, dumpFullDetails, - dumpDalvik, dumpSummaryOnly, pid, r.baseName, 0, 0, 0, 0, 0, 0); - if (isCheckinRequest) { + ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest, opts.dumpFullDetails, + opts.dumpDalvik, opts.dumpSummaryOnly, pid, r.baseName, 0, 0, 0, 0, 0, 0); + if (opts.isCheckinRequest) { pw.println(); } } return; } } - pw.println("No process found for: " + args[opti]); + pw.println("No process found for: " + innerArgs[0]); return; } - if (!brief && !oomOnly && (procs.size() == 1 || isCheckinRequest || packages)) { - dumpDetails = true; + if (!brief && !opts.oomOnly && (procs.size() == 1 || opts.isCheckinRequest || opts.packages)) { + opts.dumpDetails = true; } - dumpApplicationMemoryUsageHeader(pw, uptime, realtime, isCheckinRequest, isCompact); - - String[] innerArgs = new String[args.length-opti]; - System.arraycopy(args, opti, innerArgs, 0, args.length-opti); + dumpApplicationMemoryUsageHeader(pw, uptime, realtime, opts.isCheckinRequest, opts.isCompact); ArrayList<MemItem> procMems = new ArrayList<MemItem>(); final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>(); @@ -17221,9 +17263,9 @@ public class ActivityManagerService extends IActivityManager.Stub long nativeSwapPss = 0; long dalvikPss = 0; long dalvikSwapPss = 0; - long[] dalvikSubitemPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : + long[] dalvikSubitemPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : EmptyArray.LONG; - long[] dalvikSubitemSwapPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : + long[] dalvikSubitemSwapPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : EmptyArray.LONG; long otherPss = 0; long otherSwapPss = 0; @@ -17255,24 +17297,24 @@ public class ActivityManagerService extends IActivityManager.Stub hasActivities = r.activities.size() > 0; } if (thread != null) { - if (!isCheckinRequest && dumpDetails) { + if (!opts.isCheckinRequest && opts.dumpDetails) { pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **"); } if (mi == null) { mi = new Debug.MemoryInfo(); } - if (dumpDetails || (!brief && !oomOnly)) { + if (opts.dumpDetails || (!brief && !opts.oomOnly)) { Debug.getMemoryInfo(pid, mi); hasSwapPss = mi.hasSwappedOutPss; } else { mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null); mi.dalvikPrivateDirty = (int)tmpLong[0]; } - if (dumpDetails) { - if (localOnly) { - ActivityThread.dumpMemInfoTable(pw, mi, isCheckinRequest, dumpFullDetails, - dumpDalvik, dumpSummaryOnly, pid, r.processName, 0, 0, 0, 0, 0, 0); - if (isCheckinRequest) { + if (opts.dumpDetails) { + if (opts.localOnly) { + ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest, opts.dumpFullDetails, + opts.dumpDalvik, opts.dumpSummaryOnly, pid, r.processName, 0, 0, 0, 0, 0, 0); + if (opts.isCheckinRequest) { pw.println(); } } else { @@ -17281,19 +17323,19 @@ public class ActivityManagerService extends IActivityManager.Stub TransferPipe tp = new TransferPipe(); try { thread.dumpMemInfo(tp.getWriteFd(), - mi, isCheckinRequest, dumpFullDetails, - dumpDalvik, dumpSummaryOnly, dumpUnreachable, innerArgs); + mi, opts.isCheckinRequest, opts.dumpFullDetails, + opts.dumpDalvik, opts.dumpSummaryOnly, opts.dumpUnreachable, innerArgs); tp.go(fd); } finally { tp.kill(); } } catch (IOException e) { - if (!isCheckinRequest) { + if (!opts.isCheckinRequest) { pw.println("Got IoException! " + e); pw.flush(); } } catch (RemoteException e) { - if (!isCheckinRequest) { + if (!opts.isCheckinRequest) { pw.println("Got RemoteException! " + e); pw.flush(); } @@ -17312,7 +17354,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - if (!isCheckinRequest && mi != null) { + if (!opts.isCheckinRequest && mi != null) { totalPss += myTotalPss; totalSwapPss += myTotalSwapPss; MemItem pssItem = new MemItem(r.processName + " (pid " + pid + @@ -17365,7 +17407,7 @@ public class ActivityManagerService extends IActivityManager.Stub long nativeProcTotalPss = 0; - if (!isCheckinRequest && procs.size() > 1 && !packages) { + if (!opts.isCheckinRequest && procs.size() > 1 && !opts.packages) { // If we are showing aggregations, also look for native processes to // include so that our aggregations are more accurate. updateCpuStatsNow(); @@ -17378,7 +17420,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (mi == null) { mi = new Debug.MemoryInfo(); } - if (!brief && !oomOnly) { + if (!brief && !opts.oomOnly) { Debug.getMemoryInfo(st.pid, mi); } else { mi.nativePss = (int)Debug.getPss(st.pid, tmpLong, null); @@ -17465,7 +17507,7 @@ public class ActivityManagerService extends IActivityManager.Stub ArrayList<MemItem> oomMems = new ArrayList<MemItem>(); for (int j=0; j<oomPss.length; j++) { if (oomPss[j] != 0) { - String label = isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j] + String label = opts.isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j] : DUMP_MEM_OOM_LABEL[j]; MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j], DUMP_MEM_OOM_ADJ[j]); @@ -17474,26 +17516,26 @@ public class ActivityManagerService extends IActivityManager.Stub } } - dumpSwapPss = dumpSwapPss && hasSwapPss && totalSwapPss != 0; - if (!brief && !oomOnly && !isCompact) { + opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && totalSwapPss != 0; + if (!brief && !opts.oomOnly && !opts.isCompact) { pw.println(); pw.println("Total PSS by process:"); - dumpMemItems(pw, " ", "proc", procMems, true, isCompact, dumpSwapPss); + dumpMemItems(pw, " ", "proc", procMems, true, opts.isCompact, opts.dumpSwapPss); pw.println(); } - if (!isCompact) { + if (!opts.isCompact) { pw.println("Total PSS by OOM adjustment:"); } - dumpMemItems(pw, " ", "oom", oomMems, false, isCompact, dumpSwapPss); - if (!brief && !oomOnly) { + dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, opts.dumpSwapPss); + if (!brief && !opts.oomOnly) { PrintWriter out = categoryPw != null ? categoryPw : pw; - if (!isCompact) { + if (!opts.isCompact) { out.println(); out.println("Total PSS by category:"); } - dumpMemItems(out, " ", "cat", catMems, true, isCompact, dumpSwapPss); + dumpMemItems(out, " ", "cat", catMems, true, opts.isCompact, opts.dumpSwapPss); } - if (!isCompact) { + if (!opts.isCompact) { pw.println(); } MemInfoReader memInfo = new MemInfoReader(); @@ -17511,7 +17553,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } if (!brief) { - if (!isCompact) { + if (!opts.isCompact) { pw.print("Total RAM: "); pw.print(stringifyKBSize(memInfo.getTotalSizeKb())); pw.print(" (status "); switch (mLastMemoryLevel) { @@ -17552,7 +17594,7 @@ public class ActivityManagerService extends IActivityManager.Stub long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb(); - if (!isCompact) { + if (!opts.isCompact) { pw.print(" Used RAM: "); pw.print(stringifyKBSize(totalPss - cachedPss + memInfo.getKernelUsedSizeKb())); pw.print(" ("); pw.print(stringifyKBSize(totalPss - cachedPss)); pw.print(" used pss + "); @@ -17563,7 +17605,7 @@ public class ActivityManagerService extends IActivityManager.Stub } if (!brief) { if (memInfo.getZramTotalSizeKb() != 0) { - if (!isCompact) { + if (!opts.isCompact) { pw.print(" ZRAM: "); pw.print(stringifyKBSize(memInfo.getZramTotalSizeKb())); pw.print(" physical used for "); @@ -17579,7 +17621,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } final long[] ksm = getKsmInfo(); - if (!isCompact) { + if (!opts.isCompact) { if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0 || ksm[KSM_VOLATILE] != 0) { pw.print(" KSM: "); pw.print(stringifyKBSize(ksm[KSM_SHARING])); @@ -17628,6 +17670,17 @@ public class ActivityManagerService extends IActivityManager.Stub } } + final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, + MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief, + ArrayList<ProcessRecord> procs) { + ProtoOutputStream proto = new ProtoOutputStream(fd); + + // TODO: implement + pw.println("Not yet implemented. Have a cookie instead! :]"); + + proto.flush(); + } + private void appendBasicMemEntry(StringBuilder sb, int oomAdj, int procState, long pss, long memtrack, String name) { sb.append(" "); diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index f9422656c7db..7eb922c9cd80 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -373,7 +373,7 @@ final class ActivityManagerShellCommand extends ShellCommand { if (mProfileFile != null || mAgent != null) { ParcelFileDescriptor fd = null; if (mProfileFile != null) { - fd = openOutputFileForSystem(mProfileFile); + fd = openFileForSystem(mProfileFile, "w"); if (fd == null) { return 1; } @@ -668,7 +668,7 @@ final class ActivityManagerShellCommand extends ShellCommand { File file = new File(filename); file.delete(); - ParcelFileDescriptor fd = openOutputFileForSystem(filename); + ParcelFileDescriptor fd = openFileForSystem(filename, "w"); if (fd == null) { return -1; } @@ -756,7 +756,7 @@ final class ActivityManagerShellCommand extends ShellCommand { if (start) { profileFile = getNextArgRequired(); - fd = openOutputFileForSystem(profileFile); + fd = openFileForSystem(profileFile, "w"); if (fd == null) { return -1; } @@ -820,7 +820,7 @@ final class ActivityManagerShellCommand extends ShellCommand { File file = new File(heapFile); file.delete(); - ParcelFileDescriptor fd = openOutputFileForSystem(heapFile); + ParcelFileDescriptor fd = openFileForSystem(heapFile, "w"); if (fd == null) { return -1; } @@ -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/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 2f0b64918bf9..481575e88c70 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -77,7 +77,6 @@ import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET; import static android.os.Build.VERSION_CODES.HONEYCOMB; import static android.os.Build.VERSION_CODES.O; -import static android.os.Build.VERSION_CODES.O_MR1; import static android.os.Process.SYSTEM_UID; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.view.WindowManagerPolicy.NAV_BAR_LEFT; @@ -886,6 +885,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo Entry ent = AttributeCache.instance().get(packageName, realTheme, com.android.internal.R.styleable.Window, userId); + if (ent != null) { fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array); hasWallpaper = ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false); @@ -2119,11 +2119,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } void setRequestedOrientation(int requestedOrientation) { - if (ActivityInfo.isFixedOrientation(requestedOrientation) && !fullscreen - && appInfo.targetSdkVersion >= O_MR1) { - throw new IllegalStateException("Only fullscreen activities can request orientation"); - } - final int displayId = getDisplayId(); final Configuration displayConfig = mStackSupervisor.getDisplayOverrideConfiguration(displayId); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index ba41bd4c3b6f..88403fc5dde7 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -3474,7 +3474,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } if (endTask) { - mService.mLockTaskController.removeLockedTask(task); + mService.mLockTaskController.clearLockedTask(task); } } else if (r.state != ActivityState.PAUSING) { // If the activity is PAUSING, we will complete the finish once @@ -4334,8 +4334,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } Slog.i(TAG, "moveTaskToBack: " + tr); - // If the task is locked, then show the lock task toast - if (mService.mLockTaskController.checkLockedTask(tr)) { + // In LockTask mode, moving a locked task to the back of the stack may expose unlocked + // ones. Therefore we need to check if this operation is allowed. + if (!mService.mLockTaskController.canMoveTaskToBack(tr)) { return false; } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 3506d6b1602b..43a2a9f1f9bd 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.ACTIVITY_EMBEDDING; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.START_ANY_ACTIVITY; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; +import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; @@ -83,6 +84,7 @@ import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING; import static com.android.server.am.TaskRecord.INVALID_TASK_ID; import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; +import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED; import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; @@ -1306,8 +1308,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mService.updateLruProcessLocked(app, true, null); mService.updateOomAdjLocked(); - if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE || - task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV) { + if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV + || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED + && mService.mLockTaskController.getLockTaskModeState() + == LOCK_TASK_MODE_LOCKED)) { mService.mLockTaskController.startLockTaskMode(task, false, 0 /* blank UID */); } @@ -2769,6 +2774,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (tr != null) { tr.removeTaskActivitiesLocked(pauseImmediately); cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents); + mService.mLockTaskController.clearLockedTask(tr); if (tr.isPersistable) { mService.notifyTaskPersisterLocked(null, true); } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index db12ae252abc..52f97020b5a7 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -23,6 +23,7 @@ import android.content.pm.PackageManager; import android.net.wifi.WifiActivityEnergyInfo; import android.os.PowerSaveState; import android.os.BatteryStats; +import android.os.BatteryStatsInternal; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -177,9 +178,22 @@ public 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/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java index 4b2a08439f2d..e87b4e63c5b0 100644 --- a/services/core/java/com/android/server/am/LockTaskController.java +++ b/services/core/java/com/android/server/am/LockTaskController.java @@ -143,9 +143,17 @@ public class LockTaskController { LockTaskNotify mLockTaskNotify; /** - * The chain of tasks in lockTask mode. The current frontmost task is at the top, and tasks - * may be finished until there is only one entry left. If this is empty the system is not - * in lockTask mode. + * The chain of tasks in LockTask mode, in the order of when they first entered LockTask mode. + * + * The first task in the list, which started the current LockTask session, is called the root + * task. It coincides with the Home task in a typical multi-app kiosk deployment. When there are + * more than one locked tasks, the root task can't be finished. Nor can it be moved to the back + * of the stack by {@link ActivityStack#moveTaskToBackLocked(int)}; + * + * Calling {@link Activity#stopLockTask()} on the root task will finish all tasks but itself in + * this list, and the device will exit LockTask mode. + * + * The list is empty if LockTask is inactive. */ private final ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>(); @@ -164,7 +172,7 @@ public class LockTaskController { * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED}, * {@link ActivityManager#LOCK_TASK_MODE_PINNED} */ - private int mLockTaskModeState; + private int mLockTaskModeState = LOCK_TASK_MODE_NONE; /** * This is ActivityStackSupervisor's Handler. @@ -199,24 +207,28 @@ public class LockTaskController { * @return whether the given task is locked at the moment. Locked tasks cannot be moved to the * back of the stack. */ - boolean checkLockedTask(TaskRecord task) { - if (mLockTaskModeTasks.contains(task)) { - showLockTaskToast(); - return true; - } - return false; + @VisibleForTesting + boolean isTaskLocked(TaskRecord task) { + return mLockTaskModeTasks.contains(task); } /** - * @return whether the given activity is blocked from finishing, because it is the root activity + * @return {@code true} whether this task first started the current LockTask session. + */ + private boolean isRootTask(TaskRecord task) { + return mLockTaskModeTasks.indexOf(task) == 0; + } + + /** + * @return whether the given activity is blocked from finishing, because it is the only activity * of the last locked task and finishing it would mean that lock task mode is ended illegally. */ boolean activityBlockedFromFinish(ActivityRecord activity) { - TaskRecord task = activity.getTask(); + final TaskRecord task = activity.getTask(); if (activity == task.getRootActivity() + && activity == task.getTopActivity() && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV - && mLockTaskModeTasks.size() == 1 - && mLockTaskModeTasks.contains(task)) { + && isRootTask(task)) { Slog.i(TAG, "Not finishing task in lock task mode"); showLockTaskToast(); return true; @@ -225,6 +237,19 @@ public class LockTaskController { } /** + * @return whether the given task can be moved to the back of the stack with + * {@link ActivityStack#moveTaskToBackLocked(int)} + * @see #mLockTaskModeTasks + */ + boolean canMoveTaskToBack(TaskRecord task) { + if (isRootTask(task)) { + showLockTaskToast(); + return false; + } + return true; + } + + /** * @return whether the requested task is allowed to be launched. */ boolean isLockTaskModeViolation(TaskRecord task) { @@ -246,7 +271,7 @@ public class LockTaskController { private boolean isLockTaskModeViolationInternal(TaskRecord task, boolean isNewClearTask) { // TODO: Double check what's going on here. If the task is already in lock task mode, it's // likely whitelisted, so will return false below. - if (getLockedTask() == task && !isNewClearTask) { + if (isTaskLocked(task) && !isNewClearTask) { // If the task is already at the top and won't be cleared, then allow the operation return false; } @@ -270,80 +295,116 @@ public class LockTaskController { /** * Stop the current lock task mode. * - * @param isSystemInitiated indicates whether this request was initiated by the system via - * {@link ActivityManagerService#stopSystemLockTaskMode()}. + * This is called by {@link ActivityManagerService} and performs various checks before actually + * finishing the locked task. + * + * @param task the task that requested the end of lock task mode ({@code null} for quitting app + * pinning mode) + * @param isSystemCaller indicates whether this request comes from the system via + * {@link ActivityManagerService#stopSystemLockTaskMode()}. If + * {@code true}, it means the user intends to stop pinned mode through UI; + * otherwise, it's called by an app and we need to stop locked or pinned + * mode, subject to checks. * @param callingUid the caller that requested the end of lock task mode. + * @throws IllegalArgumentException if the calling task is invalid (e.g., {@code null} or not in + * foreground) * @throws SecurityException if the caller is not authorized to stop the lock task mode, i.e. if * they differ from the one that launched lock task mode. */ - void stopLockTaskMode(boolean isSystemInitiated, int callingUid) { - final TaskRecord lockTask = getLockedTask(); - if (lockTask == null || mLockTaskModeState == LOCK_TASK_MODE_NONE) { - // Our work here is done. + void stopLockTaskMode(@Nullable TaskRecord task, boolean isSystemCaller, int callingUid) { + if (mLockTaskModeState == LOCK_TASK_MODE_NONE) { return; } - if (isSystemInitiated && mLockTaskModeState == LOCK_TASK_MODE_LOCKED) { - // As system can only start app pinning, we also only let it unlock in this mode. - showLockTaskToast(); - return; + if (isSystemCaller) { + if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) { + clearLockedTasks("stopAppPinning"); + } else { + Slog.e(TAG_LOCKTASK, "Attempted to stop LockTask with isSystemCaller=true"); + showLockTaskToast(); + } + + } else { + // Ensure calling activity is not null + if (task == null) { + throw new IllegalArgumentException("can't stop LockTask for null task"); + } + + // Ensure the same caller for startLockTaskMode and stopLockTaskMode. + // It is possible lockTaskMode was started by the system process because + // android:lockTaskMode is set to a locking value in the application manifest + // instead of the app calling startLockTaskMode. In this case + // {@link TaskRecord.mLockTaskUid} will be 0, so we compare the callingUid to the + // {@link TaskRecord.effectiveUid} instead. Also caller with + // {@link MANAGE_ACTIVITY_STACKS} can stop any lock task. + if (callingUid != task.mLockTaskUid + && (task.mLockTaskUid != 0 || callingUid != task.effectiveUid)) { + throw new SecurityException("Invalid uid, expected " + task.mLockTaskUid + + " callingUid=" + callingUid + " effectiveUid=" + task.effectiveUid); + } + + // We don't care if it's pinned or locked mode; this will stop it anyways. + clearLockedTask(task); } + } - // Ensure the same caller for startLockTaskMode and stopLockTaskMode. - // It is possible lockTaskMode was started by the system process because - // android:lockTaskMode is set to a locking value in the application manifest - // instead of the app calling startLockTaskMode. In this case - // {@link TaskRecord.mLockTaskUid} will be 0, so we compare the callingUid to the - // {@link TaskRecord.effectiveUid} instead. Also caller with - // {@link MANAGE_ACTIVITY_STACKS} can stop any lock task. - if (!isSystemInitiated && callingUid != lockTask.mLockTaskUid - && (lockTask.mLockTaskUid != 0 || callingUid != lockTask.effectiveUid)) { - throw new SecurityException("Invalid uid, expected " + lockTask.mLockTaskUid - + " callingUid=" + callingUid + " effectiveUid=" + lockTask.effectiveUid); + /** + * Clear all locked tasks and request the end of LockTask mode. + * + * This method is called by {@link UserController} when starting a new foreground user, and, + * unlike {@link #stopLockTaskMode(TaskRecord, boolean, int)}, it doesn't perform the checks. + */ + void clearLockedTasks(String reason) { + if (DEBUG_LOCKTASK) Slog.i(TAG_LOCKTASK, "clearLockedTasks: " + reason); + if (!mLockTaskModeTasks.isEmpty()) { + clearLockedTask(mLockTaskModeTasks.get(0)); + } + } + + /** + * Clear one locked task from LockTask mode. + * + * If the requested task is the root task (see {@link #mLockTaskModeTasks}), then all locked + * tasks are cleared. Otherwise, only the requested task is cleared. LockTask mode is stopped + * when the last locked task is cleared. + * + * @param task the task to be cleared from LockTask mode. + */ + void clearLockedTask(final TaskRecord task) { + if (task == null || mLockTaskModeTasks.isEmpty()) return; + + if (task == mLockTaskModeTasks.get(0)) { + // We're removing the root task while there are other locked tasks. Therefore we should + // clear all locked tasks in reverse order. + for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx > 0; --taskNdx) { + clearLockedTask(mLockTaskModeTasks.get(taskNdx)); + } } - clearLockTaskMode("stopLockTask"); + removeLockedTask(task); + if (mLockTaskModeTasks.isEmpty()) { + return; + } + task.performClearTaskLocked(); + mSupervisor.resumeFocusedStackTopActivityLocked(); } /** * Remove the given task from the locked task list. If this was the last task in the list, * lock task mode is stopped. */ - void removeLockedTask(final TaskRecord task) { + private void removeLockedTask(final TaskRecord task) { if (!mLockTaskModeTasks.remove(task)) { return; } - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "removeLockedTask: removed " + task); + if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: removed " + task); if (mLockTaskModeTasks.isEmpty()) { - // Last one. if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task + " last task, reverting locktask mode. Callers=" + Debug.getCallers(3)); mHandler.post(() -> performStopLockTask(task.userId)); } } - /** - * Remove the topmost task from the locked task list. If this is the last task in the list, it - * will result in the end of locked task mode. - */ - void clearLockTaskMode(String reason) { - // Take out of lock task mode if necessary - final TaskRecord lockedTask = getLockedTask(); - if (lockedTask != null) { - removeLockedTask(lockedTask); - if (!mLockTaskModeTasks.isEmpty()) { - // There are locked tasks remaining, can only finish this task, not unlock it. - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, - "setLockTaskMode: Tasks remaining, can't unlock"); - lockedTask.performClearTaskLocked(); - mSupervisor.resumeFocusedStackTopActivityLocked(); - return; - } - } - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, - "setLockTaskMode: No tasks to unlock. Callers=" + Debug.getCallers(4)); - } - // This method should only be called on the handler thread private void performStopLockTask(int userId) { // When lock task ends, we enable the status bars. @@ -382,17 +443,18 @@ public class LockTaskController { * Method to start lock task mode on a given task. * * @param task the task that should be locked. - * @param isSystemInitiated indicates whether this request was initiated by the system via - * {@link ActivityManagerService#startSystemLockTaskMode(int)}. + * @param isSystemCaller indicates whether this request was initiated by the system via + * {@link ActivityManagerService#startSystemLockTaskMode(int)}. If + * {@code true}, this intends to start pinned mode; otherwise, we look + * at the calling task's mLockTaskAuth to decide which mode to start. * @param callingUid the caller that requested the launch of lock task mode. */ - void startLockTaskMode(@NonNull TaskRecord task, boolean isSystemInitiated, - int callingUid) { - if (!isSystemInitiated) { + void startLockTaskMode(@NonNull TaskRecord task, boolean isSystemCaller, int callingUid) { + if (!isSystemCaller) { task.mLockTaskUid = callingUid; if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) { // startLockTask() called by app, but app is not part of lock task whitelist. Show - // app pinning request. We will come back here with isSystemInitiated true. + // app pinning request. We will come back here with isSystemCaller true. if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user"); StatusBarManagerInternal statusBarManager = LocalServices.getService( StatusBarManagerInternal.class); @@ -404,8 +466,9 @@ public class LockTaskController { } // System can only initiate screen pinning, not full lock task mode - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, isSystemInitiated ? "Locking pinned" : "Locking fully"); - setLockTaskMode(task, isSystemInitiated ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED, + if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, + isSystemCaller ? "Locking pinned" : "Locking fully"); + setLockTaskMode(task, isSystemCaller ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED, "startLockTask", true); } @@ -434,12 +497,12 @@ public class LockTaskController { task.userId, lockTaskModeState)); } - - // Add it or move it to the top. if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskMode: Locking to " + task + " Callers=" + Debug.getCallers(4)); - mLockTaskModeTasks.remove(task); - mLockTaskModeTasks.add(task); + + if (!mLockTaskModeTasks.contains(task)) { + mLockTaskModeTasks.add(task); + } if (task.mLockTaskUid == -1) { task.mLockTaskUid = task.effectiveUid; @@ -556,8 +619,7 @@ public class LockTaskController { } mLockTaskFeatures.put(userId, flags); - TaskRecord lockedTask = getLockedTask(); - if (lockedTask != null && userId == lockedTask.userId) { + if (!mLockTaskModeTasks.isEmpty() && userId == mLockTaskModeTasks.get(0).userId) { mHandler.post(() -> { if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) { setStatusBarState(mLockTaskModeState, userId); @@ -672,17 +734,6 @@ public class LockTaskController { return mLockTaskFeatures.get(userId, DevicePolicyManager.LOCK_TASK_FEATURE_NONE); } - /** - * @return the topmost locked task - */ - private TaskRecord getLockedTask() { - final int top = mLockTaskModeTasks.size() - 1; - if (top >= 0) { - return mLockTaskModeTasks.get(top); - } - return null; - } - // Should only be called on the handler thread @Nullable private IStatusBarService getStatusBarService() { 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/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 1b5a1ce33cff..1a4f9d4fbd5b 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -438,7 +438,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } void removeWindowContainer() { - mService.mLockTaskController.removeLockedTask(this); + mService.mLockTaskController.clearLockedTask(this); mWindowContainerController.removeContainer(); if (!getWindowConfiguration().persistTaskBounds()) { // Reset current bounds for task whose bounds shouldn't be persisted so it uses diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 44f83b0e08fb..2df5dc93768c 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -872,9 +872,7 @@ class UserController implements Handler.Callback { } if (foreground) { - // TODO: I don't think this does what the caller think it does. Seems to only - // remove one locked task and won't work if multiple locked tasks are present. - mInjector.clearLockTaskMode("startUser"); + mInjector.clearAllLockedTasks("startUser"); } final UserInfo userInfo = getUserInfo(userId); @@ -2053,9 +2051,9 @@ class UserController implements Handler.Callback { } } - protected void clearLockTaskMode(String reason) { + protected void clearAllLockedTasks(String reason) { synchronized (mService) { - mService.mLockTaskController.clearLockTaskMode(reason); + mService.mLockTaskController.clearLockedTasks(reason); } } } 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/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java index 2493dfb42a60..d50ffe965c7f 100644 --- a/services/core/java/com/android/server/location/GeofenceManager.java +++ b/services/core/java/com/android/server/location/GeofenceManager.java @@ -145,7 +145,8 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish */ private void updateMinInterval() { mEffectiveMinIntervalMs = Settings.Global.getLong(mResolver, - Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, DEFAULT_MIN_INTERVAL_MS); + Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, + DEFAULT_MIN_INTERVAL_MS); } public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent, diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 4cf35bc4accb..660659fdfa5c 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/net/OWNERS b/services/core/java/com/android/server/net/OWNERS index 061fd8da9d60..6b77d8326a39 100644 --- a/services/core/java/com/android/server/net/OWNERS +++ b/services/core/java/com/android/server/net/OWNERS @@ -2,7 +2,7 @@ set noparent ek@google.com hugobenichi@google.com -jsharkey@google.com +jsharkey@android.com lorenzo@google.com satk@google.com silberst@google.com diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 238d87b7c396..6cebdd64d8f9 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -460,7 +460,7 @@ public class NotificationManagerService extends SystemService { mRankingHelper.readXml(parser, forRestore); } // No non-system managed services are allowed on low ram devices - if (!ActivityManager.isLowRamDeviceStatic()) { + if (canUseManagedServices()) { if (mListeners.getConfig().xmlTag.equals(parser.getName())) { mListeners.readXml(parser); migratedManagedServices = true; @@ -548,12 +548,6 @@ public class NotificationManagerService extends SystemService { out.endDocument(); } - /** Use this to check if a package can post a notification or toast. */ - private boolean checkNotificationOp(String pkg, int uid) { - return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) - == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid); - } - private static final class ToastRecord { final int pid; @@ -1226,7 +1220,6 @@ public class NotificationManagerService extends SystemService { mAccessibilityManager = am; } - // TODO: All tests should use this init instead of the one-off setters above. @VisibleForTesting void init(Looper looper, IPackageManager packageManager, @@ -2818,19 +2811,25 @@ public class NotificationManagerService extends SystemService { @Override public void setNotificationPolicyAccessGranted(String pkg, boolean granted) throws RemoteException { + setNotificationPolicyAccessGrantedForUser( + pkg, getCallingUserHandle().getIdentifier(), granted); + } + + @Override + public void setNotificationPolicyAccessGrantedForUser( + String pkg, int userId, boolean granted) { checkCallerIsSystemOrShell(); final long identity = Binder.clearCallingIdentity(); try { - if (!mActivityManager.isLowRamDevice()) { + if (canUseManagedServices()) { mConditionProviders.setPackageOrComponentEnabled( - pkg, getCallingUserHandle().getIdentifier(), true, granted); + pkg, userId, true, granted); getContext().sendBroadcastAsUser(new Intent( NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) .setPackage(pkg) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), - getCallingUserHandle(), null); - + UserHandle.of(userId), null); savePolicyFile(); } } finally { @@ -2918,18 +2917,17 @@ public class NotificationManagerService extends SystemService { checkCallerIsSystemOrShell(); final long identity = Binder.clearCallingIdentity(); try { - if (!mActivityManager.isLowRamDevice()) { + if (canUseManagedServices()) { mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(), userId, false, granted); mListeners.setPackageOrComponentEnabled(listener.flattenToString(), userId, true, granted); getContext().sendBroadcastAsUser(new Intent( - NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) - + NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) .setPackage(listener.getPackageName()) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), - getCallingUserHandle(), null); + UserHandle.of(userId), null); savePolicyFile(); } @@ -2945,7 +2943,7 @@ public class NotificationManagerService extends SystemService { checkCallerIsSystemOrShell(); final long identity = Binder.clearCallingIdentity(); try { - if (!mActivityManager.isLowRamDevice()) { + if (canUseManagedServices()) { mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(), userId, false, granted); mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(), @@ -2955,7 +2953,7 @@ public class NotificationManagerService extends SystemService { NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) .setPackage(assistant.getPackageName()) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), - getCallingUserHandle(), null); + UserHandle.of(userId), null); savePolicyFile(); } @@ -5434,6 +5432,11 @@ public class NotificationManagerService extends SystemService { } } + private boolean canUseManagedServices() { + return !mActivityManager.isLowRamDevice() + || mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_WATCH); + } + private class TrimCache { StatusBarNotification heavy; StatusBarNotification sbnClone; 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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7be0cde4a94d..7837b029830f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -8687,6 +8687,7 @@ public class PackageManagerService extends IPackageManager.Stub pkg.applicationInfo.primaryCpuAbi = updatedPkg.primaryCpuAbiString; pkg.applicationInfo.secondaryCpuAbi = updatedPkg.secondaryCpuAbiString; } + pkg.mExtras = updatedPkg; throw new PackageManagerException(Log.WARN, "Package " + ps.name + " at " + scanFile + " ignored: updated version " + ps.versionCode 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/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index a2099e6080e5..807eb1a8aac4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -54,6 +54,7 @@ import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.IUserManager; +import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -78,7 +79,6 @@ import dalvik.system.DexFile; import libcore.io.IoUtils; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -102,8 +102,6 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO class PackageManagerShellCommand extends ShellCommand { /** Path for streaming APK content */ private static final String STDIN_PATH = "-"; - /** Whether or not APK content must be streamed from stdin */ - private static final boolean FORCE_STREAM_INSTALL = true; final IPackageManager mInterface; final private WeakHashMap<String, Resources> mResourceCache = @@ -255,30 +253,27 @@ class PackageManagerShellCommand extends ShellCommand { } private void setParamsSize(InstallParams params, String inPath) { - // If we're forced to stream the package, the params size - // must be set via command-line argument. There's nothing - // to do here. - if (FORCE_STREAM_INSTALL) { - return; - } - final PrintWriter pw = getOutPrintWriter(); if (params.sessionParams.sizeBytes == -1 && !STDIN_PATH.equals(inPath)) { - File file = new File(inPath); - if (file.isFile()) { + final ParcelFileDescriptor fd = openFileForSystem(inPath, "r"); + if (fd == null) { + getErrPrintWriter().println("Error: Can't open file: " + inPath); + throw new IllegalArgumentException("Error: Can't open file: " + inPath); + } + try { + ApkLite baseApk = PackageParser.parseApkLite(fd.getFileDescriptor(), inPath, 0); + PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null, + null, null); + params.sessionParams.setSize(PackageHelper.calculateInstalledSize( + pkgLite, params.sessionParams.abiOverride)); + } catch (PackageParserException | IOException e) { + getErrPrintWriter().println("Error: Failed to parse APK file: " + inPath); + throw new IllegalArgumentException( + "Error: Failed to parse APK file: " + inPath, e); + } finally { try { - ApkLite baseApk = PackageParser.parseApkLite(file, 0); - PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null, - null, null); - params.sessionParams.setSize(PackageHelper.calculateInstalledSize( - pkgLite, params.sessionParams.abiOverride)); - } catch (PackageParserException | IOException e) { - pw.println("Error: Failed to parse APK file: " + file); - throw new IllegalArgumentException( - "Error: Failed to parse APK file: " + file, e); + fd.close(); + } catch (IOException e) { } - } else { - pw.println("Error: Can't open non-file: " + inPath); - throw new IllegalArgumentException("Error: Can't open non-file: " + inPath); } } } @@ -1914,6 +1909,12 @@ class PackageManagerShellCommand extends ShellCommand { throw new IllegalArgumentException("Missing inherit package name"); } break; + case "--pkg": + sessionParams.appPackageName = getNextArg(); + if (sessionParams.appPackageName == null) { + throw new IllegalArgumentException("Missing package name"); + } + break; case "-S": final long sizeBytes = Long.parseLong(getNextArg()); if (sizeBytes <= 0) { @@ -1925,6 +1926,7 @@ class PackageManagerShellCommand extends ShellCommand { sessionParams.abiOverride = checkAbiArgument(getNextArg()); break; case "--ephemeral": + case "--instant": case "--instantapp": sessionParams.setInstallAsInstantApp(true /*isInstantApp*/); break; @@ -2092,20 +2094,24 @@ class PackageManagerShellCommand extends ShellCommand { private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName, boolean logSuccess) throws RemoteException { final PrintWriter pw = getOutPrintWriter(); - if (FORCE_STREAM_INSTALL && inPath != null && !STDIN_PATH.equals(inPath)) { - pw.println("Error: APK content must be streamed"); - return 1; - } + final ParcelFileDescriptor fd; if (STDIN_PATH.equals(inPath)) { - inPath = null; + fd = null; } else if (inPath != null) { - final File file = new File(inPath); - if (file.isFile()) { - sizeBytes = file.length(); + fd = openFileForSystem(inPath, "r"); + if (fd == null) { + return -1; + } + sizeBytes = fd.getStatSize(); + if (sizeBytes < 0) { + getErrPrintWriter().println("Unable to get size of: " + inPath); + return -1; } + } else { + fd = null; } if (sizeBytes <= 0) { - pw.println("Error: must specify a APK size"); + getErrPrintWriter().println("Error: must specify a APK size"); return 1; } @@ -2118,8 +2124,8 @@ class PackageManagerShellCommand extends ShellCommand { session = new PackageInstaller.Session( mInterface.getPackageInstaller().openSession(sessionId)); - if (inPath != null) { - in = new FileInputStream(inPath); + if (fd != null) { + in = new ParcelFileDescriptor.AutoCloseInputStream(fd); } else { in = new SizedInputStream(getRawInputStream(), sizeBytes); } @@ -2144,7 +2150,7 @@ class PackageManagerShellCommand extends ShellCommand { } return 0; } catch (IOException e) { - pw.println("Error: failed to write; " + e.getMessage()); + getErrPrintWriter().println("Error: failed to write; " + e.getMessage()); return 1; } finally { IoUtils.closeQuietly(out); 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/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 533b261998ae..8014acf653ee 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -383,6 +383,7 @@ public final class DefaultPermissionGrantPolicy { MediaStore.AUTHORITY, userId); if (mediaStorePackage != null) { grantRuntimePermissions(mediaStorePackage, STORAGE_PERMISSIONS, true, userId); + grantRuntimePermissions(mediaStorePackage, PHONE_PERMISSIONS, true, userId); } // Downloads provider 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 e873d32e7278..98db80ef0a3b 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -21,7 +21,6 @@ import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; -import static android.os.Build.VERSION_CODES.O_MR1; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; @@ -1101,52 +1100,54 @@ 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 @@ -1201,15 +1202,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree */ @Override int getOrientation(int candidate) { - // We do not allow non-fullscreen apps to influence orientation starting in O-MR1. While we - // do throw an exception in {@link Activity#onCreate} and - // {@link Activity#setRequestedOrientation}, we also ignore the orientation here so that - // other calculations aren't affected. - if (!fillsParent() && mTargetSdk >= O_MR1) { - // Can't specify orientation if app doesn't fill parent. - return SCREEN_ORIENTATION_UNSET; - } - if (candidate == SCREEN_ORIENTATION_BEHIND) { // Allow app to specify orientation regardless of its visibility state if the current // candidate want us to use orientation behind. I.e. the visible app on-top of this one diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java index 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 4c6ab3f7cc4f..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"); } @@ -1452,17 +1452,17 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** * @return The primary split-screen stack, but only if it is visible, and {@code null} otherwise. */ - TaskStack getSplitScreenPrimaryStackStack() { - TaskStack stack = mTaskStackContainers.getSplitScreenPrimaryStackStack(); + TaskStack getSplitScreenPrimaryStack() { + TaskStack stack = mTaskStackContainers.getSplitScreenPrimaryStack(); return (stack != null && stack.isVisible()) ? stack : null; } /** - * Like {@link #getSplitScreenPrimaryStackStack}, but also returns the stack if it's currently + * Like {@link #getSplitScreenPrimaryStack}, but also returns the stack if it's currently * not visible. */ - TaskStack getSplitScreenPrimaryStackStackIgnoringVisibility() { - return mTaskStackContainers.getSplitScreenPrimaryStackStack(); + TaskStack getSplitScreenPrimaryStackIgnoringVisibility() { + return mTaskStackContainers.getSplitScreenPrimaryStack(); } TaskStack getPinnedStack() { @@ -1877,7 +1877,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION); } // TODO(multi-display): Support docked stacks on secondary displays. - if (mDisplayId == DEFAULT_DISPLAY && getSplitScreenPrimaryStackStack() != null) { + if (mDisplayId == DEFAULT_DISPLAY && getSplitScreenPrimaryStack() != null) { mDividerControllerLocked.getTouchRegion(mTmpRect); mTmpRegion.set(mTmpRect); mTouchExcludeRegion.op(mTmpRegion, Op.UNION); @@ -2232,7 +2232,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (pinnedStack != null) { pw.println(prefix + "pinnedStack=" + pinnedStack.getName()); } - final TaskStack splitScreenPrimaryStack = getSplitScreenPrimaryStackStack(); + final TaskStack splitScreenPrimaryStack = getSplitScreenPrimaryStack(); if (splitScreenPrimaryStack != null) { pw.println(prefix + "splitScreenPrimaryStack=" + splitScreenPrimaryStack.getName()); } @@ -3401,7 +3401,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mPinnedStack; } - TaskStack getSplitScreenPrimaryStackStack() { + TaskStack getSplitScreenPrimaryStack() { return mSplitScreenPrimaryStack; } diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 5ce4d46c6b4a..5ea0e1d041ba 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -321,7 +321,7 @@ public class DockedStackDividerController implements DimLayerUser { if (mWindow == null) { return; } - TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility(); + TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility(); // If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide final boolean visible = stack != null; @@ -361,7 +361,7 @@ public class DockedStackDividerController implements DimLayerUser { } void positionDockedStackedDivider(Rect frame) { - TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackStack(); + TaskStack stack = mDisplayContent.getSplitScreenPrimaryStack(); if (stack == null) { // Unfortunately we might end up with still having a divider, even though the underlying // stack was already removed. This is because we are on AM thread and the removal of the @@ -458,7 +458,7 @@ public class DockedStackDividerController implements DimLayerUser { long animDuration = 0; if (animate) { final TaskStack stack = - mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility(); + mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility(); final long transitionDuration = isAnimationMaximizing() ? mService.mAppTransition.getLastClipRevealTransitionDuration() : DEFAULT_APP_TRANSITION_DURATION; @@ -513,7 +513,7 @@ public class DockedStackDividerController implements DimLayerUser { mDockedStackListeners.register(listener); notifyDockedDividerVisibilityChanged(wasVisible()); notifyDockedStackExistsChanged( - mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility() != null); + mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility() != null); notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */, isHomeStackResizable()); notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */); @@ -531,7 +531,7 @@ public class DockedStackDividerController implements DimLayerUser { final TaskStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED ? mDisplayContent.getStack(targetWindowingMode) : null; - final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStackStack(); + final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack(); boolean visibleAndValid = visible && stack != null && dockedStack != null; if (visibleAndValid) { stack.getDimBounds(mTmpRect); @@ -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; @@ -616,7 +616,7 @@ public class DockedStackDividerController implements DimLayerUser { } private void checkMinimizeChanged(boolean animate) { - if (mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility() == null) { + if (mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility() == null) { return; } final TaskStack homeStack = mDisplayContent.getHomeStack(); @@ -778,7 +778,7 @@ public class DockedStackDividerController implements DimLayerUser { } private boolean setMinimizedDockedStack(boolean minimized) { - final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility(); + final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility(); notifyDockedStackMinimizedChanged(minimized, false /* animate */, isHomeStackResizable()); return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f); } @@ -829,7 +829,7 @@ public class DockedStackDividerController implements DimLayerUser { } private boolean animateForMinimizedDockedStack(long now) { - final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility(); + final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility(); if (!mAnimationStarted) { mAnimationStarted = true; mAnimationStartTime = now; 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/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java index 36753b7d5e04..d40db8cb5443 100644 --- a/services/core/java/com/android/server/wm/InputConsumerImpl.java +++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java @@ -16,21 +16,36 @@ package com.android.server.wm; +import android.os.IBinder; import android.os.Process; +import android.os.RemoteException; +import android.os.UserHandle; import android.view.Display; import android.view.InputChannel; import android.view.WindowManager; import com.android.server.input.InputApplicationHandle; import com.android.server.input.InputWindowHandle; -class InputConsumerImpl { +import java.io.PrintWriter; + +class InputConsumerImpl implements IBinder.DeathRecipient { final WindowManagerService mService; final InputChannel mServerChannel, mClientChannel; final InputApplicationHandle mApplicationHandle; final InputWindowHandle mWindowHandle; - InputConsumerImpl(WindowManagerService service, String name, InputChannel inputChannel) { + final IBinder mToken; + final String mName; + final int mClientPid; + final UserHandle mClientUser; + + InputConsumerImpl(WindowManagerService service, IBinder token, String name, + InputChannel inputChannel, int clientPid, UserHandle clientUser) { mService = service; + mToken = token; + mName = name; + mClientPid = clientPid; + mClientUser = clientUser; InputChannel[] channels = InputChannel.openInputChannelPair(name); mServerChannel = channels[0]; @@ -68,6 +83,26 @@ class InputConsumerImpl { mWindowHandle.scaleFactor = 1.0f; } + void linkToDeathRecipient() { + if (mToken == null) { + return; + } + + try { + mToken.linkToDeath(this, 0); + } catch (RemoteException e) { + // Client died, do nothing + } + } + + void unlinkFromDeathRecipient() { + if (mToken == null) { + return; + } + + mToken.unlinkToDeath(this, 0); + } + void layout(int dw, int dh) { mWindowHandle.touchableRegion.set(0, 0, dw, dh); mWindowHandle.frameLeft = 0; @@ -86,5 +121,19 @@ class InputConsumerImpl { mService.mInputManager.unregisterInputChannel(mServerChannel); mClientChannel.dispose(); mServerChannel.dispose(); + unlinkFromDeathRecipient(); + } + + @Override + public void binderDied() { + synchronized (mService.getWindowManagerLock()) { + // Clean up the input consumer + mService.mInputMonitor.destroyInputConsumer(mName); + unlinkFromDeathRecipient(); + } + } + + void dump(PrintWriter pw, String name, String prefix) { + pw.println(prefix + " name=" + name + " pid=" + mClientPid + " user=" + mClientUser); } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index d23369efd297..40eab45a26d9 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -25,6 +25,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLP import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; + import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT; @@ -34,8 +35,11 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.app.ActivityManager; import android.graphics.Rect; import android.os.Debug; +import android.os.IBinder; import android.os.Looper; +import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; @@ -43,7 +47,6 @@ import android.view.InputChannel; import android.view.InputEventReceiver; import android.view.KeyEvent; import android.view.WindowManager; - import android.view.WindowManagerPolicy; import com.android.server.input.InputApplicationHandle; @@ -106,8 +109,9 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { EventReceiverInputConsumer(WindowManagerService service, InputMonitor monitor, Looper looper, String name, - InputEventReceiver.Factory inputEventReceiverFactory) { - super(service, name, null); + InputEventReceiver.Factory inputEventReceiverFactory, + int clientPid, UserHandle clientUser) { + super(service, null /* token */, name, null /* inputChannel */, clientPid, clientUser); mInputMonitor = monitor; mInputEventReceiver = inputEventReceiverFactory.createInputEventReceiver( mClientChannel, looper); @@ -129,6 +133,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { private void addInputConsumer(String name, InputConsumerImpl consumer) { mInputConsumers.put(name, consumer); + consumer.linkToDeathRecipient(); updateInputWindowsLw(true /* force */); } @@ -166,17 +171,20 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { } final EventReceiverInputConsumer consumer = new EventReceiverInputConsumer(mService, - this, looper, name, inputEventReceiverFactory); + this, looper, name, inputEventReceiverFactory, Process.myPid(), + UserHandle.SYSTEM); addInputConsumer(name, consumer); return consumer; } - void createInputConsumer(String name, InputChannel inputChannel) { + void createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid, + UserHandle clientUser) { if (mInputConsumers.containsKey(name)) { throw new IllegalStateException("Existing input consumer found with name: " + name); } - final InputConsumerImpl consumer = new InputConsumerImpl(mService, name, inputChannel); + final InputConsumerImpl consumer = new InputConsumerImpl(mService, token, name, + inputChannel, clientPid, clientUser); switch (name) { case INPUT_CONSUMER_WALLPAPER: consumer.mWindowHandle.hasWallpaper = true; @@ -593,7 +601,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { if (!inputConsumerKeys.isEmpty()) { pw.println(prefix + "InputConsumers:"); for (String key : inputConsumerKeys) { - pw.println(prefix + " name=" + key); + mInputConsumers.get(key).dump(pw, key, prefix); } } } 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 7620cb0dfdb9..13435d76cd07 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -419,7 +419,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU return mFillsParent || !inSplitScreenSecondaryWindowingMode() || displayContent == null - || displayContent.getSplitScreenPrimaryStackStackIgnoringVisibility() != null; + || displayContent.getSplitScreenPrimaryStackIgnoringVisibility() != null; } /** Original bounds of the task if applicable, otherwise fullscreen rect. */ @@ -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 7cee66ba064a..6e89e3ea37bb 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -294,7 +294,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye if (mFillsParent || !inSplitScreenSecondaryWindowingMode() || mDisplayContent == null - || mDisplayContent.getSplitScreenPrimaryStackStack() != null) { + || mDisplayContent.getSplitScreenPrimaryStack() != null) { return true; } return false; @@ -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. @@ -693,7 +693,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye "animation background stackId=" + mStackId); Rect bounds = null; - final TaskStack dockedStack = dc.getSplitScreenPrimaryStackStackIgnoringVisibility(); + final TaskStack dockedStack = dc.getSplitScreenPrimaryStackIgnoringVisibility(); if (inSplitScreenPrimaryWindowingMode() || (dockedStack != null && inSplitScreenSecondaryWindowingMode() && !dockedStack.fillsParent())) { @@ -773,7 +773,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye } final TaskStack dockedStack = - mDisplayContent.getSplitScreenPrimaryStackStackIgnoringVisibility(); + mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility(); if (dockedStack == null) { // Not sure why you are calling this method when there is no docked stack... throw new IllegalStateException( 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 c982d1576d46..bf5f4bc83e6c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -80,7 +80,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIO import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD; @@ -162,7 +161,9 @@ import android.os.PowerManager; 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; @@ -202,7 +203,6 @@ import android.view.IWindowSession; import android.view.IWindowSessionCallback; import android.view.InputChannel; import android.view.InputDevice; -import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.KeyEvent; import android.view.MagnificationSpec; @@ -363,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. @@ -822,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); @@ -931,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) { @@ -961,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) { @@ -1070,7 +1085,7 @@ public class WindowManagerService extends IWindowManager.Stub try { createWatermarkInTransaction(); } finally { - closeSurfaceTransaction(); + closeSurfaceTransaction("createWatermarkInTransaction"); } showEmulatorDisplayOverlayIfNeeded(); @@ -3286,7 +3301,7 @@ public class WindowManagerService extends IWindowManager.Stub // Notify whether the docked stack exists for the current user final DisplayContent displayContent = getDefaultDisplayContentLocked(); final TaskStack stack = - displayContent.getSplitScreenPrimaryStackStackIgnoringVisibility(); + displayContent.getSplitScreenPrimaryStackIgnoringVisibility(); displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged( stack != null && stack.hasTaskForUser(newUserId)); @@ -3574,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 + ")"); } @@ -3599,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"); } @@ -6137,9 +6152,10 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void createInputConsumer(String name, InputChannel inputChannel) { + public void createInputConsumer(IBinder token, String name, InputChannel inputChannel) { synchronized (mWindowMap) { - mInputMonitor.createInputConsumer(name, inputChannel); + mInputMonitor.createInputConsumer(token, name, inputChannel, Binder.getCallingPid(), + Binder.getCallingUserHandle()); } } @@ -6321,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) { @@ -6340,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)"); @@ -6908,7 +6935,7 @@ public class WindowManagerService extends IWindowManager.Stub public int getDockedStackSide() { synchronized (mWindowMap) { final TaskStack dockedStack = getDefaultDisplayContentLocked() - .getSplitScreenPrimaryStackStackIgnoringVisibility(); + .getSplitScreenPrimaryStackIgnoringVisibility(); return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(); } } 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 4370a7637e01..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() { @@ -2361,7 +2360,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // also reset drag resizing state, because the owner can't do it // anymore. final TaskStack stack = - dc.getSplitScreenPrimaryStackStackIgnoringVisibility(); + dc.getSplitScreenPrimaryStackIgnoringVisibility(); if (stack != null) { stack.resetDockedStackToMiddle(); } 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/coverage/java/com/android/server/coverage/CoverageService.java b/services/coverage/java/com/android/server/coverage/CoverageService.java index d600aa8c4276..ed8d24aa3b8f 100644 --- a/services/coverage/java/com/android/server/coverage/CoverageService.java +++ b/services/coverage/java/com/android/server/coverage/CoverageService.java @@ -112,7 +112,7 @@ public class CoverageService extends Binder { } // Try to open the destination file - ParcelFileDescriptor fd = openOutputFileForSystem(dest); + ParcelFileDescriptor fd = openFileForSystem(dest, "w"); if (fd == null) { return -1; } 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/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java index 9f5f856d66c1..70e8a162b1cf 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -20,10 +20,12 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; +import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; @@ -72,6 +74,7 @@ import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableContext; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.util.ArrayMap; @@ -113,7 +116,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { private IPackageManager mPackageManager; @Mock private PackageManager mPackageManagerClient; - private Context mContext = getContext(); + private TestableContext mContext = spy(getContext()); private final String PKG = mContext.getPackageName(); private TestableLooper mTestableLooper; @Mock @@ -174,15 +177,18 @@ public class NotificationManagerServiceTest extends NotificationTestCase { mTestableLooper = TestableLooper.get(this); mHandler = mService.new WorkerHandler(mTestableLooper.getLooper()); // MockPackageManager - default returns ApplicationInfo with matching calling UID + mContext.setMockPackageManager(mPackageManagerClient); final ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.uid = mUid; when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())) .thenReturn(applicationInfo); when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) .thenReturn(applicationInfo); + when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid); final LightsManager mockLightsManager = mock(LightsManager.class); when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class)); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); + when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false); // write to a test file; the system file isn't readable from tests mFile = new File(mContext.getCacheDir(), "test.xml"); @@ -227,6 +233,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { mBinderService.createNotificationChannels( PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel))); + assertNotNull(mBinderService.getNotificationChannel(PKG, TEST_CHANNEL_ID)); } @After @@ -1338,6 +1345,72 @@ public class NotificationManagerServiceTest extends NotificationTestCase { } @Test + public void testSetListenerAccessForUser() throws Exception { + UserHandle user = UserHandle.of(10); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + try { + mBinderService.setNotificationListenerAccessGrantedForUser( + c, user.getIdentifier(), true); + } catch (SecurityException e) { + if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { + throw e; + } + } + + verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); + verify(mListeners, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), true, true); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), false, true); + verify(mAssistants, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test + public void testSetAssistantAccessForUser() throws Exception { + UserHandle user = UserHandle.of(10); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + try { + mBinderService.setNotificationAssistantAccessGrantedForUser( + c, user.getIdentifier(), true); + } catch (SecurityException e) { + if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { + throw e; + } + } + + verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); + verify(mAssistants, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), true, true); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), false, true); + verify(mListeners, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test + public void testSetDndAccessForUser() throws Exception { + UserHandle user = UserHandle.of(10); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + try { + mBinderService.setNotificationPolicyAccessGrantedForUser( + c.getPackageName(), user.getIdentifier(), true); + } catch (SecurityException e) { + if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { + throw e; + } + } + + verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.getPackageName(), user.getIdentifier(), true, true); + verify(mAssistants, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + verify(mListeners, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test public void testSetListenerAccess() throws Exception { ComponentName c = ComponentName.unflattenFromString("package/Component"); try { @@ -1401,9 +1474,9 @@ public class NotificationManagerServiceTest extends NotificationTestCase { mBinderService.setNotificationListenerAccessGranted(c, true); verify(mListeners, never()).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + anyString(), anyInt(), anyBoolean(), anyBoolean()); verify(mConditionProviders, never()).setPackageOrComponentEnabled( - c.flattenToString(), 0, false, true); + anyString(), anyInt(), anyBoolean(), anyBoolean()); verify(mAssistants, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -1414,11 +1487,10 @@ public class NotificationManagerServiceTest extends NotificationTestCase { ComponentName c = ComponentName.unflattenFromString("package/Component"); mBinderService.setNotificationAssistantAccessGranted(c, true); - verify(mListeners, never()).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + anyString(), anyInt(), anyBoolean(), anyBoolean()); verify(mConditionProviders, never()).setPackageOrComponentEnabled( - c.flattenToString(), 0, false, true); + anyString(), anyInt(), anyBoolean(), anyBoolean()); verify(mAssistants, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -1430,14 +1502,77 @@ public class NotificationManagerServiceTest extends NotificationTestCase { mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true); verify(mListeners, never()).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + anyString(), anyInt(), anyBoolean(), anyBoolean()); verify(mConditionProviders, never()).setPackageOrComponentEnabled( + anyString(), anyInt(), anyBoolean(), anyBoolean()); + verify(mAssistants, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test + public void testSetListenerAccess_doesNothingOnLowRam_exceptWatch() throws Exception { + when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true); + when(mActivityManager.isLowRamDevice()).thenReturn(true); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + try { + mBinderService.setNotificationListenerAccessGranted(c, true); + } catch (SecurityException e) { + if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { + throw e; + } + } + + verify(mListeners, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), 0, true, true); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, true); verify(mAssistants, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @Test + public void testSetAssistantAccess_doesNothingOnLowRam_exceptWatch() throws Exception { + when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true); + when(mActivityManager.isLowRamDevice()).thenReturn(true); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + try { + mBinderService.setNotificationAssistantAccessGranted(c, true); + } catch (SecurityException e) { + if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { + throw e; + } + } + + verify(mListeners, never()).setPackageOrComponentEnabled( + anyString(), anyInt(), anyBoolean(), anyBoolean()); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), 0, false, true); + verify(mAssistants, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), 0, true, true); + } + + @Test + public void testSetDndAccess_doesNothingOnLowRam_exceptWatch() throws Exception { + when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true); + when(mActivityManager.isLowRamDevice()).thenReturn(true); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + try { + mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true); + } catch (SecurityException e) { + if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { + throw e; + } + } + + verify(mListeners, never()).setPackageOrComponentEnabled( + anyString(), anyInt(), anyBoolean(), anyBoolean()); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.getPackageName(), 0, true, true); + verify(mAssistants, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test public void testOnlyAutogroupIfGroupChanged_noPriorNoti_autogroups() throws Exception { NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false); mService.addEnqueuedNotification(r); diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java index 44a79ab3a6f3..1adfb67cd6ed 100644 --- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java @@ -148,7 +148,7 @@ public class LockTaskControllerTest { // THEN the lock task mode state should be LOCKED assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState()); // THEN the task should be locked - assertTrue(mLockTaskController.checkLockedTask(tr)); + assertTrue(mLockTaskController.isTaskLocked(tr)); // THEN lock task mode should be started verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); @@ -167,8 +167,8 @@ public class LockTaskControllerTest { // THEN the lock task mode state should be LOCKED assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState()); // THEN neither of the tasks should be able to move to back of stack - assertTrue(mLockTaskController.checkLockedTask(tr1)); - assertTrue(mLockTaskController.checkLockedTask(tr2)); + assertTrue(mLockTaskController.isTaskLocked(tr1)); + assertTrue(mLockTaskController.isTaskLocked(tr2)); // THEN lock task mode should be started verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); @@ -197,7 +197,7 @@ public class LockTaskControllerTest { // THEN the lock task mode state should be PINNED assertEquals(LOCK_TASK_MODE_PINNED, mLockTaskController.getLockTaskModeState()); // THEN the task should be locked - assertTrue(mLockTaskController.checkLockedTask(tr)); + assertTrue(mLockTaskController.isTaskLocked(tr)); // THEN lock task mode should be started verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE); @@ -238,36 +238,36 @@ public class LockTaskControllerTest { mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN the same caller calls stopLockTaskMode - mLockTaskController.stopLockTaskMode(false, TEST_UID); + mLockTaskController.stopLockTaskMode(tr, false, TEST_UID); // THEN the lock task mode should be NONE assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState()); // THEN the task should no longer be locked - assertFalse(mLockTaskController.checkLockedTask(tr)); + assertFalse(mLockTaskController.isTaskLocked(tr)); // THEN lock task mode should have been finished verifyLockTaskStopped(times(1)); } @Test(expected = SecurityException.class) - public void testStopLockTaskMode_DifferentCaller() throws Exception { + public void testStopLockTaskMode_differentCaller() throws Exception { // GIVEN one task record with whitelisted auth that is in lock task mode TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN a different caller calls stopLockTaskMode - mLockTaskController.stopLockTaskMode(false, TEST_UID + 1); + mLockTaskController.stopLockTaskMode(tr, false, TEST_UID + 1); // THEN security exception should be thrown, because different caller tried to unlock } @Test - public void testStopLockTaskMode_SystemCaller() throws Exception { + public void testStopLockTaskMode_systemCaller() throws Exception { // GIVEN one task record with whitelisted auth that is in lock task mode TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN system calls stopLockTaskMode - mLockTaskController.stopLockTaskMode(true, SYSTEM_UID); + mLockTaskController.stopLockTaskMode(tr, true, SYSTEM_UID); // THEN lock task mode should still be active assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState()); @@ -282,19 +282,40 @@ public class LockTaskControllerTest { mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); // WHEN calling stopLockTaskMode - mLockTaskController.stopLockTaskMode(false, TEST_UID); + mLockTaskController.stopLockTaskMode(tr2, false, TEST_UID); // THEN the lock task mode should still be active assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState()); // THEN the first task should still be locked - assertTrue(mLockTaskController.checkLockedTask(tr1)); + assertTrue(mLockTaskController.isTaskLocked(tr1)); // THEN the top task should no longer be locked - assertFalse(mLockTaskController.checkLockedTask(tr2)); + assertFalse(mLockTaskController.isTaskLocked(tr2)); // THEN lock task mode should not have been finished verifyLockTaskStopped(never()); } @Test + public void testStopLockTaskMode_rootTask() throws Exception { + // GIVEN two task records with whitelisted auth that is in lock task mode + TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED); + TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED); + mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); + mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); + + // WHEN calling stopLockTaskMode on the root task + mLockTaskController.stopLockTaskMode(tr1, false, TEST_UID); + + // THEN the lock task mode should be inactive + assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState()); + // THEN the first task should no longer be locked + assertFalse(mLockTaskController.isTaskLocked(tr1)); + // THEN the top task should no longer be locked + assertFalse(mLockTaskController.isTaskLocked(tr2)); + // THEN lock task mode should be finished + verifyLockTaskStopped(times(1)); + } + + @Test public void testStopLockTaskMode_pinned() throws Exception { // GIVEN one task records that is in pinned mode TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE); @@ -307,12 +328,12 @@ public class LockTaskControllerTest { reset(mStatusBarService); // WHEN calling stopLockTask - mLockTaskController.stopLockTaskMode(true, SYSTEM_UID); + mLockTaskController.stopLockTaskMode(null, true, SYSTEM_UID); // THEN the lock task mode should no longer be active assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState()); // THEN the task should no longer be locked - assertFalse(mLockTaskController.checkLockedTask(tr)); + assertFalse(mLockTaskController.isTaskLocked(tr)); // THEN lock task mode should have been finished verifyLockTaskStopped(times(1)); // THEN the keyguard should be shown @@ -322,6 +343,27 @@ public class LockTaskControllerTest { } @Test + public void testClearLockedTasks() throws Exception { + // GIVEN two task records with whitelisted auth that is in lock task mode + TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED); + TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED); + mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); + mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); + + // WHEN calling stopLockTaskMode on the root task + mLockTaskController.clearLockedTasks("testClearLockedTasks"); + + // THEN the lock task mode should be inactive + assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState()); + // THEN the first task should no longer be locked + assertFalse(mLockTaskController.isTaskLocked(tr1)); + // THEN the top task should no longer be locked + assertFalse(mLockTaskController.isTaskLocked(tr2)); + // THEN lock task mode should be finished + verifyLockTaskStopped(times(1)); + } + + @Test public void testUpdateLockTaskPackages() throws Exception { String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; String[] whitelist2 = {TEST_PACKAGE_NAME}; @@ -367,8 +409,8 @@ public class LockTaskControllerTest { mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState()); - assertTrue(mLockTaskController.checkLockedTask(tr1)); - assertTrue(mLockTaskController.checkLockedTask(tr2)); + assertTrue(mLockTaskController.isTaskLocked(tr1)); + assertTrue(mLockTaskController.isTaskLocked(tr2)); verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); // WHEN removing one package from whitelist @@ -377,10 +419,10 @@ public class LockTaskControllerTest { // THEN the task running that package should be stopped verify(tr2).performClearTaskLocked(); - assertFalse(mLockTaskController.checkLockedTask(tr2)); + assertFalse(mLockTaskController.isTaskLocked(tr2)); // THEN the other task should remain locked assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState()); - assertTrue(mLockTaskController.checkLockedTask(tr1)); + assertTrue(mLockTaskController.isTaskLocked(tr1)); verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); // WHEN removing the last package from whitelist @@ -389,7 +431,7 @@ public class LockTaskControllerTest { // THEN the last task should be cleared, and the system should quit LockTask mode verify(tr1).performClearTaskLocked(); - assertFalse(mLockTaskController.checkLockedTask(tr1)); + assertFalse(mLockTaskController.isTaskLocked(tr1)); assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState()); verifyLockTaskStopped(times(1)); } diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index d6d0209bca7d..6fdb02988feb 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -60,7 +60,6 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; @@ -104,7 +103,7 @@ public class UserControllerTest extends AndroidTestCase { public void setUp() throws Exception { super.setUp(); mInjector = Mockito.spy(new TestInjector(getContext())); - doNothing().when(mInjector).clearLockTaskMode(anyString()); + doNothing().when(mInjector).clearAllLockedTasks(anyString()); doNothing().when(mInjector).startHomeActivity(anyInt(), anyString()); doReturn(false).when(mInjector).stackSupervisorSwitchUser(anyInt(), any()); doNothing().when(mInjector).stackSupervisorResumeFocusedStackTopActivity(); @@ -126,7 +125,7 @@ public class UserControllerTest extends AndroidTestCase { Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen(); Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean()); Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true); - Mockito.verify(mInjector).clearLockTaskMode(anyString()); + Mockito.verify(mInjector).clearAllLockedTasks(anyString()); startForegroundUserAssertions(); } @@ -136,7 +135,7 @@ public class UserControllerTest extends AndroidTestCase { Mockito.verify( mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt()); Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean()); - Mockito.verify(mInjector, never()).clearLockTaskMode(anyString()); + Mockito.verify(mInjector, never()).clearAllLockedTasks(anyString()); startBackgroundUserAssertions(); } diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java index 9ad7addab858..ea4f4b907824 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java @@ -193,7 +193,7 @@ public class AppWindowTokenTests extends WindowTestsBase { token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); token.setFillsParent(false); - // Can specify orientation if app doesn't fill parent. Allowed for SDK <= 25. + // Can specify orientation if app doesn't fill parent. assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation()); token.setFillsParent(true); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java new file mode 100644 index 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 eb8a8b99372d..3fc220857676 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1198,24 +1198,21 @@ public class CarrierConfigManager { */ public static final String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int"; - /** - * @hide - * The default value for preferred CDMA roaming mode (aka CDMA system select.) - * CDMA_ROAMING_MODE_RADIO_DEFAULT = the default roaming mode from the radio - * CDMA_ROAMING_MODE_HOME = Home Networks - * CDMA_ROAMING_MODE_AFFILIATED = Roaming on Affiliated networks - * CDMA_ROAMING_MODE_ANY = Roaming on any networks + * The CDMA roaming mode (aka CDMA system select). + * + * <p>The value should be one of the CDMA_ROAMING_MODE_ constants in {@link TelephonyManager}. + * Values other than {@link TelephonyManager#CDMA_ROAMING_MODE_RADIO_DEFAULT} (which is the + * default) will take precedence over user selection. + * + * @see TelephonyManager#CDMA_ROAMING_MODE_RADIO_DEFAULT + * @see TelephonyManager#CDMA_ROAMING_MODE_HOME + * @see TelephonyManager#CDMA_ROAMING_MODE_AFFILIATED + * @see TelephonyManager#CDMA_ROAMING_MODE_ANY */ public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int"; - /** @hide */ - public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; - /** @hide */ - public static final int CDMA_ROAMING_MODE_HOME = 0; - /** @hide */ - public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; - /** @hide */ - public static final int CDMA_ROAMING_MODE_ANY = 2; + + /** * Boolean indicating if support is provided for directly dialing FDN number from FDN list. * If false, this feature is not supported. @@ -1546,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 */ @@ -1833,7 +1837,8 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true); sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false); sDefaults.putBoolean(KEY_FORCE_IMEI_BOOL, false); - sDefaults.putInt(KEY_CDMA_ROAMING_MODE_INT, CDMA_ROAMING_MODE_RADIO_DEFAULT); + sDefaults.putInt( + KEY_CDMA_ROAMING_MODE_INT, TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT); sDefaults.putString(KEY_RCS_CONFIG_SERVER_URL_STRING, ""); // Carrier Signalling Receivers @@ -1904,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 c0564c55a7e0..42c3de563492 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -953,6 +953,27 @@ public class TelephonyManager { */ public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; + /** + * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which leaves the roaming + * mode set to the radio default or to the user's preference if they've indicated one. + */ + public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; + /** + * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which only permits + * connections on home networks. + */ + public static final int CDMA_ROAMING_MODE_HOME = 0; + /** + * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which permits roaming on + * affiliated networks. + */ + public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; + /** + * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which permits roaming on + * any network. + */ + public static final int CDMA_ROAMING_MODE_ANY = 2; + // // // Device Info @@ -2145,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/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp index 99b4c31884bb..8b6c524e5c8d 100644 --- a/tools/aapt2/compile/InlineXmlFormatParser.cpp +++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp @@ -147,6 +147,10 @@ bool InlineXmlFormatParser::Consume(IAaptContext* context, xml::XmlResource* doc } else { new_doc->root.reset(static_cast<xml::Element*>(child.release())); new_doc->root->parent = nullptr; + // Copy down the namespace declarations + new_doc->root->namespace_decls = doc->root->namespace_decls; + // Recurse for nested inlines + Consume(context, new_doc.get()); } } diff --git a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp index d6d734d395b5..2b4ab96d5c3a 100644 --- a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp +++ b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp @@ -141,4 +141,47 @@ TEST(InlineXmlFormatParserTest, ExtractTwoXmlResources) { EXPECT_THAT(extracted_doc_drawable->root->name, StrEq("vector")); } +TEST(InlineXmlFormatParserTest, ExtractNestedXmlResources) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"( + <base_root xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="inline_xml"> + <inline_root> + <aapt:attr name="nested_inline_xml"> + <nested_inline_root/> + </aapt:attr> + <aapt:attr name="another_nested_inline_xml"> + <root/> + </aapt:attr> + </inline_root> + </aapt:attr> + <aapt:attr name="turtles"> + <root1> + <aapt:attr name="all"> + <root2> + <aapt:attr name="the"> + <root3> + <aapt:attr name="way"> + <root4> + <aapt:attr name="down"> + <root5/> + </aapt:attr> + </root4> + </aapt:attr> + </root3> + </aapt:attr> + </root2> + </aapt:attr> + </root1> + </aapt:attr> + </base_root>)"); + + doc->file.name = test::ParseNameOrDie("layout/main"); + + InlineXmlFormatParser parser; + ASSERT_TRUE(parser.Consume(context.get(), doc.get())); + // Confirm that all of the nested inline xmls are parsed out. + ASSERT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(8u)); +} } // namespace aapt 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/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index 5d2926821164..f76196dd2a19 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -22,6 +22,7 @@ namespace android { namespace stats_log_api_gen { +using google::protobuf::EnumDescriptor; using google::protobuf::FieldDescriptor; using google::protobuf::FileDescriptor; using google::protobuf::SourceLocation; @@ -120,7 +121,7 @@ java_type(const FieldDescriptor* field) case FieldDescriptor::TYPE_UINT32: return JAVA_TYPE_INT; case FieldDescriptor::TYPE_ENUM: - return JAVA_TYPE_INT; + return JAVA_TYPE_ENUM; case FieldDescriptor::TYPE_SFIXED32: return JAVA_TYPE_INT; case FieldDescriptor::TYPE_SFIXED64: @@ -208,7 +209,6 @@ collate_atoms(const Descriptor* descriptor, Atoms* atoms) errorCount++; continue; } - } // Check that if there's a WorkSource, it's at position 1. @@ -228,15 +228,26 @@ collate_atoms(const Descriptor* descriptor, Atoms* atoms) AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name()); - // Build the type signature + // Build the type signature and the atom data. vector<java_type_t> signature; for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end(); it++) { const FieldDescriptor* field = it->second; java_type_t javaType = java_type(field); - atomDecl.fields.push_back(AtomField(field->name(), javaType)); - signature.push_back(javaType); + AtomField atField(field->name(), javaType); + if (javaType == JAVA_TYPE_ENUM) { + // All enums are treated as ints when it comes to function signatures. + signature.push_back(JAVA_TYPE_INT); + const EnumDescriptor* enumDescriptor = field->enum_type(); + for (int i = 0; i < enumDescriptor->value_count(); i++) { + atField.enumValues[enumDescriptor->value(i)->number()] = + enumDescriptor->value(i)->name().c_str(); + } + } else { + signature.push_back(javaType); + } + atomDecl.fields.push_back(atField); } atoms->signatures.insert(signature); @@ -261,5 +272,3 @@ collate_atoms(const Descriptor* descriptor, Atoms* atoms) } // namespace stats_log_api_gen } // namespace android - - diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h index 50af7ea2d335..2f840d7f4469 100644 --- a/tools/stats_log_api_gen/Collation.h +++ b/tools/stats_log_api_gen/Collation.h @@ -22,10 +22,12 @@ #include <set> #include <vector> +#include <map> namespace android { namespace stats_log_api_gen { +using std::map; using std::set; using std::string; using std::vector; @@ -44,6 +46,7 @@ typedef enum { JAVA_TYPE_FLOAT = 5, JAVA_TYPE_DOUBLE = 6, JAVA_TYPE_STRING = 7, + JAVA_TYPE_ENUM = 8, JAVA_TYPE_OBJECT = -1, JAVA_TYPE_BYTE_ARRAY = -2, @@ -57,8 +60,13 @@ struct AtomField { string name; java_type_t javaType; + // If the field is of type enum, the following map contains the list of enum values. + map<int /* numeric value */, string /* value name */> enumValues; + inline AtomField() :name(), javaType(JAVA_TYPE_UNKNOWN) {} - inline AtomField(const AtomField& that) :name(that.name), javaType(that.javaType) {} + inline AtomField(const AtomField& that) :name(that.name), + javaType(that.javaType), + enumValues(that.enumValues) {} inline AtomField(string n, java_type_t jt) :name(n), javaType(jt) {} inline ~AtomField() {} }; diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index aaea4f6fe749..6350b725f09a 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -56,6 +56,7 @@ cpp_type_name(java_type_t type) case JAVA_TYPE_BOOLEAN: return "bool"; case JAVA_TYPE_INT: + case JAVA_TYPE_ENUM: return "int32_t"; case JAVA_TYPE_LONG: return "int64_t"; @@ -77,6 +78,7 @@ java_type_name(java_type_t type) case JAVA_TYPE_BOOLEAN: return "boolean"; case JAVA_TYPE_INT: + case JAVA_TYPE_ENUM: return "int"; case JAVA_TYPE_LONG: return "long"; @@ -173,7 +175,7 @@ write_stats_log_header(FILE* out, const Atoms& atoms) fprintf(out, " */\n"); fprintf(out, "\n"); fprintf(out, "/**\n"); - fprintf(out, " * Constants for event codes.\n"); + fprintf(out, " * Constants for atom codes.\n"); fprintf(out, " */\n"); fprintf(out, "enum {\n"); @@ -240,9 +242,9 @@ write_stats_log_java(FILE* out, const Atoms& atoms) fprintf(out, " * @hide\n"); fprintf(out, " */\n"); fprintf(out, "public final class StatsLog {\n"); - fprintf(out, " // Constants for event codes.\n"); + fprintf(out, " // Constants for atom codes.\n"); - // Print constants + // Print constants for the atom codes. for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); atom++) { string constant = make_constant_name(atom->name); @@ -260,6 +262,27 @@ write_stats_log_java(FILE* out, const Atoms& atoms) } fprintf(out, "\n"); + // Print constants for the enum values. + fprintf(out, " // Constants for enum values.\n\n"); + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); + atom != atoms.decls.end(); atom++) { + for (vector<AtomField>::const_iterator field = atom->fields.begin(); + field != atom->fields.end(); field++) { + if (field->javaType == JAVA_TYPE_ENUM) { + fprintf(out, " // Values for %s.%s\n", atom->message.c_str(), field->name.c_str()); + for (map<int, string>::const_iterator value = field->enumValues.begin(); + value != field->enumValues.end(); value++) { + fprintf(out, " public static final int %s__%s__%s = %d;\n", + make_constant_name(atom->message).c_str(), + make_constant_name(field->name).c_str(), + make_constant_name(value->second).c_str(), + value->first); + } + fprintf(out, "\n"); + } + } + } + // Print write methods fprintf(out, " // Write methods\n"); for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin(); @@ -286,6 +309,7 @@ jni_type_name(java_type_t type) case JAVA_TYPE_BOOLEAN: return "jboolean"; case JAVA_TYPE_INT: + case JAVA_TYPE_ENUM: return "jint"; case JAVA_TYPE_LONG: return "jlong"; @@ -311,6 +335,7 @@ jni_function_name(const vector<java_type_t>& signature) result += "_boolean"; break; case JAVA_TYPE_INT: + case JAVA_TYPE_ENUM: result += "_int"; break; case JAVA_TYPE_LONG: @@ -340,6 +365,7 @@ java_type_signature(java_type_t type) case JAVA_TYPE_BOOLEAN: return "Z"; case JAVA_TYPE_INT: + case JAVA_TYPE_ENUM: return "I"; case JAVA_TYPE_LONG: return "J"; diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp index 1bd2e3de332e..073f2cfce6a8 100644 --- a/tools/stats_log_api_gen/test_collation.cpp +++ b/tools/stats_log_api_gen/test_collation.cpp @@ -24,6 +24,7 @@ namespace android { namespace stats_log_api_gen { +using std::map; using std::set; using std::vector; @@ -54,6 +55,29 @@ set_contains_vector(const set<vector<java_type_t>>& s, int count, ...) EXPECT_TRUE(set_contains_vector(s, count, __VA_ARGS__)); \ } while(0) +/** Expects that the provided atom has no enum values for any field. */ +#define EXPECT_NO_ENUM_FIELD(atom) \ + do { \ + for (vector<AtomField>::const_iterator field = atom->fields.begin(); \ + field != atom->fields.end(); field++) { \ + EXPECT_TRUE(field->enumValues.empty()); \ + } \ + } while(0) + +/** Expects that exactly one specific field has expected enum values. */ +#define EXPECT_HAS_ENUM_FIELD(atom, field_name, values) \ + do { \ + for (vector<AtomField>::const_iterator field = atom->fields.begin(); \ + field != atom->fields.end(); field++) { \ + if (field->name == field_name) { \ + EXPECT_EQ(field->enumValues, values); \ + } else { \ + EXPECT_TRUE(field->enumValues.empty()); \ + } \ + } \ + } while(0) + + /** * Test a correct collation, with all the types. */ @@ -94,21 +118,28 @@ TEST(CollationTest, CollateStats) { EXPECT_EQ(1, atom->code); EXPECT_EQ("int_atom", atom->name); EXPECT_EQ("IntAtom", atom->message); + EXPECT_NO_ENUM_FIELD(atom); atom++; EXPECT_EQ(2, atom->code); EXPECT_EQ("out_of_order_atom", atom->name); EXPECT_EQ("OutOfOrderAtom", atom->message); + EXPECT_NO_ENUM_FIELD(atom); atom++; EXPECT_EQ(3, atom->code); EXPECT_EQ("another_int_atom", atom->name); EXPECT_EQ("AnotherIntAtom", atom->message); + EXPECT_NO_ENUM_FIELD(atom); atom++; EXPECT_EQ(4, atom->code); EXPECT_EQ("all_types_atom", atom->name); EXPECT_EQ("AllTypesAtom", atom->message); + map<int, string> enumValues; + enumValues[0] = "VALUE0"; + enumValues[1] = "VALUE1"; + EXPECT_HAS_ENUM_FIELD(atom, "enum_field", enumValues); atom++; EXPECT_TRUE(atom == atoms.decls.end()); @@ -125,7 +156,7 @@ TEST(CollationTest, NonMessageTypeFails) { } /** - * Test that atoms that have non-primitve types are rejected. + * Test that atoms that have non-primitive types are rejected. */ TEST(CollationTest, FailOnBadTypes) { Atoms atoms; @@ -165,7 +196,5 @@ TEST(CollationTest, FailBadWorkSourcePosition) { EXPECT_EQ(1, errorCount); } - } // namespace stats_log_api_gen } // namespace android - diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp index 756549c5e880..dc5c14e32cbd 100644 --- a/tools/streaming_proto/Android.bp +++ b/tools/streaming_proto/Android.bp @@ -21,10 +21,32 @@ cc_defaults { name: "protoc-gen-stream-defaults", srcs: [ "Errors.cpp", + "stream_proto_utils.cpp", "string_utils.cpp", ], + + shared_libs: ["libprotoc"], } +cc_library { + name: "streamingflags", + host_supported: true, + proto: { + export_proto_headers: true, + include_dirs: ["external/protobuf/src"], + }, + + target: { + host: { + proto: { + type: "full", + }, + srcs: [ + "stream.proto", + ], + }, + }, +} cc_binary_host { name: "protoc-gen-javastream", @@ -33,7 +55,6 @@ cc_binary_host { ], defaults: ["protoc-gen-stream-defaults"], - shared_libs: ["libprotoc"], } cc_binary_host { @@ -43,5 +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 d4e1b7aede92..481698432711 100644 --- a/tools/streaming_proto/cpp/main.cpp +++ b/tools/streaming_proto/cpp/main.cpp @@ -1,106 +1,23 @@ #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 <frameworks/base/tools/streaming_proto/stream.pb.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) { @@ -115,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) { @@ -160,6 +56,12 @@ write_field(stringstream& text, const FieldDescriptorProto& field, const string& text << endl; } +static inline bool +should_generate_fields_mapping(const DescriptorProto& message) +{ + return message.options().GetExtension(stream).enable_fields_mapping(); +} + static void write_message(stringstream& text, const DescriptorProto& message, const string& indent) { @@ -167,8 +69,7 @@ write_message(stringstream& text, const DescriptorProto& message, const string& const string indented = indent + INDENT; text << indent << "// message " << message.name() << endl; - text << indent << "class " << message.name() << " {" << endl; - text << indent << "public:" << endl; + text << indent << "namespace " << message.name() << " {" << endl; // Enums N = message.enum_type_size(); @@ -188,12 +89,27 @@ write_message(stringstream& text, const DescriptorProto& message, const string& write_field(text, message.field(i), indented); } - text << indent << "};" << endl; + if (should_generate_fields_mapping(message)) { + N = message.field_size(); + text << indented << "const int _FIELD_COUNT = " << N << ";" << endl; + text << indented << "const char* _FIELD_NAMES[" << N << "] = {" << endl; + for (int i=0; i<N; i++) { + text << indented << INDENT << "\"" << message.field(i).name() << "\"," << endl; + } + text << indented << "};" << endl; + text << indented << "const uint64_t _FIELD_IDS[" << N << "] = {" << endl; + for (int i=0; i<N; i++) { + text << indented << INDENT << make_constant_name(message.field(i).name()) << "," << endl; + } + text << indented << "};" << endl << endl; + } + + text << indent << "} //" << message.name() << endl; text << endl; } static void -write_cpp_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor) +write_header_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor) { stringstream text; @@ -255,7 +171,7 @@ int main(int argc, char const *argv[]) for (int i=0; i<N; i++) { const FileDescriptorProto& file_descriptor = request.proto_file(i); if (should_generate_for_file(request, file_descriptor.name())) { - write_cpp_file(&response, file_descriptor); + write_header_file(&response, file_descriptor); } } @@ -270,4 +186,4 @@ int main(int argc, char const *argv[]) /* code */ return 0; -}
\ No newline at end of file +} 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 b/tools/streaming_proto/stream.proto new file mode 100644 index 000000000000..123506c03cfd --- /dev/null +++ b/tools/streaming_proto/stream.proto @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +import "google/protobuf/descriptor.proto"; + +package android.stream_proto; + +// This option tells streaming proto plugin to compile .proto files with extra features. +message StreamFlags { + // creates a mapping of field names of the message to its field ids + optional bool enable_fields_mapping = 1; +} + +extend google.protobuf.MessageOptions { + // Flags used by streaming proto plugins + optional StreamFlags stream = 126856794; +} 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. |