diff options
251 files changed, 8160 insertions, 2256 deletions
diff --git a/Android.bp b/Android.bp index 1caa4977a168..037c29c39cb0 100644 --- a/Android.bp +++ b/Android.bp @@ -462,6 +462,8 @@ java_library { "media/java/android/media/session/ISessionController.aidl", "media/java/android/media/session/ISessionControllerCallback.aidl", "media/java/android/media/session/ISessionManager.aidl", + "media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl", + "media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl", "media/java/android/media/tv/ITvInputClient.aidl", "media/java/android/media/tv/ITvInputHardware.aidl", "media/java/android/media/tv/ITvInputHardwareCallback.aidl", diff --git a/Android.mk b/Android.mk index e2f88e8990bd..ee8fbe0f5ad1 100644 --- a/Android.mk +++ b/Android.mk @@ -861,39 +861,42 @@ include $(BUILD_STATIC_JAVA_LIBRARY) # ==== hiddenapi lists ======================================= -# Copy blacklist and light greylist over into the build folder. +# Copy light and dark greylist over into the build folder. # This is for ART buildbots which need to mock these lists and have alternative # rules for building them. Other rules in the build system should depend on the # files in the build folder. -$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\ - $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))) - # Temporarily merge light greylist from two files. Vendor list will become dark # grey once we remove the UI toast. $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): frameworks/base/config/hiddenapi-light-greylist.txt \ frameworks/base/config/hiddenapi-vendor-list.txt sort $^ > $@ +$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\ + $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))) + # Generate dark greylist as private API minus (blacklist plus light greylist). -$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) -$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) -$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) -$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \ - $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \ - $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) - if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(LIGHT_GREYLIST))`" ]; then \ - echo "There should be no overlap between $(BLACKLIST) and $(LIGHT_GREYLIST)" 1>&2; \ +$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) +$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) +$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) +$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \ + $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \ + $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) + if [ ! -z "`comm -12 <(sort $(LIGHT_GREYLIST)) <(sort $(DARK_GREYLIST))`" ]; then \ + echo "There should be no overlap between $(LIGHT_GREYLIST) and $(DARK_GREYLIST)" 1>&2; \ + comm -12 <(sort $(LIGHT_GREYLIST)) <(sort $(DARK_GREYLIST)) 1>&2; \ exit 1; \ - elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST))`" ]; then \ - echo "$(BLACKLIST) must be a subset of $(PRIVATE_API)" 1>&2; \ - exit 2; \ elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST))`" ]; then \ echo "$(LIGHT_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \ + comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST)) 1>&2; \ + exit 2; \ + elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST))`" ]; then \ + echo "$(DARK_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \ + comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST)) 1>&2; \ exit 3; \ fi - comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(LIGHT_GREYLIST)) > $@ + comm -23 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST) $(DARK_GREYLIST)) > $@ # Include subdirectory makefiles # ============================================================ diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java index 73e17242ae78..ccbccca87b69 100644 --- a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java +++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java @@ -119,8 +119,12 @@ public class PrecomputedTextMemoryUsageTest { // Report median of randomly generated PrecomputedText. for (int i = 0; i < TRIAL_COUNT; ++i) { CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); - memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length()) - .getMemoryUsage(); + PrecomputedText.ParagraphInfo[] paragraphInfo = + PrecomputedText.createMeasuredParagraphs(cs, param, 0, cs.length(), false); + memories[i] = 0; + for (PrecomputedText.ParagraphInfo info : paragraphInfo) { + memories[i] += info.measured.getMemoryUsage(); + } } reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly"); } @@ -136,8 +140,12 @@ public class PrecomputedTextMemoryUsageTest { // Report median of randomly generated PrecomputedText. for (int i = 0; i < TRIAL_COUNT; ++i) { CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); - memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length()) - .getMemoryUsage(); + PrecomputedText.ParagraphInfo[] paragraphInfo = + PrecomputedText.createMeasuredParagraphs(cs, param, 0, cs.length(), false); + memories[i] = 0; + for (PrecomputedText.ParagraphInfo info : paragraphInfo) { + memories[i] += info.measured.getMemoryUsage(); + } } reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly"); } diff --git a/api/current.txt b/api/current.txt index bbd5bbec4305..bfd60b56ebac 100644 --- a/api/current.txt +++ b/api/current.txt @@ -7193,6 +7193,7 @@ package android.app.slice { field public static final deprecated java.lang.String EXTRA_SLIDER_VALUE = "android.app.slice.extra.SLIDER_VALUE"; field public static final java.lang.String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE"; field public static final java.lang.String HINT_ACTIONS = "actions"; + field public static final java.lang.String HINT_ERROR = "error"; field public static final java.lang.String HINT_HORIZONTAL = "horizontal"; field public static final java.lang.String HINT_KEY_WORDS = "key_words"; field public static final java.lang.String HINT_LARGE = "large"; @@ -7218,29 +7219,22 @@ package android.app.slice { } public static class Slice.Builder { - ctor public Slice.Builder(android.net.Uri); + ctor public deprecated Slice.Builder(android.net.Uri); + ctor public Slice.Builder(android.net.Uri, android.app.slice.SliceSpec); ctor public Slice.Builder(android.app.slice.Slice.Builder); - method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice); method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice, java.lang.String); - method public android.app.slice.Slice.Builder addBundle(android.os.Bundle, java.lang.String, java.lang.String...); method public android.app.slice.Slice.Builder addBundle(android.os.Bundle, java.lang.String, java.util.List<java.lang.String>); - method public android.app.slice.Slice.Builder addHints(java.lang.String...); method public android.app.slice.Slice.Builder addHints(java.util.List<java.lang.String>); - method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.lang.String...); method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.util.List<java.lang.String>); - method public android.app.slice.Slice.Builder addInt(int, java.lang.String, java.lang.String...); method public android.app.slice.Slice.Builder addInt(int, java.lang.String, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addLong(long, java.lang.String, java.util.List<java.lang.String>); method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.util.List<java.lang.String>); - method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.lang.String...); - method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice); method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice, java.lang.String); - method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.lang.String...); method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.util.List<java.lang.String>); - method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.lang.String...); - method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.util.List<java.lang.String>); + method public deprecated android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.util.List<java.lang.String>); method public android.app.slice.Slice build(); method public android.app.slice.Slice.Builder setCallerNeeded(boolean); - method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec); + method public deprecated android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec); } public final class SliceItem implements android.os.Parcelable { @@ -7263,10 +7257,11 @@ package android.app.slice { field public static final java.lang.String FORMAT_BUNDLE = "bundle"; field public static final java.lang.String FORMAT_IMAGE = "image"; field public static final java.lang.String FORMAT_INT = "int"; + field public static final java.lang.String FORMAT_LONG = "long"; field public static final java.lang.String FORMAT_REMOTE_INPUT = "input"; field public static final java.lang.String FORMAT_SLICE = "slice"; field public static final java.lang.String FORMAT_TEXT = "text"; - field public static final java.lang.String FORMAT_TIMESTAMP = "timestamp"; + field public static final deprecated java.lang.String FORMAT_TIMESTAMP = "long"; } public class SliceManager { @@ -15812,6 +15807,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> INFO_SUPPORTED_HARDWARE_LEVEL; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.String> INFO_VERSION; field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size[]> JPEG_AVAILABLE_THUMBNAIL_SIZES; + field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_DISTORTION; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_FACING; field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INFO_AVAILABLE_APERTURES; field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INFO_AVAILABLE_FILTER_DENSITIES; @@ -15824,7 +15820,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_POSE_REFERENCE; field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_ROTATION; field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_TRANSLATION; - field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_RADIAL_DISTORTION; + field public static final deprecated android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_RADIAL_DISTORTION; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REPROCESS_MAX_CAPTURE_STALL; @@ -16282,6 +16278,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> JPEG_THUMBNAIL_QUALITY; field public static final android.hardware.camera2.CaptureResult.Key<android.util.Size> JPEG_THUMBNAIL_SIZE; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_APERTURE; + field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_DISTORTION; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FILTER_DENSITY; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FOCAL_LENGTH; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FOCUS_DISTANCE; @@ -16290,7 +16287,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE; field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_POSE_ROTATION; field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_POSE_TRANSLATION; - field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_RADIAL_DISTORTION; + field public static final deprecated android.hardware.camera2.CaptureResult.Key<float[]> LENS_RADIAL_DISTORTION; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_STATE; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> NOISE_REDUCTION_MODE; field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR; @@ -23791,10 +23788,8 @@ 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_COLUMNS = "grid-cols"; 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"; @@ -23823,6 +23818,8 @@ package android.media { field public static final java.lang.String KEY_SLICE_HEIGHT = "slice-height"; field public static final java.lang.String KEY_STRIDE = "stride"; field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema"; + field public static final java.lang.String KEY_TILE_HEIGHT = "tile-height"; + field public static final java.lang.String KEY_TILE_WIDTH = "tile-width"; field public static final java.lang.String KEY_TRACK_ID = "track-id"; field public static final java.lang.String KEY_WIDTH = "width"; field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; @@ -24072,12 +24069,16 @@ package android.media { method public java.lang.String extractMetadata(int); method public byte[] getEmbeddedPicture(); method public android.graphics.Bitmap getFrameAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams); + 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 java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int, android.media.MediaMetadataRetriever.BitmapParams); + method public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int); method public android.graphics.Bitmap getImageAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams); + method public android.graphics.Bitmap getImageAtIndex(int); method public android.graphics.Bitmap getPrimaryImage(android.media.MediaMetadataRetriever.BitmapParams); + 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; @@ -40056,6 +40057,7 @@ package android.service.notification { method public int getSuppressedVisualEffects(); method public int getUserSentiment(); method public boolean isAmbient(); + method public boolean isSuspended(); method public boolean matchesInterruptionFilter(); field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0 @@ -43352,21 +43354,21 @@ package android.telephony.data { public class ApnSetting implements android.os.Parcelable { method public int describeContents(); method public java.lang.String getApnName(); + method public int getApnTypeBitmask(); method public int getAuthType(); method public java.lang.String getEntryName(); method public int getId(); - method public int getMmsPort(); - method public java.net.InetAddress getMmsProxy(); - method public java.net.URL getMmsc(); - method public java.lang.String getMvnoType(); + method public java.net.InetAddress getMmsProxyAddress(); + method public int getMmsProxyPort(); + method public android.net.Uri getMmsc(); + method public int getMvnoType(); method public int getNetworkTypeBitmask(); method public java.lang.String getOperatorNumeric(); method public java.lang.String getPassword(); - method public int getPort(); - method public java.lang.String getProtocol(); - method public java.net.InetAddress getProxy(); - method public java.lang.String getRoamingProtocol(); - method public java.util.List<java.lang.String> getTypes(); + method public int getProtocol(); + method public java.net.InetAddress getProxyAddress(); + method public int getProxyPort(); + method public int getRoamingProtocol(); method public java.lang.String getUser(); method public boolean isEnabled(); method public void writeToParcel(android.os.Parcel, int); @@ -43375,46 +43377,45 @@ package android.telephony.data { field public static final int AUTH_TYPE_PAP = 1; // 0x1 field public static final int AUTH_TYPE_PAP_OR_CHAP = 3; // 0x3 field public static final android.os.Parcelable.Creator<android.telephony.data.ApnSetting> CREATOR; - field public static final java.lang.String MVNO_TYPE_GID = "gid"; - field public static final java.lang.String MVNO_TYPE_ICCID = "iccid"; - field public static final java.lang.String MVNO_TYPE_IMSI = "imsi"; - field public static final java.lang.String MVNO_TYPE_SPN = "spn"; - field public static final java.lang.String PROTOCOL_IP = "IP"; - field public static final java.lang.String PROTOCOL_IPV4V6 = "IPV4V6"; - field public static final java.lang.String PROTOCOL_IPV6 = "IPV6"; - field public static final java.lang.String PROTOCOL_PPP = "PPP"; - field public static final java.lang.String TYPE_ALL = "*"; - field public static final java.lang.String TYPE_CBS = "cbs"; - field public static final java.lang.String TYPE_DEFAULT = "default"; - field public static final java.lang.String TYPE_DUN = "dun"; - field public static final java.lang.String TYPE_EMERGENCY = "emergency"; - field public static final java.lang.String TYPE_FOTA = "fota"; - field public static final java.lang.String TYPE_HIPRI = "hipri"; - field public static final java.lang.String TYPE_IA = "ia"; - field public static final java.lang.String TYPE_IMS = "ims"; - field public static final java.lang.String TYPE_MMS = "mms"; - field public static final java.lang.String TYPE_SUPL = "supl"; + field public static final int MVNO_TYPE_GID = 2; // 0x2 + field public static final int MVNO_TYPE_ICCID = 3; // 0x3 + field public static final int MVNO_TYPE_IMSI = 1; // 0x1 + field public static final int MVNO_TYPE_SPN = 0; // 0x0 + field public static final int PROTOCOL_IP = 0; // 0x0 + field public static final int PROTOCOL_IPV4V6 = 2; // 0x2 + field public static final int PROTOCOL_IPV6 = 1; // 0x1 + field public static final int PROTOCOL_PPP = 3; // 0x3 + field public static final int TYPE_CBS = 128; // 0x80 + field public static final int TYPE_DEFAULT = 17; // 0x11 + field public static final int TYPE_DUN = 8; // 0x8 + field public static final int TYPE_EMERGENCY = 512; // 0x200 + field public static final int TYPE_FOTA = 32; // 0x20 + field public static final int TYPE_HIPRI = 16; // 0x10 + field public static final int TYPE_IA = 256; // 0x100 + field public static final int TYPE_IMS = 64; // 0x40 + field public static final int TYPE_MMS = 2; // 0x2 + field public static final int TYPE_SUPL = 4; // 0x4 } public static class ApnSetting.Builder { ctor public ApnSetting.Builder(); method public android.telephony.data.ApnSetting build(); method public android.telephony.data.ApnSetting.Builder setApnName(java.lang.String); + method public android.telephony.data.ApnSetting.Builder setApnTypeBitmask(int); method public android.telephony.data.ApnSetting.Builder setAuthType(int); method public android.telephony.data.ApnSetting.Builder setCarrierEnabled(boolean); method public android.telephony.data.ApnSetting.Builder setEntryName(java.lang.String); - method public android.telephony.data.ApnSetting.Builder setMmsPort(int); - method public android.telephony.data.ApnSetting.Builder setMmsProxy(java.net.InetAddress); - method public android.telephony.data.ApnSetting.Builder setMmsc(java.net.URL); - method public android.telephony.data.ApnSetting.Builder setMvnoType(java.lang.String); + method public android.telephony.data.ApnSetting.Builder setMmsProxyAddress(java.net.InetAddress); + method public android.telephony.data.ApnSetting.Builder setMmsProxyPort(int); + method public android.telephony.data.ApnSetting.Builder setMmsc(android.net.Uri); + method public android.telephony.data.ApnSetting.Builder setMvnoType(int); method public android.telephony.data.ApnSetting.Builder setNetworkTypeBitmask(int); method public android.telephony.data.ApnSetting.Builder setOperatorNumeric(java.lang.String); method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String); - method public android.telephony.data.ApnSetting.Builder setPort(int); - method public android.telephony.data.ApnSetting.Builder setProtocol(java.lang.String); - method public android.telephony.data.ApnSetting.Builder setProxy(java.net.InetAddress); - method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(java.lang.String); - method public android.telephony.data.ApnSetting.Builder setTypes(java.util.List<java.lang.String>); + method public android.telephony.data.ApnSetting.Builder setProtocol(int); + method public android.telephony.data.ApnSetting.Builder setProxyAddress(java.net.InetAddress); + method public android.telephony.data.ApnSetting.Builder setProxyPort(int); + method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(int); method public android.telephony.data.ApnSetting.Builder setUser(java.lang.String); } @@ -51591,7 +51592,6 @@ package android.webkit { field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4 field public static final int RECORD_CONTINUOUSLY = 1; // 0x1 field public static final int RECORD_UNTIL_FULL = 0; // 0x0 - field public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2; // 0x2 } public static class TracingConfig.Builder { @@ -51998,6 +51998,7 @@ package android.webkit { method public android.webkit.WebChromeClient getWebChromeClient(); method public static java.lang.ClassLoader getWebViewClassLoader(); method public android.webkit.WebViewClient getWebViewClient(); + method public android.os.Looper getWebViewLooper(); method public void goBack(); method public void goBackOrForward(int); method public void goForward(); diff --git a/api/system-current.txt b/api/system-current.txt index d1ed86e5787a..1e2fe3df0d2c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -32,6 +32,7 @@ package android { field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE"; field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE"; field public static final java.lang.String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE"; + field public static final java.lang.String BIND_SOUND_TRIGGER_DETECTION_SERVICE = "android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE"; field public static final java.lang.String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE"; field public static final java.lang.String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE"; field public static final java.lang.String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE"; @@ -99,6 +100,7 @@ package android { field public static final java.lang.String MANAGE_CARRIER_OEM_UNLOCK_STATE = "android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE"; field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES"; field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS"; + field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER"; field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS"; field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB"; field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS"; @@ -723,6 +725,8 @@ package android.app.usage { method public java.lang.String getNotificationChannelId(); field public static final int NOTIFICATION_INTERRUPTION = 12; // 0xc field public static final int NOTIFICATION_SEEN = 10; // 0xa + field public static final int SLICE_PINNED = 14; // 0xe + field public static final int SLICE_PINNED_PRIV = 13; // 0xd field public static final int SYSTEM_INTERACTION = 6; // 0x6 } @@ -1233,7 +1237,9 @@ package android.hardware.display { public final class DisplayManager { method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats(); + method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration(); method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents(); + method public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration(); method public android.graphics.Point getStableDisplaySize(); method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration); } @@ -2220,6 +2226,21 @@ package android.hardware.radio { } +package android.hardware.soundtrigger { + + public class SoundTrigger { + field public static final int STATUS_OK = 0; // 0x0 + } + + public static class SoundTrigger.RecognitionEvent { + method public android.media.AudioFormat getCaptureFormat(); + method public int getCaptureSession(); + method public byte[] getData(); + method public boolean isCaptureAvailable(); + } + +} + package android.hardware.usb { public class UsbDeviceConnection { @@ -2735,6 +2756,16 @@ package android.media.session { package android.media.soundtrigger { + public abstract class SoundTriggerDetectionService extends android.app.Service { + ctor public SoundTriggerDetectionService(); + method public void onConnected(java.util.UUID, android.os.Bundle); + method public void onDisconnected(java.util.UUID, android.os.Bundle); + method public void onError(java.util.UUID, android.os.Bundle, int, int); + method public void onGenericRecognitionEvent(java.util.UUID, android.os.Bundle, int, android.hardware.soundtrigger.SoundTrigger.RecognitionEvent); + method public abstract void onStopOperation(java.util.UUID, android.os.Bundle, int); + method public final void operationFinished(java.util.UUID, int); + } + public final class SoundTriggerDetector { method public boolean startRecognition(int); method public boolean stopRecognition(); @@ -2759,6 +2790,7 @@ package android.media.soundtrigger { public final class SoundTriggerManager { method public android.media.soundtrigger.SoundTriggerDetector createSoundTriggerDetector(java.util.UUID, android.media.soundtrigger.SoundTriggerDetector.Callback, android.os.Handler); method public void deleteModel(java.util.UUID); + method public int getDetectionServiceOperationsTimeout(); method public android.media.soundtrigger.SoundTriggerManager.Model getModel(java.util.UUID); method public void updateModel(android.media.soundtrigger.SoundTriggerManager.Model); } @@ -5443,6 +5475,7 @@ package android.telephony.euicc { public class EuiccManager { method public void continueOperation(android.content.Intent, android.os.Bundle); + method public void eraseSubscriptions(android.app.PendingIntent); method public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent); method public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent); method public int getOtaStatus(); diff --git a/api/test-current.txt b/api/test-current.txt index e8d27a1e876c..70e3cf34f6b0 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -370,7 +370,9 @@ package android.hardware.display { public final class DisplayManager { method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats(); + method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration(); method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents(); + method public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration(); method public android.graphics.Point getStableDisplaySize(); method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration); } diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 5d6a1d1a6c15..ca0611b0ca05 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -177,6 +177,7 @@ LOCAL_SRC_FILES := \ tests/LogEvent_test.cpp \ tests/MetricsManager_test.cpp \ tests/StatsLogProcessor_test.cpp \ + tests/StatsService_test.cpp \ tests/UidMap_test.cpp \ tests/FieldValue_test.cpp \ tests/condition/CombinationConditionTracker_test.cpp \ diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 8b681eddb605..82274a6e4c59 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -239,14 +239,13 @@ void StatsLogProcessor::dumpStates(FILE* out, bool verbose) { } } +/* + * onDumpReport dumps serialized ConfigMetricsReportList into outData. + */ void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t dumpTimeStampNs, vector<uint8_t>* outData) { std::lock_guard<std::mutex> lock(mMetricsMutex); - onDumpReportLocked(key, dumpTimeStampNs, outData); -} -void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeStampNs, - vector<uint8_t>* outData) { auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end()) { ALOGW("Config source %s does not exist", key.ToString().c_str()); @@ -269,35 +268,14 @@ void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, const uint64_t // Start of ConfigMetricsReport (reports). uint64_t reportsToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS); - - int64_t lastReportTimeNs = it->second->getLastReportTimeNs(); - int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs(); - - // First, fill in ConfigMetricsReport using current data on memory, which - // starts from filling in StatsLogReport's. - it->second->onDumpReport(dumpTimeStampNs, &proto); - - // Fill in UidMap. - vector<uint8_t> uidMap; - mUidMap->getOutput(key, &uidMap); - proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMap.data()); - - // Fill in the timestamps. - proto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS, - (long long)lastReportTimeNs); - proto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS, - (long long)dumpTimeStampNs); - proto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS, - (long long)lastReportWallClockNs); - proto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS, - (long long)getWallClockNs()); - - // End of ConfigMetricsReport (reports). + onConfigMetricsReportLocked(key, dumpTimeStampNs, &proto); proto.end(reportsToken); + // End of ConfigMetricsReport (reports). + // Then, check stats-data directory to see there's any file containing // ConfigMetricsReport from previous shutdowns to concatenate to reports. - StorageManager::appendConfigMetricsReport(proto); + StorageManager::appendConfigMetricsReport(key, &proto); if (outData != nullptr) { outData->clear(); @@ -315,6 +293,40 @@ void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, const uint64_t StatsdStats::getInstance().noteMetricsReportSent(key); } +/* + * onConfigMetricsReportLocked dumps serialized ConfigMetricsReport into outData. + */ +void StatsLogProcessor::onConfigMetricsReportLocked(const ConfigKey& key, + const uint64_t dumpTimeStampNs, + ProtoOutputStream* proto) { + // We already checked whether key exists in mMetricsManagers in + // WriteDataToDisk. + auto it = mMetricsManagers.find(key); + int64_t lastReportTimeNs = it->second->getLastReportTimeNs(); + int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs(); + + // First, fill in ConfigMetricsReport using current data on memory, which + // starts from filling in StatsLogReport's. + it->second->onDumpReport(dumpTimeStampNs, proto); + + // Fill in UidMap. + uint64_t uidMapToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP); + mUidMap->appendUidMap(key, proto); + proto->end(uidMapToken); + + // Fill in the timestamps. + proto->write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS, + (long long)lastReportTimeNs); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS, + (long long)dumpTimeStampNs); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS, + (long long)lastReportWallClockNs); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS, + (long long)getWallClockNs()); + + +} + void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { std::lock_guard<std::mutex> lock(mMetricsMutex); auto it = mMetricsManagers.find(key); @@ -368,11 +380,17 @@ void StatsLogProcessor::WriteDataToDisk() { std::lock_guard<std::mutex> lock(mMetricsMutex); for (auto& pair : mMetricsManagers) { const ConfigKey& key = pair.first; - vector<uint8_t> data; - onDumpReportLocked(key, getElapsedRealtimeNs(), &data); + ProtoOutputStream proto; + onConfigMetricsReportLocked(key, getElapsedRealtimeNs(), &proto); string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, (long)getWallClockSec(), key.GetUid(), (long long)key.GetId()); - StorageManager::writeFile(file_name.c_str(), &data[0], data.size()); + android::base::unique_fd fd(open(file_name.c_str(), + O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)); + if (fd == -1) { + VLOG("Attempt to write %s but failed", file_name.c_str()); + return; + } + proto.flush(fd.get()); } } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 8b42146c6621..1be4dc5d58c7 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -86,8 +86,8 @@ private: sp<AlarmMonitor> mPeriodicAlarmMonitor; - void onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeNs, - vector<uint8_t>* outData); + void onConfigMetricsReportLocked(const ConfigKey& key, const uint64_t dumpTimeStampNs, + util::ProtoOutputStream* proto); /* Check if we should send a broadcast if approaching memory limits and if we're over, we * actually delete the data. */ diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index c5dfc4488c1b..b86646a32ac8 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -867,14 +867,11 @@ Status StatsService::addConfiguration(int64_t key, bool* success) { IPCThreadState* ipc = IPCThreadState::self(); if (checkCallingPermission(String16(kPermissionDump))) { - ConfigKey configKey(ipc->getCallingUid(), key); - StatsdConfig cfg; - if (config.empty() || !cfg.ParseFromArray(&config[0], config.size())) { + if (addConfigurationChecked(ipc->getCallingUid(), key, config)) { + *success = true; + } else { *success = false; - return Status::ok(); } - mConfigManager->UpdateConfig(configKey, cfg); - *success = true; return Status::ok(); } else { *success = false; @@ -882,6 +879,18 @@ Status StatsService::addConfiguration(int64_t key, } } +bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config) { + ConfigKey configKey(uid, key); + StatsdConfig cfg; + if (config.size() > 0) { // If the config is empty, skip parsing. + if (!cfg.ParseFromArray(&config[0], config.size())) { + return false; + } + } + mConfigManager->UpdateConfig(configKey, cfg); + return true; +} + Status StatsService::removeDataFetchOperation(int64_t key, bool* success) { IPCThreadState* ipc = IPCThreadState::self(); if (checkCallingPermission(String16(kPermissionDump))) { diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 02b61244b3bb..0ec31ef81c94 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -17,6 +17,7 @@ #ifndef STATS_SERVICE_H #define STATS_SERVICE_H +#include <gtest/gtest_prod.h> #include "StatsLogProcessor.h" #include "anomaly/AlarmMonitor.h" #include "config/ConfigManager.h" @@ -216,6 +217,11 @@ private: status_t cmd_clear_puller_cache(FILE* out); /** + * Adds a configuration after checking permissions and obtaining UID from binder call. + */ + bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config); + + /** * Update a configuration. */ void set_config(int uid, const string& name, const StatsdConfig& config); @@ -254,6 +260,10 @@ private: * Whether this is an eng build. */ bool mEngBuild; + + FRIEND_TEST(StatsServiceTest, TestAddConfig_simple); + FRIEND_TEST(StatsServiceTest, TestAddConfig_empty); + FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid); }; } // namespace statsd diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp index 72a00cb6834e..fb0be733bc74 100644 --- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp +++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp @@ -166,6 +166,11 @@ void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> re // Round it to the nearest minutes. This is the limit of alarm manager. // In practice, we should limit it higher. long roundedIntervalMs = intervalMs/1000/60 * 1000 * 60; + // Scheduled pulling should be at least 1 min apart. + // This can be lower in cts tests, in which case we round it to 1 min. + if (roundedIntervalMs < 60 * 1000) { + roundedIntervalMs = 60 * 1000; + } // There is only one alarm for all pulled events. So only set it to the smallest denom. if (roundedIntervalMs < mCurrentPullingInterval) { VLOG("Updating pulling interval %ld", intervalMs); diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index e97d8b2af1ae..3ba4b7a20fbe 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -331,25 +331,25 @@ size_t UidMap::getBytesUsed() const { return mBytesUsed; } -void UidMap::getOutput(const ConfigKey& key, vector<uint8_t>* outData) { - getOutput(getElapsedRealtimeNs(), key, outData); +void UidMap::appendUidMap(const ConfigKey& key, ProtoOutputStream* proto) { + appendUidMap(getElapsedRealtimeNs(), key, proto); } -void UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key, vector<uint8_t>* outData) { +void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, + ProtoOutputStream* proto) { lock_guard<mutex> lock(mMutex); // Lock for updates - ProtoOutputStream proto; for (const ChangeRecord& record : mChanges) { if (record.timestampNs > mLastUpdatePerConfigKey[key]) { uint64_t changesToken = - proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CHANGES); - proto.write(FIELD_TYPE_BOOL | FIELD_ID_CHANGE_DELETION, (bool)record.deletion); - proto.write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_TIMESTAMP, - (long long)record.timestampNs); - proto.write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_VERSION, (int)record.version); - proto.end(changesToken); + proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CHANGES); + proto->write(FIELD_TYPE_BOOL | FIELD_ID_CHANGE_DELETION, (bool)record.deletion); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_TIMESTAMP, + (long long)record.timestampNs); + proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_VERSION, (int)record.version); + proto->end(changesToken); } } @@ -360,13 +360,13 @@ void UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key, vector<ui if ((count == mSnapshots.size() - 1 && !atLeastOneSnapshot) || record.timestampNs > mLastUpdatePerConfigKey[key]) { uint64_t snapshotsToken = - proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS); + proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS); atLeastOneSnapshot = true; count++; - proto.write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, - (long long)record.timestampNs); - proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_SNAPSHOT_PACKAGE_INFO, record.bytes.data()); - proto.end(snapshotsToken); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, + (long long)record.timestampNs); + proto->write(FIELD_TYPE_MESSAGE | FIELD_ID_SNAPSHOT_PACKAGE_INFO, record.bytes.data()); + proto->end(snapshotsToken); } } @@ -398,47 +398,36 @@ void UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key, vector<ui // Produce another snapshot. This results in extra data being uploaded but // helps ensure we can re-construct the UID->app name, versionCode mapping // in server. - ProtoOutputStream proto; - uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_SNAPSHOT_PACKAGE_INFO); + ProtoOutputStream snapshotProto; + uint64_t token = snapshotProto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_SNAPSHOT_PACKAGE_INFO); for (const auto& it : mMap) { - proto.write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, - it.second.packageName); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION, - (int)it.second.versionCode); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, (int)it.first); + snapshotProto.write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, + it.second.packageName); + snapshotProto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION, + (int)it.second.versionCode); + snapshotProto.write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, + (int)it.first); } - proto.end(token); + snapshotProto.end(token); // Copy ProtoOutputStream output to - auto iter = proto.data(); - vector<char> outData(proto.size()); + auto iter = snapshotProto.data(); + vector<char> snapshotData(snapshotProto.size()); size_t pos = 0; while (iter.readBuffer() != NULL) { size_t toRead = iter.currentToRead(); - std::memcpy(&(outData[pos]), iter.readBuffer(), toRead); + std::memcpy(&(snapshotData[pos]), iter.readBuffer(), toRead); pos += toRead; iter.rp()->move(toRead); } - mSnapshots.emplace_back(timestamp, outData); - mBytesUsed += kBytesTimestampField + outData.size(); + mSnapshots.emplace_back(timestamp, snapshotData); + mBytesUsed += kBytesTimestampField + snapshotData.size(); } } StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); StatsdStats::getInstance().setUidMapChanges(mChanges.size()); StatsdStats::getInstance().setUidMapSnapshots(mSnapshots.size()); - if (outData != nullptr) { - outData->clear(); - outData->resize(proto.size()); - size_t pos = 0; - auto iter = proto.data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&((*outData)[pos]), iter.readBuffer(), toRead); - pos += toRead; - iter.rp()->move(toRead); - } - } } void UidMap::printUidMap(FILE* out) const { diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index b0181f7280b5..9dc73f4859c6 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -19,6 +19,7 @@ #include "config/ConfigKey.h" #include "config/ConfigListener.h" #include "packages/PackageInfoListener.h" +#include "stats_util.h" #include <binder/IResultReceiver.h> #include <binder/IShellCallback.h> @@ -32,8 +33,11 @@ #include <string> #include <unordered_map> +using namespace android; using namespace std; +using android::util::ProtoOutputStream; + namespace android { namespace os { namespace statsd { @@ -45,8 +49,8 @@ struct AppData { AppData(const string& a, const int64_t v) : packageName(a), versionCode(v){}; }; -// When calling getOutput, we retrieve all the ChangeRecords since the last -// timestamp we called getOutput for this configuration key. +// When calling appendUidMap, we retrieve all the ChangeRecords since the last +// timestamp we called appendUidMap for this configuration key. struct ChangeRecord { const bool deletion; const int64_t timestampNs; @@ -70,8 +74,8 @@ const unsigned int kBytesChangeRecord = sizeof(struct ChangeRecord); // less because of varint encoding). const unsigned int kBytesTimestampField = 10; -// When calling getOutput, we retrieve all the snapshots since the last -// timestamp we called getOutput for this configuration key. +// When calling appendUidMap, we retrieve all the snapshots since the last +// timestamp we called appendUidMap for this configuration key. struct SnapshotRecord { const int64_t timestampNs; @@ -135,7 +139,7 @@ public: // Gets all snapshots and changes that have occurred since the last output. // If every config key has received a change or snapshot record, then this // record is deleted. - void getOutput(const ConfigKey& key, vector<uint8_t>* outData); + void appendUidMap(const ConfigKey& key, util::ProtoOutputStream* proto); // 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 @@ -158,7 +162,8 @@ private: const int64_t& versionCode); void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid); - void getOutput(const int64_t& timestamp, const ConfigKey& key, vector<uint8_t>* outData); + void appendUidMap(const int64_t& timestamp, const ConfigKey& key, + util::ProtoOutputStream* proto); void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output); diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index cd41f533fecf..3d8aa475c11b 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -160,38 +160,46 @@ void StorageManager::sendBroadcast(const char* path, } } -void StorageManager::appendConfigMetricsReport(ProtoOutputStream& proto) { +void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto) { unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir); if (dir == NULL) { VLOG("Path %s does not exist", STATS_DATA_DIR); return; } + const char* suffix = + StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId()).c_str(); + dirent* de; while ((de = readdir(dir.get()))) { char* name = de->d_name; if (name[0] == '.') continue; - VLOG("file %s", name); - int64_t result[3]; - parseFileName(name, result); - if (result[0] == -1) continue; - int64_t timestamp = result[0]; - int64_t uid = result[1]; - int64_t configID = result[2]; - string file_name = getFilePath(STATS_DATA_DIR, timestamp, uid, configID); - int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); - if (fd != -1) { - string content; - if (android::base::ReadFdToString(fd, &content)) { - proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS, - content.c_str()); + size_t nameLen = strlen(name); + size_t suffixLen = strlen(suffix); + if (suffixLen <= nameLen && + strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) { + int64_t result[3]; + parseFileName(name, result); + if (result[0] == -1) continue; + int64_t timestamp = result[0]; + int64_t uid = result[1]; + int64_t configID = result[2]; + + string file_name = getFilePath(STATS_DATA_DIR, timestamp, uid, configID); + int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); + if (fd != -1) { + string content; + if (android::base::ReadFdToString(fd, &content)) { + proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS, + content.c_str(), content.size()); + } + close(fd); } - close(fd); - } - // Remove file from disk after reading. - remove(file_name.c_str()); + // Remove file from disk after reading. + remove(file_name.c_str()); + } } } @@ -275,9 +283,11 @@ bool StorageManager::hasIdenticalConfig(const ConfigKey& key, if (android::base::ReadFdToString(fd, &content)) { vector<uint8_t> vec(content.begin(), content.end()); if (vec == config) { + close(fd); return true; } } + close(fd); } } } diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h index 4b7562855f30..fb7b0853ec4c 100644 --- a/cmds/statsd/src/storage/StorageManager.h +++ b/cmds/statsd/src/storage/StorageManager.h @@ -66,7 +66,7 @@ public: * Appends ConfigMetricsReport found on disk to the specific proto and * delete it. */ - static void appendConfigMetricsReport(ProtoOutputStream& proto); + static void appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto); /** * Call to load the saved configs from disk. diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp new file mode 100644 index 000000000000..a7b413666266 --- /dev/null +++ b/cmds/statsd/tests/StatsService_test.cpp @@ -0,0 +1,67 @@ +// 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 "StatsService.h" +#include "config/ConfigKey.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <stdio.h> + +using namespace android; +using namespace testing; + +namespace android { +namespace os { +namespace statsd { + +using android::util::ProtoOutputStream; + +#ifdef __ANDROID__ + +TEST(StatsServiceTest, TestAddConfig_simple) { + StatsService service(nullptr); + StatsdConfig config; + config.set_id(12345); + string serialized = config.SerializeAsString(); + + EXPECT_TRUE( + service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()})); +} + +TEST(StatsServiceTest, TestAddConfig_empty) { + StatsService service(nullptr); + string serialized = ""; + + EXPECT_TRUE( + service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()})); +} + +TEST(StatsServiceTest, TestAddConfig_invalid) { + StatsService service(nullptr); + string serialized = "Invalid config!"; + + EXPECT_FALSE( + service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()})); +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index ee7d770ec53d..c9492eb609f9 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -20,6 +20,7 @@ #include "statslog.h" #include "statsd_test_util.h" +#include <android/util/ProtoOutputStream.h> #include <gtest/gtest.h> #include <stdio.h> @@ -30,6 +31,8 @@ namespace android { namespace os { namespace statsd { +using android::util::ProtoOutputStream; + #ifdef __ANDROID__ const string kApp1 = "app1.sharing.1"; const string kApp2 = "app2.sharing.1"; @@ -156,6 +159,20 @@ TEST(UidMapTest, TestUpdateApp) { EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end()); } +static void protoOutputStreamToUidMapping(ProtoOutputStream* proto, UidMapping* results) { + vector<uint8_t> bytes; + bytes.resize(proto->size()); + size_t pos = 0; + auto iter = proto->data(); + while (iter.readBuffer() != NULL) { + size_t toRead = iter.currentToRead(); + std::memcpy(&((bytes)[pos]), iter.readBuffer(), toRead); + pos += toRead; + iter.rp()->move(toRead); + } + results->ParseFromArray(bytes.data(), bytes.size()); +} + TEST(UidMapTest, TestClearingOutput) { UidMap m; @@ -176,26 +193,26 @@ TEST(UidMapTest, TestClearingOutput) { m.updateMap(1, uids, versions, apps); EXPECT_EQ(1U, m.mSnapshots.size()); - vector<uint8_t> bytes; - m.getOutput(2, config1, &bytes); + ProtoOutputStream proto; + m.appendUidMap(2, config1, &proto); UidMapping results; - results.ParseFromArray(bytes.data(), bytes.size()); + protoOutputStreamToUidMapping(&proto, &results); EXPECT_EQ(1, results.snapshots_size()); // It should be cleared now EXPECT_EQ(1U, m.mSnapshots.size()); - bytes.clear(); - m.getOutput(2, config1, &bytes); - results.ParseFromArray(bytes.data(), bytes.size()); + proto.clear(); + m.appendUidMap(2, config1, &proto); + protoOutputStreamToUidMapping(&proto, &results); EXPECT_EQ(1, results.snapshots_size()); // Now add another configuration. m.OnConfigUpdated(config2); m.updateApp(5, String16(kApp1.c_str()), 1000, 40); EXPECT_EQ(1U, m.mChanges.size()); - bytes.clear(); - m.getOutput(6, config1, &bytes); - results.ParseFromArray(bytes.data(), bytes.size()); + proto.clear(); + m.appendUidMap(6, config1, &proto); + protoOutputStreamToUidMapping(&proto, &results); EXPECT_EQ(1, results.snapshots_size()); EXPECT_EQ(1, results.changes_size()); EXPECT_EQ(1U, m.mChanges.size()); @@ -205,16 +222,16 @@ TEST(UidMapTest, TestClearingOutput) { EXPECT_EQ(2U, m.mChanges.size()); // We still can't remove anything. - bytes.clear(); - m.getOutput(8, config1, &bytes); - results.ParseFromArray(bytes.data(), bytes.size()); + proto.clear(); + m.appendUidMap(8, config1, &proto); + protoOutputStreamToUidMapping(&proto, &results); EXPECT_EQ(1, results.snapshots_size()); EXPECT_EQ(1, results.changes_size()); EXPECT_EQ(2U, m.mChanges.size()); - bytes.clear(); - m.getOutput(9, config2, &bytes); - results.ParseFromArray(bytes.data(), bytes.size()); + proto.clear(); + m.appendUidMap(9, config2, &proto); + protoOutputStreamToUidMapping(&proto, &results); EXPECT_EQ(1, results.snapshots_size()); EXPECT_EQ(2, results.changes_size()); // At this point both should be cleared. @@ -242,11 +259,12 @@ TEST(UidMapTest, TestMemoryComputed) { m.updateApp(3, String16(kApp1.c_str()), 1000, 40); EXPECT_TRUE(m.mBytesUsed > snapshot_bytes); + ProtoOutputStream proto; vector<uint8_t> bytes; - m.getOutput(2, config1, &bytes); + m.appendUidMap(2, config1, &proto); size_t prevBytes = m.mBytesUsed; - m.getOutput(4, config1, &bytes); + m.appendUidMap(4, config1, &proto); EXPECT_TRUE(m.mBytesUsed < prevBytes); } @@ -286,4 +304,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n"; } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index e2121dd48679..e238f0609da5 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -20,8 +20,20 @@ Landroid/app/ActivityManagerNative;->getDefault()Landroid/app/IActivityManager; Landroid/app/ActivityManager;->PROCESS_STATE_IMPORTANT_BACKGROUND:I Landroid/app/ActivityManager;->PROCESS_STATE_TOP:I Landroid/app/ActivityManager$RecentTaskInfo;->firstActiveTime:J +Landroid/app/ActivityManager$RecentTaskInfo;->lastActiveTime:J +Landroid/app/ActivityManager$RecentTaskInfo;->resizeMode:I +Landroid/app/ActivityManager$RecentTaskInfo;->supportsSplitScreenMultiWindow:Z +Landroid/app/ActivityManager$RecentTaskInfo;->userId:I Landroid/app/ActivityManager$RunningAppProcessInfo;->flags:I Landroid/app/ActivityManager$RunningAppProcessInfo;->processState:I +Landroid/app/ActivityManager;->setPersistentVrThread(I)V +Landroid/app/ActivityManager$TaskDescription;->getBackgroundColor()I +Landroid/app/ActivityManager$TaskDescription;->getInMemoryIcon()Landroid/graphics/Bitmap; +Landroid/app/ActivityManager$TaskSnapshot;->getContentInsets()Landroid/graphics/Rect; +Landroid/app/ActivityManager$TaskSnapshot;->getOrientation()I +Landroid/app/ActivityManager$TaskSnapshot;->getScale()F +Landroid/app/ActivityManager$TaskSnapshot;->isRealSnapshot()Z +Landroid/app/ActivityManager$TaskSnapshot;->isReducedResolution()Z Landroid/app/Activity;->mApplication:Landroid/app/Application; Landroid/app/Activity;->mComponent:Landroid/content/ComponentName; Landroid/app/Activity;->mFragments:Landroid/app/FragmentController; @@ -35,6 +47,7 @@ Landroid/app/Activity;->mResumed:Z Landroid/app/Activity;->mToken:Landroid/os/IBinder; Landroid/app/Activity;->mWindow:Landroid/view/Window; Landroid/app/Activity;->mWindowManager:Landroid/view/WindowManager; +Landroid/app/ActivityOptions;->makeMultiThumbFutureAspectScaleAnimation(Landroid/content/Context;Landroid/os/Handler;Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/app/ActivityOptions$OnAnimationStartedListener;Z)Landroid/app/ActivityOptions; Landroid/app/Activity;->setDisablePreviewScreenshots(Z)V Landroid/app/Activity;->setPersistent(Z)V Landroid/app/ActivityThread$ActivityClientRecord;->activityInfo:Landroid/content/pm/ActivityInfo; @@ -317,6 +330,13 @@ Landroid/app/trust/ITrustManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/app/usage/UsageStatsManager;->mService:Landroid/app/usage/IUsageStatsManager; Landroid/app/usage/UsageStats;->mLastEvent:I Landroid/app/usage/UsageStats;->mTotalTimeInForeground:J +Landroid/app/Vr2dDisplayProperties$Builder;->build()Landroid/app/Vr2dDisplayProperties; +Landroid/app/Vr2dDisplayProperties$Builder;-><init>()V +Landroid/app/Vr2dDisplayProperties$Builder;->setEnabled(Z)Landroid/app/Vr2dDisplayProperties$Builder; +Landroid/app/Vr2dDisplayProperties;-><init>(III)V +Landroid/app/VrManager;->getPersistentVrModeEnabled()Z +Landroid/app/VrManager;->registerVrStateCallback(Landroid/app/VrStateCallback;Landroid/os/Handler;)V +Landroid/app/VrManager;->setVr2dDisplayProperties(Landroid/app/Vr2dDisplayProperties;)V Landroid/app/WallpaperColors;->getColorHints()I Landroid/app/WallpaperManager;->getBitmap()Landroid/graphics/Bitmap; Landroid/app/WallpaperManager;->getBitmap(Z)Landroid/graphics/Bitmap; @@ -497,6 +517,7 @@ Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/Inp Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z Landroid/content/res/AssetManager;->setConfiguration(IILjava/lang/String;IIIIIIIIIIIIIII)V Landroid/content/res/ColorStateList$ColorStateListFactory;-><init>(Landroid/content/res/ColorStateList;)V +Landroid/content/res/ColorStateList;->getColors()[I Landroid/content/res/ColorStateList;->mColors:[I Landroid/content/res/ColorStateList;->mDefaultColor:I Landroid/content/res/ColorStateList;->mFactory:Landroid/content/res/ColorStateList$ColorStateListFactory; @@ -563,6 +584,7 @@ Landroid/database/sqlite/SQLiteDebug$PagerStats;->memoryUsed:I Landroid/database/sqlite/SQLiteDebug$PagerStats;->pageCacheOverflow:I Landroid/database/sqlite/SQLiteOpenHelper;->mName:Ljava/lang/String; Landroid/ddm/DdmHandleAppName;->getAppName()Ljava/lang/String; +Landroid/graphics/AvoidXfermode$Mode;->TARGET:Landroid/graphics/AvoidXfermode$Mode; Landroid/graphics/BaseCanvas;->mNativeCanvasWrapper:J Landroid/graphics/Bitmap$Config;->nativeInt:I Landroid/graphics/Bitmap$Config;->nativeToConfig(I)Landroid/graphics/Bitmap$Config; @@ -643,6 +665,7 @@ Landroid/graphics/FontFamily;-><init>()V Landroid/graphics/fonts/FontVariationAxis;->mStyleValue:F Landroid/graphics/fonts/FontVariationAxis;->mTag:I Landroid/graphics/GraphicBuffer;->createFromExisting(IIIIJ)Landroid/graphics/GraphicBuffer; +Landroid/graphics/GraphicBuffer;->CREATOR:Landroid/os/Parcelable$Creator; Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V Landroid/graphics/GraphicBuffer;->mNativeObject:J Landroid/graphics/ImageDecoder;-><init>(JIIZ)V @@ -671,8 +694,13 @@ Landroid/graphics/Typeface;->sDefaults:[Landroid/graphics/Typeface; Landroid/graphics/Typeface;->setDefault(Landroid/graphics/Typeface;)V Landroid/graphics/Typeface;->sSystemFontMap:Ljava/util/Map; Landroid/hardware/camera2/CameraCharacteristics$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V +Landroid/hardware/camera2/CameraCharacteristics$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V Landroid/hardware/camera2/CaptureResult$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;J)V +Landroid/hardware/camera2/CaptureResult$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V +Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_TIMESTAMPS:Landroid/hardware/camera2/CaptureResult$Key; +Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_X_SHIFTS:Landroid/hardware/camera2/CaptureResult$Key; +Landroid/hardware/camera2/CaptureResult;->STATISTICS_OIS_Y_SHIFTS:Landroid/hardware/camera2/CaptureResult$Key; Landroid/hardware/camera2/impl/CameraMetadataNative;->mMetadataPtr:J Landroid/hardware/Camera;->addCallbackBuffer([BI)V Landroid/hardware/Camera;->mNativeContext:J @@ -829,6 +857,7 @@ Landroid/media/AudioHandle;->mId:I Landroid/media/AudioManager;->forceVolumeControlStream(I)V Landroid/media/AudioManager;->getOutputLatency(I)I Landroid/media/AudioManager;->getService()Landroid/media/IAudioService; +Landroid/media/AudioManager;-><init>(Landroid/content/Context;)V Landroid/media/AudioManager;->mAudioFocusIdListenerMap:Ljava/util/concurrent/ConcurrentHashMap; Landroid/media/AudioManager;->setMasterMute(ZI)V Landroid/media/AudioManager;->startBluetoothScoVirtualCall()V @@ -891,6 +920,7 @@ Landroid/media/IAudioService;->getStreamVolume(I)I Landroid/media/IAudioService;->setStreamVolume(IIILjava/lang/String;)V Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService; Landroid/media/IAudioService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V +Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController; Landroid/media/JetPlayer;->mNativePlayerInJavaObj:J Landroid/media/JetPlayer;->postEventFromNative(Ljava/lang/Object;III)V Landroid/media/MediaCodec$CodecException;-><init>(IILjava/lang/String;)V @@ -1128,6 +1158,7 @@ Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Ti Landroid/os/Binder;->execTransact(IJJI)Z Landroid/os/Binder;->mObject:J Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String; +Landroid/os/Build;->IS_DEBUGGABLE:Z Landroid/os/Build$VERSION;->ACTIVE_CODENAMES:[Ljava/lang/String; Landroid/os/Bundle;->getIBinder(Ljava/lang/String;)Landroid/os/IBinder; Landroid/os/Bundle;->putIBinder(Ljava/lang/String;Landroid/os/IBinder;)V @@ -1212,8 +1243,10 @@ Landroid/os/Message;->when:J Landroid/os/ParcelFileDescriptor;-><init>(Ljava/io/FileDescriptor;)V Landroid/os/Parcel;->mNativePtr:J Landroid/os/Parcel;->readArrayMap(Landroid/util/ArrayMap;Ljava/lang/ClassLoader;)V +Landroid/os/Parcel;->readParcelableList(Ljava/util/List;Ljava/lang/ClassLoader;)Ljava/util/List; Landroid/os/Parcel$ReadWriteHelper;-><init>()V Landroid/os/Parcel;->writeArrayMap(Landroid/util/ArrayMap;)V +Landroid/os/Parcel;->writeParcelableList(Ljava/util/List;I)V Landroid/os/PowerManager;->getDefaultScreenBrightnessSetting()I Landroid/os/PowerManager;->getMaximumScreenBrightnessSetting()I Landroid/os/PowerManager;->getMinimumScreenBrightnessSetting()I @@ -1584,6 +1617,8 @@ Landroid/R$styleable;->View_filterTouchesWhenObscured:I Landroid/R$styleable;->View_fitsSystemWindows:I Landroid/R$styleable;->View_focusable:I Landroid/R$styleable;->View_focusableInTouchMode:I +Landroid/R$styleable;->ViewGroup_Layout:[I +Landroid/R$styleable;->ViewGroup_MarginLayout:[I Landroid/R$styleable;->View_hapticFeedbackEnabled:I Landroid/R$styleable;->View:[I Landroid/R$styleable;->View_id:I @@ -1673,6 +1708,7 @@ Landroid/service/notification/NotificationListenerService;->registerAsSystemServ Landroid/service/notification/NotificationListenerService;->unregisterAsSystemService()V Landroid/service/voice/AlwaysOnHotwordDetector$EventPayload;->getCaptureSession()Ljava/lang/Integer; Landroid/service/voice/VoiceInteractionService;->isKeyphraseAndLocaleSupportedForHotword(Ljava/lang/String;Ljava/util/Locale;)Z +Landroid/service/vr/IVrManager;->getVr2dDisplayId()I Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String; Landroid/system/Int32Ref;->value:I @@ -1814,6 +1850,7 @@ Landroid/text/DynamicLayout;-><init>(Ljava/lang/CharSequence;Ljava/lang/CharSequ Landroid/text/DynamicLayout;->sStaticLayout:Landroid/text/StaticLayout; Landroid/text/Html;->withinStyle(Ljava/lang/StringBuilder;Ljava/lang/CharSequence;II)V Landroid/text/Layout;->DIRS_ALL_LEFT_TO_RIGHT:Landroid/text/Layout$Directions; +Landroid/text/Layout;->getPrimaryHorizontal(IZ)F Landroid/text/method/LinkMovementMethod;->sInstance:Landroid/text/method/LinkMovementMethod; Landroid/text/SpannableStringBuilder;->mGapLength:I Landroid/text/SpannableStringBuilder;->mGapStart:I @@ -1962,9 +1999,11 @@ Landroid/view/inputmethod/InputMethodManager;->sInstance:Landroid/view/inputmeth Landroid/view/inputmethod/InputMethodManager;->windowDismissed(Landroid/os/IBinder;)V Landroid/view/inputmethod/InputMethodSubtypeArray;-><init>(Ljava/util/List;)V Landroid/view/InputQueue;->finishInputEvent(JZ)V +Landroid/view/IRecentsAnimationController;->setAnimationTargetsBehindSystemBars(Z)V Landroid/view/IWindowManager;->getAnimationScale(I)F Landroid/view/IWindowManager;->hasNavigationBar()Z Landroid/view/IWindowManager;->setAnimationScale(IF)V +Landroid/view/IWindowManager;->setShelfHeight(ZI)V Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager; Landroid/view/IWindowManager$Stub$Proxy;->getBaseDisplayDensity(I)I @@ -2012,6 +2051,18 @@ Landroid/view/PointerIcon;->mDurationPerFrame:I Landroid/view/PointerIcon;->mHotSpotX:F Landroid/view/PointerIcon;->mHotSpotY:F Landroid/view/PointerIcon;->mType:I +Landroid/view/RemoteAnimationDefinition;->addRemoteAnimation(IILandroid/view/RemoteAnimationAdapter;)V +Landroid/view/RemoteAnimationTarget;->clipRect:Landroid/graphics/Rect; +Landroid/view/RemoteAnimationTarget;->contentInsets:Landroid/graphics/Rect; +Landroid/view/RemoteAnimationTarget;->isNotInRecents:Z +Landroid/view/RemoteAnimationTarget;->isTranslucent:Z +Landroid/view/RemoteAnimationTarget;->leash:Landroid/view/SurfaceControl; +Landroid/view/RemoteAnimationTarget;->mode:I +Landroid/view/RemoteAnimationTarget;->position:Landroid/graphics/Point; +Landroid/view/RemoteAnimationTarget;->prefixOrderIndex:I +Landroid/view/RemoteAnimationTarget;->sourceContainerBounds:Landroid/graphics/Rect; +Landroid/view/RemoteAnimationTarget;->taskId:I +Landroid/view/RemoteAnimationTarget;->windowConfiguration:Landroid/app/WindowConfiguration; Landroid/view/RenderNodeAnimator;->callOnFinished(Landroid/view/RenderNodeAnimator;)V Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener; Landroid/view/ScaleGestureDetector;->mMinSpan:I @@ -2035,6 +2086,7 @@ Landroid/view/SurfaceView;->mRequestedFormat:I Landroid/view/SurfaceView;->mSurfaceHolder:Landroid/view/SurfaceHolder; Landroid/view/SurfaceView;->surfacePositionLost_uiRtSync(J)V Landroid/view/SurfaceView;->updateSurfacePosition_renderWorker(JIIII)V +Landroid/view/textclassifier/Logger;->DISABLED:Landroid/view/textclassifier/Logger; Landroid/view/textclassifier/logging/SmartSelectionEventTracker;-><init>(Landroid/content/Context;I)V Landroid/view/textclassifier/logging/SmartSelectionEventTracker;->logEvent(Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;)V Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionAction(III)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent; @@ -2061,6 +2113,7 @@ Landroid/view/View$AttachInfo;->mStableInsets:Landroid/graphics/Rect; Landroid/view/View;->clearAccessibilityFocus()V Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z Landroid/view/View;->computeOpaqueFlags()V +Landroid/view/ViewConfiguration;->getDeviceGlobalActionKeyTimeout()J Landroid/view/ViewConfiguration;->getDoubleTapMinTime()I Landroid/view/ViewConfiguration;->mFadingMarqueeEnabled:Z Landroid/view/ViewConfiguration;->sHasPermanentMenuKeySet:Z @@ -2072,11 +2125,15 @@ Landroid/view/View;->dispatchAttachedToWindow(Landroid/view/View$AttachInfo;I)V Landroid/view/View;->dispatchDetachedFromWindow()V Landroid/view/View;->fitsSystemWindows()Z Landroid/view/View;->getAccessibilityDelegate()Landroid/view/View$AccessibilityDelegate; +Landroid/view/View;->getBoundsOnScreen(Landroid/graphics/Rect;)V Landroid/view/View;->getInverseMatrix()Landroid/graphics/Matrix; Landroid/view/View;->getListenerInfo()Landroid/view/View$ListenerInfo; Landroid/view/View;->getLocationOnScreen()[I +Landroid/view/View;->getRawTextAlignment()I +Landroid/view/View;->getRawTextDirection()I Landroid/view/View;->getTransitionAlpha()F Landroid/view/View;->getViewRootImpl()Landroid/view/ViewRootImpl; +Landroid/view/View;->getWindowDisplayFrame(Landroid/graphics/Rect;)V Landroid/view/ViewGroup;->dispatchViewAdded(Landroid/view/View;)V Landroid/view/ViewGroup;->dispatchViewRemoved(Landroid/view/View;)V Landroid/view/ViewGroup;->FLAG_SUPPORT_STATIC_TRANSFORMATIONS:I @@ -2089,6 +2146,11 @@ Landroid/view/ViewGroup;->mChildren:[Landroid/view/View; Landroid/view/ViewGroup;->mFirstTouchTarget:Landroid/view/ViewGroup$TouchTarget; Landroid/view/ViewGroup;->mGroupFlags:I Landroid/view/ViewGroup;->mOnHierarchyChangeListener:Landroid/view/ViewGroup$OnHierarchyChangeListener; +Landroid/view/ViewGroup;->resetResolvedDrawables()V +Landroid/view/ViewGroup;->resetResolvedLayoutDirection()V +Landroid/view/ViewGroup;->resetResolvedPadding()V +Landroid/view/ViewGroup;->resetResolvedTextAlignment()V +Landroid/view/ViewGroup;->resetResolvedTextDirection()V Landroid/view/ViewGroup;->suppressLayout(Z)V Landroid/view/View;->initializeScrollbars(Landroid/content/res/TypedArray;)V Landroid/view/View;->internalSetPadding(IIII)V @@ -2128,10 +2190,17 @@ Landroid/view/View;->recomputePadding()V Landroid/view/View;->requestAccessibilityFocus()Z Landroid/view/View;->resetDisplayList()V Landroid/view/View;->resetPaddingToInitialValues()V +Landroid/view/View;->resetResolvedDrawables()V +Landroid/view/View;->resetResolvedLayoutDirection()V +Landroid/view/View;->resetResolvedPadding()V +Landroid/view/View;->resetResolvedTextAlignment()V +Landroid/view/View;->resetResolvedTextDirection()V +Landroid/view/View;->resetRtlProperties()V Landroid/view/ViewRootImpl;->detachFunctor(J)V Landroid/view/ViewRootImpl;->enqueueInputEvent(Landroid/view/InputEvent;)V Landroid/view/ViewRootImpl;->invokeFunctor(JZ)V Landroid/view/ViewRootImpl;->mStopped:Z +Landroid/view/ViewRootImpl;->mSurface:Landroid/view/Surface; Landroid/view/ViewRootImpl;->mView:Landroid/view/View; Landroid/view/View$ScrollabilityCache;->scrollBar:Landroid/widget/ScrollBarDrawable; Landroid/view/View;->setAlphaNoInvalidation(F)Z @@ -2241,6 +2310,10 @@ Landroid/widget/AutoCompleteTextView;->ensureImeVisible(Z)V Landroid/widget/AutoCompleteTextView;->mPopup:Landroid/widget/ListPopupWindow; Landroid/widget/AutoCompleteTextView;->setDropDownAlwaysVisible(Z)V Landroid/widget/CompoundButton;->mButtonDrawable:Landroid/graphics/drawable/Drawable; +Landroid/widget/CursorAdapter;->mChangeObserver:Landroid/widget/CursorAdapter$ChangeObserver; +Landroid/widget/CursorAdapter;->mDataSetObserver:Landroid/database/DataSetObserver; +Landroid/widget/CursorAdapter;->mDataValid:Z +Landroid/widget/CursorAdapter;->mRowIDColumn:I Landroid/widget/DatePicker;->mDelegate:Landroid/widget/DatePicker$DatePickerDelegate; Landroid/widget/EdgeEffect;->mPaint:Landroid/graphics/Paint; Landroid/widget/Editor;->invalidateTextDisplayList()V @@ -2290,6 +2363,8 @@ Landroid/widget/ListPopupWindow;->setForceIgnoreOutsideTouch(Z)V Landroid/widget/ListView;->fillDown(II)Landroid/view/View; Landroid/widget/ListView;->fillSpecific(II)Landroid/view/View; Landroid/widget/ListView;->fillUp(II)Landroid/view/View; +Landroid/widget/ListView;->findViewTraversal(I)Landroid/view/View; +Landroid/widget/ListView;->findViewWithTagTraversal(Ljava/lang/Object;)Landroid/view/View; Landroid/widget/ListView;->mAreAllItemsSelectable:Z Landroid/widget/ListView;->setSelectionInt(I)V Landroid/widget/MediaController;->mAnchor:Landroid/view/View; @@ -2375,6 +2450,8 @@ Landroid/widget/TabHost$TabSpec;->mContentStrategy:Landroid/widget/TabHost$Conte Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V Landroid/widget/TextView;->assumeLayout()V Landroid/widget/TextView;->createEditorIfNeeded()V +Landroid/widget/TextView;->getHorizontallyScrolling()Z +Landroid/widget/TextView;->getTextColor(Landroid/content/Context;Landroid/content/res/TypedArray;I)I Landroid/widget/TextView;->isSingleLine()Z Landroid/widget/TextView;->mCursorDrawableRes:I Landroid/widget/TextView;->mCurTextColor:I @@ -2388,6 +2465,10 @@ Landroid/widget/TextView;->mTextPaint:Landroid/text/TextPaint; Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;ZI)V Landroid/widget/Toast;->getService()Landroid/app/INotificationManager; Landroid/widget/Toast;->sService:Landroid/app/INotificationManager; +Landroid/widget/VideoView2;->getMediaController()Landroid/media/session/MediaController; +Landroid/widget/VideoView2$OnViewTypeChangedListener;->onViewTypeChanged(Landroid/view/View;I)V +Landroid/widget/VideoView2;->setOnViewTypeChangedListener(Landroid/widget/VideoView2$OnViewTypeChangedListener;)V +Landroid/widget/VideoView2;->setVideoPath(Ljava/lang/String;)V Landroid/widget/VideoView;->mCurrentBufferPercentage:I Landroid/widget/VideoView;->mMediaController:Landroid/widget/MediaController; Landroid/widget/VideoView;->mSHCallback:Landroid/view/SurfaceHolder$Callback; diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt index 952b28b8b820..fe9a5dbb1c24 100644 --- a/config/hiddenapi-vendor-list.txt +++ b/config/hiddenapi-vendor-list.txt @@ -1,9 +1,24 @@ +Landroid/accounts/AccountManager;-><init>(Landroid/content/Context;Landroid/accounts/IAccountManager;Landroid/os/Handler;)V +Landroid/app/Activity;->managedQuery(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor; +Landroid/app/ActivityManagerNative;->broadcastStickyIntent(Landroid/content/Intent;Ljava/lang/String;I)V Landroid/app/ActivityManager$RecentTaskInfo;->configuration:Landroid/content/res/Configuration; Landroid/app/ActivityManager$TaskDescription;->loadTaskDescriptionIcon(Ljava/lang/String;I)Landroid/graphics/Bitmap; Landroid/app/ActivityManager$TaskSnapshot;->getSnapshot()Landroid/graphics/GraphicBuffer; Landroid/app/ActivityOptions;->makeRemoteAnimation(Landroid/view/RemoteAnimationAdapter;)Landroid/app/ActivityOptions; Landroid/app/ActivityOptions;->setSplitScreenCreateMode(I)V Landroid/app/Activity;->registerRemoteAnimations(Landroid/view/RemoteAnimationDefinition;)V +Landroid/app/ActivityView;-><init>(Landroid/content/Context;)V +Landroid/app/ActivityView;->release()V +Landroid/app/ActivityView;->startActivity(Landroid/app/PendingIntent;)V +Landroid/app/ActivityView;->startActivity(Landroid/content/Intent;)V +Landroid/app/AppOpsManager;->getPackagesForOps([I)Ljava/util/List; +Landroid/app/AppOpsManager;->getToken(Lcom/android/internal/app/IAppOpsService;)Landroid/os/IBinder; +Landroid/app/AppOpsManager$OpEntry;->getOp()I +Landroid/app/AppOpsManager$OpEntry;->getTime()J +Landroid/app/AppOpsManager$OpEntry;->isRunning()Z +Landroid/app/AppOpsManager$PackageOps;->getOps()Ljava/util/List; +Landroid/app/AppOpsManager$PackageOps;->getPackageName()Ljava/lang/String; +Landroid/app/AppOpsManager$PackageOps;->getUid()I Landroid/app/IActivityController$Stub;-><init>()V Landroid/app/IActivityManager;->cancelRecentsAnimation()V Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V @@ -11,12 +26,18 @@ Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo; Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List; Landroid/app/IActivityManager;->getLockTaskModeState()I +Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo; Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice; +Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List; Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot; Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V Landroid/app/IActivityManager;->removeTask(I)Z +Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I +Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V +Landroid/app/IAlarmManager;->setTime(J)Z +Landroid/app/IAlarmManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IAlarmManager; Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V Landroid/app/IAssistDataReceiver$Stub;-><init>()V @@ -40,6 +61,8 @@ Landroid/app/TaskStackListener;->onTaskStackChanged()V Landroid/app/VrStateCallback;-><init>()V Landroid/app/VrStateCallback;->onPersistentVrStateChanged(Z)V Landroid/app/WallpaperColors;-><init>(Landroid/graphics/Color;Landroid/graphics/Color;Landroid/graphics/Color;I)V +Landroid/bluetooth/BluetoothHeadset;->phoneStateChanged(IIILjava/lang/String;I)V +Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V Landroid/companion/AssociationRequest;->getDeviceFilters()Ljava/util/List; Landroid/companion/AssociationRequest;->isSingleDevice()Z Landroid/companion/BluetoothDeviceFilter;->getAddress()Ljava/lang/String; @@ -53,19 +76,31 @@ Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelected(L Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V +Landroid/content/ContentProvider;->attachInfoForTesting(Landroid/content/Context;Landroid/content/pm/ProviderInfo;)V +Landroid/content/ContentProvider;->getIContentProvider()Landroid/content/IContentProvider; +Landroid/content/ContentProvider;-><init>(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;[Landroid/content/pm/PathPermission;)V +Landroid/content/ContentResolver;->registerContentObserver(Landroid/net/Uri;ZLandroid/database/ContentObserver;I)V +Landroid/content/ContentValues;->getStringArrayList(Ljava/lang/String;)Ljava/util/ArrayList; +Landroid/content/ContentValues;->putStringArrayList(Ljava/lang/String;Ljava/util/ArrayList;)V Landroid/content/Context;->getOpPackageName()Ljava/lang/String; Landroid/content/Context;->registerReceiverAsUser(Landroid/content/BroadcastReceiver;Landroid/os/UserHandle;Landroid/content/IntentFilter;Ljava/lang/String;Landroid/os/Handler;)Landroid/content/Intent; Landroid/content/Context;->startActivityAsUser(Landroid/content/Intent;Landroid/os/UserHandle;)V Landroid/content/Context;->startServiceAsUser(Landroid/content/Intent;Landroid/os/UserHandle;)Landroid/content/ComponentName; Landroid/content/ContextWrapper;->getThemeResId()I +Landroid/content/Intent;->getExtra(Ljava/lang/String;)Ljava/lang/Object; +Landroid/content/Intent;->getIBinderExtra(Ljava/lang/String;)Landroid/os/IBinder; +Landroid/content/Intent;->resolveSystemService(Landroid/content/pm/PackageManager;I)Landroid/content/ComponentName; Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo; +Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo; Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName; +Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo; Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V Landroid/database/sqlite/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri; +Landroid/database/sqlite/SqliteWrapper;->query(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor; Landroid/graphics/AvoidXfermode;-><init>(IILandroid/graphics/AvoidXfermode$Mode;)V Landroid/graphics/Bitmap;->createGraphicBufferHandle()Landroid/graphics/GraphicBuffer; Landroid/graphics/Bitmap;->createHardwareBitmap(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap; @@ -74,17 +109,25 @@ Landroid/graphics/Canvas;->clipRegion(Landroid/graphics/Region;)Z Landroid/graphics/drawable/Drawable;->isProjected()Z Landroid/graphics/drawable/Drawable;->updateTintFilter(Landroid/graphics/PorterDuffColorFilter;Landroid/content/res/ColorStateList;Landroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuffColorFilter; Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V +Landroid/hardware/display/DisplayManagerGlobal;->getInstance()Landroid/hardware/display/DisplayManagerGlobal; +Landroid/hardware/display/DisplayManagerGlobal;->getRealDisplay(I)Landroid/view/Display; Landroid/hardware/location/GeofenceHardware;-><init>(Landroid/hardware/location/IGeofenceHardware;)V Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V Landroid/location/IFusedProvider;->onFusedLocationHardwareChange(Landroid/hardware/location/IFusedLocationHardware;)V Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String; Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String; Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V +Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String; Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V +Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z +Landroid/location/INetInitiatedListener$Stub;-><init>()V +Landroid/location/Location;->setExtraLocation(Ljava/lang/String;Landroid/location/Location;)V Landroid/media/AudioManager;->registerAudioPortUpdateListener(Landroid/media/AudioManager$OnAudioPortUpdateListener;)V Landroid/media/AudioManager;->unregisterAudioPortUpdateListener(Landroid/media/AudioManager$OnAudioPortUpdateListener;)V Landroid/media/AudioSystem;->checkAudioFlinger()I +Landroid/media/AudioSystem;->getForceUse(I)I Landroid/media/AudioSystem;->getParameters(Ljava/lang/String;)Ljava/lang/String; +Landroid/media/AudioSystem;->setForceUse(II)I Landroid/media/AudioSystem;->setParameters(Ljava/lang/String;)I Landroid/media/MediaDrm$Certificate;->getContent()[B Landroid/media/MediaDrm$Certificate;->getWrappedPrivateKey()[B @@ -103,14 +146,18 @@ Landroid/media/tv/ITvRemoteServiceInput;->sendPointerDown(Landroid/os/IBinder;II Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V +Landroid/net/ConnectivityManager;->getActiveNetworkQuotaInfo()Landroid/net/NetworkQuotaInfo; Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V Landroid/net/ConnectivityManager$PacketKeepaliveCallback;->onError(I)V Landroid/net/ConnectivityManager$PacketKeepaliveCallback;->onStarted()V Landroid/net/ConnectivityManager$PacketKeepaliveCallback;->onStopped()V Landroid/net/ConnectivityManager$PacketKeepalive;->stop()V +Landroid/net/ConnectivityManager;->setAirplaneMode(Z)V Landroid/net/ConnectivityManager;->startNattKeepalive(Landroid/net/Network;ILandroid/net/ConnectivityManager$PacketKeepaliveCallback;Ljava/net/InetAddress;ILjava/net/InetAddress;)Landroid/net/ConnectivityManager$PacketKeepalive; Landroid/net/ConnectivityManager;->startUsingNetworkFeature(ILjava/lang/String;)I Landroid/net/ConnectivityManager;->stopUsingNetworkFeature(ILjava/lang/String;)I +Landroid/net/ConnectivityManager;->tether(Ljava/lang/String;)I +Landroid/net/ConnectivityManager;->untether(Ljava/lang/String;)I Landroid/net/DhcpResults;-><init>(Landroid/net/DhcpResults;)V Landroid/net/DhcpResults;-><init>(Landroid/net/StaticIpConfiguration;)V Landroid/net/DhcpResults;-><init>()V @@ -138,6 +185,8 @@ Landroid/net/LinkProperties;->addRoute(Landroid/net/RouteInfo;)Z Landroid/net/LinkProperties;->addStackedLink(Landroid/net/LinkProperties;)Z Landroid/net/LinkProperties;->clear()V Landroid/net/LinkProperties;->compareProvisioning(Landroid/net/LinkProperties;Landroid/net/LinkProperties;)Landroid/net/LinkProperties$ProvisioningChange; +Landroid/net/LinkProperties;->getAllInterfaceNames()Ljava/util/List; +Landroid/net/LinkProperties;->getAllRoutes()Ljava/util/List; Landroid/net/LinkProperties;->getMtu()I Landroid/net/LinkProperties;->getStackedLinks()Ljava/util/List; Landroid/net/LinkProperties;->hasGlobalIPv6Address()Z @@ -226,12 +275,23 @@ Landroid/net/NetworkCapabilities;->getSignalStrength()I Landroid/net/NetworkCapabilities;->hasSignalStrength()Z Landroid/net/NetworkCapabilities;->transportNamesOf([I)Ljava/lang/String; Landroid/net/Network;-><init>(I)V +Landroid/net/Network;->netId:I Landroid/net/NetworkQuotaInfo;->getEstimatedBytes()J Landroid/net/NetworkQuotaInfo;->getHardLimitBytes()J Landroid/net/NetworkQuotaInfo;->getSoftLimitBytes()J Landroid/net/NetworkRequest$Builder;->setSignalStrength(I)Landroid/net/NetworkRequest$Builder; +Landroid/net/NetworkRequest;->networkCapabilities:Landroid/net/NetworkCapabilities; +Landroid/net/NetworkState;->network:Landroid/net/Network; Landroid/net/NetworkStats;->combineValues(Landroid/net/NetworkStats$Entry;)Landroid/net/NetworkStats; +Landroid/net/NetworkStats$Entry;->iface:Ljava/lang/String; Landroid/net/NetworkStats$Entry;-><init>()V +Landroid/net/NetworkStats$Entry;->rxBytes:J +Landroid/net/NetworkStats$Entry;->rxPackets:J +Landroid/net/NetworkStats$Entry;->set:I +Landroid/net/NetworkStats$Entry;->tag:I +Landroid/net/NetworkStats$Entry;->txBytes:J +Landroid/net/NetworkStats$Entry;->txPackets:J +Landroid/net/NetworkStats$Entry;->uid:I Landroid/net/NetworkStatsHistory$Entry;->txBytes:J Landroid/net/NetworkStatsHistory;->getStart()J Landroid/net/NetworkStatsHistory;->getValues(JJLandroid/net/NetworkStatsHistory$Entry;)Landroid/net/NetworkStatsHistory$Entry; @@ -245,7 +305,10 @@ Landroid/net/NetworkUtils;->netmaskToPrefixLength(Ljava/net/Inet4Address;)I Landroid/net/NetworkUtils;->protectFromVpn(Ljava/io/FileDescriptor;)Z Landroid/net/RouteInfo;->hasGateway()Z Landroid/net/RouteInfo;-><init>(Landroid/net/IpPrefix;Ljava/net/InetAddress;Ljava/lang/String;)V +Landroid/net/RouteInfo;->selectBestRoute(Ljava/util/Collection;Ljava/net/InetAddress;)Landroid/net/RouteInfo; Landroid/net/SntpClient;->getNtpTime()J +Landroid/net/SntpClient;->getNtpTimeReference()J +Landroid/net/SntpClient;->getRoundTripTime()J Landroid/net/SntpClient;->requestTime(Ljava/lang/String;I)Z Landroid/net/StaticIpConfiguration;->dnsServers:Ljava/util/ArrayList; Landroid/net/StaticIpConfiguration;->domains:Ljava/lang/String; @@ -268,27 +331,36 @@ Landroid/os/BatteryStats$HistoryItem;->cmd:B Landroid/os/BatteryStats$HistoryItem;-><init>()V Landroid/os/BatteryStats$HistoryItem;->states:I Landroid/os/BatteryStats$HistoryItem;->time:J +Landroid/os/BatteryStats$Timer;->getCountLocked(I)I Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J Landroid/os/BatteryStats$Uid;-><init>()V Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer; +Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V +Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V +Landroid/os/Broadcaster;-><init>()V +Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V +Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File; Landroid/os/Handler;->getMain()Landroid/os/Handler; Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V Landroid/os/HwBinder;->reportSyspropChanged()V Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V +Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService; Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V +Landroid/os/IPowerManager;->goToSleep(JII)V Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V Landroid/os/IRemoteCallback$Stub;-><init>()V Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message; Landroid/os/Parcel;->readBlob()[B Landroid/os/Parcel;->readStringArray()[Ljava/lang/String; Landroid/os/Parcel;->writeBlob([B)V +Landroid/os/PowerManager;->goToSleep(J)V Landroid/os/PowerManager;->isScreenBrightnessBoosted()Z Landroid/os/Registrant;->clear()V Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V @@ -303,10 +375,35 @@ Landroid/os/Registrant;->notifyRegistrant(Landroid/os/AsyncResult;)V Landroid/os/Registrant;->notifyRegistrant()V Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException; Landroid/os/ServiceSpecificException;->errorCode:I +Landroid/os/storage/DiskInfo;->getId()Ljava/lang/String; +Landroid/os/storage/StorageEventListener;-><init>()V +Landroid/os/storage/StorageManager;->findVolumeById(Ljava/lang/String;)Landroid/os/storage/VolumeInfo; +Landroid/os/storage/StorageManager;->from(Landroid/content/Context;)Landroid/os/storage/StorageManager; +Landroid/os/storage/StorageManager;->registerListener(Landroid/os/storage/StorageEventListener;)V +Landroid/os/storage/StorageManager;->unregisterListener(Landroid/os/storage/StorageEventListener;)V +Landroid/os/storage/StorageVolume;->getId()Ljava/lang/String; +Landroid/os/storage/VolumeInfo;->getId()Ljava/lang/String; Landroid/os/SystemProperties;->reportSyspropChanged()V +Landroid/os/SystemService;->start(Ljava/lang/String;)V +Landroid/os/SystemService;->stop(Ljava/lang/String;)V +Landroid/os/SystemVibrator;-><init>()V +Landroid/os/UserHandle;->isSameApp(II)Z +Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z +Landroid/os/UserManager;->isAdminUser()Z Landroid/print/PrintDocumentAdapter$LayoutResultCallback;-><init>()V Landroid/print/PrintDocumentAdapter$WriteResultCallback;-><init>()V Landroid/provider/CalendarContract$Events;->PROVIDER_WRITABLE_COLUMNS:[Ljava/lang/String; +Landroid/provider/ContactsContract$CommonDataKinds$Phone;->getDisplayLabel(Landroid/content/Context;ILjava/lang/CharSequence;)Ljava/lang/CharSequence; +Landroid/provider/Settings$Global;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String; +Landroid/provider/Settings$Secure;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String; +Landroid/provider/Telephony$Mms;->isEmailAddress(Ljava/lang/String;)Z +Landroid/provider/Telephony$Sms$Draft;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)Landroid/net/Uri; +Landroid/provider/Telephony$Sms$Outbox;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZJ)Landroid/net/Uri; +Landroid/R$styleable;->CheckBoxPreference:[I +Landroid/service/dreams/DreamService;->canDoze()Z +Landroid/service/dreams/DreamService;->isDozing()Z +Landroid/service/dreams/DreamService;->startDozing()V +Landroid/service/dreams/DreamService;->stopDozing()V Landroid/service/vr/VrListenerService;->onCurrentVrActivityChanged(Landroid/content/ComponentName;ZI)V Landroid/system/NetlinkSocketAddress;-><init>(II)V Landroid/system/Os;->bind(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V @@ -316,6 +413,11 @@ Landroid/system/Os;->setsockoptIfreq(Ljava/io/FileDescriptor;IILjava/lang/String Landroid/system/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V Landroid/system/PacketSocketAddress;-><init>(I[B)V Landroid/system/PacketSocketAddress;-><init>(SI)V +Landroid/telecom/ParcelableCall;->CREATOR:Landroid/os/Parcelable$Creator; +Landroid/telecom/ParcelableCall;->getConnectTimeMillis()J +Landroid/telecom/ParcelableCall;->getDisconnectCause()Landroid/telecom/DisconnectCause; +Landroid/telecom/ParcelableCall;->getHandle()Landroid/net/Uri; +Landroid/telecom/ParcelableCall;->getId()Ljava/lang/String; Landroid/telecom/TelecomManager;->from(Landroid/content/Context;)Landroid/telecom/TelecomManager; Landroid/telecom/VideoProfile$CameraCapabilities;-><init>(IIZF)V Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V @@ -329,6 +431,14 @@ Landroid/telephony/ims/ImsExternalCallState;-><init>(ILandroid/net/Uri;ZIIZ)V Landroid/telephony/ims/ImsReasonInfo;-><init>(IILjava/lang/String;)V Landroid/telephony/ims/ImsReasonInfo;-><init>(II)V Landroid/telephony/ims/ImsStreamMediaProfile;-><init>()V +Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V +Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V +Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri; +Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I +Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I +Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I +Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService; +Landroid/telephony/PhoneNumberUtils;->formatNumber(Ljava/lang/String;I)Ljava/lang/String; Landroid/telephony/PhoneNumberUtils;->isEmergencyNumber(ILjava/lang/String;)Z Landroid/telephony/PhoneNumberUtils;->isPotentialEmergencyNumber(ILjava/lang/String;)Z Landroid/telephony/PhoneNumberUtils;->isPotentialLocalEmergencyNumber(Landroid/content/Context;ILjava/lang/String;)Z @@ -342,12 +452,20 @@ Landroid/telephony/Rlog;->d(Ljava/lang/String;Ljava/lang/String;)I Landroid/telephony/Rlog;->e(Ljava/lang/String;Ljava/lang/String;)I Landroid/telephony/Rlog;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I Landroid/telephony/Rlog;->i(Ljava/lang/String;Ljava/lang/String;)I +Landroid/telephony/ServiceState;->getDataRegState()I Landroid/telephony/ServiceState;->getDataRoaming()Z Landroid/telephony/ServiceState;->getRilDataRadioTechnology()I +Landroid/telephony/ServiceState;->getVoiceNetworkType()I Landroid/telephony/ServiceState;->getVoiceRegState()I Landroid/telephony/ServiceState;->isCdma(I)Z Landroid/telephony/ServiceState;->isEmergencyOnly()Z +Landroid/telephony/ServiceState;->isGsm(I)Z Landroid/telephony/ServiceState;->mergeServiceStates(Landroid/telephony/ServiceState;Landroid/telephony/ServiceState;)Landroid/telephony/ServiceState; +Landroid/telephony/ServiceState;->rilRadioTechnologyToString(I)Ljava/lang/String; +Landroid/telephony/SubscriptionInfo;->setDisplayName(Ljava/lang/CharSequence;)V +Landroid/telephony/SubscriptionInfo;->setIconTint(I)V +Landroid/telephony/SubscriptionManager;->clearDefaultsForInactiveSubIds()V +Landroid/telephony/SubscriptionManager;->getDefaultVoicePhoneId()I Landroid/telephony/SubscriptionManager;->getResourcesForSubId(Landroid/content/Context;I)Landroid/content/res/Resources; Landroid/telephony/SubscriptionManager;->isActiveSubId(I)Z Landroid/telephony/SubscriptionManager;->isUsableSubIdValue(I)Z @@ -355,9 +473,18 @@ Landroid/telephony/SubscriptionManager;->isValidPhoneId(I)Z Landroid/telephony/SubscriptionManager;->isValidSlotIndex(I)Z Landroid/telephony/SubscriptionManager;->isValidSubscriptionId(I)Z Landroid/telephony/SubscriptionManager;->putPhoneIdAndSubIdExtra(Landroid/content/Intent;II)V +Landroid/telephony/SubscriptionManager;->putPhoneIdAndSubIdExtra(Landroid/content/Intent;I)V +Landroid/telephony/SubscriptionManager;->setDefaultDataSubId(I)V +Landroid/telephony/SubscriptionManager;->setDisplayName(Ljava/lang/String;IJ)I +Landroid/telephony/SubscriptionManager;->setIconTint(II)I Landroid/telephony/TelephonyManager;->getIntAtIndex(Landroid/content/ContentResolver;Ljava/lang/String;I)I +Landroid/telephony/TelephonyManager;->getNetworkTypeName()Ljava/lang/String; +Landroid/telephony/TelephonyManager;->getVoiceMessageCount()I Landroid/telephony/TelephonyManager;->getVoiceNetworkType(I)I +Landroid/telephony/TelephonyManager$MultiSimVariants;->DSDA:Landroid/telephony/TelephonyManager$MultiSimVariants; +Landroid/telephony/TelephonyManager$MultiSimVariants;->DSDS:Landroid/telephony/TelephonyManager$MultiSimVariants; Landroid/telephony/TelephonyManager;->putIntAtIndex(Landroid/content/ContentResolver;Ljava/lang/String;II)Z +Landroid/text/TextUtils;->isPrintableAsciiOnly(Ljava/lang/CharSequence;)Z Landroid/util/FloatMath;->ceil(F)F Landroid/util/FloatMath;->cos(F)F Landroid/util/FloatMath;->exp(F)F @@ -374,6 +501,9 @@ Landroid/util/LongArray;->add(IJ)V Landroid/util/LongArray;->get(I)J Landroid/util/LongArray;-><init>()V Landroid/util/LongArray;->size()I +Landroid/util/Slog;->e(Ljava/lang/String;Ljava/lang/String;)I +Landroid/util/Slog;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I +Landroid/util/Slog;->println(ILjava/lang/String;Ljava/lang/String;)I Landroid/util/Slog;->wtf(Ljava/lang/String;Ljava/lang/String;)I Landroid/view/AppTransitionAnimationSpec;-><init>(ILandroid/graphics/GraphicBuffer;Landroid/graphics/Rect;)V Landroid/view/BatchedInputEventReceiver;-><init>(Landroid/view/InputChannel;Landroid/os/Looper;Landroid/view/Choreographer;)V @@ -525,6 +655,19 @@ Lcom/android/ims/internal/uce/uceservice/IUceService;->isServiceStarted()Z Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V +Lcom/android/internal/app/AlertController$AlertParams;->mIconId:I +Lcom/android/internal/app/AlertController$AlertParams;->mMessage:Ljava/lang/CharSequence; +Lcom/android/internal/app/AlertController$AlertParams;->mNegativeButtonListener:Landroid/content/DialogInterface$OnClickListener; +Lcom/android/internal/app/AlertController$AlertParams;->mNegativeButtonText:Ljava/lang/CharSequence; +Lcom/android/internal/app/AlertController$AlertParams;->mPositiveButtonListener:Landroid/content/DialogInterface$OnClickListener; +Lcom/android/internal/app/AlertController$AlertParams;->mPositiveButtonText:Ljava/lang/CharSequence; +Lcom/android/internal/app/AlertController$AlertParams;->mTitle:Ljava/lang/CharSequence; +Lcom/android/internal/app/AlertController$AlertParams;->mView:Landroid/view/View; +Lcom/android/internal/app/AlertController;->getButton(I)Landroid/widget/Button; +Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V +Lcom/android/internal/content/PackageMonitor;-><init>()V +Lcom/android/internal/content/PackageMonitor;->register(Landroid/content/Context;Landroid/os/Looper;Landroid/os/UserHandle;Z)V +Lcom/android/internal/content/PackageMonitor;->unregister()V Lcom/android/internal/location/ILocationProvider;->disable()V Lcom/android/internal/location/ILocationProvider;->enable()V Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties; @@ -532,6 +675,11 @@ Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle; Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V +Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider; +Lcom/android/internal/location/ProviderRequest;-><init>()V +Lcom/android/internal/location/ProviderRequest;->interval:J +Lcom/android/internal/location/ProviderRequest;->locationRequests:Ljava/util/List; +Lcom/android/internal/location/ProviderRequest;->reportLocation:Z Lcom/android/internal/os/BatteryStatsImpl;->getDischargeCurrentLevel()I Lcom/android/internal/os/BatteryStatsImpl;->getDischargeStartLevel()I Lcom/android/internal/os/BatteryStatsImpl;->getPhoneOnTime(JI)J @@ -541,7 +689,22 @@ Lcom/android/internal/os/BatteryStatsImpl;->getScreenBrightnessTime(IJI)J Lcom/android/internal/os/BatteryStatsImpl;->getWifiOnTime(JI)J Lcom/android/internal/os/SomeArgs;->obtain()Lcom/android/internal/os/SomeArgs; Lcom/android/internal/os/SomeArgs;->recycle()V +Lcom/android/internal/R$styleable;->NumberPicker:[I +Lcom/android/internal/R$styleable;->TwoLineListItem:[I +Lcom/android/internal/telephony/GsmAlphabet;->gsm7BitPackedToString([BII)Ljava/lang/String; +Lcom/android/internal/telephony/GsmAlphabet;->stringToGsm7BitPacked(Ljava/lang/String;)[B Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z +Lcom/android/internal/telephony/OperatorInfo;->CREATOR:Landroid/os/Parcelable$Creator; +Lcom/android/internal/telephony/OperatorInfo;->getOperatorAlphaLong()Ljava/lang/String; +Lcom/android/internal/telephony/OperatorInfo;->getOperatorAlphaShort()Ljava/lang/String; +Lcom/android/internal/telephony/OperatorInfo;->getOperatorNumeric()Ljava/lang/String; +Lcom/android/internal/telephony/OperatorInfo;->getState()Lcom/android/internal/telephony/OperatorInfo$State; +Lcom/android/internal/telephony/OperatorInfo;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V +Lcom/android/internal/telephony/OperatorInfo$State;->CURRENT:Lcom/android/internal/telephony/OperatorInfo$State; +Lcom/android/internal/telephony/OperatorInfo$State;->FORBIDDEN:Lcom/android/internal/telephony/OperatorInfo$State; +Lcom/android/internal/util/AsyncChannel;->connect(Landroid/content/Context;Landroid/os/Handler;Landroid/os/Messenger;)V +Lcom/android/internal/util/AsyncChannel;-><init>()V +Lcom/android/internal/util/AsyncChannel;->sendMessage(Landroid/os/Message;)V Lcom/android/internal/util/IndentingPrintWriter;->decreaseIndent()Lcom/android/internal/util/IndentingPrintWriter; Lcom/android/internal/util/IndentingPrintWriter;->increaseIndent()Lcom/android/internal/util/IndentingPrintWriter; Lcom/android/internal/util/IndentingPrintWriter;-><init>(Ljava/io/Writer;Ljava/lang/String;)V @@ -550,3 +713,4 @@ Lcom/android/internal/util/XmlUtils;->nextElement(Lorg/xmlpull/v1/XmlPullParser; Ljava/lang/System;->arraycopy([BI[BII)V Ljava/net/Inet4Address;->ALL:Ljava/net/InetAddress; Ljava/net/Inet4Address;->ANY:Ljava/net/InetAddress; +Ljava/net/Inet6Address;->ANY:Ljava/net/InetAddress; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 833b0f7da567..1d069bc19872 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -30,12 +30,15 @@ import android.annotation.Nullable; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; import android.app.backup.BackupAgent; +import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ActivityLifecycleItem.LifecycleState; +import android.app.servertransaction.ActivityRelaunchItem; import android.app.servertransaction.ActivityResultItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.PendingTransactionActions; import android.app.servertransaction.PendingTransactionActions.StopInfo; import android.app.servertransaction.TransactionExecutor; +import android.app.servertransaction.TransactionExecutorHelper; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; import android.content.ComponentName; @@ -520,6 +523,10 @@ public final class ActivityThread extends ClientTransactionHandler { return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS; } + public boolean isVisibleFromServer() { + return activity != null && activity.mVisibleFromServer; + } + public String toString() { ComponentName componentName = intent != null ? intent.getComponent() : null; return "ActivityRecord{" @@ -1797,6 +1804,7 @@ public final class ActivityThread extends ClientTransactionHandler { // message is handled. transaction.recycle(); } + // TODO(lifecycler): Recycle locally scheduled transactions. break; } Object obj = msg.obj; @@ -4723,14 +4731,25 @@ public final class ActivityThread extends ClientTransactionHandler { return; } - // TODO(b/73747058): Investigate converting this to use transaction to relaunch. - handleRelaunchActivityInner(r, 0 /* configChanges */, null /* pendingResults */, - null /* pendingIntents */, null /* pendingActions */, prevState != ON_RESUME, - r.overrideConfig, "handleRelaunchActivityLocally"); - // Restore back to the previous state before relaunch if needed. - if (prevState != r.getLifecycleState()) { - mTransactionExecutor.cycleToPath(r, prevState); + // Initialize a relaunch request. + final MergedConfiguration mergedConfiguration = new MergedConfiguration( + r.createdConfig != null ? r.createdConfig : mConfiguration, + r.overrideConfig); + final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain( + null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */, + mergedConfiguration, r.mPreserveWindow); + // Make sure to match the existing lifecycle state in the end of the transaction. + final ActivityLifecycleItem lifecycleRequest = + TransactionExecutorHelper.getLifecycleRequestForCurrentState(r); + // Schedule the transaction. + final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token); + transaction.addCallback(activityRelaunchItem); + transaction.setLifecycleStateRequest(lifecycleRequest); + try { + mAppThread.scheduleTransaction(transaction); + } catch (RemoteException e) { + // Local scheduling } } diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 5d0143a505f3..7032a2fe21cf 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -27,6 +27,7 @@ import android.os.RemoteException; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; +import android.view.IWindowManager; import android.view.InputDevice; import android.view.InputEvent; import android.view.MotionEvent; @@ -308,8 +309,14 @@ public class ActivityView extends ViewGroup { return; } - mInputForwarder = InputManager.getInstance().createInputForwarder( - mVirtualDisplay.getDisplay().getDisplayId()); + final int displayId = mVirtualDisplay.getDisplay().getDisplayId(); + final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + try { + wm.dontOverrideDisplayInfo(displayId); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + mInputForwarder = InputManager.getInstance().createInputForwarder(displayId); mTaskStackListener = new TaskBackgroundChangeListener(); try { mActivityManager.registerTaskStackListener(mTaskStackListener); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 21fb18a212a8..b8c4ef783a60 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -20,6 +20,7 @@ import android.annotation.DrawableRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; +import android.annotation.UserIdInt; import android.annotation.XmlRes; import android.content.ComponentName; import android.content.ContentResolver; @@ -1031,19 +1032,25 @@ public class ApplicationPackageManager extends PackageManager { } @Override - public ResolveInfo resolveService(Intent intent, int flags) { + public ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags, + @UserIdInt int userId) { try { return mPM.resolveService( intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags, - mContext.getUserId()); + userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override + public ResolveInfo resolveService(Intent intent, int flags) { + return resolveServiceAsUser(intent, flags, mContext.getUserId()); + } + + @Override @SuppressWarnings("unchecked") public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) { try { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index d8e1c895a6eb..c6568e16086c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5577,6 +5577,24 @@ public class Notification implements Parcelable public void setRebuildStyledRemoteViews(boolean rebuild) { mRebuildStyledRemoteViews = rebuild; } + + /** + * Get the text that should be displayed in the statusBar when heads upped. This is + * usually just the app name, but may be different depending on the style. + * + * @param publicMode If true, return a text that is safe to display in public. + * + * @hide + */ + public CharSequence getHeadsUpStatusBarText(boolean publicMode) { + if (mStyle != null && !publicMode) { + CharSequence text = mStyle.getHeadsUpStatusBarText(); + if (!TextUtils.isEmpty(text)) { + return text; + } + } + return loadHeaderAppName(); + } } /** @@ -5954,6 +5972,16 @@ public class Notification implements Parcelable * @hide */ public abstract boolean areNotificationsVisiblyDifferent(Style other); + + /** + * @return the the text that should be displayed in the statusBar when heads-upped. + * If {@code null} is returned, the default implementation will be used. + * + * @hide + */ + public CharSequence getHeadsUpStatusBarText() { + return null; + } } /** @@ -6403,6 +6431,23 @@ public class Notification implements Parcelable } /** + * @return the the text that should be displayed in the statusBar when heads upped. + * If {@code null} is returned, the default implementation will be used. + * + * @hide + */ + @Override + public CharSequence getHeadsUpStatusBarText() { + CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle) + ? super.mBigContentTitle + : mConversationTitle; + if (!TextUtils.isEmpty(conversationTitle) && !hasOnlyWhiteSpaceSenders()) { + return conversationTitle; + } + return null; + } + + /** * @return the user to be displayed for any replies sent by the user */ public Person getUser() { diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java index 7e66fd7a2ead..01b13a28aed1 100644 --- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java +++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java @@ -26,7 +26,7 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE; import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED; -import android.app.ActivityThread; +import android.app.ActivityThread.ActivityClientRecord; import android.util.IntArray; import com.android.internal.annotations.VisibleForTesting; @@ -124,7 +124,7 @@ public class TransactionExecutorHelper { * {@link ActivityLifecycleItem#UNDEFINED} if there is not path. */ @VisibleForTesting - public int getClosestPreExecutionState(ActivityThread.ActivityClientRecord r, + public int getClosestPreExecutionState(ActivityClientRecord r, int postExecutionState) { switch (postExecutionState) { case UNDEFINED: @@ -147,7 +147,7 @@ public class TransactionExecutorHelper { * were provided or there is not path. */ @VisibleForTesting - public int getClosestOfStates(ActivityThread.ActivityClientRecord r, int[] finalStates) { + public int getClosestOfStates(ActivityClientRecord r, int[] finalStates) { if (finalStates == null || finalStates.length == 0) { return UNDEFINED; } @@ -168,6 +168,27 @@ public class TransactionExecutorHelper { return closestState; } + /** Get the lifecycle state request to match the current state in the end of a transaction. */ + public static ActivityLifecycleItem getLifecycleRequestForCurrentState(ActivityClientRecord r) { + final int prevState = r.getLifecycleState(); + final ActivityLifecycleItem lifecycleItem; + switch (prevState) { + // TODO(lifecycler): Extend to support all possible states. + case ON_PAUSE: + lifecycleItem = PauseActivityItem.obtain(); + break; + case ON_STOP: + lifecycleItem = StopActivityItem.obtain(r.isVisibleFromServer(), + 0 /* configChanges */); + break; + default: + lifecycleItem = ResumeActivityItem.obtain(false /* isForward */); + break; + } + + return lifecycleItem; + } + /** * Check if there is a destruction involved in the path. We want to avoid a lifecycle sequence * that involves destruction and recreation if there is another path. diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java index d6f23521bf3c..61679cb4d631 100644 --- a/core/java/android/app/slice/Slice.java +++ b/core/java/android/app/slice/Slice.java @@ -66,10 +66,27 @@ public final class Slice implements Parcelable { HINT_HORIZONTAL, HINT_PARTIAL, HINT_SEE_MORE, - HINT_KEY_WORDS + HINT_KEY_WORDS, + HINT_ERROR, }) @Retention(RetentionPolicy.SOURCE) public @interface SliceHint {} + /** + * @hide + */ + @StringDef(prefix = { "SUBTYPE_" }, value = { + SUBTYPE_COLOR, + SUBTYPE_CONTENT_DESCRIPTION, + SUBTYPE_MAX, + SUBTYPE_MESSAGE, + SUBTYPE_PRIORITY, + SUBTYPE_RANGE, + SUBTYPE_SOURCE, + SUBTYPE_TOGGLE, + SUBTYPE_VALUE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SliceSubtype {} /** * Hint that this content is a title of other content in the slice. This can also indicate that @@ -149,9 +166,14 @@ public final class Slice implements Parcelable { /** * A hint to indicate that the contents of this subslice represent a list of keywords * related to the parent slice. + * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE}. */ public static final String HINT_KEY_WORDS = "key_words"; /** + * A hint to indicate that this slice represents an error. + */ + public static final String HINT_ERROR = "error"; + /** * Key to retrieve an extra added to an intent when a control is changed. */ public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE"; @@ -168,14 +190,18 @@ public final class Slice implements Parcelable { /** * Subtype to indicate that this is a message as part of a communication * sequence in this slice. + * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE}. */ public static final String SUBTYPE_MESSAGE = "message"; /** * Subtype to tag the source (i.e. sender) of a {@link #SUBTYPE_MESSAGE}. + * Expected to be on an item of format {@link SliceItem#FORMAT_TEXT}, + * {@link SliceItem#FORMAT_IMAGE} or an {@link SliceItem#FORMAT_SLICE} containing them. */ public static final String SUBTYPE_SOURCE = "source"; /** * Subtype to tag an item as representing a color. + * Expected to be on an item of format {@link SliceItem#FORMAT_INT}. */ public static final String SUBTYPE_COLOR = "color"; /** @@ -186,14 +212,18 @@ public final class Slice implements Parcelable { public static final String SUBTYPE_SLIDER = "slider"; /** * Subtype to tag an item as representing a range. + * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE} containing + * a {@link #SUBTYPE_VALUE} and possibly a {@link #SUBTYPE_MAX}. */ public static final String SUBTYPE_RANGE = "range"; /** * Subtype to tag an item as representing the max int value for a {@link #SUBTYPE_RANGE}. + * Expected to be on an item of format {@link SliceItem#FORMAT_INT}. */ public static final String SUBTYPE_MAX = "max"; /** * Subtype to tag an item as representing the current int value for a {@link #SUBTYPE_RANGE}. + * Expected to be on an item of format {@link SliceItem#FORMAT_INT}. */ public static final String SUBTYPE_VALUE = "value"; /** @@ -205,10 +235,12 @@ public final class Slice implements Parcelable { public static final String SUBTYPE_TOGGLE = "toggle"; /** * Subtype to tag an item representing priority. + * Expected to be on an item of format {@link SliceItem#FORMAT_INT}. */ public static final String SUBTYPE_PRIORITY = "priority"; /** * Subtype to tag an item to use as a content description. + * Expected to be on an item of format {@link SliceItem#FORMAT_TEXT}. */ public static final String SUBTYPE_CONTENT_DESCRIPTION = "content_description"; @@ -305,14 +337,24 @@ public final class Slice implements Parcelable { private SliceSpec mSpec; /** - * Create a builder which will construct a {@link Slice} for the Given Uri. - * @param uri Uri to tag for this slice. + * @deprecated TO BE REMOVED */ + @Deprecated public Builder(@NonNull Uri uri) { mUri = uri; } /** + * Create a builder which will construct a {@link Slice} for the given Uri. + * @param uri Uri to tag for this slice. + * @param spec the spec for this slice. + */ + public Builder(@NonNull Uri uri, SliceSpec spec) { + mUri = uri; + mSpec = spec; + } + + /** * Create a builder for a {@link Slice} that is a sub-slice of the slice * being constructed by the provided builder. * @param parent The builder constructing the parent slice @@ -340,20 +382,13 @@ public final class Slice implements Parcelable { /** * Add hints to the Slice being constructed */ - public Builder addHints(@SliceHint String... hints) { - mHints.addAll(Arrays.asList(hints)); - return this; - } - - /** - * Add hints to the Slice being constructed - */ public Builder addHints(@SliceHint List<String> hints) { - return addHints(hints.toArray(new String[hints.size()])); + mHints.addAll(hints); + return this; } /** - * Add the spec for this slice. + * @deprecated TO BE REMOVED */ public Builder setSpec(SliceSpec spec) { mSpec = spec; @@ -362,17 +397,10 @@ public final class Slice implements Parcelable { /** * Add a sub-slice to the slice being constructed - */ - public Builder addSubSlice(@NonNull Slice slice) { - return addSubSlice(slice, null); - } - - /** - * Add a sub-slice to the slice being constructed * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} */ - public Builder addSubSlice(@NonNull Slice slice, @Nullable String subType) { + public Builder addSubSlice(@NonNull Slice slice, @Nullable @SliceSubtype String subType) { mItems.add(new SliceItem(slice, SliceItem.FORMAT_SLICE, subType, slice.getHints().toArray(new String[slice.getHints().size()]))); return this; @@ -380,18 +408,11 @@ public final class Slice implements Parcelable { /** * Add an action to the slice being constructed - */ - public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) { - return addAction(action, s, null); - } - - /** - * Add an action to the slice being constructed * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} */ public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s, - @Nullable String subType) { + @Nullable @SliceSubtype String subType) { List<String> hints = s.getHints(); s.mSpec = null; mItems.add(new SliceItem(action, s, SliceItem.FORMAT_ACTION, subType, hints.toArray( @@ -404,58 +425,31 @@ public final class Slice implements Parcelable { * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} */ - public Builder addText(CharSequence text, @Nullable String subType, - @SliceHint String... hints) { + public Builder addText(CharSequence text, @Nullable @SliceSubtype String subType, + @SliceHint List<String> hints) { mItems.add(new SliceItem(text, SliceItem.FORMAT_TEXT, subType, hints)); return this; } /** - * Add text to the slice being constructed - * @param subType Optional template-specific type information - * @see {@link SliceItem#getSubType()} - */ - public Builder addText(CharSequence text, @Nullable String subType, - @SliceHint List<String> hints) { - return addText(text, subType, hints.toArray(new String[hints.size()])); - } - - /** * Add an image to the slice being constructed * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} */ - public Builder addIcon(Icon icon, @Nullable String subType, @SliceHint String... hints) { + public Builder addIcon(Icon icon, @Nullable @SliceSubtype String subType, + @SliceHint List<String> hints) { mItems.add(new SliceItem(icon, SliceItem.FORMAT_IMAGE, subType, hints)); return this; } /** - * Add an image to the slice being constructed - * @param subType Optional template-specific type information - * @see {@link SliceItem#getSubType()} - */ - public Builder addIcon(Icon icon, @Nullable String subType, @SliceHint List<String> hints) { - return addIcon(icon, subType, hints.toArray(new String[hints.size()])); - } - - /** * Add remote input to the slice being constructed * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} */ - public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType, + public Slice.Builder addRemoteInput(RemoteInput remoteInput, + @Nullable @SliceSubtype String subType, @SliceHint List<String> hints) { - return addRemoteInput(remoteInput, subType, hints.toArray(new String[hints.size()])); - } - - /** - * Add remote input to the slice being constructed - * @param subType Optional template-specific type information - * @see {@link SliceItem#getSubType()} - */ - public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType, - @SliceHint String... hints) { mItems.add(new SliceItem(remoteInput, SliceItem.FORMAT_REMOTE_INPUT, subType, hints)); return this; @@ -466,41 +460,31 @@ public final class Slice implements Parcelable { * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} */ - public Builder addInt(int value, @Nullable String subType, @SliceHint String... hints) { + public Builder addInt(int value, @Nullable @SliceSubtype String subType, + @SliceHint List<String> hints) { mItems.add(new SliceItem(value, SliceItem.FORMAT_INT, subType, hints)); return this; } /** - * Add an integer to the slice being constructed - * @param subType Optional template-specific type information - * @see {@link SliceItem#getSubType()} + * @deprecated TO BE REMOVED. */ - public Builder addInt(int value, @Nullable String subType, + @Deprecated + public Slice.Builder addTimestamp(long time, @Nullable @SliceSubtype String subType, @SliceHint List<String> hints) { - return addInt(value, subType, hints.toArray(new String[hints.size()])); + return addLong(time, subType, hints); } /** - * Add a timestamp to the slice being constructed + * Add a long to the slice being constructed * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} */ - public Slice.Builder addTimestamp(long time, @Nullable String subType, - @SliceHint String... hints) { - mItems.add(new SliceItem(time, SliceItem.FORMAT_TIMESTAMP, subType, - hints)); - return this; - } - - /** - * Add a timestamp to the slice being constructed - * @param subType Optional template-specific type information - * @see {@link SliceItem#getSubType()} - */ - public Slice.Builder addTimestamp(long time, @Nullable String subType, + public Slice.Builder addLong(long value, @Nullable @SliceSubtype String subType, @SliceHint List<String> hints) { - return addTimestamp(time, subType, hints.toArray(new String[hints.size()])); + mItems.add(new SliceItem(value, SliceItem.FORMAT_LONG, subType, + hints.toArray(new String[hints.size()]))); + return this; } /** @@ -510,26 +494,14 @@ public final class Slice implements Parcelable { * @param subType Optional template-specific type information * @see {@link SliceItem#getSubType()} */ - public Slice.Builder addBundle(Bundle bundle, @Nullable String subType, - @SliceHint String... hints) { + public Slice.Builder addBundle(Bundle bundle, @Nullable @SliceSubtype String subType, + @SliceHint List<String> hints) { mItems.add(new SliceItem(bundle, SliceItem.FORMAT_BUNDLE, subType, hints)); return this; } /** - * Add a bundle to the slice being constructed. - * <p>Expected to be used for support library extension, should not be used for general - * development - * @param subType Optional template-specific type information - * @see {@link SliceItem#getSubType()} - */ - public Slice.Builder addBundle(Bundle bundle, @Nullable String subType, - @SliceHint List<String> hints) { - return addBundle(bundle, subType, hints.toArray(new String[hints.size()])); - } - - /** * Construct the slice. */ public Slice build() { diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java index 9eb2bb896356..019ae4926636 100644 --- a/core/java/android/app/slice/SliceItem.java +++ b/core/java/android/app/slice/SliceItem.java @@ -67,7 +67,7 @@ public final class SliceItem implements Parcelable { FORMAT_IMAGE, FORMAT_ACTION, FORMAT_INT, - FORMAT_TIMESTAMP, + FORMAT_LONG, FORMAT_REMOTE_INPUT, FORMAT_BUNDLE, }) @@ -98,9 +98,14 @@ public final class SliceItem implements Parcelable { */ public static final String FORMAT_INT = "int"; /** - * A {@link SliceItem} that contains a timestamp. + * A {@link SliceItem} that contains a long. */ - public static final String FORMAT_TIMESTAMP = "timestamp"; + public static final String FORMAT_LONG = "long"; + /** + * @deprecated TO BE REMOVED + */ + @Deprecated + public static final String FORMAT_TIMESTAMP = FORMAT_LONG; /** * A {@link SliceItem} that contains a {@link RemoteInput}. */ @@ -123,6 +128,14 @@ public final class SliceItem implements Parcelable { * @hide */ public SliceItem(Object obj, @SliceType String format, String subType, + List<String> hints) { + this(obj, format, subType, hints.toArray(new String[hints.size()])); + } + + /** + * @hide + */ + public SliceItem(Object obj, @SliceType String format, String subType, @Slice.SliceHint String[] hints) { mHints = hints; mFormat = format; diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index aa2cf46891d8..dd892937be71 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -41,6 +41,7 @@ import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -430,9 +431,11 @@ public abstract class SliceProvider extends ContentProvider { return new Slice.Builder(sliceUri) .addAction(createPermissionIntent(context, sliceUri, callingPackage), new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build()) - .addText(getPermissionString(context, callingPackage), null) - .build()) - .addHints(Slice.HINT_LIST_ITEM) + .addText(getPermissionString(context, callingPackage), null, + Collections.emptyList()) + .build(), + null) + .addHints(Arrays.asList(Slice.HINT_LIST_ITEM)) .build(); } diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 8550ac0f32cd..b354e8122a98 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -125,6 +125,20 @@ public final class UsageEvents implements Parcelable { @SystemApi public static final int NOTIFICATION_INTERRUPTION = 12; + /** + * A Slice was pinned by the default launcher or the default assistant. + * @hide + */ + @SystemApi + public static final int SLICE_PINNED_PRIV = 13; + + /** + * A Slice was pinned by an app. + * @hide + */ + @SystemApi + public static final int SLICE_PINNED = 14; + /** @hide */ public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0; diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 59f001c5a7c3..1c3845802dfd 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -107,25 +107,35 @@ public final class UsageStatsManager { public static final int STANDBY_BUCKET_EXEMPTED = 5; /** - * The app was used very recently, currently in use or likely to be used very soon. + * The app was used very recently, currently in use or likely to be used very soon. Standby + * bucket values that are ≤ {@link #STANDBY_BUCKET_ACTIVE} will not be throttled by the + * system while they are in this bucket. Buckets > {@link #STANDBY_BUCKET_ACTIVE} will most + * likely be restricted in some way. For instance, jobs and alarms may be deferred. * @see #getAppStandbyBucket() */ public static final int STANDBY_BUCKET_ACTIVE = 10; /** - * The app was used recently and/or likely to be used in the next few hours. + * The app was used recently and/or likely to be used in the next few hours. Restrictions will + * apply to these apps, such as deferral of jobs and alarms. * @see #getAppStandbyBucket() */ public static final int STANDBY_BUCKET_WORKING_SET = 20; /** * The app was used in the last few days and/or likely to be used in the next few days. + * Restrictions will apply to these apps, such as deferral of jobs and alarms. The delays may be + * greater than for apps in higher buckets (lower bucket value). Bucket values > + * {@link #STANDBY_BUCKET_FREQUENT} may additionally have network access limited. * @see #getAppStandbyBucket() */ public static final int STANDBY_BUCKET_FREQUENT = 30; /** * The app has not be used for several days and/or is unlikely to be used for several days. + * Apps in this bucket will have the most restrictions, including network restrictions, except + * during certain short periods (at a minimum, once a day) when they are allowed to execute + * jobs, access the network, etc. * @see #getAppStandbyBucket() */ public static final int STANDBY_BUCKET_RARE = 40; @@ -393,11 +403,19 @@ public final class UsageStatsManager { /** * Returns the current standby bucket of the calling app. The system determines the standby * state of the app based on app usage patterns. Standby buckets determine how much an app will - * be restricted from running background tasks such as jobs, alarms and certain PendingIntent - * callbacks. + * be restricted from running background tasks such as jobs and alarms. * <p>Restrictions increase progressively from {@link #STANDBY_BUCKET_ACTIVE} to * {@link #STANDBY_BUCKET_RARE}, with {@link #STANDBY_BUCKET_ACTIVE} being the least * restrictive. The battery level of the device might also affect the restrictions. + * <p>Apps in buckets ≤ {@link #STANDBY_BUCKET_ACTIVE} have no standby restrictions imposed. + * Apps in buckets > {@link #STANDBY_BUCKET_FREQUENT} may have network access restricted when + * running in the background. + * <p>The standby state of an app can change at any time either due to a user interaction or a + * system interaction or some algorithm determining that the app can be restricted for a period + * of time before the user has a need for it. + * <p>You can also query the recent history of standby bucket changes by calling + * {@link #queryEventsForSelf(long, long)} and searching for + * {@link UsageEvents.Event#STANDBY_BUCKET_CHANGED}. * * @return the current standby bucket of the calling app. One of STANDBY_BUCKET_* constants. */ diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index bd7961fffca8..3536eea85423 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4183,6 +4183,12 @@ public abstract class PackageManager { public abstract ResolveInfo resolveService(Intent intent, @ResolveInfoFlags int flags); /** + * @hide + */ + public abstract ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags, + @UserIdInt int userId); + + /** * Retrieve all services that can match the given intent. * * @param intent The desired intent as per resolveService(). diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index 2a791ec9a42e..316c796f17ed 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -293,9 +293,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0; // Use compatibility WAL unless an app explicitly set journal/synchronous mode // or DISABLE_COMPATIBILITY_WAL flag is set - final boolean useCompatibilityWal = mConfiguration.journalMode == null - && mConfiguration.syncMode == null - && (mConfiguration.openFlags & SQLiteDatabase.DISABLE_COMPATIBILITY_WAL) == 0; + final boolean useCompatibilityWal = mConfiguration.useCompatibilityWal(); if (walEnabled || useCompatibilityWal) { setJournalMode("WAL"); if (useCompatibilityWal && SQLiteCompatibilityWalFlags.areFlagsSet()) { diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java index dadb95b7a741..e51930237527 100644 --- a/core/java/android/database/sqlite/SQLiteConnectionPool.java +++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java @@ -23,6 +23,7 @@ import android.os.Looper; import android.os.Message; import android.os.OperationCanceledException; import android.os.SystemClock; +import android.text.TextUtils; import android.util.Log; import android.util.PrefixPrinter; import android.util.Printer; @@ -1111,6 +1112,11 @@ public final class SQLiteConnectionPool implements Closeable { printer.println(" Open: " + mIsOpen); printer.println(" Max connections: " + mMaxConnectionPoolSize); printer.println(" Total execution time: " + mTotalExecutionTimeCounter); + printer.println(" Configuration: openFlags=" + mConfiguration.openFlags + + ", useCompatibilityWal=" + mConfiguration.useCompatibilityWal() + + ", journalMode=" + TextUtils.emptyIfNull(mConfiguration.journalMode) + + ", syncMode=" + TextUtils.emptyIfNull(mConfiguration.syncMode)); + if (SQLiteCompatibilityWalFlags.areFlagsSet()) { printer.println(" Compatibility WAL settings: compatibility_wal_supported=" + SQLiteCompatibilityWalFlags diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java index 275043f83743..8b9dfcf51ce5 100644 --- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java +++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java @@ -194,6 +194,11 @@ public final class SQLiteDatabaseConfiguration { return path.equalsIgnoreCase(MEMORY_DB_PATH); } + boolean useCompatibilityWal() { + return journalMode == null && syncMode == null + && (openFlags & SQLiteDatabase.DISABLE_COMPATIBILITY_WAL) == 0; + } + private static String stripPathForLogs(String path) { if (path.indexOf('@') == -1) { return path; diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index d5b052e485c4..390b83fe10cf 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1242,7 +1242,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * from the main sensor along the +X axis (to the right from the user's perspective) will * report <code>(0.03, 0, 0)</code>.</p> * <p>To transform a pixel coordinates between two cameras facing the same direction, first - * the source camera {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for. Then the source + * the source camera {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} must be corrected for. Then the source * camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the source camera, the translation of the source camera * relative to the destination camera, the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination @@ -1256,10 +1256,10 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Units</b>: Meters</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * + * @see CameraCharacteristics#LENS_DISTORTION * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION * @see CameraCharacteristics#LENS_POSE_REFERENCE * @see CameraCharacteristics#LENS_POSE_ROTATION - * @see CameraCharacteristics#LENS_RADIAL_DISTORTION */ @PublicKey public static final Key<float[]> LENS_POSE_TRANSLATION = @@ -1305,7 +1305,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * where <code>(0,0)</code> is the top-left of the * preCorrectionActiveArraySize rectangle. Once the pose and * intrinsic calibration transforms have been applied to a - * world point, then the {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} + * world point, then the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} * transform needs to be applied, and the result adjusted to * be in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate * system (where <code>(0, 0)</code> is the top-left of the @@ -1318,9 +1318,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * coordinate system.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * + * @see CameraCharacteristics#LENS_DISTORTION * @see CameraCharacteristics#LENS_POSE_ROTATION * @see CameraCharacteristics#LENS_POSE_TRANSLATION - * @see CameraCharacteristics#LENS_RADIAL_DISTORTION * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE */ @@ -1362,7 +1362,14 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION + * @deprecated + * <p>This field was inconsistently defined in terms of its + * normalization. Use {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} instead.</p> + * + * @see CameraCharacteristics#LENS_DISTORTION + */ + @Deprecated @PublicKey public static final Key<float[]> LENS_RADIAL_DISTORTION = new Key<float[]>("android.lens.radialDistortion", float[].class); @@ -1387,6 +1394,46 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Integer>("android.lens.poseReference", int.class); /** + * <p>The correction coefficients to correct for this camera device's + * radial and tangential lens distortion.</p> + * <p>Replaces the deprecated {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} field, which was + * inconsistently defined.</p> + * <p>Three radial distortion coefficients <code>[kappa_1, kappa_2, + * kappa_3]</code> and two tangential distortion coefficients + * <code>[kappa_4, kappa_5]</code> that can be used to correct the + * lens's geometric distortion with the mapping equations:</p> + * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) + + * kappa_4 * (2 * x_i * y_i) + kappa_5 * ( r^2 + 2 * x_i^2 ) + * y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) + + * kappa_5 * (2 * x_i * y_i) + kappa_4 * ( r^2 + 2 * y_i^2 ) + * </code></pre> + * <p>Here, <code>[x_c, y_c]</code> are the coordinates to sample in the + * input image that correspond to the pixel values in the + * corrected image at the coordinate <code>[x_i, y_i]</code>:</p> + * <pre><code> correctedImage(x_i, y_i) = sample_at(x_c, y_c, inputImage) + * </code></pre> + * <p>The pixel coordinates are defined in a coordinate system + * related to the {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} + * calibration fields; see that entry for details of the mapping stages. + * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code> + * have <code>(0,0)</code> at the lens optical center <code>[c_x, c_y]</code>, and + * the range of the coordinates depends on the focal length + * terms of the intrinsic calibration.</p> + * <p>Finally, <code>r</code> represents the radial distance from the + * optical center, <code>r^2 = x_i^2 + y_i^2</code>.</p> + * <p>The distortion model used is the Brown-Conrady model.</p> + * <p><b>Units</b>: + * Unitless coefficients.</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION + * @see CameraCharacteristics#LENS_RADIAL_DISTORTION + */ + @PublicKey + public static final Key<float[]> LENS_DISTORTION = + new Key<float[]>("android.lens.distortion", float[].class); + + /** * <p>List of noise reduction modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} that are supported * by this camera device.</p> * <p>Full-capability camera devices will always support OFF and FAST.</p> @@ -1418,6 +1465,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * consideration of future support.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * @deprecated + * <p>Not used in HALv3 or newer; replaced by better partials mechanism</p> + * @hide */ @Deprecated @@ -1808,6 +1857,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>When set to YUV_420_888, application can access the YUV420 data directly.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * @deprecated + * <p>Not used in HALv3 or newer</p> + * @hide */ @Deprecated @@ -1828,6 +1879,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * TODO: Remove property.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * @deprecated + * <p>Not used in HALv3 or newer</p> + * @hide */ @Deprecated @@ -1844,6 +1897,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE * @deprecated + * <p>Not used in HALv3 or newer</p> + * @hide */ @Deprecated @@ -1883,6 +1938,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Units</b>: Nanoseconds</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * @deprecated + * <p>Not used in HALv3 or newer</p> + * @hide */ @Deprecated @@ -1905,6 +1962,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * check if it limits the maximum size for image data.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * @deprecated + * <p>Not used in HALv3 or newer</p> + * @hide */ @Deprecated @@ -2544,7 +2603,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p> * <p>The currently supported fields that correct for geometric distortion are:</p> * <ol> - * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}.</li> + * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}.</li> * </ol> * <p>If all of the geometric distortion fields are no-ops, this rectangle will be the same * as the post-distortion-corrected rectangle given in @@ -2557,7 +2616,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Units</b>: Pixel coordinates on the image sensor</p> * <p>This key is available on all devices.</p> * - * @see CameraCharacteristics#LENS_RADIAL_DISTORTION + * @see CameraCharacteristics#LENS_DISTORTION * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 14c2865cf3e3..7669c018acd1 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -684,7 +684,7 @@ public abstract class CameraMetadata<TKey> { * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li> * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li> * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li> - * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li> + * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}</li> * </ul> * </li> * <li>The {@link CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE android.depth.depthIsExclusive} entry is listed by this device.</li> @@ -702,12 +702,12 @@ public abstract class CameraMetadata<TKey> { * rate, including depth stall time.</p> * * @see CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE + * @see CameraCharacteristics#LENS_DISTORTION * @see CameraCharacteristics#LENS_FACING * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION * @see CameraCharacteristics#LENS_POSE_REFERENCE * @see CameraCharacteristics#LENS_POSE_ROTATION * @see CameraCharacteristics#LENS_POSE_TRANSLATION - * @see CameraCharacteristics#LENS_RADIAL_DISTORTION * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES */ public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; @@ -826,7 +826,7 @@ public abstract class CameraMetadata<TKey> { * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li> * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li> * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li> - * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li> + * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}</li> * </ul> * </li> * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be @@ -852,11 +852,11 @@ public abstract class CameraMetadata<TKey> { * not slow down the frame rate of the capture, as long as the minimum frame duration * of the physical and logical streams are the same.</p> * + * @see CameraCharacteristics#LENS_DISTORTION * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION * @see CameraCharacteristics#LENS_POSE_REFERENCE * @see CameraCharacteristics#LENS_POSE_ROTATION * @see CameraCharacteristics#LENS_POSE_TRANSLATION - * @see CameraCharacteristics#LENS_RADIAL_DISTORTION * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES */ diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index d7c55645e20c..b0cbec71dfe6 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -2791,8 +2791,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li> * </ul></p> * <p><b>Available values for this device:</b><br> - * android.Statistics.info.availableOisDataModes</p> + * {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES android.statistics.info.availableOisDataModes}</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES * @see #STATISTICS_OIS_DATA_MODE_OFF * @see #STATISTICS_OIS_DATA_MODE_ON */ diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index e84e48f8fbcb..633194243512 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2783,7 +2783,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * from the main sensor along the +X axis (to the right from the user's perspective) will * report <code>(0.03, 0, 0)</code>.</p> * <p>To transform a pixel coordinates between two cameras facing the same direction, first - * the source camera {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for. Then the source + * the source camera {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} must be corrected for. Then the source * camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the source camera, the translation of the source camera * relative to the destination camera, the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination @@ -2797,10 +2797,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p><b>Units</b>: Meters</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * + * @see CameraCharacteristics#LENS_DISTORTION * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION * @see CameraCharacteristics#LENS_POSE_REFERENCE * @see CameraCharacteristics#LENS_POSE_ROTATION - * @see CameraCharacteristics#LENS_RADIAL_DISTORTION */ @PublicKey public static final Key<float[]> LENS_POSE_TRANSLATION = @@ -2846,7 +2846,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * where <code>(0,0)</code> is the top-left of the * preCorrectionActiveArraySize rectangle. Once the pose and * intrinsic calibration transforms have been applied to a - * world point, then the {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} + * world point, then the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} * transform needs to be applied, and the result adjusted to * be in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate * system (where <code>(0, 0)</code> is the top-left of the @@ -2859,9 +2859,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * coordinate system.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * + * @see CameraCharacteristics#LENS_DISTORTION * @see CameraCharacteristics#LENS_POSE_ROTATION * @see CameraCharacteristics#LENS_POSE_TRANSLATION - * @see CameraCharacteristics#LENS_RADIAL_DISTORTION * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE */ @@ -2903,12 +2903,59 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION + * @deprecated + * <p>This field was inconsistently defined in terms of its + * normalization. Use {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} instead.</p> + * + * @see CameraCharacteristics#LENS_DISTORTION + */ + @Deprecated @PublicKey public static final Key<float[]> LENS_RADIAL_DISTORTION = new Key<float[]>("android.lens.radialDistortion", float[].class); /** + * <p>The correction coefficients to correct for this camera device's + * radial and tangential lens distortion.</p> + * <p>Replaces the deprecated {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} field, which was + * inconsistently defined.</p> + * <p>Three radial distortion coefficients <code>[kappa_1, kappa_2, + * kappa_3]</code> and two tangential distortion coefficients + * <code>[kappa_4, kappa_5]</code> that can be used to correct the + * lens's geometric distortion with the mapping equations:</p> + * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) + + * kappa_4 * (2 * x_i * y_i) + kappa_5 * ( r^2 + 2 * x_i^2 ) + * y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) + + * kappa_5 * (2 * x_i * y_i) + kappa_4 * ( r^2 + 2 * y_i^2 ) + * </code></pre> + * <p>Here, <code>[x_c, y_c]</code> are the coordinates to sample in the + * input image that correspond to the pixel values in the + * corrected image at the coordinate <code>[x_i, y_i]</code>:</p> + * <pre><code> correctedImage(x_i, y_i) = sample_at(x_c, y_c, inputImage) + * </code></pre> + * <p>The pixel coordinates are defined in a coordinate system + * related to the {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} + * calibration fields; see that entry for details of the mapping stages. + * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code> + * have <code>(0,0)</code> at the lens optical center <code>[c_x, c_y]</code>, and + * the range of the coordinates depends on the focal length + * terms of the intrinsic calibration.</p> + * <p>Finally, <code>r</code> represents the radial distance from the + * optical center, <code>r^2 = x_i^2 + y_i^2</code>.</p> + * <p>The distortion model used is the Brown-Conrady model.</p> + * <p><b>Units</b>: + * Unitless coefficients.</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION + * @see CameraCharacteristics#LENS_RADIAL_DISTORTION + */ + @PublicKey + public static final Key<float[]> LENS_DISTORTION = + new Key<float[]>("android.lens.distortion", float[].class); + + /** * <p>Mode of operation for the noise reduction algorithm.</p> * <p>The noise reduction algorithm attempts to improve image quality by removing * excessive noise added by the capture process, especially in dark conditions.</p> @@ -2981,6 +3028,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * Optional. Default value is FINAL.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * @deprecated + * <p>Not used in HALv3 or newer</p> + * @hide */ @Deprecated @@ -2997,6 +3046,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * > 0</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * @deprecated + * <p>Not used in HALv3 or newer</p> + * @hide */ @Deprecated @@ -3777,6 +3828,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * * @see CaptureRequest#COLOR_CORRECTION_GAINS * @deprecated + * <p>Never fully implemented or specified; do not use</p> + * @hide */ @Deprecated @@ -3801,6 +3854,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * regardless of the android.control.* current values.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * @deprecated + * <p>Never fully implemented or specified; do not use</p> + * @hide */ @Deprecated @@ -3919,8 +3974,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li> * </ul></p> * <p><b>Available values for this device:</b><br> - * android.Statistics.info.availableOisDataModes</p> + * {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES android.statistics.info.availableOisDataModes}</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES * @see #STATISTICS_OIS_DATA_MODE_OFF * @see #STATISTICS_OIS_DATA_MODE_ON */ diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index e81fbeed34b0..a3b2d22deccb 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -668,6 +668,45 @@ public final class DisplayManager { } /** + * Gets the global display brightness configuration or the default curve if one hasn't been set. + * + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) + public BrightnessConfiguration getBrightnessConfiguration() { + return getBrightnessConfigurationForUser(mContext.getUserId()); + } + + /** + * Gets the global display brightness configuration or the default curve if one hasn't been set + * for a specific user. + * + * Note this requires the INTERACT_ACROSS_USERS permission if getting the configuration for a + * user other than the one you're currently running as. + * + * @hide + */ + public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) { + return mGlobal.getBrightnessConfigurationForUser(userId); + } + + /** + * Gets the default global display brightness configuration or null one hasn't + * been configured. + * + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) + @Nullable + public BrightnessConfiguration getDefaultBrightnessConfiguration() { + return mGlobal.getDefaultBrightnessConfiguration(); + } + + /** * Temporarily sets the brightness of the display. * <p> * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission. diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index d7f7c865b8fb..1f67a6bf71a7 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -490,6 +490,32 @@ public final class DisplayManagerGlobal { } /** + * Gets the global brightness configuration for a given user or null if one hasn't been set. + * + * @hide + */ + public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) { + try { + return mDm.getBrightnessConfigurationForUser(userId); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Gets the default brightness configuration or null if one hasn't been configured. + * + * @hide + */ + public BrightnessConfiguration getDefaultBrightnessConfiguration() { + try { + return mDm.getDefaultBrightnessConfiguration(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** * Temporarily sets the brightness of the display. * <p> * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission. diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 0571ae1fe825..9fcb9d3fc265 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -96,6 +96,14 @@ interface IDisplayManager { void setBrightnessConfigurationForUser(in BrightnessConfiguration c, int userId, String packageName); + // Gets the global brightness configuration for a given user. Requires + // CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user is not + // the same as the calling user. + BrightnessConfiguration getBrightnessConfigurationForUser(int userId); + + // Gets the default brightness configuration if configured. + BrightnessConfiguration getDefaultBrightnessConfiguration(); + // Temporarily sets the display brightness. void setTemporaryBrightness(int brightness); diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index b635088cb11b..dde8a3327172 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -16,6 +16,14 @@ package android.hardware.soundtrigger; +import static android.system.OsConstants.EINVAL; +import static android.system.OsConstants.ENODEV; +import static android.system.OsConstants.ENOSYS; +import static android.system.OsConstants.EPERM; +import static android.system.OsConstants.EPIPE; + +import android.annotation.Nullable; +import android.annotation.SystemApi; import android.media.AudioFormat; import android.os.Handler; import android.os.Parcel; @@ -25,22 +33,33 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.UUID; -import static android.system.OsConstants.*; - /** * The SoundTrigger class provides access via JNI to the native service managing * the sound trigger HAL. * * @hide */ +@SystemApi public class SoundTrigger { + private SoundTrigger() { + } + + /** + * Status code used when the operation succeeded + */ public static final int STATUS_OK = 0; + /** @hide */ public static final int STATUS_ERROR = Integer.MIN_VALUE; + /** @hide */ public static final int STATUS_PERMISSION_DENIED = -EPERM; + /** @hide */ public static final int STATUS_NO_INIT = -ENODEV; + /** @hide */ public static final int STATUS_BAD_VALUE = -EINVAL; + /** @hide */ public static final int STATUS_DEAD_OBJECT = -EPIPE; + /** @hide */ public static final int STATUS_INVALID_OPERATION = -ENOSYS; /***************************************************************************** @@ -48,6 +67,8 @@ public class SoundTrigger { * managed by the native sound trigger service. Each module has a unique * ID used to target any API call to this paricular module. Module * properties are returned by listModules() method. + * + * @hide ****************************************************************************/ public static class ModuleProperties implements Parcelable { /** Unique module ID provided by the native service */ @@ -187,6 +208,8 @@ public class SoundTrigger { * implementation to detect a particular sound pattern. * A specialized version {@link KeyphraseSoundModel} is defined for key phrase * sound models. + * + * @hide ****************************************************************************/ public static class SoundModel { /** Undefined sound model type */ @@ -261,6 +284,8 @@ public class SoundTrigger { /***************************************************************************** * A Keyphrase describes a key phrase that can be detected by a * {@link KeyphraseSoundModel} + * + * @hide ****************************************************************************/ public static class Keyphrase implements Parcelable { /** Unique identifier for this keyphrase */ @@ -382,6 +407,8 @@ public class SoundTrigger { * A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases. * It contains data needed by the hardware to detect a certain number of key phrases * and the list of corresponding {@link Keyphrase} descriptors. + * + * @hide ****************************************************************************/ public static class KeyphraseSoundModel extends SoundModel implements Parcelable { /** Key phrases in this sound model */ @@ -468,6 +495,8 @@ public class SoundTrigger { /***************************************************************************** * A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound * patterns. + * + * @hide ****************************************************************************/ public static class GenericSoundModel extends SoundModel implements Parcelable { @@ -524,52 +553,115 @@ public class SoundTrigger { /** * Modes for key phrase recognition */ - /** Simple recognition of the key phrase */ + + /** + * Simple recognition of the key phrase + * + * @hide + */ public static final int RECOGNITION_MODE_VOICE_TRIGGER = 0x1; - /** Trigger only if one user is identified */ + /** + * Trigger only if one user is identified + * + * @hide + */ public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 0x2; - /** Trigger only if one user is authenticated */ + /** + * Trigger only if one user is authenticated + * + * @hide + */ public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4; /** * Status codes for {@link RecognitionEvent} */ - /** Recognition success */ + /** + * Recognition success + * + * @hide + */ public static final int RECOGNITION_STATUS_SUCCESS = 0; - /** Recognition aborted (e.g. capture preempted by anotehr use case */ + /** + * Recognition aborted (e.g. capture preempted by anotehr use case + * + * @hide + */ public static final int RECOGNITION_STATUS_ABORT = 1; - /** Recognition failure */ + /** + * Recognition failure + * + * @hide + */ public static final int RECOGNITION_STATUS_FAILURE = 2; /** * A RecognitionEvent is provided by the - * {@link StatusListener#onRecognition(RecognitionEvent)} + * {@code StatusListener#onRecognition(RecognitionEvent)} * callback upon recognition success or failure. */ - public static class RecognitionEvent implements Parcelable { - /** Recognition status e.g {@link #RECOGNITION_STATUS_SUCCESS} */ + public static class RecognitionEvent { + /** + * Recognition status e.g RECOGNITION_STATUS_SUCCESS + * + * @hide + */ public final int status; - /** Sound Model corresponding to this event callback */ + /** + * + * Sound Model corresponding to this event callback + * + * @hide + */ public final int soundModelHandle; - /** True if it is possible to capture audio from this utterance buffered by the hardware */ + /** + * True if it is possible to capture audio from this utterance buffered by the hardware + * + * @hide + */ public final boolean captureAvailable; - /** Audio session ID to be used when capturing the utterance with an AudioRecord - * if captureAvailable() is true. */ + /** + * Audio session ID to be used when capturing the utterance with an AudioRecord + * if captureAvailable() is true. + * + * @hide + */ public final int captureSession; - /** Delay in ms between end of model detection and start of audio available for capture. - * A negative value is possible (e.g. if keyphrase is also available for capture) */ + /** + * Delay in ms between end of model detection and start of audio available for capture. + * A negative value is possible (e.g. if keyphrase is also available for capture) + * + * @hide + */ public final int captureDelayMs; - /** Duration in ms of audio captured before the start of the trigger. 0 if none. */ + /** + * Duration in ms of audio captured before the start of the trigger. 0 if none. + * + * @hide + */ public final int capturePreambleMs; - /** True if the trigger (key phrase capture is present in binary data */ + /** + * True if the trigger (key phrase capture is present in binary data + * + * @hide + */ public final boolean triggerInData; - /** Audio format of either the trigger in event data or to use for capture of the - * rest of the utterance */ - public AudioFormat captureFormat; - /** Opaque data for use by system applications who know about voice engine internals, - * typically during enrollment. */ + /** + * Audio format of either the trigger in event data or to use for capture of the + * rest of the utterance + * + * @hide + */ + public final AudioFormat captureFormat; + /** + * Opaque data for use by system applications who know about voice engine internals, + * typically during enrollment. + * + * @hide + */ public final byte[] data; + /** @hide */ public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat, byte[] data) { @@ -584,6 +676,46 @@ public class SoundTrigger { this.data = data; } + /** + * Check if is possible to capture audio from this utterance buffered by the hardware. + * + * @return {@code true} iff a capturing is possible + */ + public boolean isCaptureAvailable() { + return captureAvailable; + } + + /** + * Get the audio format of either the trigger in event data or to use for capture of the + * rest of the utterance + * + * @return the audio format + */ + @Nullable public AudioFormat getCaptureFormat() { + return captureFormat; + } + + /** + * Get Audio session ID to be used when capturing the utterance with an {@link AudioRecord} + * if {@link #isCaptureAvailable()} is true. + * + * @return The id of the capture session + */ + public int getCaptureSession() { + return captureSession; + } + + /** + * Get the opaque data for use by system applications who know about voice engine + * internals, typically during enrollment. + * + * @return The data of the event + */ + public byte[] getData() { + return data; + } + + /** @hide */ public static final Parcelable.Creator<RecognitionEvent> CREATOR = new Parcelable.Creator<RecognitionEvent>() { public RecognitionEvent createFromParcel(Parcel in) { @@ -595,6 +727,7 @@ public class SoundTrigger { } }; + /** @hide */ protected static RecognitionEvent fromParcel(Parcel in) { int status = in.readInt(); int soundModelHandle = in.readInt(); @@ -619,12 +752,12 @@ public class SoundTrigger { captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data); } - @Override + /** @hide */ public int describeContents() { return 0; } - @Override + /** @hide */ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(status); dest.writeInt(soundModelHandle); @@ -726,6 +859,8 @@ public class SoundTrigger { * A RecognitionConfig is provided to * {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the * recognition request. + * + * @hide */ public static class RecognitionConfig implements Parcelable { /** True if the DSP should capture the trigger sound and make it available for further @@ -744,7 +879,7 @@ public class SoundTrigger { public final byte[] data; public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, - KeyphraseRecognitionExtra keyphrases[], byte[] data) { + KeyphraseRecognitionExtra[] keyphrases, byte[] data) { this.captureRequested = captureRequested; this.allowMultipleTriggers = allowMultipleTriggers; this.keyphrases = keyphrases; @@ -799,6 +934,8 @@ public class SoundTrigger { * When used in a {@link RecognitionConfig} it indicates the minimum confidence level that * should trigger a recognition. * - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}. + * + * @hide */ public static class ConfidenceLevel implements Parcelable { public final int userId; @@ -872,6 +1009,8 @@ public class SoundTrigger { /** * Additional data conveyed by a {@link KeyphraseRecognitionEvent} * for a key phrase detection. + * + * @hide */ public static class KeyphraseRecognitionExtra implements Parcelable { /** The keyphrase ID */ @@ -970,8 +1109,10 @@ public class SoundTrigger { /** * Specialized {@link RecognitionEvent} for a key phrase detection. + * + * @hide */ - public static class KeyphraseRecognitionEvent extends RecognitionEvent { + public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable { /** Indicates if the key phrase is present in the buffered audio available for capture */ public final KeyphraseRecognitionExtra[] keyphraseExtras; @@ -1091,8 +1232,10 @@ public class SoundTrigger { /** * Sub-class of RecognitionEvent specifically for sound-trigger based sound * models(non-keyphrase). Currently does not contain any additional fields. + * + * @hide */ - public static class GenericRecognitionEvent extends RecognitionEvent { + public static class GenericRecognitionEvent extends RecognitionEvent implements Parcelable { public GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat, @@ -1140,13 +1283,19 @@ public class SoundTrigger { /** * Status codes for {@link SoundModelEvent} */ - /** Sound Model was updated */ + /** + * Sound Model was updated + * + * @hide + */ public static final int SOUNDMODEL_STATUS_UPDATED = 0; /** * A SoundModelEvent is provided by the * {@link StatusListener#onSoundModelUpdate(SoundModelEvent)} * callback when a sound model has been updated by the implementation + * + * @hide */ public static class SoundModelEvent implements Parcelable { /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */ @@ -1231,9 +1380,17 @@ public class SoundTrigger { * Native service state. {@link StatusListener#onServiceStateChange(int)} */ // Keep in sync with system/core/include/system/sound_trigger.h - /** Sound trigger service is enabled */ + /** + * Sound trigger service is enabled + * + * @hide + */ public static final int SERVICE_STATE_ENABLED = 0; - /** Sound trigger service is disabled */ + /** + * Sound trigger service is disabled + * + * @hide + */ public static final int SERVICE_STATE_DISABLED = 1; /** @@ -1245,6 +1402,8 @@ public class SoundTrigger { * - {@link #STATUS_NO_INIT} if the native service cannot be reached * - {@link #STATUS_BAD_VALUE} if modules is null * - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails + * + * @hide */ public static native int listModules(ArrayList <ModuleProperties> modules); @@ -1256,6 +1415,8 @@ public class SoundTrigger { * @param handler the Handler that will receive the callabcks. Can be null if default handler * is OK. * @return a valid sound module in case of success or null in case of error. + * + * @hide */ public static SoundTriggerModule attachModule(int moduleId, StatusListener listener, @@ -1270,6 +1431,8 @@ public class SoundTrigger { /** * Interface provided by the client application when attaching to a {@link SoundTriggerModule} * to received recognition and error notifications. + * + * @hide */ public static interface StatusListener { /** diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl index 3ce0283d7f23..3a3ddcc48360 100644 --- a/core/java/android/net/IIpSecService.aidl +++ b/core/java/android/net/IIpSecService.aidl @@ -16,6 +16,7 @@ package android.net; +import android.net.LinkAddress; import android.net.Network; import android.net.IpSecConfig; import android.net.IpSecUdpEncapResponse; @@ -48,11 +49,11 @@ interface IIpSecService void addAddressToTunnelInterface( int tunnelResourceId, - String localAddr); + in LinkAddress localAddr); void removeAddressFromTunnelInterface( int tunnelResourceId, - String localAddr); + in LinkAddress localAddr); void deleteTunnelInterface(int resourceId); diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java index c69a4d4c0bee..f4b328e412e0 100644 --- a/core/java/android/net/IpSecAlgorithm.java +++ b/core/java/android/net/IpSecAlgorithm.java @@ -38,6 +38,13 @@ public final class IpSecAlgorithm implements Parcelable { private static final String TAG = "IpSecAlgorithm"; /** + * Null cipher. + * + * @hide + */ + public static final String CRYPT_NULL = "ecb(cipher_null)"; + + /** * AES-CBC Encryption/Ciphering Algorithm. * * <p>Valid lengths for this key are {128, 192, 256}. diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index fbf305633a71..4e1f83430abf 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -656,10 +656,14 @@ public final class IpSecManager { * tunneled traffic. * * @param address the local address for traffic inside the tunnel - * @throws IOException if the address could not be added * @hide */ - public void addAddress(LinkAddress address) throws IOException { + public void addAddress(LinkAddress address) { + try { + mService.addAddressToTunnelInterface(mResourceId, address); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -668,10 +672,14 @@ public final class IpSecManager { * <p>Remove an address which was previously added to the IpSecTunnelInterface * * @param address to be removed - * @throws IOException if the address could not be removed * @hide */ - public void removeAddress(LinkAddress address) throws IOException { + public void removeAddress(LinkAddress address) { + try { + mService.removeAddressFromTunnelInterface(mResourceId, address); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } private IpSecTunnelInterface(@NonNull IIpSecService service, diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 1681f11fa526..13e4e38df5f6 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -65,4 +65,7 @@ interface IPowerManager // sets the attention light (used by phone app only) void setAttentionLight(boolean on, int color); + + // controls whether PowerManager should doze after the screen turns off or not + void setDozeAfterScreenOff(boolean on); } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 66fa6294ecee..c00100b7e0cf 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1280,6 +1280,19 @@ public final class PowerManager { } /** + * If true, the doze component is not started until after the screen has been + * turned off and the screen off animation has been performed. + * @hide + */ + public void setDozeAfterScreenOff(boolean dozeAfterScreenOf) { + try { + mService.setDozeAfterScreenOff(dozeAfterScreenOf); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns the reason the phone was last shutdown. Calling app must have the * {@link android.Manifest.permission#DEVICE_POWER} permission to request this information. * @return Reason for shutdown as an int, {@link #SHUTDOWN_REASON_UNKNOWN} if the file could diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 42fc5429c4c2..b9aad113c677 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -783,6 +783,21 @@ public final class Settings { "android.settings.APPLICATION_DETAILS_SETTINGS"; /** + * Activity Action: Show the "Open by Default" page in a particular application's details page. + * <p> + * In some cases, a matching Activity may not exist, so ensure you safeguard against this. + * <p> + * Input: The Intent's data URI specifies the application package name + * to be shown, with the "package" scheme. That is "package:com.my.app". + * <p> + * Output: Nothing. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_APPLICATION_DETAILS_SETTINGS_OPEN_BY_DEFAULT_PAGE = + "android.settings.APPLICATION_DETAILS_SETTINGS_OPEN_BY_DEFAULT_PAGE"; + + /** * Activity Action: Show list of applications that have been running * foreground services (to the user "running in the background"). * <p> @@ -11592,6 +11607,24 @@ public final class Settings { "hidden_api_blacklist_exemptions"; /** + * Timeout for a single {@link android.media.soundtrigger.SoundTriggerDetectionService} + * operation (in ms). + * + * @hide + */ + public static final String SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT = + "sound_trigger_detection_service_op_timeout"; + + /** + * Maximum number of {@link android.media.soundtrigger.SoundTriggerDetectionService} + * operations per day. + * + * @hide + */ + public static final String MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY = + "max_sound_trigger_detection_service_ops_per_day"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * @@ -12327,6 +12360,16 @@ public final class Settings { "zram_enabled"; /** + * Whether we have enable CPU frequency scaling for this device. + * For Wear, default is disable. + * + * The value is "1" for enable, "0" for disable. + * @hide + */ + public static final String CPU_SCALING_ENABLED = + "cpu_frequency_scaling_enabled"; + + /** * Configuration flags for smart replies in notifications. * This is encoded as a key=value list, separated by commas. Ex: * diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java index 7523afdf8041..10c1c9ee1b90 100644 --- a/core/java/android/security/keystore/recovery/RecoveryController.java +++ b/core/java/android/security/keystore/recovery/RecoveryController.java @@ -547,10 +547,7 @@ public class RecoveryController { if (grantAlias == null) { throw new InternalRecoveryServiceException("null grant alias"); } - return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( - mKeyStore, - grantAlias, - KeyStore.UID_SELF); + return getKeyFromGrant(grantAlias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (UnrecoverableKeyException e) { @@ -581,10 +578,7 @@ public class RecoveryController { if (grantAlias == null) { throw new InternalRecoveryServiceException("Null grant alias"); } - return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( - mKeyStore, - grantAlias, - KeyStore.UID_SELF); + return getKeyFromGrant(alias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (UnrecoverableKeyException e) { @@ -614,10 +608,7 @@ public class RecoveryController { if (grantAlias == null) { return null; } - return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( - mKeyStore, - grantAlias, - KeyStore.UID_SELF); + return getKeyFromGrant(grantAlias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { @@ -626,6 +617,16 @@ public class RecoveryController { } /** + * Returns the key with the given {@code grantAlias}. + */ + Key getKeyFromGrant(String grantAlias) throws UnrecoverableKeyException { + return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( + mKeyStore, + grantAlias, + KeyStore.UID_SELF); + } + + /** * Removes a key called {@code alias} from the recoverable key store. * * @param alias The key alias. diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java index 137dd8946c9a..744bfa3029be 100644 --- a/core/java/android/security/keystore/recovery/RecoverySession.java +++ b/core/java/android/security/keystore/recovery/RecoverySession.java @@ -16,17 +16,22 @@ package android.security.keystore.recovery; +import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.os.RemoteException; import android.os.ServiceSpecificException; +import android.util.ArrayMap; import android.util.Log; +import java.security.Key; import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; import java.security.cert.CertPath; import java.security.cert.CertificateException; import java.util.List; +import java.util.Locale; import java.util.Map; /** @@ -136,6 +141,63 @@ public class RecoverySession implements AutoCloseable { byte[] recoveryClaim = mRecoveryController.getBinder().startRecoverySessionWithCertPath( mSessionId, + /*rootCertificateAlias=*/ "", // Use the default root cert + recoveryCertPath, + vaultParams, + vaultChallenge, + secrets); + return recoveryClaim; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (ServiceSpecificException e) { + if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT + || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) { + throw new CertificateException(e.getMessage()); + } + throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); + } + } + + /** + * Starts a recovery session and returns a blob with proof of recovery secret possession. + * The method generates a symmetric key for a session, which trusted remote device can use to + * return recovery key. + * + * @param rootCertificateAlias The alias of the root certificate that is already in the Android + * OS. The root certificate will be used for validating {@code verifierCertPath}. + * @param verifierCertPath The certificate path used to create the recovery blob on the source + * device. Keystore will verify the certificate path by using the root of trust. + * @param vaultParams Must match the parameters in the corresponding field in the recovery blob. + * Used to limit number of guesses. + * @param vaultChallenge Data passed from server for this recovery session and used to prevent + * replay attacks. + * @param secrets Secrets provided by user, the method only uses type and secret fields. + * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is + * encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric + * key and parameters necessary to identify the counter with the number of failed recovery + * attempts. + * @throws CertificateException if the {@code verifierCertPath} is invalid. + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) + @NonNull public byte[] start( + @NonNull String rootCertificateAlias, + @NonNull CertPath verifierCertPath, + @NonNull byte[] vaultParams, + @NonNull byte[] vaultChallenge, + @NonNull List<KeyChainProtectionParams> secrets) + throws CertificateException, InternalRecoveryServiceException { + // Wrap the CertPath in a Parcelable so it can be passed via Binder calls. + RecoveryCertPath recoveryCertPath = + RecoveryCertPath.createRecoveryCertPath(verifierCertPath); + try { + byte[] recoveryClaim = + mRecoveryController.getBinder().startRecoverySessionWithCertPath( + mSessionId, + rootCertificateAlias, recoveryCertPath, vaultParams, vaultChallenge, @@ -187,6 +249,63 @@ public class RecoverySession implements AutoCloseable { } /** + * Imports key chain snapshot recovered from a remote vault. + * + * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session. + * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob + * and session. + * @throws SessionExpiredException if {@code session} has since been closed. + * @throws DecryptionFailedException if unable to decrypt the snapshot. + * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service. + * + * @hide + */ + @RequiresPermission(Manifest.permission.RECOVER_KEYSTORE) + public Map<String, Key> recoverKeyChainSnapshot( + @NonNull byte[] recoveryKeyBlob, + @NonNull List<WrappedApplicationKey> applicationKeys + ) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException { + try { + Map<String, String> grantAliases = mRecoveryController + .getBinder() + .recoverKeyChainSnapshot(mSessionId, recoveryKeyBlob, applicationKeys); + return getKeysFromGrants(grantAliases); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (ServiceSpecificException e) { + if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) { + throw new DecryptionFailedException(e.getMessage()); + } + if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) { + throw new SessionExpiredException(e.getMessage()); + } + throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); + } + } + + /** Given a map from alias to grant alias, returns a map from alias to a {@link Key} handle. */ + private Map<String, Key> getKeysFromGrants(Map<String, String> grantAliases) + throws InternalRecoveryServiceException { + ArrayMap<String, Key> keysByAlias = new ArrayMap<>(grantAliases.size()); + for (String alias : grantAliases.keySet()) { + String grantAlias = grantAliases.get(alias); + Key key; + try { + key = mRecoveryController.getKeyFromGrant(grantAlias); + } catch (UnrecoverableKeyException e) { + throw new InternalRecoveryServiceException( + String.format( + Locale.US, + "Failed to get key '%s' from grant '%s'", + alias, + grantAlias), e); + } + keysByAlias.put(alias, key); + } + return keysByAlias; + } + + /** * An internal session ID, used by the framework to match recovery claims to snapshot responses. * * @hide diff --git a/core/java/android/security/backup/TrustedRootCertificates.java b/core/java/android/security/keystore/recovery/TrustedRootCertificates.java index ed922eda4d10..4bdde8a2f5b2 100644 --- a/core/java/android/security/backup/TrustedRootCertificates.java +++ b/core/java/android/security/keystore/recovery/TrustedRootCertificates.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package android.security.backup; +package android.security.keystore.recovery; -import static android.security.backup.X509CertificateParsingUtils.decodeBase64Cert; +import static android.security.keystore.recovery.X509CertificateParsingUtils.decodeBase64Cert; import android.util.ArrayMap; @@ -77,10 +77,27 @@ public class TrustedRootCertificates { private static final int NUMBER_OF_ROOT_CERTIFICATES = 1; + private static final ArrayMap<String, X509Certificate> ALL_ROOT_CERTIFICATES = + constructRootCertificateMap(); + /** * Returns all available root certificates, keyed by alias. */ public static Map<String, X509Certificate> listRootCertificates() { + return new ArrayMap(ALL_ROOT_CERTIFICATES); + } + + /** + * Gets a root certificate referenced by the given {@code alias}. + * + * @param alias the alias of the certificate + * @return the certificate referenced by the alias, or null if such a certificate doesn't exist. + */ + public static X509Certificate getRootCertificate(String alias) { + return ALL_ROOT_CERTIFICATES.get(alias); + } + + private static ArrayMap<String, X509Certificate> constructRootCertificateMap() { ArrayMap<String, X509Certificate> certificates = new ArrayMap<>(NUMBER_OF_ROOT_CERTIFICATES); certificates.put( diff --git a/core/java/android/security/backup/X509CertificateParsingUtils.java b/core/java/android/security/keystore/recovery/X509CertificateParsingUtils.java index 30495decd917..fa72c836b5df 100644 --- a/core/java/android/security/backup/X509CertificateParsingUtils.java +++ b/core/java/android/security/keystore/recovery/X509CertificateParsingUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.security.backup; +package android.security.keystore.recovery; import java.io.ByteArrayInputStream; import java.io.InputStream; diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index ea10ae7b3808..3726e66d01f9 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1418,6 +1418,7 @@ public abstract class NotificationListenerService extends Service { private ArrayList<SnoozeCriterion> mSnoozeCriteria; private boolean mShowBadge; private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL; + private boolean mHidden; public Ranking() {} @@ -1557,6 +1558,16 @@ public abstract class NotificationListenerService extends Service { } /** + * Returns whether the app that posted this notification is suspended, so this notification + * should be hidden. + * + * @return true if the notification should be hidden, false otherwise. + */ + public boolean isSuspended() { + return mHidden; + } + + /** * @hide */ @VisibleForTesting @@ -1565,7 +1576,7 @@ public abstract class NotificationListenerService extends Service { CharSequence explanation, String overrideGroupKey, NotificationChannel channel, ArrayList<String> overridePeople, ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge, - int userSentiment) { + int userSentiment, boolean hidden) { mKey = key; mRank = rank; mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW; @@ -1580,6 +1591,7 @@ public abstract class NotificationListenerService extends Service { mSnoozeCriteria = snoozeCriteria; mShowBadge = showBadge; mUserSentiment = userSentiment; + mHidden = hidden; } /** @@ -1628,6 +1640,7 @@ public abstract class NotificationListenerService extends Service { private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria; private ArrayMap<String, Boolean> mShowBadge; private ArrayMap<String, Integer> mUserSentiment; + private ArrayMap<String, Boolean> mHidden; private RankingMap(NotificationRankingUpdate rankingUpdate) { mRankingUpdate = rankingUpdate; @@ -1656,7 +1669,7 @@ public abstract class NotificationListenerService extends Service { getVisibilityOverride(key), getSuppressedVisualEffects(key), getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key), getChannel(key), getOverridePeople(key), getSnoozeCriteria(key), - getShowBadge(key), getUserSentiment(key)); + getShowBadge(key), getUserSentiment(key), getHidden(key)); return rank >= 0; } @@ -1784,6 +1797,16 @@ public abstract class NotificationListenerService extends Service { ? Ranking.USER_SENTIMENT_NEUTRAL : userSentiment.intValue(); } + private boolean getHidden(String key) { + synchronized (this) { + if (mHidden == null) { + buildHiddenLocked(); + } + } + Boolean hidden = mHidden.get(key); + return hidden == null ? false : hidden.booleanValue(); + } + // Locked by 'this' private void buildRanksLocked() { String[] orderedKeys = mRankingUpdate.getOrderedKeys(); @@ -1892,6 +1915,15 @@ public abstract class NotificationListenerService extends Service { } } + // Locked by 'this' + private void buildHiddenLocked() { + Bundle hidden = mRankingUpdate.getHidden(); + mHidden = new ArrayMap<>(hidden.size()); + for (String key : hidden.keySet()) { + mHidden.put(key, hidden.getBoolean(key)); + } + } + // ----------- Parcelable @Override diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java index 6d51db096a27..00c47ec0ee89 100644 --- a/core/java/android/service/notification/NotificationRankingUpdate.java +++ b/core/java/android/service/notification/NotificationRankingUpdate.java @@ -36,12 +36,13 @@ public class NotificationRankingUpdate implements Parcelable { private final Bundle mSnoozeCriteria; private final Bundle mShowBadge; private final Bundle mUserSentiment; + private final Bundle mHidden; public NotificationRankingUpdate(String[] keys, String[] interceptedKeys, Bundle visibilityOverrides, Bundle suppressedVisualEffects, int[] importance, Bundle explanation, Bundle overrideGroupKeys, Bundle channels, Bundle overridePeople, Bundle snoozeCriteria, - Bundle showBadge, Bundle userSentiment) { + Bundle showBadge, Bundle userSentiment, Bundle hidden) { mKeys = keys; mInterceptedKeys = interceptedKeys; mVisibilityOverrides = visibilityOverrides; @@ -54,6 +55,7 @@ public class NotificationRankingUpdate implements Parcelable { mSnoozeCriteria = snoozeCriteria; mShowBadge = showBadge; mUserSentiment = userSentiment; + mHidden = hidden; } public NotificationRankingUpdate(Parcel in) { @@ -70,6 +72,7 @@ public class NotificationRankingUpdate implements Parcelable { mSnoozeCriteria = in.readBundle(); mShowBadge = in.readBundle(); mUserSentiment = in.readBundle(); + mHidden = in.readBundle(); } @Override @@ -91,6 +94,7 @@ public class NotificationRankingUpdate implements Parcelable { out.writeBundle(mSnoozeCriteria); out.writeBundle(mShowBadge); out.writeBundle(mUserSentiment); + out.writeBundle(mHidden); } public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR @@ -151,4 +155,8 @@ public class NotificationRankingUpdate implements Parcelable { public Bundle getUserSentiment() { return mUserSentiment; } + + public Bundle getHidden() { + return mHidden; + } } diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java index 40e84b9604e7..61277e285b86 100644 --- a/core/java/android/service/trust/TrustAgentService.java +++ b/core/java/android/service/trust/TrustAgentService.java @@ -545,7 +545,7 @@ public class TrustAgentService extends Service { */ public final void unlockUserWithToken(long handle, byte[] token, UserHandle user) { UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); - if (um.isUserUnlocked()) { + if (um.isUserUnlocked(user)) { Slog.i(TAG, "User already unlocked"); return; } diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java index aafcf44a73fc..980f47043269 100644 --- a/core/java/android/text/MeasuredParagraph.java +++ b/core/java/android/text/MeasuredParagraph.java @@ -675,7 +675,7 @@ public class MeasuredParagraph { /** * This only works if the MeasuredParagraph is computed with buildForStaticLayout. */ - @IntRange(from = 0) int getMemoryUsage() { + public @IntRange(from = 0) int getMemoryUsage() { return nGetMemoryUsage(mNativePtr); } diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java index b74019373f57..9458184ee888 100644 --- a/core/java/android/text/PrecomputedText.java +++ b/core/java/android/text/PrecomputedText.java @@ -19,7 +19,6 @@ package android.text; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import android.util.IntArray; import com.android.internal.util.Preconditions; @@ -267,6 +266,22 @@ public class PrecomputedText implements Spanned { } }; + /** @hide */ + public static class ParagraphInfo { + public final @IntRange(from = 0) int paragraphEnd; + public final @NonNull MeasuredParagraph measured; + + /** + * @param paraEnd the end offset of this paragraph + * @param measured a measured paragraph + */ + public ParagraphInfo(@IntRange(from = 0) int paraEnd, @NonNull MeasuredParagraph measured) { + this.paragraphEnd = paraEnd; + this.measured = measured; + } + }; + + // The original text. private final @NonNull SpannedString mText; @@ -278,11 +293,8 @@ public class PrecomputedText implements Spanned { private final @NonNull Params mParams; - // The measured paragraph texts. - private final @NonNull MeasuredParagraph[] mMeasuredParagraphs; - - // The sorted paragraph end offsets. - private final @NonNull int[] mParagraphBreakPoints; + // The list of measured paragraph info. + private final @NonNull ParagraphInfo[] mParagraphInfo; /** * Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph @@ -293,28 +305,25 @@ public class PrecomputedText implements Spanned { * </p> * * @param text the text to be measured - * @param param parameters that define how text will be precomputed + * @param params parameters that define how text will be precomputed * @return A {@link PrecomputedText} */ - public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params param) { - return createInternal(text, param, 0, text.length(), true /* compute full Layout */); + public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params params) { + ParagraphInfo[] paraInfo = createMeasuredParagraphs( + text, params, 0, text.length(), true /* computeLayout */); + return new PrecomputedText(text, 0, text.length(), params, paraInfo); } /** @hide */ - public static PrecomputedText createWidthOnly(@NonNull CharSequence text, @NonNull Params param, - @IntRange(from = 0) int start, @IntRange(from = 0) int end) { - return createInternal(text, param, start, end, false /* compute width only */); - } - - private static PrecomputedText createInternal(@NonNull CharSequence text, @NonNull Params param, + public static ParagraphInfo[] createMeasuredParagraphs( + @NonNull CharSequence text, @NonNull Params params, @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean computeLayout) { - Preconditions.checkNotNull(text); - Preconditions.checkNotNull(param); - final boolean needHyphenation = param.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE - && param.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE; + ArrayList<ParagraphInfo> result = new ArrayList<>(); - final IntArray paragraphEnds = new IntArray(); - final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>(); + Preconditions.checkNotNull(text); + Preconditions.checkNotNull(params); + final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE + && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE; int paraEnd = 0; for (int paraStart = start; paraStart < end; paraStart = paraEnd) { @@ -327,27 +336,22 @@ public class PrecomputedText implements Spanned { paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. } - paragraphEnds.add(paraEnd); - measuredTexts.add(MeasuredParagraph.buildForStaticLayout( - param.getTextPaint(), text, paraStart, paraEnd, param.getTextDirection(), - needHyphenation, computeLayout, null /* no recycle */)); + result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout( + params.getTextPaint(), text, paraStart, paraEnd, params.getTextDirection(), + needHyphenation, computeLayout, null /* no recycle */))); } - - return new PrecomputedText(text, start, end, param, - measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]), - paragraphEnds.toArray()); + return result.toArray(new ParagraphInfo[result.size()]); } // Use PrecomputedText.create instead. private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start, - @IntRange(from = 0) int end, @NonNull Params param, - @NonNull MeasuredParagraph[] measuredTexts, @NonNull int[] paragraphBreakPoints) { + @IntRange(from = 0) int end, @NonNull Params params, + @NonNull ParagraphInfo[] paraInfo) { mText = new SpannedString(text); mStart = start; mEnd = end; - mParams = param; - mMeasuredParagraphs = measuredTexts; - mParagraphBreakPoints = paragraphBreakPoints; + mParams = params; + mParagraphInfo = paraInfo; } /** @@ -384,7 +388,7 @@ public class PrecomputedText implements Spanned { * Returns the count of paragraphs. */ public @IntRange(from = 0) int getParagraphCount() { - return mParagraphBreakPoints.length; + return mParagraphInfo.length; } /** @@ -392,7 +396,7 @@ public class PrecomputedText implements Spanned { */ public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) { Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); - return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1]; + return paraIndex == 0 ? mStart : getParagraphEnd(paraIndex - 1); } /** @@ -400,12 +404,17 @@ public class PrecomputedText implements Spanned { */ public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) { Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); - return mParagraphBreakPoints[paraIndex]; + return mParagraphInfo[paraIndex].paragraphEnd; } /** @hide */ public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) { - return mMeasuredParagraphs[paraIndex]; + return mParagraphInfo[paraIndex].measured; + } + + /** @hide */ + public @NonNull ParagraphInfo[] getParagraphInfo() { + return mParagraphInfo; } /** @@ -425,13 +434,13 @@ public class PrecomputedText implements Spanned { public int findParaIndex(@IntRange(from = 0) int pos) { // TODO: Maybe good to remove paragraph concept from PrecomputedText and add substring // layout support to StaticLayout. - for (int i = 0; i < mParagraphBreakPoints.length; ++i) { - if (pos < mParagraphBreakPoints[i]) { + for (int i = 0; i < mParagraphInfo.length; ++i) { + if (pos < mParagraphInfo[i].paragraphEnd) { return i; } } throw new IndexOutOfBoundsException( - "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1] + "pos must be less than " + mParagraphInfo[mParagraphInfo.length - 1].paragraphEnd + ", gave " + pos); } diff --git a/core/java/android/text/SpannableString.java b/core/java/android/text/SpannableString.java index 56d0946babf2..afb5df809bc0 100644 --- a/core/java/android/text/SpannableString.java +++ b/core/java/android/text/SpannableString.java @@ -16,7 +16,6 @@ package android.text; - /** * This is the class for text whose content is immutable but to which * markup objects can be attached and detached. @@ -26,12 +25,27 @@ public class SpannableString extends SpannableStringInternal implements CharSequence, GetChars, Spannable { + /** + * @param source source object to copy from + * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source} + * @hide + */ + public SpannableString(CharSequence source, boolean ignoreNoCopySpan) { + super(source, 0, source.length(), ignoreNoCopySpan); + } + + /** + * For the backward compatibility reasons, this constructor copies all spans including {@link + * android.text.NoCopySpan}. + * @param source source text + */ public SpannableString(CharSequence source) { - super(source, 0, source.length()); + this(source, false /* ignoreNoCopySpan */); // preserve existing NoCopySpan behavior } private SpannableString(CharSequence source, int start, int end) { - super(source, start, end); + // preserve existing NoCopySpan behavior + super(source, start, end, false /* ignoreNoCopySpan */); } public static SpannableString valueOf(CharSequence source) { diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java index 366ec145ffc4..5dd1a52b4a7a 100644 --- a/core/java/android/text/SpannableStringInternal.java +++ b/core/java/android/text/SpannableStringInternal.java @@ -26,7 +26,7 @@ import java.lang.reflect.Array; /* package */ abstract class SpannableStringInternal { /* package */ SpannableStringInternal(CharSequence source, - int start, int end) { + int start, int end, boolean ignoreNoCopySpan) { if (start == 0 && end == source.length()) mText = source.toString(); else @@ -38,24 +38,37 @@ import java.lang.reflect.Array; if (source instanceof Spanned) { if (source instanceof SpannableStringInternal) { - copySpans((SpannableStringInternal) source, start, end); + copySpans((SpannableStringInternal) source, start, end, ignoreNoCopySpan); } else { - copySpans((Spanned) source, start, end); + copySpans((Spanned) source, start, end, ignoreNoCopySpan); } } } /** + * This unused method is left since this is listed in hidden api list. + * + * Due to backward compatibility reasons, we copy even NoCopySpan by default + */ + /* package */ SpannableStringInternal(CharSequence source, int start, int end) { + this(source, start, end, false /* ignoreNoCopySpan */); + } + + /** * Copies another {@link Spanned} object's spans between [start, end] into this object. * * @param src Source object to copy from. * @param start Start index in the source object. * @param end End index in the source object. + * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source} */ - private final void copySpans(Spanned src, int start, int end) { + private void copySpans(Spanned src, int start, int end, boolean ignoreNoCopySpan) { Object[] spans = src.getSpans(start, end, Object.class); for (int i = 0; i < spans.length; i++) { + if (ignoreNoCopySpan && spans[i] instanceof NoCopySpan) { + continue; + } int st = src.getSpanStart(spans[i]); int en = src.getSpanEnd(spans[i]); int fl = src.getSpanFlags(spans[i]); @@ -76,35 +89,48 @@ import java.lang.reflect.Array; * @param src Source object to copy from. * @param start Start index in the source object. * @param end End index in the source object. + * @param ignoreNoCopySpan copy NoCopySpan for backward compatible reasons. */ - private final void copySpans(SpannableStringInternal src, int start, int end) { - if (start == 0 && end == src.length()) { + private void copySpans(SpannableStringInternal src, int start, int end, + boolean ignoreNoCopySpan) { + int count = 0; + final int[] srcData = src.mSpanData; + final Object[] srcSpans = src.mSpans; + final int limit = src.mSpanCount; + boolean hasNoCopySpan = false; + + for (int i = 0; i < limit; i++) { + int spanStart = srcData[i * COLUMNS + START]; + int spanEnd = srcData[i * COLUMNS + END]; + if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; + if (srcSpans[i] instanceof NoCopySpan) { + hasNoCopySpan = true; + if (ignoreNoCopySpan) { + continue; + } + } + count++; + } + + if (count == 0) return; + + if (!hasNoCopySpan && start == 0 && end == src.length()) { mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length); mSpanData = new int[src.mSpanData.length]; mSpanCount = src.mSpanCount; System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length); System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length); } else { - int count = 0; - int[] srcData = src.mSpanData; - int limit = src.mSpanCount; - for (int i = 0; i < limit; i++) { - int spanStart = srcData[i * COLUMNS + START]; - int spanEnd = srcData[i * COLUMNS + END]; - if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; - count++; - } - - if (count == 0) return; - - Object[] srcSpans = src.mSpans; mSpanCount = count; mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount); mSpanData = new int[mSpans.length * COLUMNS]; for (int i = 0, j = 0; i < limit; i++) { int spanStart = srcData[i * COLUMNS + START]; int spanEnd = srcData[i * COLUMNS + END]; - if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; + if (isOutOfCopyRange(start, end, spanStart, spanEnd) + || (ignoreNoCopySpan && srcSpans[i] instanceof NoCopySpan)) { + continue; + } if (spanStart < start) spanStart = start; if (spanEnd > end) spanEnd = end; @@ -494,6 +520,21 @@ import java.lang.reflect.Array; return hash; } + /** + * Following two unused methods are left since these are listed in hidden api list. + * + * Due to backward compatibility reasons, we copy even NoCopySpan by default + */ + private void copySpans(Spanned src, int start, int end) { + copySpans(src, start, end, false); + } + + private void copySpans(SpannableStringInternal src, int start, int end) { + copySpans(src, start, end, false); + } + + + private String mText; private Object[] mSpans; private int[] mSpanData; diff --git a/core/java/android/text/SpannedString.java b/core/java/android/text/SpannedString.java index afed221f4152..acee3c5f1a41 100644 --- a/core/java/android/text/SpannedString.java +++ b/core/java/android/text/SpannedString.java @@ -26,12 +26,27 @@ public final class SpannedString extends SpannableStringInternal implements CharSequence, GetChars, Spanned { + /** + * @param source source object to copy from + * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source} + * @hide + */ + public SpannedString(CharSequence source, boolean ignoreNoCopySpan) { + super(source, 0, source.length(), ignoreNoCopySpan); + } + + /** + * For the backward compatibility reasons, this constructor copies all spans including {@link + * android.text.NoCopySpan}. + * @param source source text + */ public SpannedString(CharSequence source) { - super(source, 0, source.length()); + this(source, false /* ignoreNoCopySpan */); // preserve existing NoCopySpan behavior } private SpannedString(CharSequence source, int start, int end) { - super(source, start, end); + // preserve existing NoCopySpan behavior + super(source, start, end, false /* ignoreNoCopySpan */); } public CharSequence subSequence(int start, int end) { diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 299bde239fcf..0899074174a2 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -651,31 +651,29 @@ public class StaticLayout extends Layout { b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE, indents, mLeftPaddings, mRightPaddings); - PrecomputedText measured = null; - final Spanned spanned; + PrecomputedText.ParagraphInfo[] paragraphInfo = null; + final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null; if (source instanceof PrecomputedText) { - measured = (PrecomputedText) source; - if (!measured.canUseMeasuredResult(bufStart, bufEnd, textDir, paint, b.mBreakStrategy, - b.mHyphenationFrequency)) { + PrecomputedText precomputed = (PrecomputedText) source; + if (precomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint, + b.mBreakStrategy, b.mHyphenationFrequency)) { // Some parameters are different from the ones when measured text is created. - measured = null; + paragraphInfo = precomputed.getParagraphInfo(); } } - if (measured == null) { + if (paragraphInfo == null) { final PrecomputedText.Params param = new PrecomputedText.Params(paint, textDir, b.mBreakStrategy, b.mHyphenationFrequency); - measured = PrecomputedText.createWidthOnly(source, param, bufStart, bufEnd); - spanned = (source instanceof Spanned) ? (Spanned) source : null; - } else { - final CharSequence original = measured.getText(); - spanned = (original instanceof Spanned) ? (Spanned) original : null; + paragraphInfo = PrecomputedText.createMeasuredParagraphs(source, param, bufStart, + bufEnd, false /* computeLayout */); } try { - for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) { - final int paraStart = measured.getParagraphStart(paraIndex); - final int paraEnd = measured.getParagraphEnd(paraIndex); + for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) { + final int paraStart = paraIndex == 0 + ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd; + final int paraEnd = paragraphInfo[paraIndex].paragraphEnd; int firstWidthLineCount = 1; int firstWidth = outerWidth; @@ -741,7 +739,7 @@ public class StaticLayout extends Layout { } } - final MeasuredParagraph measuredPara = measured.getMeasuredParagraph(paraIndex); + final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured; final char[] chs = measuredPara.getChars(); final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray(); final int[] fmCache = measuredPara.getFontMetrics().getRawArray(); diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java index c44f42b19ced..46e316931935 100644 --- a/core/java/android/util/DebugUtils.java +++ b/core/java/android/util/DebugUtils.java @@ -219,7 +219,7 @@ public class DebugUtils { && field.getType().equals(int.class) && field.getName().startsWith(prefix)) { try { if (value == field.getInt(null)) { - return field.getName().substring(prefix.length()); + return constNameWithoutPrefix(prefix, field); } } catch (IllegalAccessException ignored) { } @@ -236,6 +236,7 @@ public class DebugUtils { */ public static String flagsToString(Class<?> clazz, String prefix, int flags) { final StringBuilder res = new StringBuilder(); + boolean flagsWasZero = flags == 0; for (Field field : clazz.getDeclaredFields()) { final int modifiers = field.getModifiers(); @@ -243,9 +244,12 @@ public class DebugUtils { && field.getType().equals(int.class) && field.getName().startsWith(prefix)) { try { final int value = field.getInt(null); + if (value == 0 && flagsWasZero) { + return constNameWithoutPrefix(prefix, field); + } if ((flags & value) != 0) { flags &= ~value; - res.append(field.getName().substring(prefix.length())).append('|'); + res.append(constNameWithoutPrefix(prefix, field)).append('|'); } } catch (IllegalAccessException ignored) { } @@ -258,4 +262,8 @@ public class DebugUtils { } return res.toString(); } + + private static String constNameWithoutPrefix(String prefix, Field field) { + return field.getName().substring(prefix.length()); + } } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 613e6d8c6b14..6486230f8e3a 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -428,4 +428,14 @@ interface IWindowManager * on the next user activity. */ void requestUserActivityNotification(); + + /** + * Notify WindowManager that it should not override the info in DisplayManager for the specified + * display. This can disable letter- or pillar-boxing applied in DisplayManager when the metrics + * of the logical display reported from WindowManager do not correspond to the metrics of the + * physical display it is based on. + * + * @param displayId The id of the display. + */ + void dontOverrideDisplayInfo(int displayId); } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index b7524fb1a522..7ff4f21dd26f 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -566,7 +566,7 @@ public class SurfaceControl implements Parcelable { */ private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags, SurfaceControl parent, int windowType, int ownerUid) - throws OutOfResourcesException { + throws OutOfResourcesException, IllegalArgumentException { if (session == null) { throw new IllegalArgumentException("session must not be null"); } diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java index 779eefb14c74..886f5c822fb4 100644 --- a/core/java/android/view/textservice/SpellCheckerSession.java +++ b/core/java/android/view/textservice/SpellCheckerSession.java @@ -445,9 +445,15 @@ public class SpellCheckerSession { private void processOrEnqueueTask(SpellCheckerParams scp) { ISpellCheckerSession session; synchronized (this) { + if (scp.mWhat == TASK_CLOSE && (mState == STATE_CLOSED_AFTER_CONNECTION + || mState == STATE_CLOSED_BEFORE_CONNECTION)) { + // It is OK to call SpellCheckerSession#close() multiple times. + // Don't output confusing/misleading warning messages. + return; + } if (mState != STATE_WAIT_CONNECTION && mState != STATE_CONNECTED) { Log.e(TAG, "ignoring processOrEnqueueTask due to unexpected mState=" - + taskToString(scp.mWhat) + + stateToString(mState) + " scp.mWhat=" + taskToString(scp.mWhat)); return; } diff --git a/core/java/android/webkit/TracingConfig.java b/core/java/android/webkit/TracingConfig.java index 68badecaec3a..d95ca61dff3f 100644 --- a/core/java/android/webkit/TracingConfig.java +++ b/core/java/android/webkit/TracingConfig.java @@ -35,9 +35,9 @@ public class TracingConfig { private @TracingMode int mTracingMode; /** @hide */ - @IntDef(flag = true, value = {CATEGORIES_NONE, CATEGORIES_WEB_DEVELOPER, - CATEGORIES_INPUT_LATENCY, CATEGORIES_RENDERING, CATEGORIES_JAVASCRIPT_AND_RENDERING, - CATEGORIES_FRAME_VIEWER}) + @IntDef(flag = true, value = {CATEGORIES_NONE, CATEGORIES_ALL, CATEGORIES_ANDROID_WEBVIEW, + CATEGORIES_WEB_DEVELOPER, CATEGORIES_INPUT_LATENCY, CATEGORIES_RENDERING, + CATEGORIES_JAVASCRIPT_AND_RENDERING, CATEGORIES_FRAME_VIEWER}) @Retention(RetentionPolicy.SOURCE) public @interface PredefinedCategories {} @@ -90,34 +90,28 @@ public class TracingConfig { public static final int CATEGORIES_FRAME_VIEWER = 1 << 6; /** @hide */ - @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY, RECORD_UNTIL_FULL_LARGE_BUFFER}) + @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY}) @Retention(RetentionPolicy.SOURCE) public @interface TracingMode {} /** - * Record trace events until the internal tracing buffer is full. Default tracing mode. - * Typically the buffer memory usage is between {@link #RECORD_CONTINUOUSLY} and the - * {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}. Depending on the implementation typically allows - * up to 256k events to be stored. + * Record trace events until the internal tracing buffer is full. + * + * Typically the buffer memory usage is larger than {@link #RECORD_CONTINUOUSLY}. + * Depending on the implementation typically allows up to 256k events to be stored. */ public static final int RECORD_UNTIL_FULL = 0; /** - * Record trace events continuously using an internal ring buffer. Overwrites - * old events if they exceed buffer capacity. Uses less memory than both - * {@link #RECORD_UNTIL_FULL} and {@link #RECORD_UNTIL_FULL_LARGE_BUFFER} modes. - * Depending on the implementation typically allows up to 64k events to be stored. + * Record trace events continuously using an internal ring buffer. Default tracing mode. + * + * Overwrites old events if they exceed buffer capacity. Uses less memory than the + * {@link #RECORD_UNTIL_FULL} mode. Depending on the implementation typically allows + * up to 64k events to be stored. */ public static final int RECORD_CONTINUOUSLY = 1; /** - * Record trace events using a larger internal tracing buffer until it is full. - * Uses significantly more memory than {@link #RECORD_UNTIL_FULL} and may not be - * suitable on devices with smaller RAM. - */ - public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2; - - /** * @hide */ public TracingConfig(@PredefinedCategories int predefinedCategories, @@ -182,7 +176,7 @@ public class TracingConfig { public static class Builder { private @PredefinedCategories int mPredefinedCategories = CATEGORIES_NONE; private final List<String> mCustomIncludedCategories = new ArrayList<String>(); - private @TracingMode int mTracingMode = RECORD_UNTIL_FULL; + private @TracingMode int mTracingMode = RECORD_CONTINUOUSLY; /** * Default constructor for Builder. @@ -202,7 +196,9 @@ public class TracingConfig { * * @param predefinedCategories list or bitmask of predefined category sets to use: * {@link #CATEGORIES_NONE}, {@link #CATEGORIES_ALL}, - * {@link #CATEGORIES_WEB_DEVELOPER}, {@link #CATEGORIES_INPUT_LATENCY}, + * {@link #CATEGORIES_ANDROID_WEBVIEW}, + * {@link #CATEGORIES_WEB_DEVELOPER}, + * {@link #CATEGORIES_INPUT_LATENCY}, * {@link #CATEGORIES_RENDERING}, * {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or * {@link #CATEGORIES_FRAME_VIEWER}. @@ -250,9 +246,8 @@ public class TracingConfig { /** * Sets the tracing mode for this configuration. * - * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL}, - * {@link #RECORD_CONTINUOUSLY} or - * {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}. + * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL} or + * {@link #RECORD_CONTINUOUSLY}. * @return The builder to facilitate chaining. */ public Builder setTracingMode(@TracingMode int tracingMode) { diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java index 7871021a33c8..50068f5abfb2 100644 --- a/core/java/android/webkit/TracingController.java +++ b/core/java/android/webkit/TracingController.java @@ -60,9 +60,8 @@ public abstract class TracingController { * Starts tracing all webviews. Depending on the trace mode in traceConfig * specifies how the trace events are recorded. * - * For tracing modes {@link TracingConfig#RECORD_UNTIL_FULL}, - * {@link TracingConfig#RECORD_CONTINUOUSLY} and - * {@link TracingConfig#RECORD_UNTIL_FULL_LARGE_BUFFER} the events are recorded + * For tracing modes {@link TracingConfig#RECORD_UNTIL_FULL} and + * {@link TracingConfig#RECORD_CONTINUOUSLY} the events are recorded * using an internal buffer and flushed to the outputStream when * {@link #stop(OutputStream, Executor)} is called. * diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 5178a97e6b68..fc94b1f21fa1 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -2451,6 +2451,14 @@ public class WebView extends AbsoluteLayout * Returns the {@link Looper} corresponding to the thread on which WebView calls must be made. */ @NonNull + public Looper getWebViewLooper() { + return mWebViewThread; + } + + /** + * Returns the {@link Looper} corresponding to the thread on which WebView calls must be made. + */ + @NonNull public Looper getLooper() { return mWebViewThread; } diff --git a/core/java/com/android/internal/app/ISoundTriggerService.aidl b/core/java/com/android/internal/app/ISoundTriggerService.aidl index 1bee6924454d..93730df7dd47 100644 --- a/core/java/com/android/internal/app/ISoundTriggerService.aidl +++ b/core/java/com/android/internal/app/ISoundTriggerService.aidl @@ -17,8 +17,10 @@ package com.android.internal.app; import android.app.PendingIntent; +import android.content.ComponentName; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.SoundTrigger; +import android.os.Bundle; import android.os.ParcelUuid; /** @@ -44,9 +46,15 @@ interface ISoundTriggerService { int startRecognitionForIntent(in ParcelUuid soundModelId, in PendingIntent callbackIntent, in SoundTrigger.RecognitionConfig config); + + int startRecognitionForService(in ParcelUuid soundModelId, in Bundle params, + in ComponentName callbackIntent,in SoundTrigger.RecognitionConfig config); + + /** For both ...Intent and ...Service based usage */ int stopRecognitionForIntent(in ParcelUuid soundModelId); int unloadSoundModel(in ParcelUuid soundModelId); + /** For both ...Intent and ...Service based usage */ boolean isRecognitionActive(in ParcelUuid parcelUuid); } diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java index 82ac2412e4b2..d53090b4678c 100644 --- a/core/java/com/android/internal/util/FunctionalUtils.java +++ b/core/java/com/android/internal/util/FunctionalUtils.java @@ -17,6 +17,7 @@ package com.android.internal.util; import android.os.RemoteException; +import android.util.ExceptionUtils; import java.util.function.Consumer; import java.util.function.Supplier; @@ -36,21 +37,45 @@ public class FunctionalUtils { } /** - * + * Wraps a given {@code action} into one that ignores any {@link RemoteException}s */ public static <T> Consumer<T> ignoreRemoteException(RemoteExceptionIgnoringConsumer<T> action) { return action; } /** + * Wraps the given {@link ThrowingRunnable} into one that handles any exceptions using the + * provided {@code handler} + */ + public static Runnable handleExceptions(ThrowingRunnable r, Consumer<Throwable> handler) { + return () -> { + try { + r.run(); + } catch (Throwable t) { + handler.accept(t); + } + }; + } + + /** * An equivalent of {@link Runnable} that allows throwing checked exceptions * * This can be used to specify a lambda argument without forcing all the checked exceptions * to be handled within it */ @FunctionalInterface - public interface ThrowingRunnable { + @SuppressWarnings("FunctionalInterfaceMethodChanged") + public interface ThrowingRunnable extends Runnable { void runOrThrow() throws Exception; + + @Override + default void run() { + try { + runOrThrow(); + } catch (Exception ex) { + throw ExceptionUtils.propagate(ex); + } + } } /** @@ -80,7 +105,7 @@ public class FunctionalUtils { try { acceptOrThrow(t); } catch (Exception ex) { - throw new RuntimeException(ex); + throw ExceptionUtils.propagate(ex); } } } diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 25e1589db74e..bec70fd1d5a1 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -78,10 +78,14 @@ interface ILockSettings { byte[] startRecoverySession(in String sessionId, in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge, in List<KeyChainProtectionParams> secrets); - byte[] startRecoverySessionWithCertPath(in String sessionId, + byte[] startRecoverySessionWithCertPath(in String sessionId, in String rootCertificateAlias, in RecoveryCertPath verifierCertPath, in byte[] vaultParams, in byte[] vaultChallenge, in List<KeyChainProtectionParams> secrets); Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob, in List<WrappedApplicationKey> applicationKeys); + Map/*<String, String>*/ recoverKeyChainSnapshot( + in String sessionId, + in byte[] recoveryKeyBlob, + in List<WrappedApplicationKey> applicationKeys); void closeSession(in String sessionId); } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index fde7e961b843..d4ab4265b3fb 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -16,6 +16,15 @@ package com.android.internal.widget; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + import android.annotation.IntDef; import android.annotation.Nullable; import android.app.admin.DevicePolicyManager; @@ -59,7 +68,6 @@ import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collection; import java.util.List; - /** * Utilities for the lock pattern and its settings. */ @@ -585,7 +593,7 @@ public class LockPatternUtils { return quality; } - return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + return PASSWORD_QUALITY_UNSPECIFIED; } /** @@ -604,13 +612,16 @@ public class LockPatternUtils { * Clear any lock pattern or password. */ public void clearLock(String savedCredential, int userHandle) { - setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle); + final int currentQuality = getKeyguardStoredPasswordQuality(userHandle); + setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userHandle); try{ getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential, - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle); - } catch (RemoteException e) { - // well, we tried... + PASSWORD_QUALITY_UNSPECIFIED, userHandle); + } catch (Exception e) { + Log.e(TAG, "Failed to clear lock", e); + setKeyguardStoredPasswordQuality(currentQuality, userHandle); + return; } if (userHandle == UserHandle.USER_SYSTEM) { @@ -669,32 +680,34 @@ public class LockPatternUtils { * @param userId the user whose pattern is to be saved. */ public void saveLockPattern(List<LockPatternView.Cell> pattern, String savedPattern, int userId) { - try { - if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) { - throw new IllegalArgumentException("pattern must not be null and at least " - + MIN_LOCK_PATTERN_SIZE + " dots long."); - } - - setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId); - getLockSettings().setLockCredential(patternToString(pattern), CREDENTIAL_TYPE_PATTERN, - savedPattern, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId); + if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) { + throw new IllegalArgumentException("pattern must not be null and at least " + + MIN_LOCK_PATTERN_SIZE + " dots long."); + } - // Update the device encryption password. - if (userId == UserHandle.USER_SYSTEM - && LockPatternUtils.isDeviceEncryptionEnabled()) { - if (!shouldEncryptWithCredentials(true)) { - clearEncryptionPassword(); - } else { - String stringPattern = patternToString(pattern); - updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern); - } + final String stringPattern = patternToString(pattern); + final int currentQuality = getKeyguardStoredPasswordQuality(userId); + setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_SOMETHING, userId); + try { + getLockSettings().setLockCredential(stringPattern, CREDENTIAL_TYPE_PATTERN, + savedPattern, PASSWORD_QUALITY_SOMETHING, userId); + } catch (Exception e) { + Log.e(TAG, "Couldn't save lock pattern", e); + setKeyguardStoredPasswordQuality(currentQuality, userId); + return; + } + // Update the device encryption password. + if (userId == UserHandle.USER_SYSTEM + && LockPatternUtils.isDeviceEncryptionEnabled()) { + if (!shouldEncryptWithCredentials(true)) { + clearEncryptionPassword(); + } else { + updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern); } - - reportPatternWasChosen(userId); - onAfterChangingPassword(userId); - } catch (RemoteException re) { - Log.e(TAG, "Couldn't save lock pattern " + re); } + + reportPatternWasChosen(userId); + onAfterChangingPassword(userId); } private void updateCryptoUserInfo(int userId) { @@ -796,25 +809,27 @@ public class LockPatternUtils { */ public void saveLockPassword(String password, String savedPassword, int requestedQuality, int userHandle) { - try { - if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) { - throw new IllegalArgumentException("password must not be null and at least " - + "of length " + MIN_LOCK_PASSWORD_SIZE); - } - - setLong(PASSWORD_TYPE_KEY, - computePasswordQuality(CREDENTIAL_TYPE_PASSWORD, password, requestedQuality), - userHandle); - getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword, - requestedQuality, userHandle); + if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) { + throw new IllegalArgumentException("password must not be null and at least " + + "of length " + MIN_LOCK_PASSWORD_SIZE); + } - updateEncryptionPasswordIfNeeded(password, - PasswordMetrics.computeForPassword(password).quality, userHandle); - updatePasswordHistory(password, userHandle); - } catch (RemoteException re) { - // Cant do much - Log.e(TAG, "Unable to save lock password " + re); + final int currentQuality = getKeyguardStoredPasswordQuality(userHandle); + setKeyguardStoredPasswordQuality( + computePasswordQuality(CREDENTIAL_TYPE_PASSWORD, password, requestedQuality), + userHandle); + try { + getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, + savedPassword, requestedQuality, userHandle); + } catch (Exception e) { + Log.e(TAG, "Unable to save lock password", e); + setKeyguardStoredPasswordQuality(currentQuality, userHandle); + return; } + + updateEncryptionPasswordIfNeeded(password, + PasswordMetrics.computeForPassword(password).quality, userHandle); + updatePasswordHistory(password, userHandle); } /** @@ -828,9 +843,8 @@ public class LockPatternUtils { if (!shouldEncryptWithCredentials(true)) { clearEncryptionPassword(); } else { - boolean numeric = quality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; - boolean numericComplex = quality - == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; + boolean numeric = quality == PASSWORD_QUALITY_NUMERIC; + boolean numericComplex = quality == PASSWORD_QUALITY_NUMERIC_COMPLEX; int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD; updateEncryptionPassword(type, password); @@ -894,8 +908,11 @@ public class LockPatternUtils { * @return stored password quality */ public int getKeyguardStoredPasswordQuality(int userHandle) { - return (int) getLong(PASSWORD_TYPE_KEY, - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle); + return (int) getLong(PASSWORD_TYPE_KEY, PASSWORD_QUALITY_UNSPECIFIED, userHandle); + } + + private void setKeyguardStoredPasswordQuality(int quality, int userHandle) { + setLong(PASSWORD_TYPE_KEY, quality, userHandle); } /** @@ -909,9 +926,9 @@ public class LockPatternUtils { int computedQuality = PasswordMetrics.computeForPassword(credential).quality; quality = Math.max(requestedQuality, computedQuality); } else if (type == CREDENTIAL_TYPE_PATTERN) { - quality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; + quality = PASSWORD_QUALITY_SOMETHING; } else /* if (type == CREDENTIAL_TYPE_NONE) */ { - quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + quality = PASSWORD_QUALITY_UNSPECIFIED; } return quality; } @@ -1125,12 +1142,12 @@ public class LockPatternUtils { } private boolean isLockPasswordEnabled(int mode, int userId) { - final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC - || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC - || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX - || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC - || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX - || mode == DevicePolicyManager.PASSWORD_QUALITY_MANAGED; + final boolean passwordEnabled = mode == PASSWORD_QUALITY_ALPHABETIC + || mode == PASSWORD_QUALITY_NUMERIC + || mode == PASSWORD_QUALITY_NUMERIC_COMPLEX + || mode == PASSWORD_QUALITY_ALPHANUMERIC + || mode == PASSWORD_QUALITY_COMPLEX + || mode == PASSWORD_QUALITY_MANAGED; return passwordEnabled && savedPasswordExists(userId); } @@ -1155,8 +1172,7 @@ public class LockPatternUtils { } private boolean isLockPatternEnabled(int mode, int userId) { - return mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING - && savedPatternExists(userId); + return mode == PASSWORD_QUALITY_SOMETHING && savedPatternExists(userId); } /** @@ -1551,7 +1567,7 @@ public class LockPatternUtils { token, quality, userId)) { return false; } - setLong(PASSWORD_TYPE_KEY, quality, userId); + setKeyguardStoredPasswordQuality(quality, userId); updateEncryptionPasswordIfNeeded(credential, quality, userId); updatePasswordHistory(credential, userId); @@ -1560,12 +1576,10 @@ public class LockPatternUtils { throw new IllegalArgumentException("password must be emtpy for NONE type"); } if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE, - tokenHandle, token, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, - userId)) { + tokenHandle, token, PASSWORD_QUALITY_UNSPECIFIED, userId)) { return false; } - setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, - userId); + setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userId); if (userId == UserHandle.USER_SYSTEM) { // Set the encryption password to default. diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp index d6496cdf499a..7166c757e602 100644 --- a/core/jni/android/graphics/AnimatedImageDrawable.cpp +++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp @@ -42,7 +42,6 @@ static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, } auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder); - auto info = imageDecoder->mCodec->getInfo(); const SkISize scaledSize = SkISize::Make(width, height); SkIRect subset; if (jsubset) { @@ -51,6 +50,35 @@ static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, subset = SkIRect::MakeWH(width, height); } + auto info = imageDecoder->mCodec->getInfo(); + bool hasRestoreFrame = false; + if (imageDecoder->mCodec->getEncodedFormat() == SkEncodedImageFormat::kWEBP) { + if (width < info.width() && height < info.height()) { + // WebP will scale its SkBitmap to the scaled size. + // FIXME: b/73529447 GIF should do the same. + info = info.makeWH(width, height); + } + } else { + const int frameCount = imageDecoder->mCodec->codec()->getFrameCount(); + for (int i = 0; i < frameCount; ++i) { + SkCodec::FrameInfo frameInfo; + if (!imageDecoder->mCodec->codec()->getFrameInfo(i, &frameInfo)) { + doThrowIOE(env, "Failed to read frame info!"); + return 0; + } + if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious) { + hasRestoreFrame = true; + break; + } + } + } + + size_t bytesUsed = info.computeMinByteSize(); + // SkAnimatedImage has one SkBitmap for decoding, plus an extra one if there is a + // kRestorePrevious frame. AnimatedImageDrawable has two SkPictures storing the current + // frame and the next frame. (The former assumes that the image is animated, and the + // latter assumes that it is drawn to a hardware canvas.) + bytesUsed *= hasRestoreFrame ? 4 : 3; sk_sp<SkPicture> picture; if (jpostProcess) { SkRect bounds = SkRect::MakeWH(subset.width(), subset.height()); @@ -63,6 +91,7 @@ static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, return 0; } picture = recorder.finishRecordingAsPicture(); + bytesUsed += picture->approximateBytesUsed(); } @@ -74,7 +103,10 @@ static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, return 0; } - sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(animatedImg)); + bytesUsed += sizeof(animatedImg.get()); + + sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(std::move(animatedImg), + bytesUsed)); return reinterpret_cast<jlong>(drawable.release()); } @@ -202,10 +234,9 @@ static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobjec } } -static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { +static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); - // FIXME: Report the size of the internal SkBitmap etc. - return sizeof(drawable); + return drawable->byteSize(); } static void AnimatedImageDrawable_nMarkInvisible(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index a30b2ade0e57..04cb08f51efa 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -116,9 +116,13 @@ static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj, ScopedUtfChars name(env, nameStr); sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj)); SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject); - sp<SurfaceControl> surface = client->createSurface( - String8(name.c_str()), w, h, format, flags, parent, windowType, ownerUid); - if (surface == NULL) { + sp<SurfaceControl> surface; + status_t err = client->createSurfaceChecked( + String8(name.c_str()), w, h, format, &surface, flags, parent, windowType, ownerUid); + if (err == NAME_NOT_FOUND) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return 0; + } else if (err != NO_ERROR) { jniThrowException(env, OutOfResourcesException, NULL); return 0; } diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 4657dc478059..6a3aaabb3614 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -118,17 +118,17 @@ message IncidentProto { // Stack dumps optional android.os.BackTraceProto native_traces = 1200 [ - (section).type = SECTION_TOMBSTONE, + (section).type = SECTION_NONE, (section).args = "native" ]; optional android.os.BackTraceProto hal_traces = 1201 [ - (section).type = SECTION_TOMBSTONE, + (section).type = SECTION_NONE, (section).args = "hal" ]; optional android.os.BackTraceProto java_traces = 1202 [ - (section).type = SECTION_TOMBSTONE, + (section).type = SECTION_NONE, (section).args = "java" ]; @@ -169,7 +169,7 @@ message IncidentProto { ]; optional GZippedFileProto last_kmsg = 2007 [ - (section).type = SECTION_GZIP, + (section).type = SECTION_NONE, // disable until selinux permission is gained (section).args = "/sys/fs/pstore/console-ramoops /sys/fs/pstore/console-ramoops-0 /proc/last_kmsg", (privacy).dest = DEST_AUTOMATIC ]; diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto index 53e6532419a6..4e781d67d693 100644 --- a/core/proto/android/providers/settings.proto +++ b/core/proto/android/providers/settings.proto @@ -468,6 +468,8 @@ message GlobalSettingsProto { optional SettingProto chained_battery_attribution_enabled = 356 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto autofill_compat_allowed_packages = 357 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto hidden_api_blacklist_exemptions = 358 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto sound_trigger_detection_service_op_timeout = 387 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto max_sound_trigger_detection_service_ops_per_day = 388 [ (android.privacy).dest = DEST_AUTOMATIC ]; // Subscription to be used for voice call on a multi sim device. The // supported values are 0 = SUB1, 1 = SUB2 and etc. optional SettingProto multi_sim_voice_call_subscription = 359 [ (android.privacy).dest = DEST_AUTOMATIC ]; @@ -505,7 +507,7 @@ message GlobalSettingsProto { optional SettingsProto backup_agent_timeout_parameters = 386; // Please insert fields in the same order as in // frameworks/base/core/java/android/provider/Settings.java. - // Next tag = 387; + // Next tag = 389; } message SecureSettingsProto { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 9b11a33593bd..1b4d571d3f4c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3754,10 +3754,17 @@ <!-- Must be required by system/priv apps when accessing the sound trigger APIs given by {@link SoundTriggerManager}. - @hide <p>Not for use by third-party applications.</p> --> + @hide + @SystemApi --> <permission android:name="android.permission.MANAGE_SOUND_TRIGGER" android:protectionLevel="signature|privileged" /> + <!-- Must be required by system/priv apps implementing sound trigger detection services + @hide + @SystemApi --> + <permission android:name="android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE" + android:protectionLevel="signature" /> + <!-- @SystemApi Allows trusted applications to dispatch managed provisioning message to Managed Provisioning app. If requesting app does not have permission, it will be ignored. @hide --> @@ -3879,6 +3886,12 @@ <permission android:name="android.permission.WATCH_APPOPS" android:protectionLevel="signature" /> + <!-- Allows an application to directly open the "Open by default" page inside a package's + Details screen. + @hide <p>Not for use by third-party applications. --> + <permission android:name="android.permission.OPEN_APPLICATION_DETAILS_OPEN_BY_DEFAULT_PAGE" + android:protectionLevel="signature" /> + <application android:process="system" android:persistent="true" android:hasCode="false" diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml index 5c5b985b26a2..fa690388472f 100644 --- a/core/res/res/layout/autofill_save.xml +++ b/core/res/res/layout/autofill_save.xml @@ -35,8 +35,8 @@ <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" - android:paddingLeft="16dp" - android:paddingRight="16dp" + android:paddingStart="16dp" + android:paddingEnd="16dp" android:orientation="vertical"> <LinearLayout @@ -52,7 +52,7 @@ <TextView android:id="@+id/autofill_save_title" - android:paddingLeft="8dp" + android:paddingStart="8dp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/autofill_save_title" diff --git a/core/res/res/layout/shutdown_dialog.xml b/core/res/res/layout/shutdown_dialog.xml index 398bfb1824c7..2d214b32164b 100644 --- a/core/res/res/layout/shutdown_dialog.xml +++ b/core/res/res/layout/shutdown_dialog.xml @@ -29,7 +29,7 @@ <TextView android:id="@id/text1" android:layout_width="wrap_content" - android:layout_height="32dp" + android:layout_height="32sp" android:text="@string/shutdown_progress" android:textDirection="locale" android:textSize="24sp" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 4a72bf99dbca..75b3bcf54b62 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2151,6 +2151,9 @@ @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES --> <enum name="shortEdges" value="1" /> + <!-- Use {@code shortEdges} instead. This is temporarily here to unblock pushing the SDK + until all usages have been migrated to {@code shortEdges} --> + <enum name="always" value="1" /> <!-- The window is never allowed to overlap with the DisplayCutout area. <p> This should be used with windows that transiently set {@code SYSTEM_UI_FLAG_FULLSCREEN} diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ad4d7ddfc269..ac01c4efc3ea 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3232,6 +3232,7 @@ <java-symbol type="id" name="remote_input_progress" /> <java-symbol type="id" name="remote_input_send" /> <java-symbol type="id" name="remote_input" /> + <java-symbol type="dimen" name="notification_content_margin" /> <java-symbol type="dimen" name="slice_shortcut_size" /> <java-symbol type="dimen" name="slice_icon_size" /> <java-symbol type="dimen" name="slice_padding" /> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 63343bef67e7..2036e2f95225 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -170,6 +170,7 @@ public class SettingsBackupTest { Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS, Settings.Global.CONTACT_METADATA_SYNC_ENABLED, Settings.Global.CONTACTS_DATABASE_WAL_ENABLED, + Settings.Global.CPU_SCALING_ENABLED, Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE, Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI, Settings.Global.DATABASE_CREATION_BUILDID, @@ -267,6 +268,7 @@ public class SettingsBackupTest { Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, Settings.Global.LTE_SERVICE_FORCED, Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, + Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY, Settings.Global.MDC_INITIAL_MAX_RETRY, Settings.Global.MHL_INPUT_SWITCHING_ENABLED, Settings.Global.MHL_POWER_CHARGE_ENABLED, @@ -370,6 +372,7 @@ public class SettingsBackupTest { Settings.Global.SMS_SHORT_CODE_RULE, Settings.Global.SMS_SHORT_CODES_UPDATE_CONTENT_URL, Settings.Global.SMS_SHORT_CODES_UPDATE_METADATA_URL, + Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT, Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS, Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, Settings.Global.STORAGE_BENCHMARK_INTERVAL, diff --git a/core/tests/coretests/src/android/security/backup/TrustedRootCertificatesTest.java b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java index 0f9cb45b8899..3b4ad3801135 100644 --- a/core/tests/coretests/src/android/security/backup/TrustedRootCertificatesTest.java +++ b/core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package android.security.backup; +package android.security.keystore.recovery; -import static android.security.backup.TrustedRootCertificates.listRootCertificates; +import static android.security.keystore.recovery.TrustedRootCertificates.listRootCertificates; import static org.junit.Assert.assertTrue; diff --git a/core/tests/coretests/src/android/security/backup/X509CertificateParsingUtilsTest.java b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java index ad85c2518325..7f0eb43f23be 100644 --- a/core/tests/coretests/src/android/security/backup/X509CertificateParsingUtilsTest.java +++ b/core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package android.security.backup; +package android.security.keystore.recovery; -import static android.security.backup.X509CertificateParsingUtils.decodeBase64Cert; +import static android.security.keystore.recovery.X509CertificateParsingUtils.decodeBase64Cert; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; diff --git a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java new file mode 100644 index 000000000000..c205f9695bb8 --- /dev/null +++ b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2018 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.text; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +import android.annotation.NonNull; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.text.style.QuoteSpan; +import android.text.style.UnderlineSpan; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SpannableStringNoCopyTest { + @Test + public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() { + final SpannableString first = new SpannableString("t\nest data"); + first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); + first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); + + // By default, copy NoCopySpans + final SpannedString copied = new SpannedString(first); + final Object[] spans = copied.getSpans(0, copied.length(), Object.class); + assertNotNull(spans); + assertEquals(3, spans.length); + } + + @Test + public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() { + final SpannableString first = new SpannableString("t\nest data"); + first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); + first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); + + // Do not copy NoCopySpan if specified so. + final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */); + final Object[] spans = copied.getSpans(0, copied.length(), Object.class); + assertNotNull(spans); + assertEquals(2, spans.length); + + for (int i = 0; i < spans.length; i++) { + assertFalse(spans[i] instanceof NoCopySpan); + } + } + + @Test + public void testCopyConstructor_copyNoCopySpans_OtherSpannableImpl() { + final SpannableString first = new SpannableString("t\nest data"); + first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); + first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); + + // By default, copy NoCopySpans + final SpannedString copied = new SpannedString(new CustomSpannable(first)); + final Object[] spans = copied.getSpans(0, copied.length(), Object.class); + assertNotNull(spans); + assertEquals(3, spans.length); + } + + @Test + public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannableImpl() { + final SpannableString first = new SpannableString("t\nest data"); + first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); + first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); + + // Do not copy NoCopySpan if specified so. + final SpannedString copied = new SpannedString( + new CustomSpannable(first), false /* copyNoCopySpan */); + final Object[] spans = copied.getSpans(0, copied.length(), Object.class); + assertNotNull(spans); + assertEquals(2, spans.length); + + for (int i = 0; i < spans.length; i++) { + assertFalse(spans[i] instanceof NoCopySpan); + } + } + + // A custom implementation of Spannable. + private static class CustomSpannable implements Spannable { + private final @NonNull Spannable mText; + + CustomSpannable(@NonNull Spannable text) { + mText = text; + } + + @Override + public void setSpan(Object what, int start, int end, int flags) { + mText.setSpan(what, start, end, flags); + } + + @Override + public void removeSpan(Object what) { + mText.removeSpan(what); + } + + @Override + public <T> T[] getSpans(int start, int end, Class<T> type) { + return mText.getSpans(start, end, type); + } + + @Override + public int getSpanStart(Object tag) { + return mText.getSpanStart(tag); + } + + @Override + public int getSpanEnd(Object tag) { + return mText.getSpanEnd(tag); + } + + @Override + public int getSpanFlags(Object tag) { + return mText.getSpanFlags(tag); + } + + @Override + public int nextSpanTransition(int start, int limit, Class type) { + return mText.nextSpanTransition(start, limit, type); + } + + @Override + public int length() { + return mText.length(); + } + + @Override + public char charAt(int index) { + return mText.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return mText.subSequence(start, end); + } + + @Override + public String toString() { + return mText.toString(); + } + }; +} diff --git a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java new file mode 100644 index 000000000000..068092484b1b --- /dev/null +++ b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2018 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.text; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +import android.annotation.NonNull; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.text.style.QuoteSpan; +import android.text.style.UnderlineSpan; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SpannedStringNoCopyTest { + @Test + public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() { + final SpannableString first = new SpannableString("t\nest data"); + first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); + first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); + + // By default, copy NoCopySpans + final SpannedString copied = new SpannedString(first); + final Object[] spans = copied.getSpans(0, copied.length(), Object.class); + assertNotNull(spans); + assertEquals(3, spans.length); + } + + @Test + public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() { + final SpannableString first = new SpannableString("t\nest data"); + first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); + first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); + + // Do not copy NoCopySpan if specified so. + final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */); + final Object[] spans = copied.getSpans(0, copied.length(), Object.class); + assertNotNull(spans); + assertEquals(2, spans.length); + + for (int i = 0; i < spans.length; i++) { + assertFalse(spans[i] instanceof NoCopySpan); + } + } + + @Test + public void testCopyConstructor_copyNoCopySpans_OtherSpannedImpl() { + final SpannableString first = new SpannableString("t\nest data"); + first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); + first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); + + // By default, copy NoCopySpans + final SpannedString copied = new SpannedString(new CustomSpanned(first)); + final Object[] spans = copied.getSpans(0, copied.length(), Object.class); + assertNotNull(spans); + assertEquals(3, spans.length); + } + + @Test + public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannedImpl() { + final SpannableString first = new SpannableString("t\nest data"); + first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH); + first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY); + + // Do not copy NoCopySpan if specified so. + final SpannedString copied = new SpannedString( + new CustomSpanned(first), false /* copyNoCopySpan */); + final Object[] spans = copied.getSpans(0, copied.length(), Object.class); + assertNotNull(spans); + assertEquals(2, spans.length); + + for (int i = 0; i < spans.length; i++) { + assertFalse(spans[i] instanceof NoCopySpan); + } + } + + // A custom implementation of Spanned + private static class CustomSpanned implements Spanned { + private final @NonNull Spanned mText; + + CustomSpanned(@NonNull Spannable text) { + mText = text; + } + + @Override + public <T> T[] getSpans(int start, int end, Class<T> type) { + return mText.getSpans(start, end, type); + } + + @Override + public int getSpanStart(Object tag) { + return mText.getSpanStart(tag); + } + + @Override + public int getSpanEnd(Object tag) { + return mText.getSpanEnd(tag); + } + + @Override + public int getSpanFlags(Object tag) { + return mText.getSpanFlags(tag); + } + + @Override + public int nextSpanTransition(int start, int limit, Class type) { + return mText.nextSpanTransition(start, limit, type); + } + + @Override + public int length() { + return mText.length(); + } + + @Override + public char charAt(int index) { + return mText.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return mText.subSequence(start, end); + } + + @Override + public String toString() { + return mText.toString(); + } + }; +} diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml index be0372deb14e..bacddf14e35d 100644 --- a/data/etc/hiddenapi-package-whitelist.xml +++ b/data/etc/hiddenapi-package-whitelist.xml @@ -25,42 +25,50 @@ which apps should be allowed to access the entire private API. <hidden-api-whitelisted-app package="android.car.input.service" /> <hidden-api-whitelisted-app package="android.car.usb.handler" /> <hidden-api-whitelisted-app package="android.ext.services" /> - <hidden-api-whitelisted-app package="android.ext.shared" /> + <hidden-api-whitelisted-app package="com.android.apps.tag" /> <hidden-api-whitelisted-app package="com.android.backupconfirm" /> + <hidden-api-whitelisted-app package="com.android.basicsmsreceiver" /> <hidden-api-whitelisted-app package="com.android.bluetooth" /> <hidden-api-whitelisted-app package="com.android.bluetoothdebug" /> <hidden-api-whitelisted-app package="com.android.bluetoothmidiservice" /> + <hidden-api-whitelisted-app package="com.android.bookmarkprovider" /> <hidden-api-whitelisted-app package="com.android.calllogbackup" /> + <hidden-api-whitelisted-app package="com.android.camera" /> <hidden-api-whitelisted-app package="com.android.captiveportallogin" /> <hidden-api-whitelisted-app package="com.android.car" /> + <hidden-api-whitelisted-app package="com.android.car.dialer" /> <hidden-api-whitelisted-app package="com.android.car.hvac" /> <hidden-api-whitelisted-app package="com.android.car.mapsplaceholder" /> <hidden-api-whitelisted-app package="com.android.car.media" /> <hidden-api-whitelisted-app package="com.android.car.media.localmediaplayer" /> + <hidden-api-whitelisted-app package="com.android.car.messenger" /> + <hidden-api-whitelisted-app package="com.android.car.overview" /> <hidden-api-whitelisted-app package="com.android.car.radio" /> <hidden-api-whitelisted-app package="com.android.car.settings" /> + <hidden-api-whitelisted-app package="com.android.car.stream" /> <hidden-api-whitelisted-app package="com.android.car.systemupdater" /> <hidden-api-whitelisted-app package="com.android.car.trust" /> <hidden-api-whitelisted-app package="com.android.carrierconfig" /> <hidden-api-whitelisted-app package="com.android.carrierdefaultapp" /> <hidden-api-whitelisted-app package="com.android.cellbroadcastreceiver" /> <hidden-api-whitelisted-app package="com.android.certinstaller" /> + <hidden-api-whitelisted-app package="com.android.companiondevicemanager" /> <hidden-api-whitelisted-app package="com.android.customlocale2" /> <hidden-api-whitelisted-app package="com.android.defcontainer" /> <hidden-api-whitelisted-app package="com.android.documentsui" /> + <hidden-api-whitelisted-app package="com.android.dreams.basic" /> <hidden-api-whitelisted-app package="com.android.egg" /> - <hidden-api-whitelisted-app package="com.android.email.policy" /> <hidden-api-whitelisted-app package="com.android.emergency" /> <hidden-api-whitelisted-app package="com.android.externalstorage" /> <hidden-api-whitelisted-app package="com.android.fakeoemfeatures" /> <hidden-api-whitelisted-app package="com.android.gallery" /> <hidden-api-whitelisted-app package="com.android.hotspot2" /> - <hidden-api-whitelisted-app package="com.android.inputdevices" /> <hidden-api-whitelisted-app package="com.android.keychain" /> <hidden-api-whitelisted-app package="com.android.location.fused" /> <hidden-api-whitelisted-app package="com.android.managedprovisioning" /> <hidden-api-whitelisted-app package="com.android.mms.service" /> <hidden-api-whitelisted-app package="com.android.mtp" /> + <hidden-api-whitelisted-app package="com.android.musicfx" /> <hidden-api-whitelisted-app package="com.android.nfc" /> <hidden-api-whitelisted-app package="com.android.osu" /> <hidden-api-whitelisted-app package="com.android.packageinstaller" /> @@ -70,12 +78,14 @@ which apps should be allowed to access the entire private API. <hidden-api-whitelisted-app package="com.android.printservice.recommendation" /> <hidden-api-whitelisted-app package="com.android.printspooler" /> <hidden-api-whitelisted-app package="com.android.providers.blockednumber" /> + <hidden-api-whitelisted-app package="com.android.providers.calendar" /> <hidden-api-whitelisted-app package="com.android.providers.contacts" /> <hidden-api-whitelisted-app package="com.android.providers.downloads" /> <hidden-api-whitelisted-app package="com.android.providers.downloads.ui" /> <hidden-api-whitelisted-app package="com.android.providers.media" /> <hidden-api-whitelisted-app package="com.android.providers.settings" /> <hidden-api-whitelisted-app package="com.android.providers.telephony" /> + <hidden-api-whitelisted-app package="com.android.providers.tv" /> <hidden-api-whitelisted-app package="com.android.providers.userdictionary" /> <hidden-api-whitelisted-app package="com.android.provision" /> <hidden-api-whitelisted-app package="com.android.proxyhandler" /> @@ -87,10 +97,15 @@ which apps should be allowed to access the entire private API. <hidden-api-whitelisted-app package="com.android.settings" /> <hidden-api-whitelisted-app package="com.android.sharedstoragebackup" /> <hidden-api-whitelisted-app package="com.android.shell" /> + <hidden-api-whitelisted-app package="com.android.smspush" /> + <hidden-api-whitelisted-app package="com.android.spare_parts" /> + <hidden-api-whitelisted-app package="com.android.statementservice" /> <hidden-api-whitelisted-app package="com.android.stk" /> + <hidden-api-whitelisted-app package="com.android.storagemanager" /> <hidden-api-whitelisted-app package="com.android.support.car.lenspicker" /> <hidden-api-whitelisted-app package="com.android.systemui" /> - <hidden-api-whitelisted-app package="com.android.systemui.theme.dark" /> + <hidden-api-whitelisted-app package="com.android.systemui.plugins" /> + <hidden-api-whitelisted-app package="com.android.terminal" /> <hidden-api-whitelisted-app package="com.android.timezone.updater" /> <hidden-api-whitelisted-app package="com.android.traceur" /> <hidden-api-whitelisted-app package="com.android.tv.settings" /> @@ -99,5 +114,5 @@ which apps should be allowed to access the entire private API. <hidden-api-whitelisted-app package="com.android.wallpaperbackup" /> <hidden-api-whitelisted-app package="com.android.wallpapercropper" /> <hidden-api-whitelisted-app package="com.googlecode.android_scripting" /> + <hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" /> </config> - diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java index c0f49208e27e..a47ecf517c70 100644 --- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -292,8 +292,7 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { mState = new State(nCreate(nativeImageDecoder, decoder, width, height, cropRect), inputStream, afd); - // FIXME: Use the right size for the native allocation. - long nativeSize = 200; + final long nativeSize = nNativeByteSize(mState.mNativePtr); NativeAllocationRegistry registry = new NativeAllocationRegistry( AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize); registry.registerNativeAllocation(mState, mState.mNativePtr); diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp index 28d0bc42ad2f..c529f8773bbd 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.cpp +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -26,8 +26,8 @@ namespace android { -AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage) - : mSkAnimatedImage(std::move(animatedImage)) { +AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed) + : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) { mTimeToShowNextSnapshot = mSkAnimatedImage->currentFrameDuration(); } diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h index f4e2ba751b70..a92b62db14f0 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.h +++ b/libs/hwui/hwui/AnimatedImageDrawable.h @@ -45,7 +45,9 @@ public: */ class ANDROID_API AnimatedImageDrawable : public SkDrawable { public: - AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage); + // bytesUsed includes the approximate sizes of the SkAnimatedImage and the SkPictures in the + // Snapshots. + AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed); /** * This updates the internal time and returns true if the animation needs @@ -100,11 +102,17 @@ public: Snapshot decodeNextFrame(); Snapshot reset(); + size_t byteSize() const { + return sizeof(this) + mBytesUsed; + } + protected: virtual void onDraw(SkCanvas* canvas) override; private: sk_sp<SkAnimatedImage> mSkAnimatedImage; + const size_t mBytesUsed; + bool mRunning = false; bool mStarting = false; diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index 6abba9543218..96a0817ae561 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -339,7 +339,8 @@ public final class LocationRequest implements Parcelable { * substantially restrict power. * * <p>In this mode, the GNSS chipset will not, on average, run power hungry operations like RF & - * signal searches for more than one second per interval {@link #mInterval} + * signal searches for more than one second per interval (specified by + * {@link #setInterval(long)}). * * @param enabled Enable or disable low power mode * @return the same object, so that setters can be chained diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index b50aa477d511..3bfbcc2039d3 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -105,10 +105,10 @@ import java.util.Map; * <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_TILE_WIDTH}</td><td>Integer</td><td>required if the image has grid</td></tr> + * <tr><td>{@link #KEY_TILE_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> + * <tr><td>{@link #KEY_GRID_COLUMNS}</td><td>Integer</td><td>required if the image has grid</td></tr> * </table> */ public final class MediaFormat { @@ -150,17 +150,17 @@ public final class MediaFormat { * 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 + * {@link #KEY_TILE_WIDTH}, {@link #KEY_TILE_HEIGHT}, + * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} 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 + * {@link #KEY_TILE_WIDTH} and {@link #KEY_TILE_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, + * by {@link #KEY_GRID_COLUMNS} 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 @@ -275,28 +275,28 @@ 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. + * A key describing the width (in pixels) of each tile 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_TILE_HEIGHT * @see #KEY_GRID_ROWS - * @see #KEY_GRID_COLS + * @see #KEY_GRID_COLUMNS */ - public static final String KEY_GRID_WIDTH = "grid-width"; + public static final String KEY_TILE_WIDTH = "tile-width"; /** - * A key describing the grid height of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} - * track. The associated value is an integer. + * A key describing the height (in pixels) of each tile 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_TILE_WIDTH * @see #KEY_GRID_ROWS - * @see #KEY_GRID_COLS + * @see #KEY_GRID_COLUMNS */ - public static final String KEY_GRID_HEIGHT = "grid-height"; + public static final String KEY_TILE_HEIGHT = "tile-height"; /** * A key describing the number of grid rows in the content in a @@ -304,9 +304,9 @@ public final class MediaFormat { * * 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 + * @see #KEY_TILE_WIDTH + * @see #KEY_TILE_HEIGHT + * @see #KEY_GRID_COLUMNS */ public static final String KEY_GRID_ROWS = "grid-rows"; @@ -316,11 +316,11 @@ public final class MediaFormat { * * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks. * - * @see #KEY_GRID_WIDTH - * @see #KEY_GRID_HEIGHT + * @see #KEY_TILE_WIDTH + * @see #KEY_TILE_HEIGHT * @see #KEY_GRID_ROWS */ - public static final String KEY_GRID_COLS = "grid-cols"; + public static final String KEY_GRID_COLUMNS = "grid-cols"; /** * A key describing the raw audio sample encoding/format. diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 1aeed6da3a31..0955dd633c1c 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -427,20 +427,40 @@ public class MediaMetadataRetriever * a valid frame. The total number of frames available for retrieval can be queried * via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key. * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). - * If null, default config will be chosen. * * @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 #getFrameAtIndex(int) * @see #getFramesAtIndex(int, int, BitmapParams) + * @see #getFramesAtIndex(int, int) */ - public Bitmap getFrameAtIndex(int frameIndex, @Nullable BitmapParams params) { + public Bitmap getFrameAtIndex(int frameIndex, @NonNull BitmapParams params) { List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1, params); - if (bitmaps == null || bitmaps.size() < 1) { - return null; - } + return bitmaps.get(0); + } + + /** + * This method is similar to {@link #getFrameAtIndex(int, BitmapParams)} except that + * the default for {@link BitmapParams} will be used. + * + * @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 #getFrameAtIndex(int, BitmapParams) + * @see #getFramesAtIndex(int, int, BitmapParams) + * @see #getFramesAtIndex(int, int) + */ + public Bitmap getFrameAtIndex(int frameIndex) { + List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1); return bitmaps.get(0); } @@ -461,7 +481,6 @@ public class MediaMetadataRetriever * @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. * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). - * If null, default config will be chosen. * * @throws IllegalStateException if the container doesn't contain video or image sequences. * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the @@ -471,8 +490,40 @@ public class MediaMetadataRetriever * array could contain less frames than requested if the retrieval fails. * * @see #getFrameAtIndex(int, BitmapParams) + * @see #getFrameAtIndex(int) + * @see #getFramesAtIndex(int, int) */ - public List<Bitmap> getFramesAtIndex( + public @NonNull List<Bitmap> getFramesAtIndex( + int frameIndex, int numFrames, @NonNull BitmapParams params) { + return getFramesAtIndexInternal(frameIndex, numFrames, params); + } + + /** + * This method is similar to {@link #getFramesAtIndex(int, int, BitmapParams)} except that + * the default for {@link BitmapParams} will be used. + * + * @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 list of Bitmaps containing the requested video frames. The returned + * array could contain less frames than requested if the retrieval fails. + * + * @see #getFrameAtIndex(int, BitmapParams) + * @see #getFrameAtIndex(int) + * @see #getFramesAtIndex(int, int, BitmapParams) + */ + public @NonNull List<Bitmap> getFramesAtIndex(int frameIndex, int numFrames) { + return getFramesAtIndexInternal(frameIndex, numFrames, null); + } + + private @NonNull List<Bitmap> getFramesAtIndexInternal( int frameIndex, int numFrames, @Nullable BitmapParams params) { if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) { throw new IllegalStateException("Does not contail video or image sequences"); @@ -487,7 +538,8 @@ public class MediaMetadataRetriever } return _getFrameAtIndex(frameIndex, numFrames, params); } - private native List<Bitmap> _getFrameAtIndex( + + private native @NonNull List<Bitmap> _getFrameAtIndex( int frameIndex, int numFrames, @Nullable BitmapParams params); /** @@ -498,29 +550,39 @@ public class MediaMetadataRetriever * used to create the bitmap from the {@code BitmapParams} argument, for instance * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}. * - * @param imageIndex 0-based index of the image, with negative value indicating - * the primary image. + * @param imageIndex 0-based index of the image. * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). - * If null, default config will be chosen. * * @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 #getImageAtIndex(int) * @see #getPrimaryImage(BitmapParams) + * @see #getPrimaryImage() */ - public Bitmap getImageAtIndex(int imageIndex, @Nullable BitmapParams params) { - 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); - } + public Bitmap getImageAtIndex(int imageIndex, @NonNull BitmapParams params) { + return getImageAtIndexInternal(imageIndex, params); + } - return _getImageAtIndex(imageIndex, params); + /** + * This method is similar to {@link #getImageAtIndex(int, BitmapParams)} except that + * the default for {@link BitmapParams} will be used. + * + * @param imageIndex 0-based index of the 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 #getImageAtIndex(int, BitmapParams) + * @see #getPrimaryImage(BitmapParams) + * @see #getPrimaryImage() + */ + public Bitmap getImageAtIndex(int imageIndex) { + return getImageAtIndexInternal(imageIndex, null); } /** @@ -532,16 +594,46 @@ public class MediaMetadataRetriever * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}. * * @param params BitmapParams that controls the returned bitmap config (such as pixel formats). - * If null, default config will be chosen. * * @return the primary image, or null if it cannot be retrieved. * * @throws IllegalStateException if the container doesn't contain still images. * * @see #getImageAtIndex(int, BitmapParams) + * @see #getImageAtIndex(int) + * @see #getPrimaryImage() */ - public Bitmap getPrimaryImage(@Nullable BitmapParams params) { - return getImageAtIndex(-1, params); + public Bitmap getPrimaryImage(@NonNull BitmapParams params) { + return getImageAtIndexInternal(-1, params); + } + + /** + * This method is similar to {@link #getPrimaryImage(BitmapParams)} except that + * the default for {@link BitmapParams} will be used. + * + * @return the primary image, or null if it cannot be retrieved. + * + * @throws IllegalStateException if the container doesn't contain still images. + * + * @see #getImageAtIndex(int, BitmapParams) + * @see #getImageAtIndex(int) + * @see #getPrimaryImage(BitmapParams) + */ + public Bitmap getPrimaryImage() { + return getImageAtIndexInternal(-1, null); + } + + private Bitmap getImageAtIndexInternal(int imageIndex, @Nullable BitmapParams params) { + 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, params); } private native Bitmap _getImageAtIndex(int imageIndex, @Nullable BitmapParams params); @@ -788,7 +880,8 @@ public class MediaMetadataRetriever public static final int METADATA_KEY_IMAGE_HEIGHT = 30; /** * If the media contains still images, this key retrieves the rotation - * of the primary image. + * angle (in degrees clockwise) of the primary image. The image rotation + * angle must be one of 0, 90, 180, or 270 degrees. */ public static final int METADATA_KEY_IMAGE_ROTATION = 31; /** diff --git a/media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl b/media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl new file mode 100644 index 000000000000..ef4bb01cb46f --- /dev/null +++ b/media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2018 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.media.soundtrigger; + +import android.media.soundtrigger.ISoundTriggerDetectionServiceClient; +import android.hardware.soundtrigger.SoundTrigger; +import android.os.Bundle; +import android.os.ParcelUuid; + +/** + * AIDL for the SoundTriggerDetectionService to run detection operations when triggered. + * + * {@hide} + */ +oneway interface ISoundTriggerDetectionService { + void setClient(in ParcelUuid uuid, in Bundle params, in ISoundTriggerDetectionServiceClient client); + void removeClient(in ParcelUuid uuid); + void onGenericRecognitionEvent(in ParcelUuid uuid, int opId, in SoundTrigger.GenericRecognitionEvent event); + void onError(in ParcelUuid uuid, int opId, int status); + void onStopOperation(in ParcelUuid uuid, int opId); +} diff --git a/media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl b/media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl new file mode 100644 index 000000000000..230735afaf42 --- /dev/null +++ b/media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 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.media.soundtrigger; + +/** + * AIDL for the callbacks from a ISoundTriggerDetectionService. + * + * {@hide} + */ +oneway interface ISoundTriggerDetectionServiceClient { + void onOpFinished(int opId); +} diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetectionService.java b/media/java/android/media/soundtrigger/SoundTriggerDetectionService.java new file mode 100644 index 000000000000..7381d977b7b5 --- /dev/null +++ b/media/java/android/media/soundtrigger/SoundTriggerDetectionService.java @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2018 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.media.soundtrigger; + +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + +import android.annotation.CallSuper; +import android.annotation.MainThread; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.hardware.soundtrigger.SoundTrigger; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; + +import java.util.UUID; + +/** + * A service that allows interaction with the actual sound trigger detection on the system. + * + * <p> Sound trigger detection refers to detectors that match generic sound patterns that are + * not voice-based. The voice-based recognition models should utilize the {@link + * android.service.voice.VoiceInteractionService} instead. Access to this class needs to be + * protected by the {@value android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE} + * permission granted only to the system. + * + * <p>This service has to be explicitly started by an app, the system does not scan for and start + * these services. + * + * <p>If an operation ({@link #onGenericRecognitionEvent}, {@link #onError}, + * {@link #onRecognitionPaused}, {@link #onRecognitionResumed}) is triggered the service is + * considered as running in the foreground. Once the operation is processed the service should call + * {@link #operationFinished(UUID, int)}. If this does not happen in + * {@link SoundTriggerManager#getDetectionServiceOperationsTimeout()} milliseconds + * {@link #onStopOperation(UUID, Bundle, int)} is called and the service is unbound. + * + * <p>The total amount of operations per day might be limited. + * + * @hide + */ +@SystemApi +public abstract class SoundTriggerDetectionService extends Service { + private static final String LOG_TAG = SoundTriggerDetectionService.class.getSimpleName(); + + private static final boolean DEBUG = false; + + private final Object mLock = new Object(); + + /** + * Client indexed by model uuid. This is needed for the {@link #operationFinished(UUID, int)} + * callbacks. + */ + @GuardedBy("mLock") + private final ArrayMap<UUID, ISoundTriggerDetectionServiceClient> mClients = + new ArrayMap<>(); + + private Handler mHandler; + + /** + * @hide + */ + @Override + protected final void attachBaseContext(Context base) { + super.attachBaseContext(base); + mHandler = new Handler(base.getMainLooper()); + } + + private void setClient(@NonNull UUID uuid, @Nullable Bundle params, + @NonNull ISoundTriggerDetectionServiceClient client) { + if (DEBUG) Log.i(LOG_TAG, uuid + ": handle setClient"); + + synchronized (mLock) { + mClients.put(uuid, client); + } + onConnected(uuid, params); + } + + private void removeClient(@NonNull UUID uuid, @Nullable Bundle params) { + if (DEBUG) Log.i(LOG_TAG, uuid + ": handle removeClient"); + + synchronized (mLock) { + mClients.remove(uuid); + } + onDisconnected(uuid, params); + } + + /** + * The system has connected to this service for the recognition registered for the model + * {@code uuid}. + * + * <p> This is called before any operations are delivered. + * + * @param uuid The {@code uuid} of the model the recognitions is registered for + * @param params The {@code params} passed when the recognition was started + */ + @MainThread + public void onConnected(@NonNull UUID uuid, @Nullable Bundle params) { + /* do nothing */ + } + + /** + * The system has disconnected from this service for the recognition registered for the model + * {@code uuid}. + * + * <p>Once this is called {@link #operationFinished} cannot be called anymore for + * {@code uuid}. + * + * <p> {@link #onConnected(UUID, Bundle)} is called before any further operations are delivered. + * + * @param uuid The {@code uuid} of the model the recognitions is registered for + * @param params The {@code params} passed when the recognition was started + */ + @MainThread + public void onDisconnected(@NonNull UUID uuid, @Nullable Bundle params) { + /* do nothing */ + } + + /** + * A new generic sound trigger event has been detected. + * + * @param uuid The {@code uuid} of the model the recognition is registered for + * @param params The {@code params} passed when the recognition was started + * @param opId The id of this operation. Once the operation is done, this service needs to call + * {@link #operationFinished(UUID, int)} + * @param event The event that has been detected + */ + @MainThread + public void onGenericRecognitionEvent(@NonNull UUID uuid, @Nullable Bundle params, int opId, + @NonNull SoundTrigger.RecognitionEvent event) { + operationFinished(uuid, opId); + } + + /** + * A error has been detected. + * + * @param uuid The {@code uuid} of the model the recognition is registered for + * @param params The {@code params} passed when the recognition was started + * @param opId The id of this operation. Once the operation is done, this service needs to call + * {@link #operationFinished(UUID, int)} + * @param status The error code detected + */ + @MainThread + public void onError(@NonNull UUID uuid, @Nullable Bundle params, int opId, int status) { + operationFinished(uuid, opId); + } + + /** + * An operation took too long and should be stopped. + * + * @param uuid The {@code uuid} of the model the recognition is registered for + * @param params The {@code params} passed when the recognition was started + * @param opId The id of the operation that took too long + */ + @MainThread + public abstract void onStopOperation(@NonNull UUID uuid, @Nullable Bundle params, int opId); + + /** + * Tell that the system that an operation has been fully processed. + * + * @param uuid The {@code uuid} of the model the recognition is registered for + * @param opId The id of the operation that is processed + */ + public final void operationFinished(@Nullable UUID uuid, int opId) { + try { + ISoundTriggerDetectionServiceClient client; + synchronized (mLock) { + client = mClients.get(uuid); + + if (client == null) { + throw new IllegalStateException("operationFinished called, but no client for " + + uuid + ". Was this called after onDisconnected?"); + } + } + client.onOpFinished(opId); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + @Override + public final IBinder onBind(Intent intent) { + return new ISoundTriggerDetectionService.Stub() { + private final Object mBinderLock = new Object(); + + /** Cached params bundles indexed by the model uuid */ + @GuardedBy("mBinderLock") + public final ArrayMap<UUID, Bundle> mParams = new ArrayMap<>(); + + @Override + public void setClient(ParcelUuid puuid, Bundle params, + ISoundTriggerDetectionServiceClient client) { + UUID uuid = puuid.getUuid(); + synchronized (mBinderLock) { + mParams.put(uuid, params); + } + + if (DEBUG) Log.i(LOG_TAG, uuid + ": setClient(" + params + ")"); + mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::setClient, + SoundTriggerDetectionService.this, uuid, params, client)); + } + + @Override + public void removeClient(ParcelUuid puuid) { + UUID uuid = puuid.getUuid(); + Bundle params; + synchronized (mBinderLock) { + params = mParams.remove(uuid); + } + + if (DEBUG) Log.i(LOG_TAG, uuid + ": removeClient"); + mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::removeClient, + SoundTriggerDetectionService.this, uuid, params)); + } + + @Override + public void onGenericRecognitionEvent(ParcelUuid puuid, int opId, + SoundTrigger.GenericRecognitionEvent event) { + UUID uuid = puuid.getUuid(); + Bundle params; + synchronized (mBinderLock) { + params = mParams.get(uuid); + } + + if (DEBUG) Log.i(LOG_TAG, uuid + "(" + opId + "): onGenericRecognitionEvent"); + mHandler.sendMessage( + obtainMessage(SoundTriggerDetectionService::onGenericRecognitionEvent, + SoundTriggerDetectionService.this, uuid, params, opId, event)); + } + + @Override + public void onError(ParcelUuid puuid, int opId, int status) { + UUID uuid = puuid.getUuid(); + Bundle params; + synchronized (mBinderLock) { + params = mParams.get(uuid); + } + + if (DEBUG) Log.i(LOG_TAG, uuid + "(" + opId + "): onError(" + status + ")"); + mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::onError, + SoundTriggerDetectionService.this, uuid, params, opId, status)); + } + + @Override + public void onStopOperation(ParcelUuid puuid, int opId) { + UUID uuid = puuid.getUuid(); + Bundle params; + synchronized (mBinderLock) { + params = mParams.get(uuid); + } + + if (DEBUG) Log.i(LOG_TAG, uuid + "(" + opId + "): onStopOperation"); + mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::onStopOperation, + SoundTriggerDetectionService.this, uuid, params, opId)); + } + }; + } + + @CallSuper + @Override + public boolean onUnbind(Intent intent) { + mClients.clear(); + + return false; + } +} diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java index 92ffae0fe560..c9ec7526fd6a 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerManager.java +++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java @@ -15,26 +15,31 @@ */ package android.media.soundtrigger; + import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR; -import android.app.PendingIntent; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.app.PendingIntent; +import android.content.ComponentName; import android.content.Context; import android.hardware.soundtrigger.SoundTrigger; -import android.hardware.soundtrigger.SoundTrigger.SoundModel; import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel; import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; +import android.hardware.soundtrigger.SoundTrigger.SoundModel; +import android.os.Bundle; import android.os.Handler; import android.os.ParcelUuid; import android.os.RemoteException; +import android.provider.Settings; import android.util.Slog; import com.android.internal.app.ISoundTriggerService; +import com.android.internal.util.Preconditions; import java.util.HashMap; import java.util.UUID; @@ -276,6 +281,40 @@ public final class SoundTriggerManager { } /** + * Starts recognition for the given model id. All events from the model will be sent to the + * service. + * + * <p>This only supports generic sound trigger events. For keyphrase events, please use + * {@link android.service.voice.VoiceInteractionService}. + * + * @param soundModelId Id of the sound model + * @param params Opaque data sent to each service call of the service as the {@code params} + * argument + * @param detectionService The component name of the service that should receive the events. + * Needs to subclass {@link SoundTriggerDetectionService} + * @param config Configures the recognition + * + * @return {@link SoundTrigger#STATUS_OK} if the recognition could be started, error code + * otherwise + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) + public int startRecognition(@NonNull UUID soundModelId, @Nullable Bundle params, + @NonNull ComponentName detectionService, @NonNull RecognitionConfig config) { + Preconditions.checkNotNull(soundModelId); + Preconditions.checkNotNull(detectionService); + Preconditions.checkNotNull(config); + + try { + return mSoundTriggerService.startRecognitionForService(new ParcelUuid(soundModelId), + params, detectionService, config); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Stops the given model's recognition. * @hide */ @@ -324,4 +363,19 @@ public final class SoundTriggerManager { throw e.rethrowFromSystemServer(); } } + + /** + * Get the amount of time (in milliseconds) an operation of the + * {@link ISoundTriggerDetectionService} is allowed to ask. + * + * @return The amount of time an sound trigger detection service operation is allowed to last + */ + public int getDetectionServiceOperationsTimeout() { + try { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT); + } catch (Settings.SettingNotFoundException e) { + return Integer.MAX_VALUE; + } + } } diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 3a6714279bb7..4f6763e55ce5 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -449,13 +449,14 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtIndex( std::vector<sp<IMemory> > frames; status_t err = retriever->getFrameAtIndex(&frames, frameIndex, numFrames, colorFormat); if (err != OK || frames.size() == 0) { - ALOGE("failed to get frames from retriever, err=%d, size=%zu", - err, frames.size()); + jniThrowException(env, + "java/lang/IllegalStateException", "No frames from retriever"); return NULL; } jobject arrayList = env->NewObject(fields.arrayListClazz, fields.arrayListInit); if (arrayList == NULL) { - ALOGE("can't create bitmap array list object"); + jniThrowException(env, + "java/lang/IllegalStateException", "Can't create bitmap array"); return NULL; } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 085e1123d67e..8f80527036ec 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -176,33 +176,34 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro @Deprecated public WifiTracker(Context context, WifiListener wifiListener, boolean includeSaved, boolean includeScans) { - this(context, new WifiListenerExecutor(wifiListener), + this(context, wifiListener, context.getSystemService(WifiManager.class), context.getSystemService(ConnectivityManager.class), context.getSystemService(NetworkScoreManager.class), newIntentFilter()); } - // TODO(Sghuman): Clean up includeSaved and includeScans from all constructors and linked + // TODO(sghuman): Clean up includeSaved and includeScans from all constructors and linked // calling apps once IC window is complete public WifiTracker(Context context, WifiListener wifiListener, @NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) { - this(context, new WifiListenerExecutor(wifiListener), + this(context, wifiListener, context.getSystemService(WifiManager.class), context.getSystemService(ConnectivityManager.class), context.getSystemService(NetworkScoreManager.class), newIntentFilter()); + lifecycle.addObserver(this); } @VisibleForTesting - WifiTracker(Context context, WifiListenerExecutor wifiListenerExecutor, + WifiTracker(Context context, WifiListener wifiListener, WifiManager wifiManager, ConnectivityManager connectivityManager, NetworkScoreManager networkScoreManager, IntentFilter filter) { mContext = context; mWifiManager = wifiManager; - mListener = wifiListenerExecutor; + mListener = new WifiListenerExecutor(wifiListener); mConnectivityManager = connectivityManager; // check if verbose logging developer option has been turned on or off @@ -853,8 +854,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro * * <p>Also logs all callbacks invocations when verbose logging is enabled. */ - @VisibleForTesting - public static class WifiListenerExecutor implements WifiListener { + @VisibleForTesting class WifiListenerExecutor implements WifiListener { private final WifiListener mDelegatee; @@ -864,27 +864,29 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro @Override public void onWifiStateChanged(int state) { - if (isVerboseLoggingEnabled()) { - Log.i(TAG, - String.format("Invoking onWifiStateChanged callback with state %d", state)); - } - ThreadUtils.postOnMainThread(() -> mDelegatee.onWifiStateChanged(state)); + runAndLog(() -> mDelegatee.onWifiStateChanged(state), + String.format("Invoking onWifiStateChanged callback with state %d", state)); } @Override public void onConnectedChanged() { - if (isVerboseLoggingEnabled()) { - Log.i(TAG, "Invoking onConnectedChanged callback"); - } - ThreadUtils.postOnMainThread(() -> mDelegatee.onConnectedChanged()); + runAndLog(mDelegatee::onConnectedChanged, "Invoking onConnectedChanged callback"); } @Override public void onAccessPointsChanged() { - if (isVerboseLoggingEnabled()) { - Log.i(TAG, "Invoking onAccessPointsChanged callback"); - } - ThreadUtils.postOnMainThread(() -> mDelegatee.onAccessPointsChanged()); + runAndLog(mDelegatee::onAccessPointsChanged, "Invoking onAccessPointsChanged callback"); + } + + private void runAndLog(Runnable r, String verboseLog) { + ThreadUtils.postOnMainThread(() -> { + if (mRegistered) { + if (isVerboseLoggingEnabled()) { + Log.i(TAG, verboseLog); + } + r.run(); + } + }); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java index fd48eea25e00..61efabc0c0ae 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java @@ -76,7 +76,8 @@ public class WifiUtils { * ["rssi 5Ghz", "num results on 5GHz" / "rssi 5Ghz", "num results on 5GHz"] * For instance [-40,5/-30,2] */ - private static String getVisibilityStatus(AccessPoint accessPoint) { + @VisibleForTesting + static String getVisibilityStatus(AccessPoint accessPoint) { final WifiInfo info = accessPoint.getInfo(); StringBuilder visibility = new StringBuilder(); StringBuilder scans24GHz = new StringBuilder(); @@ -110,6 +111,9 @@ public class WifiUtils { // TODO: sort list by RSSI or age long nowMs = SystemClock.elapsedRealtime(); for (ScanResult result : accessPoint.getScanResults()) { + if (result == null) { + continue; + } if (result.frequency >= AccessPoint.LOWER_FREQ_5GHZ && result.frequency <= AccessPoint.HIGHER_FREQ_5GHZ) { // Strictly speaking: [4915, 5825] diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java index 7fb4dc544d28..517db78d5dd1 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java @@ -58,6 +58,8 @@ import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import com.android.settingslib.utils.ThreadUtils; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -135,7 +137,7 @@ public class WifiTrackerTest { @Mock private RssiCurve mockBadgeCurve1; @Mock private RssiCurve mockBadgeCurve2; @Mock private WifiManager mockWifiManager; - @Mock private WifiTracker.WifiListenerExecutor mockWifiListenerExecutor; + @Mock private WifiTracker.WifiListener mockWifiListener; private final List<NetworkKey> mRequestedKeys = new ArrayList<>(); @@ -205,7 +207,7 @@ public class WifiTrackerTest { mAccessPointsChangedLatch.countDown(); } return null; - }).when(mockWifiListenerExecutor).onAccessPointsChanged(); + }).when(mockWifiListener).onAccessPointsChanged(); // Turn on Scoring UI features mOriginalScoringUiSettingValue = Settings.Global.getInt( @@ -271,7 +273,7 @@ public class WifiTrackerTest { private WifiTracker createMockedWifiTracker() { final WifiTracker wifiTracker = new WifiTracker( mContext, - mockWifiListenerExecutor, + mockWifiListener, mockWifiManager, mockConnectivityManager, mockNetworkScoreManager, @@ -690,7 +692,7 @@ public class WifiTrackerTest { verify(mockConnectivityManager).getNetworkInfo(any(Network.class)); // mStaleAccessPoints is true - verify(mockWifiListenerExecutor, never()).onAccessPointsChanged(); + verify(mockWifiListener, never()).onAccessPointsChanged(); assertThat(tracker.getAccessPoints().size()).isEqualTo(2); assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue(); } @@ -719,7 +721,7 @@ public class WifiTrackerTest { verify(mockConnectivityManager).getNetworkInfo(any(Network.class)); // mStaleAccessPoints is true - verify(mockWifiListenerExecutor, never()).onAccessPointsChanged(); + verify(mockWifiListener, never()).onAccessPointsChanged(); assertThat(tracker.getAccessPoints()).hasSize(1); assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue(); @@ -762,6 +764,41 @@ public class WifiTrackerTest { } @Test + public void stopTrackingShouldPreventCallbacksFromOngoingWork() throws Exception { + WifiTracker tracker = createMockedWifiTracker(); + startTracking(tracker); + + final CountDownLatch ready = new CountDownLatch(1); + final CountDownLatch latch = new CountDownLatch(1); + final CountDownLatch lock = new CountDownLatch(1); + tracker.mWorkHandler.post(() -> { + try { + ready.countDown(); + lock.await(); + + tracker.mReceiver.onReceive( + mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION)); + + latch.countDown(); + } catch (InterruptedException e) { + fail("Interrupted Exception while awaiting lock release: " + e); + } + }); + + ready.await(); // Make sure we have entered the first message handler + tracker.onStop(); + lock.countDown(); + assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); + + // Wait for main thread + final CountDownLatch latch2 = new CountDownLatch(1); + ThreadUtils.postOnMainThread(latch2::countDown); + latch2.await(); + + verify(mockWifiListener, never()).onWifiStateChanged(anyInt()); + } + + @Test public void stopTrackingShouldSetStaleBitWhichPreventsCallbacksUntilNextScanResult() throws Exception { WifiTracker tracker = createMockedWifiTracker(); @@ -778,7 +815,7 @@ public class WifiTrackerTest { mContext, new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION)); - verify(mockWifiListenerExecutor, never()).onAccessPointsChanged(); + verify(mockWifiListener, never()).onAccessPointsChanged(); sendScanResults(tracker); // verifies onAccessPointsChanged is invoked } @@ -795,7 +832,7 @@ public class WifiTrackerTest { tracker.mReceiver.onReceive( mContext, new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION)); - verify(mockWifiListenerExecutor, never()).onAccessPointsChanged(); + verify(mockWifiListener, never()).onAccessPointsChanged(); sendScanResults(tracker); // verifies onAccessPointsChanged is invoked } @@ -816,7 +853,7 @@ public class WifiTrackerTest { @Test public void onConnectedChangedCallback_shouldNotBeInvokedWhenNoStateChange() throws Exception { WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected(); - verify(mockWifiListenerExecutor, times(1)).onConnectedChanged(); + verify(mockWifiListener, times(1)).onConnectedChanged(); NetworkInfo networkInfo = new NetworkInfo( ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype"); @@ -826,13 +863,13 @@ public class WifiTrackerTest { intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo); tracker.mReceiver.onReceive(mContext, intent); - verify(mockWifiListenerExecutor, times(1)).onConnectedChanged(); + verify(mockWifiListener, times(1)).onConnectedChanged(); } @Test public void onConnectedChangedCallback_shouldBeInvokedWhenStateChanges() throws Exception { WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected(); - verify(mockWifiListenerExecutor, times(1)).onConnectedChanged(); + verify(mockWifiListener, times(1)).onConnectedChanged(); NetworkInfo networkInfo = new NetworkInfo( ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype"); @@ -844,7 +881,7 @@ public class WifiTrackerTest { tracker.mReceiver.onReceive(mContext, intent); assertThat(tracker.isConnected()).isFalse(); - verify(mockWifiListenerExecutor, times(2)).onConnectedChanged(); + verify(mockWifiListener, times(2)).onConnectedChanged(); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java index ea8ecbad99bd..91d9f91887a8 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java @@ -18,6 +18,7 @@ package com.android.settingslib.wifi; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; import android.content.Context; @@ -31,6 +32,7 @@ import android.os.Bundle; import android.os.Parcelable; import android.os.SystemClock; import android.text.format.DateUtils; +import android.util.ArraySet; import com.android.settingslib.R; import com.android.settingslib.SettingsLibRobolectricTestRunner; @@ -43,6 +45,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; +import java.util.Set; @RunWith(SettingsLibRobolectricTestRunner.class) public class WifiUtilsTest { @@ -56,6 +59,8 @@ public class WifiUtilsTest { private RssiCurve mockBadgeCurve; @Mock private WifiNetworkScoreCache mockWifiNetworkScoreCache; + @Mock + private AccessPoint mAccessPoint; @Before public void setUp() { @@ -84,6 +89,15 @@ public class WifiUtilsTest { assertThat(summary.contains(mContext.getString(R.string.speed_label_very_fast))).isTrue(); } + @Test + public void testGetVisibilityStatus_nullResultDoesNotCrash() { + doReturn(null).when(mAccessPoint).getInfo(); + Set<ScanResult> set = new ArraySet<>(); + set.add(null); + doReturn(set).when(mAccessPoint).getScanResults(); + WifiUtils.getVisibilityStatus(mAccessPoint); + } + private static ArrayList<ScanResult> buildScanResultCache() { ArrayList<ScanResult> scanResults = new ArrayList<>(); for (int i = 0; i < 5; i++) { diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index ecda53a15f48..8b783665b039 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -82,6 +82,10 @@ <string name="def_wireless_charging_started_sound" translatable="false">/system/media/audio/ui/WirelessChargingStarted.ogg</string> <string name="def_charging_started_sound" translatable="false">/system/media/audio/ui/ChargingStarted.ogg</string> + <!-- sound trigger detection service default values --> + <integer name="def_max_sound_trigger_detection_service_ops_per_day" translatable="false">200</integer> + <integer name="def_sound_trigger_detection_service_op_timeout" translatable="false">15000</integer> + <bool name="def_lockscreen_disabled">false</bool> <bool name="def_device_provisioned">false</bool> <integer name="def_dock_audio_media_enabled">1</integer> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index f895a9858b36..34aae4953d9d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1164,6 +1164,12 @@ class SettingsProtoDumpUtil { Global.HIDDEN_API_BLACKLIST_EXEMPTIONS, GlobalSettingsProto.HIDDEN_API_BLACKLIST_EXEMPTIONS); dumpSetting(s, p, + Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT, + GlobalSettingsProto.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT); + dumpSetting(s, p, + Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY, + GlobalSettingsProto.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY); + dumpSetting(s, p, Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION, GlobalSettingsProto.MULTI_SIM_VOICE_CALL_SUBSCRIPTION); dumpSetting(s, p, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index cb5763fa1315..f215c1220610 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2774,9 +2774,7 @@ public class SettingsProvider extends ContentProvider { } private void notifyForSettingsChange(int key, String name) { - final int userId = isGlobalSettingsKey(key) - ? UserHandle.USER_ALL : getUserIdFromKey(key); - + final int userId = getUserIdFromKey(key); Uri uri = getNotificationUriFor(key, name); mGenerationRegistry.incrementGeneration(key); @@ -2940,7 +2938,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 160; + private static final int SETTINGS_VERSION = 161; private final int mUserId; @@ -3670,6 +3668,35 @@ public class SettingsProvider extends ContentProvider { } currentVersion = 160; } + + if (currentVersion == 160) { + // Version 161: Set the default value for + // MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY and + // SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT + final SettingsState globalSettings = getGlobalSettingsLocked(); + + String oldValue = globalSettings.getSettingLocked( + Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY).getValue(); + if (TextUtils.equals(null, oldValue)) { + globalSettings.insertSettingLocked( + Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY, + Integer.toString(getContext().getResources().getInteger( + R.integer.def_max_sound_trigger_detection_service_ops_per_day)), + null, true, SettingsState.SYSTEM_PACKAGE_NAME); + } + + oldValue = globalSettings.getSettingLocked( + Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT).getValue(); + if (TextUtils.equals(null, oldValue)) { + globalSettings.insertSettingLocked( + Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT, + Integer.toString(getContext().getResources().getInteger( + R.integer.def_sound_trigger_detection_service_op_timeout)), + null, true, SettingsState.SYSTEM_PACKAGE_NAME); + } + currentVersion = 161; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SystemUI/res/drawable/heads_up_scrim.xml b/packages/SystemUI/res/drawable/heads_up_scrim.xml deleted file mode 100644 index 59000fcf37d2..000000000000 --- a/packages/SystemUI/res/drawable/heads_up_scrim.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- - ~ 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 - --> - -<shape xmlns:android="http://schemas.android.com/apk/res/android"> - <gradient - android:type="linear" - android:angle="-90" - android:startColor="#55000000" - android:endColor="#00000000" /> -</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml b/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml index 3345f61a3cf2..6a7f18b80b9f 100644 --- a/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml +++ b/packages/SystemUI/res/drawable/ic_ime_switcher_default.xml @@ -20,6 +20,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> <path - android:pathData="M20.000000,5.000000L4.000000,5.000000C2.900000,5.000000 2.000000,5.900000 2.000000,7.000000l0.000000,10.000000c0.000000,1.100000 0.900000,2.000000 2.000000,2.000000l16.000000,0.000000c1.100000,0.000000 2.000000,-0.900000 2.000000,-2.000000L22.000000,7.000000C22.000000,5.900000 21.100000,5.000000 20.000000,5.000000zM11.000000,8.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000L11.000000,8.000000zM11.000000,11.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000L11.000000,11.000000zM8.000000,8.000000l2.000000,0.000000l0.000000,2.000000L8.000000,10.000000L8.000000,8.000000zM8.000000,11.000000l2.000000,0.000000l0.000000,2.000000L8.000000,13.000000L8.000000,11.000000zM7.000000,13.000000L5.000000,13.000000l0.000000,-2.000000l2.000000,0.000000L7.000000,13.000000zM7.000000,10.000000L5.000000,10.000000L5.000000,8.000000l2.000000,0.000000L7.000000,10.000000zM16.000000,17.000000L8.000000,17.000000l0.000000,-2.000000l8.000000,0.000000L16.000000,17.000000zM16.000000,13.000000l-2.000000,0.000000l0.000000,-2.000000l2.000000,0.000000L16.000000,13.000000zM16.000000,10.000000l-2.000000,0.000000L14.000000,8.000000l2.000000,0.000000L16.000000,10.000000zM19.000000,13.000000l-2.000000,0.000000l0.000000,-2.000000l2.000000,0.000000L19.000000,13.000000zM19.000000,10.000000l-2.000000,0.000000L17.000000,8.000000l2.000000,0.000000L19.000000,10.000000z" + android:pathData="M21,4H3C1.9,4 1,4.9 1,6v13c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V6C23,4.9 22.1,4 21,4zM21,19H3V6h18V19zM9,8h2v2H9V8zM5,8h2v2H5V8zM8,16h8v1H8V16zM13,8h2v2h-2V8zM9,12h2v2H9V12zM5,12h2v2H5V12zM13,12h2v2h-2V12zM17,8h2v2h-2V8zM17,12h2v2h-2V12z" android:fillColor="?attr/singleToneColor"/> </vector> diff --git a/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml b/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml new file mode 100644 index 000000000000..bacb5c13d407 --- /dev/null +++ b/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 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.HeadsUpStatusBarView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:visibility="invisible" + android:id="@+id/heads_up_status_bar_view" + android:alpha="0" +> + <!-- This is a space just used as a layout and it's not actually displaying anything. We're + repositioning the statusbar icon to the position where this is laid out when showing this + view. --> + <Space + android:id="@+id/icon_placeholder" + android:layout_width="@dimen/status_bar_icon_drawing_size" + android:layout_height="@dimen/status_bar_icon_drawing_size" + android:layout_gravity="center_vertical" + /> + <TextView + android:id="@+id/text" + android:textAppearance="@style/TextAppearance.HeadsUpStatusBarText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + android:textAlignment="viewStart" + android:paddingStart="6dp" + android:layout_weight="1" + android:layout_gravity="center_vertical" + /> + +</com.android.systemui.statusbar.HeadsUpStatusBarView> diff --git a/packages/SystemUI/res/layout/notification_icon_area.xml b/packages/SystemUI/res/layout/notification_icon_area.xml index 6732e6c62e8e..fa696cc1f54c 100644 --- a/packages/SystemUI/res/layout/notification_icon_area.xml +++ b/packages/SystemUI/res/layout/notification_icon_area.xml @@ -18,12 +18,14 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/notification_icon_area_inner" android:layout_width="match_parent" - android:layout_height="match_parent" > + android:layout_height="match_parent" + android:clipChildren="false"> <com.android.systemui.statusbar.phone.NotificationIconContainer android:id="@+id/notificationIcons" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentStart="true" android:gravity="center_vertical" - android:orientation="horizontal"/> + android:orientation="horizontal" + android:clipChildren="false"/> </com.android.keyguard.AlphaOptimizedLinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/rounded_corners.xml b/packages/SystemUI/res/layout/rounded_corners.xml index 734c877d7829..26cf7920c35b 100644 --- a/packages/SystemUI/res/layout/rounded_corners.xml +++ b/packages/SystemUI/res/layout/rounded_corners.xml @@ -14,7 +14,7 @@ ** See the License for the specific language governing permissions and ** limitations under the License. --> -<FrameLayout +<com.android.systemui.RegionInterceptingFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> @@ -32,4 +32,4 @@ android:tint="#ff000000" android:layout_gravity="right|bottom" android:src="@drawable/rounded" /> -</FrameLayout> +</com.android.systemui.RegionInterceptingFrameLayout> diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index 8c0b9bab35a0..9d336e25985f 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -3,16 +3,16 @@ ** ** Copyright 2006, 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 +** 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 +** 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 +** 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. */ --> @@ -34,7 +34,7 @@ android:id="@+id/notification_lights_out" android:layout_width="@dimen/status_bar_icon_size" android:layout_height="match_parent" - android:paddingStart="6dip" + android:paddingStart="@dimen/status_bar_padding_start" android:paddingBottom="2dip" android:src="@drawable/ic_sysbar_lights_out_dot_small" android:scaleType="center" @@ -44,7 +44,7 @@ <LinearLayout android:id="@+id/status_bar_contents" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingStart="6dp" + android:paddingStart="@dimen/status_bar_padding_start" android:paddingEnd="8dp" android:orientation="horizontal" > @@ -53,33 +53,41 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout="@layout/operator_name" /> - - <LinearLayout + <FrameLayout android:layout_height="match_parent" android:layout_width="0dp" - android:layout_weight="1" - > - <com.android.systemui.statusbar.policy.Clock - android:id="@+id/clock" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:textAppearance="@style/TextAppearance.StatusBar.Clock" - android:singleLine="true" - android:paddingStart="@dimen/status_bar_left_clock_starting_padding" - android:paddingEnd="@dimen/status_bar_left_clock_end_padding" - android:gravity="center_vertical|start" - /> + android:layout_weight="1"> - <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and - PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). --> - <com.android.systemui.statusbar.AlphaOptimizedFrameLayout - android:id="@+id/notification_icon_area" - android:layout_width="0dp" + <include layout="@layout/heads_up_status_bar_layout" /> + + <LinearLayout android:layout_height="match_parent" - android:layout_weight="1" - android:orientation="horizontal" /> + android:layout_width="match_parent" + android:clipChildren="false" + > + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + android:singleLine="true" + android:paddingStart="@dimen/status_bar_left_clock_starting_padding" + android:paddingEnd="@dimen/status_bar_left_clock_end_padding" + android:gravity="center_vertical|start" + /> + + <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and + PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). --> + <com.android.systemui.statusbar.AlphaOptimizedFrameLayout + android:id="@+id/notification_icon_area" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:orientation="horizontal" + android:clipChildren="false"/> - </LinearLayout> + </LinearLayout> + </FrameLayout> <!-- Space should cover the notch (if it exists) and let other views lay out around it --> <android.widget.Space diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml index ef442e539c0c..75403b91d299 100644 --- a/packages/SystemUI/res/layout/super_status_bar.xml +++ b/packages/SystemUI/res/layout/super_status_bar.xml @@ -51,14 +51,6 @@ sysui:ignoreRightInset="true" /> - <com.android.systemui.statusbar.AlphaOptimizedView - android:id="@+id/heads_up_scrim" - android:layout_width="match_parent" - android:layout_height="@dimen/heads_up_scrim_height" - android:background="@drawable/heads_up_scrim" - sysui:ignoreRightInset="true" - android:importantForAccessibility="no"/> - <FrameLayout android:id="@+id/status_bar_container" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index cb3c282f703b..d65e42bc4e40 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -110,6 +110,12 @@ <!-- Side padding on the lockscreen on the side of notifications --> <dimen name="notification_side_paddings">4dp</dimen> + <!-- padding between the heads up and the statusbar --> + <dimen name="heads_up_status_bar_padding">8dp</dimen> + + <!-- heads up elevation that is added if the view is pinned --> + <dimen name="heads_up_pinned_elevation">16dp</dimen> + <!-- Height of a messaging notifications with actions at least. Not that this is an upper bound and the notification won't use this much, but is measured with wrap_content --> <dimen name="notification_messaging_actions_min_height">196dp</dimen> @@ -451,7 +457,6 @@ <dimen name="keyguard_clock_notifications_margin">30dp</dimen> <!-- Minimum margin between clock and status bar --> <dimen name="keyguard_clock_top_margin">36dp</dimen> - <dimen name="heads_up_scrim_height">250dp</dimen> <item name="scrim_behind_alpha" format="float" type="dimen">0.62</item> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index e0e666c22ee8..3c8a69510f45 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1845,11 +1845,14 @@ <string name="right_icon">Right icon</string> <!-- Label for area where tiles can be dragged out of [CHAR LIMIT=60] --> - <string name="drag_to_add_tiles">Drag to add tiles</string> + <string name="drag_to_add_tiles">Hold and drag to add tiles</string> <!-- Label for area where tiles can be dragged in to [CHAR LIMIT=60] --> <string name="drag_to_remove_tiles">Drag here to remove</string> + <!-- Label to indicate to users that additional tiles cannot be removed. [CHAR LIMIT=60] --> + <string name="drag_to_remove_disabled">You need at least 6 tiles</string> + <!-- Button to edit the tile ordering of quick settings [CHAR LIMIT=60] --> <string name="qs_edit">Edit</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 1470dfa4d05d..20d883435aef 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -490,6 +490,10 @@ <item name="android:paddingEnd">8dp</item> </style> + <style name="TextAppearance.HeadsUpStatusBarText" + parent="@*android:style/TextAppearance.Material.Notification.Info"> + </style> + <style name="edit_theme" parent="qs_base"> <item name="android:colorBackground">?android:attr/colorSecondary</item> </style> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 9d5fb526fa73..d24675c67188 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1726,6 +1726,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { callback.onPhoneStateChanged(mPhoneState); callback.onRefreshCarrierInfo(); callback.onClockVisibilityChanged(); + callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible); for (Entry<Integer, SimData> data : mSimDatas.entrySet()) { final SimData state = data.getValue(); callback.onSimStateChanged(state.subId, state.slotId, state.simState); diff --git a/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java new file mode 100644 index 000000000000..646f69eede94 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java @@ -0,0 +1,90 @@ +/* + * 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; + +import android.content.Context; +import android.graphics.Region; +import android.graphics.Region.Op; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.InternalInsetsInfo; +import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; +import android.widget.FrameLayout; + +/** + * Frame layout that will intercept the touches of children if they want to + */ +public class RegionInterceptingFrameLayout extends FrameLayout { + public RegionInterceptingFrameLayout(Context context) { + super(context); + } + + public RegionInterceptingFrameLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public RegionInterceptingFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public RegionInterceptingFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener); + } + + private final OnComputeInternalInsetsListener mInsetsListener = internalInsetsInfo -> { + internalInsetsInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); + internalInsetsInfo.touchableRegion.setEmpty(); + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (!(child instanceof RegionInterceptableView)) { + continue; + } + RegionInterceptableView riv = (RegionInterceptableView) child; + if (!riv.shouldInterceptTouch()) { + continue; + } + Region unionRegion = riv.getInterceptRegion(); + if (unionRegion == null) { + continue; + } + + internalInsetsInfo.touchableRegion.op(riv.getInterceptRegion(), Op.UNION); + } + }; + + public interface RegionInterceptableView { + default public boolean shouldInterceptTouch() { + return false; + } + + public Region getInterceptRegion(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 9a20c81c4919..a0fa69e61f20 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -49,6 +49,7 @@ import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; +import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.plugins.qs.QS; @@ -232,8 +233,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, - WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_SLIPPERY @@ -317,7 +317,8 @@ public class ScreenDecorations extends SystemUI implements Tunable { } } - public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener { + public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener, + RegionInterceptableView { private final DisplayInfo mInfo = new DisplayInfo(); private final Paint mPaint = new Paint(); @@ -468,5 +469,19 @@ public class ScreenDecorations extends SystemUI implements Tunable { } } } + + @Override + public boolean shouldInterceptTouch() { + return mInfo.displayCutout != null && getVisibility() == VISIBLE; + } + + @Override + public Region getInterceptRegion() { + if (mInfo.displayCutout == null) { + return null; + } + + return mInfo.displayCutout.getBounds(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 91edfda06261..391843ce55ba 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -100,10 +100,10 @@ public class SystemUIFactory { } public ScrimController createScrimController(LightBarController lightBarController, - ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim, - LockscreenWallpaper lockscreenWallpaper, Consumer<Integer> scrimVisibleListener, - DozeParameters dozeParameters, AlarmManager alarmManager) { - return new ScrimController(lightBarController, scrimBehind, scrimInFront, headsUpScrim, + ScrimView scrimBehind, ScrimView scrimInFront, LockscreenWallpaper lockscreenWallpaper, + Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters, + AlarmManager alarmManager) { + return new ScrimController(lightBarController, scrimBehind, scrimInFront, scrimVisibleListener, dozeParameters, alarmManager); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index 092f3d241d6c..5bf62f638235 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -22,8 +22,10 @@ import android.content.Context; import android.hardware.Sensor; import android.hardware.SensorManager; import android.os.Handler; +import android.os.PowerManager; import com.android.internal.hardware.AmbientDisplayConfiguration; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUIApplication; @@ -46,7 +48,7 @@ public class DozeFactory { DozeHost host = getHost(dozeService); AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(context); - DozeParameters params = new DozeParameters(context); + DozeParameters params = DozeParameters.getInstance(context); Handler handler = new Handler(); WakeLock wakeLock = new DelayedWakeLock(handler, WakeLock.createPartial(context, "Doze")); @@ -64,9 +66,9 @@ public class DozeFactory { createDozeTriggers(context, sensorManager, host, alarmManager, config, params, handler, wakeLock, machine), createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params), - new DozeScreenState(wrappedService, handler, params), + new DozeScreenState(wrappedService, handler, params, wakeLock), createDozeScreenBrightness(context, wrappedService, sensorManager, host, handler), - new DozeWallpaperState(context) + new DozeWallpaperState(context, params) }); return machine; @@ -92,7 +94,8 @@ public class DozeFactory { private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock, DozeMachine machine, Handler handler, AlarmManager alarmManager, DozeParameters params) { - return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params); + return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params, + KeyguardUpdateMonitor.getInstance(context)); } public static DozeHost getHost(DozeService service) { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index 6ff8e3db25e5..152b9fc3db4d 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -92,17 +92,17 @@ public class DozeMachine { switch (this) { case UNINITIALIZED: case INITIALIZED: - case DOZE: + case DOZE_REQUEST_PULSE: + return parameters.shouldControlScreenOff() ? Display.STATE_ON + : Display.STATE_OFF; case DOZE_AOD_PAUSED: + case DOZE: return Display.STATE_OFF; case DOZE_PULSING: return Display.STATE_ON; case DOZE_AOD: case DOZE_AOD_PAUSING: return Display.STATE_DOZE_SUSPEND; - case DOZE_REQUEST_PULSE: - return parameters.getDisplayNeedsBlanking() ? Display.STATE_OFF - : Display.STATE_ON; default: return Display.STATE_UNKNOWN; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java index 7d145644e737..f72bff5d651e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -21,6 +21,7 @@ import android.util.Log; import android.view.Display; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.util.wakelock.WakeLock; /** * Controls the screen when dozing. @@ -30,18 +31,27 @@ public class DozeScreenState implements DozeMachine.Part { private static final boolean DEBUG = DozeService.DEBUG; private static final String TAG = "DozeScreenState"; + /** + * Delay entering low power mode when animating to make sure that we'll have + * time to move all elements into their final positions while still at 60 fps. + */ + private static final int ENTER_DOZE_DELAY = 3000; + private final DozeMachine.Service mDozeService; private final Handler mHandler; private final Runnable mApplyPendingScreenState = this::applyPendingScreenState; private final DozeParameters mParameters; private int mPendingScreenState = Display.STATE_UNKNOWN; + private boolean mWakeLockHeld; + private WakeLock mWakeLock; public DozeScreenState(DozeMachine.Service service, Handler handler, - DozeParameters parameters) { + DozeParameters parameters, WakeLock wakeLock) { mDozeService = service; mHandler = handler; mParameters = parameters; + mWakeLock = wakeLock; } @Override @@ -69,12 +79,33 @@ public class DozeScreenState implements DozeMachine.Part { // that the screen turns on again before the navigation bar is hidden. To work around // that, wait for a traversal to happen before applying the initial screen state. mPendingScreenState = screenState; + + // Delay screen state transitions even longer while animations are running. + boolean shouldDelayTransition = newState == DozeMachine.State.DOZE_AOD + && mParameters.shouldControlScreenOff(); + + if (!mWakeLockHeld && shouldDelayTransition) { + mWakeLockHeld = true; + mWakeLock.acquire(); + } + if (!messagePending) { - mHandler.post(mApplyPendingScreenState); + if (DEBUG) { + Log.d(TAG, "Display state changed to " + screenState + " delayed by " + + (shouldDelayTransition ? ENTER_DOZE_DELAY : 1)); + } + + if (shouldDelayTransition) { + mHandler.postDelayed(mApplyPendingScreenState, ENTER_DOZE_DELAY); + } else { + mHandler.post(mApplyPendingScreenState); + } + } else if (DEBUG) { + Log.d(TAG, "Pending display state change to " + screenState); } - return; + } else { + applyScreenState(screenState); } - applyScreenState(screenState); } private void applyPendingScreenState() { @@ -87,6 +118,10 @@ public class DozeScreenState implements DozeMachine.Part { if (DEBUG) Log.d(TAG, "setDozeScreenState(" + screenState + ")"); mDozeService.setDozeScreenState(screenState); mPendingScreenState = Display.STATE_UNKNOWN; + if (mWakeLockHeld) { + mWakeLockHeld = false; + mWakeLock.release(); + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 75f1b501b3f4..778e63092150 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -25,6 +25,9 @@ import android.os.SystemClock; import android.text.format.Formatter; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.wakelock.WakeLock; @@ -44,22 +47,46 @@ public class DozeUi implements DozeMachine.Part { private final WakeLock mWakeLock; private final DozeMachine mMachine; private final AlarmTimeout mTimeTicker; - private final boolean mCanAnimateWakeup; + private final boolean mCanAnimateTransition; + private final DozeParameters mDozeParameters; + + private boolean mKeyguardShowing; + private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback = + new KeyguardUpdateMonitorCallback() { + + @Override + public void onKeyguardVisibilityChanged(boolean showing) { + mKeyguardShowing = showing; + updateAnimateScreenOff(); + } + }; private long mLastTimeTickElapsed = 0; public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine, WakeLock wakeLock, DozeHost host, Handler handler, - DozeParameters params) { + DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor) { mContext = context; mMachine = machine; mWakeLock = wakeLock; mHost = host; mHandler = handler; - mCanAnimateWakeup = !params.getDisplayNeedsBlanking(); - + mCanAnimateTransition = !params.getDisplayNeedsBlanking(); + mDozeParameters = params; mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler); - mHost.setAnimateScreenOff(params.getCanControlScreenOffAnimation()); + keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback); + } + + /** + * Decide if we're taking over the screen-off animation + * when the device was configured to skip doze after screen off. + */ + private void updateAnimateScreenOff() { + if (mCanAnimateTransition) { + final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing; + mDozeParameters.setControlScreenOffAnimation(controlScreenOff); + mHost.setAnimateScreenOff(controlScreenOff); + } } private void pulseWhileDozing(int reason) { @@ -118,7 +145,7 @@ public class DozeUi implements DozeMachine.Part { // Keep current state. break; default: - mHost.setAnimateWakeup(mCanAnimateWakeup); + mHost.setAnimateWakeup(mCanAnimateTransition && mDozeParameters.getAlwaysOn()); break; } } @@ -170,4 +197,9 @@ public class DozeUi implements DozeMachine.Part { scheduleTimeTick(); } + + @VisibleForTesting + KeyguardUpdateMonitorCallback getKeyguardCallback() { + return mKeyguardVisibilityCallback; + } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java index 5156272b1ade..9d110fb74d86 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java @@ -26,7 +26,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import java.io.PrintWriter; @@ -43,10 +42,10 @@ public class DozeWallpaperState implements DozeMachine.Part { private boolean mIsAmbientMode; private final DozeParameters mDozeParameters; - public DozeWallpaperState(Context context) { + public DozeWallpaperState(Context context, DozeParameters dozeParameters) { this(IWallpaperManager.Stub.asInterface( ServiceManager.getService(Context.WALLPAPER_SERVICE)), - new DozeParameters(context), KeyguardUpdateMonitor.getInstance(context)); + dozeParameters, KeyguardUpdateMonitor.getInstance(context)); } @VisibleForTesting @@ -80,7 +79,7 @@ public class DozeWallpaperState implements DozeMachine.Part { final boolean animated; if (isAmbientMode) { - animated = mDozeParameters.getCanControlScreenOffAnimation() && !mKeyguardVisible; + animated = mDozeParameters.shouldControlScreenOff(); } else { animated = !mDozeParameters.getDisplayNeedsBlanking(); } @@ -88,8 +87,10 @@ public class DozeWallpaperState implements DozeMachine.Part { if (isAmbientMode != mIsAmbientMode) { mIsAmbientMode = isAmbientMode; try { - Log.i(TAG, "AoD wallpaper state changed to: " + mIsAmbientMode - + ", animated: " + animated); + if (DEBUG) { + Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode + + ", animated: " + animated); + } mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, animated); } catch (RemoteException e) { // Cannot notify wallpaper manager service, but it's fine, let's just skip it. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java index 951c0ea6a26b..59c7f236caf7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java @@ -41,21 +41,33 @@ public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observe } public void dispatchStartedWakingUp() { + if (getWakefulness() == WAKEFULNESS_WAKING) { + return; + } setWakefulness(WAKEFULNESS_WAKING); dispatch(Observer::onStartedWakingUp); } public void dispatchFinishedWakingUp() { + if (getWakefulness() == WAKEFULNESS_AWAKE) { + return; + } setWakefulness(WAKEFULNESS_AWAKE); dispatch(Observer::onFinishedWakingUp); } public void dispatchStartedGoingToSleep() { + if (getWakefulness() == WAKEFULNESS_GOING_TO_SLEEP) { + return; + } setWakefulness(WAKEFULNESS_GOING_TO_SLEEP); dispatch(Observer::onStartedGoingToSleep); } public void dispatchFinishedGoingToSleep() { + if (getWakefulness() == WAKEFULNESS_ASLEEP) { + return; + } setWakefulness(WAKEFULNESS_ASLEEP); dispatch(Observer::onFinishedGoingToSleep); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index bdc5e7d75b48..3ba5fe67fe00 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -42,19 +42,19 @@ import android.widget.TextView; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.R; -import com.android.systemui.qs.tileimpl.QSIconViewImpl; +import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.customize.TileAdapter.Holder; import com.android.systemui.qs.customize.TileQueryHelper.TileInfo; import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener; import com.android.systemui.qs.external.CustomTile; -import com.android.systemui.qs.QSTileHost; +import com.android.systemui.qs.tileimpl.QSIconViewImpl; import com.android.systemui.statusbar.phone.SystemUIDialog; import java.util.ArrayList; import java.util.List; public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener { - + private static final int MIN_NUM_TILES = 6; private static final long DRAG_LENGTH = 100; private static final float DRAG_SCALE = 1.2f; public static final long MOVE_DURATION = 150; @@ -219,9 +219,15 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta return; } if (holder.getItemViewType() == TYPE_EDIT) { - ((TextView) holder.itemView.findViewById(android.R.id.title)).setText( - mCurrentDrag != null ? R.string.drag_to_remove_tiles - : R.string.drag_to_add_tiles); + final int titleResId; + if (mCurrentDrag == null) { + titleResId = R.string.drag_to_add_tiles; + } else if (!canRemoveTiles() && mCurrentDrag.getAdapterPosition() < mEditIndex) { + titleResId = R.string.drag_to_remove_disabled; + } else { + titleResId = R.string.drag_to_remove_tiles; + } + ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleResId); return; } if (holder.getItemViewType() == TYPE_ACCESSIBLE_DROP) { @@ -286,7 +292,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta if (mAccessibilityMoving) { selectPosition(position, v); } else { - if (position < mEditIndex) { + if (position < mEditIndex && canRemoveTiles()) { showAccessibilityDialog(position, v); } else { startAccessibleDrag(position); @@ -297,6 +303,10 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta } } } + + private boolean canRemoveTiles() { + return mCurrentSpecs.size() > MIN_NUM_TILES; + } private void selectPosition(int position, View v) { // Remove the placeholder. @@ -507,7 +517,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta break; } } - }; + } private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() { @@ -551,6 +561,9 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta @Override public boolean canDropOver(RecyclerView recyclerView, ViewHolder current, ViewHolder target) { + if (!canRemoveTiles() && current.getAdapterPosition() < mEditIndex) { + return target.getAdapterPosition() < mEditIndex; + } return target.getAdapterPosition() <= mEditIndex + 1; } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java index cb9453b8846e..b7a5d31a324b 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java @@ -28,6 +28,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; @@ -55,6 +56,7 @@ public class DividerWindowManager { mLp.token = new Binder(); mLp.setTitle(WINDOW_TITLE); mLp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; + mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 0876507465a1..8c28af511496 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -26,6 +26,7 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.RectF; import android.util.AttributeSet; +import android.util.MathUtils; import android.view.MotionEvent; import android.view.View; import android.view.ViewAnimationUtils; @@ -178,6 +179,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private boolean mNeedsDimming; private int mDimmedAlpha; private boolean mBlockNextTouch; + private boolean mIsHeadsUpAnimation; + private int mHeadsUpAddStartLocation; + private float mHeadsUpLocation; + private boolean mIsAppearing; public ActivatableNotificationView(Context context, AttributeSet attrs) { super(context, attrs); @@ -204,6 +209,18 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView makeInactive(true /* animate */); } }, super::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap); + initDimens(); + } + + private void initDimens() { + mHeadsUpAddStartLocation = getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_content_margin_start); + } + + @Override + public void onDensityOrFontScaleChanged() { + super.onDensityOrFontScaleChanged(); + initDimens(); } @Override @@ -745,27 +762,34 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } @Override - public void performRemoveAnimation(long duration, float translationDirection, - Runnable onFinishedRunnable) { + public void performRemoveAnimation(long duration, long delay, + float translationDirection, boolean isHeadsUpAnimation, float endLocation, + Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) { enableAppearDrawing(true); + mIsHeadsUpAnimation = isHeadsUpAnimation; + mHeadsUpLocation = endLocation; if (mDrawingAppearAnimation) { startAppearAnimation(false /* isAppearing */, translationDirection, - 0, duration, onFinishedRunnable); + delay, duration, onFinishedRunnable, animationListener); } else if (onFinishedRunnable != null) { onFinishedRunnable.run(); } } @Override - public void performAddAnimation(long delay, long duration) { + public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) { enableAppearDrawing(true); + mIsHeadsUpAnimation = isHeadsUpAppear; + mHeadsUpLocation = mHeadsUpAddStartLocation; if (mDrawingAppearAnimation) { - startAppearAnimation(true /* isAppearing */, -1.0f, delay, duration, null); + startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay, + duration, null, null); } } private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay, - long duration, final Runnable onFinishedRunnable) { + long duration, final Runnable onFinishedRunnable, + AnimatorListenerAdapter animationListener) { cancelAppearAnimation(); mAnimationTranslationY = translationDirection * getActualHeight(); if (mAppearAnimationFraction == -1.0f) { @@ -778,6 +802,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mAppearAnimationTranslation = 0; } } + mIsAppearing = isAppearing; float targetValue; if (isAppearing) { @@ -803,6 +828,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView invalidate(); } }); + if (animationListener != null) { + mAppearAnimator.addListener(animationListener); + } if (delay > 0) { // we need to apply the initial state already to avoid drawn frames in the wrong state updateAppearAnimationAlpha(); @@ -862,9 +890,21 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END); widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction)); widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction); - float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) * - widthFraction); - float right = getWidth() - left; + float startWidthFraction = HORIZONTAL_COLLAPSED_REST_PARTIAL; + if (mIsHeadsUpAnimation && !mIsAppearing) { + startWidthFraction = 0; + } + float width = MathUtils.lerp(startWidthFraction, 1.0f, 1.0f - widthFraction) + * getWidth(); + float left; + float right; + if (mIsHeadsUpAnimation) { + left = MathUtils.lerp(mHeadsUpLocation, 0, 1.0f - widthFraction); + right = left + width; + } else { + left = getWidth() * 0.5f - width / 2.0f; + right = getWidth() - left; + } // handle top animation float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) / @@ -1046,6 +1086,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView return calculateBgColor(false /* withTint */, false /* withOverride */); } + public boolean isPinned() { + return false; + } + + public boolean isHeadsUpAnimatingAway() { + return false; + } + public interface OnActivatedListener { void onActivated(ActivatableNotificationView view); void onActivationReset(ActivatableNotificationView view); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java index e8f0925dcc71..4728a1d17b39 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java @@ -28,11 +28,17 @@ public class CrossFadeHelper { public static final long ANIMATION_DURATION_LENGTH = 210; public static void fadeOut(final View view, final Runnable endRunnable) { + fadeOut(view, ANIMATION_DURATION_LENGTH, 0, endRunnable); + } + + public static void fadeOut(final View view, long duration, int delay, + final Runnable endRunnable) { view.animate().cancel(); view.animate() .alpha(0f) - .setDuration(ANIMATION_DURATION_LENGTH) + .setDuration(duration) .setInterpolator(Interpolators.ALPHA_OUT) + .setStartDelay(delay) .withEndAction(new Runnable() { @Override public void run() { @@ -93,6 +99,10 @@ public class CrossFadeHelper { } public static void fadeIn(final View view) { + fadeIn(view, ANIMATION_DURATION_LENGTH, 0); + } + + public static void fadeIn(final View view, long duration, int delay) { view.animate().cancel(); if (view.getVisibility() == View.INVISIBLE) { view.setAlpha(0.0f); @@ -100,7 +110,8 @@ public class CrossFadeHelper { } view.animate() .alpha(1f) - .setDuration(ANIMATION_DURATION_LENGTH) + .setDuration(duration) + .setStartDelay(delay) .setInterpolator(Interpolators.ALPHA_IN) .withEndAction(null); if (view.hasOverlappingRendering()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index e5c5dcdb0f58..9bd8080525a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -139,6 +139,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private boolean mSensitiveHiddenInGeneral; private boolean mShowingPublicInitialized; private boolean mHideSensitiveForIntrinsicHeight; + private float mHeaderVisibleAmount = 1.0f; /** * Is this notification expanded by the system. The expansion state can be overridden by the @@ -156,8 +157,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private NotificationContentView mPublicLayout; private NotificationContentView mPrivateLayout; private NotificationContentView[] mLayouts; - private int mMaxExpandHeight; - private int mHeadsUpHeight; private int mNotificationColor; private ExpansionLogger mLogger; private String mLoggingKey; @@ -534,6 +533,30 @@ public class ExpandableNotificationRow extends ActivatableNotificationView addChildNotification(row, -1); } + /** + * Set the how much the header should be visible. A value of 0 will make the header fully gone + * and a value of 1 will make the notification look just like normal. + * This is being used for heads up notifications, when they are pinned to the top of the screen + * and the header content is extracted to the statusbar. + * + * @param headerVisibleAmount the amount the header should be visible. + */ + public void setHeaderVisibleAmount(float headerVisibleAmount) { + if (mHeaderVisibleAmount != headerVisibleAmount) { + mHeaderVisibleAmount = headerVisibleAmount; + mPrivateLayout.setHeaderVisibleAmount(headerVisibleAmount); + if (mChildrenContainer != null) { + mChildrenContainer.setHeaderVisibleAmount(headerVisibleAmount); + } + notifyHeightChanged(false /* needsAnimation */); + } + } + + @Override + public float getHeaderVisibleAmount() { + return mHeaderVisibleAmount; + } + @Override public void setHeadsUpIsVisible() { super.setHeadsUpIsVisible(); @@ -722,6 +745,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } + @Override public boolean isPinned() { return mIsPinned; } @@ -741,11 +765,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mChildrenContainer.getIntrinsicHeight(); } if(mExpandedWhenPinned) { - return Math.max(getMaxExpandHeight(), mHeadsUpHeight); + return Math.max(getMaxExpandHeight(), getHeadsUpHeight()); } else if (atLeastMinHeight) { - return Math.max(getCollapsedHeight(), mHeadsUpHeight); + return Math.max(getCollapsedHeight(), getHeadsUpHeight()); } else { - return mHeadsUpHeight; + return getHeadsUpHeight(); } } @@ -1034,11 +1058,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } - public void setDismissed(boolean dismissed, boolean fromAccessibility) { - mDismissed = dismissed; + public void setDismissed(boolean fromAccessibility) { + mDismissed = true; mGroupParentWhenDismissed = mNotificationParent; mRefocusOnDismiss = fromAccessibility; mChildAfterViewWhenDismissed = null; + mEntry.icon.setDismissed(); if (isChildInGroup()) { List<ExpandableNotificationRow> notificationChildren = mNotificationParent.getNotificationChildren(); @@ -1101,6 +1126,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * @return if the view was just heads upped and is now animating away. During such a time the * layout needs to be kept consistent */ + @Override public boolean isHeadsUpAnimatingAway() { return mHeadsupDisappearRunning; } @@ -1121,7 +1147,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView groupSummary.performDismiss(fromAccessibility); } } - setDismissed(true, fromAccessibility); + setDismissed(fromAccessibility); if (isClearable()) { if (mOnDismissRunnable != null) { mOnDismissRunnable.run(); @@ -1921,9 +1947,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (isPinned() || mHeadsupDisappearRunning) { return getPinnedHeadsUpHeight(true /* atLeastMinHeight */); } else if (isExpanded()) { - return Math.max(getMaxExpandHeight(), mHeadsUpHeight); + return Math.max(getMaxExpandHeight(), getHeadsUpHeight()); } else { - return Math.max(getCollapsedHeight(), mHeadsUpHeight); + return Math.max(getCollapsedHeight(), getHeadsUpHeight()); } } else if (isExpanded()) { return getMaxExpandHeight(); @@ -1997,8 +2023,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + int intrinsicBefore = getIntrinsicHeight(); super.onLayout(changed, left, top, right, bottom); - updateMaxHeights(); + if (intrinsicBefore != getIntrinsicHeight()) { + notifyHeightChanged(true /* needsAnimation */); + } if (mMenuRow.getMenuView() != null) { mMenuRow.onHeightUpdate(); } @@ -2022,15 +2051,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } - public void updateMaxHeights() { - int intrinsicBefore = getIntrinsicHeight(); - mMaxExpandHeight = mPrivateLayout.getExpandHeight(); - mHeadsUpHeight = mPrivateLayout.getHeadsUpHeight(); - if (intrinsicBefore != getIntrinsicHeight()) { - notifyHeightChanged(true /* needsAnimation */); - } - } - @Override public void notifyHeightChanged(boolean needsAnimation) { super.notifyHeightChanged(needsAnimation); @@ -2173,7 +2193,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } public int getMaxExpandHeight() { - return mMaxExpandHeight; + return mPrivateLayout.getExpandHeight(); + } + + + private int getHeadsUpHeight() { + return mPrivateLayout.getHeadsUpHeight(); } public boolean areGutsExposed() { @@ -2273,7 +2298,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } else if (mIsSummaryWithChildren && !isGroupExpanded() && !shouldShowPublic()) { return mChildrenContainer.getMinHeight(); } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) { - return mHeadsUpHeight; + return getHeadsUpHeight(); } NotificationContentView showingLayout = getShowingLayout(); return showingLayout.getMinHeight(); @@ -2319,10 +2344,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } - public boolean isMaxExpandHeightInitialized() { - return mMaxExpandHeight != 0; - } - public NotificationContentView getShowingLayout() { return shouldShowPublic() ? mPublicLayout : mPrivateLayout; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java index 8bc22016efae..67268c00a4ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java @@ -276,12 +276,18 @@ public abstract class ExpandableOutlineView extends ExpandableView { setClipToOutline(mAlwaysRoundBothCorners); } - public void setTopRoundness(float topRoundness, boolean animate) { + /** + * Set the topRoundness of this view. + * @return Whether the roundness was changed. + */ + public boolean setTopRoundness(float topRoundness, boolean animate) { if (mTopRoundness != topRoundness) { mTopRoundness = topRoundness; PropertyAnimator.setProperty(this, TOP_ROUNDNESS, topRoundness, ROUNDNESS_PROPERTIES, animate); + return true; } + return false; } protected void applyRoundness() { @@ -305,12 +311,18 @@ public abstract class ExpandableOutlineView extends ExpandableView { return mCurrentBottomRoundness * mOutlineRadius; } - public void setBottomRoundness(float bottomRoundness, boolean animate) { + /** + * Set the bottom roundness of this view. + * @return Whether the roundness was changed. + */ + public boolean setBottomRoundness(float bottomRoundness, boolean animate) { if (mBottomRoundness != bottomRoundness) { mBottomRoundness = bottomRoundness; PropertyAnimator.setProperty(this, BOTTOM_ROUNDNESS, bottomRoundness, ROUNDNESS_PROPERTIES, animate); + return true; } + return false; } protected void setBackgroundTop(int backgroundTop) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index 204adc813f50..df6a9778142f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar; +import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.graphics.Paint; import android.graphics.Rect; @@ -296,19 +297,24 @@ public abstract class ExpandableView extends FrameLayout { /** * Perform a remove animation on this view. - * * @param duration The duration of the remove animation. + * @param delay The delay of the animation * @param translationDirection The direction value from [-1 ... 1] indicating in which the - * animation should be performed. A value of -1 means that The - * remove animation should be performed upwards, - * such that the child appears to be going away to the top. 1 - * Should mean the opposite. + * animation should be performed. A value of -1 means that The + * remove animation should be performed upwards, + * such that the child appears to be going away to the top. 1 + * Should mean the opposite. + * @param isHeadsUpAnimation Is this a headsUp animation. + * @param endLocation The location where the horizonal heads up disappear animation should end. * @param onFinishedRunnable A runnable which should be run when the animation is finished. + * @param animationListener An animation listener to add to the animation. */ - public abstract void performRemoveAnimation(long duration, float translationDirection, - Runnable onFinishedRunnable); + public abstract void performRemoveAnimation(long duration, + long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation, + Runnable onFinishedRunnable, + AnimatorListenerAdapter animationListener); - public abstract void performAddAnimation(long delay, long duration); + public abstract void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear); /** * Set the notification appearance to be below the speed bump. @@ -390,6 +396,10 @@ public abstract class ExpandableView extends FrameLayout { } } + public float getHeaderVisibleAmount() { + return 1.0f; + } + protected boolean shouldClipToActualHeight() { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java new file mode 100644 index 000000000000..5e03fbfc1222 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2018 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.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.keyguard.AlphaOptimizedLinearLayout; +import com.android.systemui.R; +import com.android.systemui.statusbar.policy.DarkIconDispatcher; + +/** + * The view in the statusBar that contains part of the heads-up information + */ +public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { + private int mAbsoluteStartPadding; + private int mEndMargin; + private View mIconPlaceholder; + private TextView mTextView; + private NotificationData.Entry mShowingEntry; + private Rect mLayoutedIconRect = new Rect(); + private int[] mTmpPosition = new int[2]; + private boolean mFirstLayout = true; + private boolean mPublicMode; + private int mMaxWidth; + private View mRootView; + private int mLeftInset; + private Rect mIconDrawingRect = new Rect(); + private Runnable mOnDrawingRectChangedListener; + + public HeadsUpStatusBarView(Context context) { + this(context, null); + } + + public HeadsUpStatusBarView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + Resources res = getResources(); + mAbsoluteStartPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings) + + res.getDimensionPixelSize( + com.android.internal.R.dimen.notification_content_margin_start); + mEndMargin = res.getDimensionPixelSize( + com.android.internal.R.dimen.notification_content_margin_end); + setPaddingRelative(mAbsoluteStartPadding, 0, mEndMargin, 0); + updateMaxWidth(); + } + + private void updateMaxWidth() { + int maxWidth = getResources().getDimensionPixelSize(R.dimen.qs_panel_width); + if (maxWidth != mMaxWidth) { + // maxWidth doesn't work with fill_parent, let's manually make it at most as big as the + // notification panel + mMaxWidth = maxWidth; + requestLayout(); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mMaxWidth > 0) { + int newSize = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth); + widthMeasureSpec = MeasureSpec.makeMeasureSpec(newSize, + MeasureSpec.getMode(widthMeasureSpec)); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + updateMaxWidth(); + } + + @VisibleForTesting + public HeadsUpStatusBarView(Context context, View iconPlaceholder, TextView textView) { + this(context); + mIconPlaceholder = iconPlaceholder; + mTextView = textView; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mIconPlaceholder = findViewById(R.id.icon_placeholder); + mTextView = findViewById(R.id.text); + } + + public void setEntry(NotificationData.Entry entry) { + if (entry != null) { + mShowingEntry = entry; + CharSequence text = entry.headsUpStatusBarText; + if (mPublicMode) { + text = entry.headsUpStatusBarTextPublic; + } + mTextView.setText(text); + } else { + mShowingEntry = null; + } + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + mIconPlaceholder.getLocationOnScreen(mTmpPosition); + int left = (int) (mTmpPosition[0] - getTranslationX()); + int top = mTmpPosition[1]; + int right = left + mIconPlaceholder.getWidth(); + int bottom = top + mIconPlaceholder.getHeight(); + mLayoutedIconRect.set(left, top, right, bottom); + updateDrawingRect(); + int targetPadding = mAbsoluteStartPadding + mLeftInset; + if (left != targetPadding) { + int newPadding = targetPadding - left + getPaddingStart(); + setPaddingRelative(newPadding, 0, mEndMargin, 0); + } + if (mFirstLayout) { + // we need to do the padding calculation in the first frame, so the layout specified + // our visibility to be INVISIBLE in the beginning. let's correct that and set it + // to GONE. + setVisibility(GONE); + mFirstLayout = false; + } + } + + @Override + public void setTranslationX(float translationX) { + super.setTranslationX(translationX); + updateDrawingRect(); + } + + private void updateDrawingRect() { + float oldLeft = mIconDrawingRect.left; + mIconDrawingRect.set(mLayoutedIconRect); + mIconDrawingRect.offset((int) getTranslationX(), 0); + if (oldLeft != mIconDrawingRect.left && mOnDrawingRectChangedListener != null) { + mOnDrawingRectChangedListener.run(); + } + } + + @Override + protected boolean fitSystemWindows(Rect insets) { + mLeftInset = insets.left; + return super.fitSystemWindows(insets); + } + + public NotificationData.Entry getShowingEntry() { + return mShowingEntry; + } + + public Rect getIconDrawingRect() { + return mIconDrawingRect; + } + + public void onDarkChanged(Rect area, float darkIntensity, int tint) { + mTextView.setTextColor(DarkIconDispatcher.getTint(area, this, tint)); + } + + public void setPublicMode(boolean publicMode) { + mPublicMode = publicMode; + } + + public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) { + mOnDrawingRectChangedListener = onDrawingRectChangedListener; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 73c87953cf45..f64b1bc2abe8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -602,14 +602,15 @@ public class NotificationContentView extends FrameLayout { && (mIsHeadsUp || mHeadsUpAnimatingAway) && !mContainingNotification.isOnKeyguard(); if (transitioningBetweenHunAndExpanded || pinned) { - return Math.min(mHeadsUpChild.getHeight(), mExpandedChild.getHeight()); + return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP), + getViewHeight(VISIBLE_TYPE_EXPANDED)); } } // Size change of the expanded version if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0 && mExpandedChild != null) { - return Math.min(mContentHeightAtAnimationStart, mExpandedChild.getHeight()); + return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED)); } int hint; @@ -619,16 +620,17 @@ public class NotificationContentView extends FrameLayout { VISIBLE_TYPE_AMBIENT_SINGLELINE)) { hint = mAmbientSingleLineChild.getHeight(); } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) { - hint = mHeadsUpChild.getHeight(); + hint = getViewHeight(VISIBLE_TYPE_HEADSUP); } else if (mExpandedChild != null) { - hint = mExpandedChild.getHeight(); + hint = getViewHeight(VISIBLE_TYPE_EXPANDED); } else { - hint = mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.notification_action_list_height); + hint = getViewHeight(VISIBLE_TYPE_CONTRACTED) + + mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_action_list_height); } if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) { - hint = Math.min(hint, mExpandedChild.getHeight()); + hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED)); } return hint; } @@ -694,8 +696,8 @@ public class NotificationContentView extends FrameLayout { } private float calculateTransformationAmount() { - int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight(); - int endHeight = getViewForVisibleType(mVisibleType).getHeight(); + int startHeight = getViewHeight(mTransformationStartVisibleType); + int endHeight = getViewHeight(mVisibleType); int progress = Math.abs(mContentHeight - startHeight); int totalDistance = Math.abs(endHeight - startHeight); if (totalDistance == 0) { @@ -717,11 +719,23 @@ public class NotificationContentView extends FrameLayout { if (mContainingNotification.isShowingAmbient()) { return getShowingAmbientView().getHeight(); } else if (mExpandedChild != null) { - return mExpandedChild.getHeight() + getExtraRemoteInputHeight(mExpandedRemoteInput); + return getViewHeight(VISIBLE_TYPE_EXPANDED) + + getExtraRemoteInputHeight(mExpandedRemoteInput); } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) { - return mHeadsUpChild.getHeight() + getExtraRemoteInputHeight(mHeadsUpRemoteInput); + return getViewHeight(VISIBLE_TYPE_HEADSUP) + + getExtraRemoteInputHeight(mHeadsUpRemoteInput); } - return mContractedChild.getHeight(); + return getViewHeight(VISIBLE_TYPE_CONTRACTED); + } + + private int getViewHeight(int visibleType) { + View view = getViewForVisibleType(visibleType); + int height = view.getHeight(); + NotificationViewWrapper viewWrapper = getWrapperForView(view); + if (viewWrapper != null) { + height += viewWrapper.getHeaderTranslation(); + } + return height; } public int getMinHeight() { @@ -732,7 +746,7 @@ public class NotificationContentView extends FrameLayout { if (mContainingNotification.isShowingAmbient()) { return getShowingAmbientView().getHeight(); } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) { - return mContractedChild.getHeight(); + return getViewHeight(VISIBLE_TYPE_CONTRACTED); } else { return mSingleLineView.getHeight(); } @@ -1046,7 +1060,7 @@ public class NotificationContentView extends FrameLayout { private int getVisualTypeForHeight(float viewHeight) { boolean noExpandedChild = mExpandedChild == null; - if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) { + if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) { return VISIBLE_TYPE_EXPANDED; } if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) { @@ -1055,13 +1069,13 @@ public class NotificationContentView extends FrameLayout { if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) { - if (viewHeight <= mHeadsUpChild.getHeight() || noExpandedChild) { + if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) { return VISIBLE_TYPE_HEADSUP; } else { return VISIBLE_TYPE_EXPANDED; } } else { - if (noExpandedChild || (viewHeight <= mContractedChild.getHeight() + if (noExpandedChild || (viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED) && (!mIsChildInGroup || isGroupExpanded() || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) { return VISIBLE_TYPE_CONTRACTED; @@ -1616,19 +1630,19 @@ public class NotificationContentView extends FrameLayout { } public int getExpandHeight() { - View expandedChild = mExpandedChild; - if (expandedChild == null) { - expandedChild = mContractedChild; + int viewType = VISIBLE_TYPE_EXPANDED; + if (mExpandedChild == null) { + viewType = VISIBLE_TYPE_CONTRACTED; } - return expandedChild.getHeight() + getExtraRemoteInputHeight(mExpandedRemoteInput); + return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput); } public int getHeadsUpHeight() { - View headsUpChild = mHeadsUpChild; - if (headsUpChild == null) { - headsUpChild = mContractedChild; + int viewType = VISIBLE_TYPE_HEADSUP; + if (mHeadsUpChild == null) { + viewType = VISIBLE_TYPE_CONTRACTED; } - return headsUpChild.getHeight()+ getExtraRemoteInputHeight(mHeadsUpRemoteInput); + return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput); } public void setRemoteInputVisible(boolean remoteInputVisible) { @@ -1641,4 +1655,16 @@ public class NotificationContentView extends FrameLayout { clipChildren = clipChildren && !mRemoteInputVisible; super.setClipChildren(clipChildren); } + + public void setHeaderVisibleAmount(float headerVisibleAmount) { + if (mContractedWrapper != null) { + mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount); + } + if (mHeadsUpWrapper != null) { + mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount); + } + if (mExpandedWrapper != null) { + mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 22a186ff1706..775faee7fbc3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -107,6 +107,8 @@ public class NotificationData { public CharSequence remoteInputTextWhenReset; public long lastRemoteInputSent = NOT_LAUNCHED_YET; public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3); + public CharSequence headsUpStatusBarText; + public CharSequence headsUpStatusBarTextPublic; public Entry(StatusBarNotification n) { this.key = n.getKey(); @@ -529,6 +531,14 @@ public class NotificationData { return null; } + public boolean shouldHide(String key) { + if (mRankingMap != null) { + getRanking(key, mTmpRanking); + return mTmpRanking.isSuspended(); + } + return false; + } + private void updateRankingAndSort(RankingMap ranking) { if (ranking != null) { mRankingMap = ranking; @@ -618,6 +628,10 @@ public class NotificationData { return true; } + if (shouldHide(sbn.getKey())) { + return true; + } + if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS && mGroupManager.isChildInGroupWithSummary(sbn)) { return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 4f09133303de..aecf5fb89d85 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -177,6 +177,9 @@ public class NotificationShelf extends ActivatableNotificationView implements mShelfState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height, getFullyClosedTranslation()); mShelfState.zTranslation = ambientState.getBaseZHeight(); + if (mAmbientState.isDark()) { + mShelfState.yTranslation = mAmbientState.getDarkTopPadding(); + } float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation()) / (getIntrinsicHeight() * 2); openedAmount = Math.min(1.0f, openedAmount); @@ -252,7 +255,8 @@ public class NotificationShelf extends ActivatableNotificationView implements } ExpandableNotificationRow row = (ExpandableNotificationRow) child; float notificationClipEnd; - boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight; + boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight + || row.isPinned(); boolean isLastChild = child == lastChild; float rowTranslationY = row.getTranslationY(); if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) { @@ -343,7 +347,7 @@ public class NotificationShelf extends ActivatableNotificationView implements float maxTop = row.getTranslationY(); StatusBarIconView icon = row.getEntry().expandedIcon; float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY(); - if (shelfIconPosition < maxTop) { + if (shelfIconPosition < maxTop && !mAmbientState.isDark()) { int top = (int) (maxTop - shelfIconPosition); Rect clipRect = new Rect(0, top, icon.getWidth(), Math.max(top, icon.getHeight())); icon.setClipBounds(clipRect); @@ -354,7 +358,7 @@ public class NotificationShelf extends ActivatableNotificationView implements private void updateContinuousClipping(final ExpandableNotificationRow row) { StatusBarIconView icon = row.getEntry().expandedIcon; - boolean needsContinuousClipping = ViewState.isAnimatingY(icon); + boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDark(); boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null; if (needsContinuousClipping && !isContinuousClipping) { ViewTreeObserver.OnPreDrawListener predrawListener = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java index 0a7ee517a24a..badc40d5f01a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar; +import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.util.AttributeSet; import android.view.View; @@ -112,14 +113,16 @@ public abstract class StackScrollerDecorView extends ExpandableView { } @Override - public void performRemoveAnimation(long duration, float translationDirection, - Runnable onFinishedRunnable) { + public void performRemoveAnimation(long duration, long delay, + float translationDirection, boolean isHeadsUpAnimation, float endLocation, + Runnable onFinishedRunnable, + AnimatorListenerAdapter animationListener) { // TODO: Use duration performVisibilityAnimation(false); } @Override - public void performAddAnimation(long delay, long duration) { + public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) { // TODO: use delay and duration performVisibilityAnimation(true); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 6cfd42fa00eb..5bb85e231f26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -27,7 +27,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.Rect; @@ -43,7 +42,6 @@ import android.util.FloatProperty; import android.util.Log; import android.util.Property; import android.util.TypedValue; -import android.view.View; import android.view.ViewDebug; import android.view.accessibility.AccessibilityEvent; import android.view.animation.Interpolator; @@ -144,6 +142,8 @@ public class StatusBarIconView extends AnimatedImageView { private ColorMatrixColorFilter mMatrixColorFilter; private boolean mIsInShelf; private Runnable mLayoutRunnable; + private boolean mDismissed; + private Runnable mOnDismissListener; public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) { this(context, slot, sbn, false); @@ -668,6 +668,19 @@ public class StatusBarIconView extends AnimatedImageView { } public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable) { + setVisibleState(visibleState, animate, endRunnable, 0); + } + + /** + * Set the visibleState of this view. + * + * @param visibleState The new state. + * @param animate Should we animate? + * @param endRunnable The runnable to run at the end. + * @param duration The duration of an animation or 0 if the default should be taken. + */ + public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable, + long duration) { boolean runnableAdded = false; if (visibleState != mVisibleState) { mVisibleState = visibleState; @@ -689,7 +702,8 @@ public class StatusBarIconView extends AnimatedImageView { mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT, currentAmount, targetAmount); mIconAppearAnimator.setInterpolator(interpolator); - mIconAppearAnimator.setDuration(ANIMATION_DURATION_FAST); + mIconAppearAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST + : duration); mIconAppearAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -711,8 +725,9 @@ public class StatusBarIconView extends AnimatedImageView { if (targetAmount != currentAmount) { mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT, currentAmount, targetAmount); - mDotAnimator.setInterpolator(interpolator); - mDotAnimator.setDuration(ANIMATION_DURATION_FAST); + mDotAnimator.setInterpolator(interpolator);; + mDotAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST + : duration); final boolean runRunnable = !runnableAdded; mDotAnimator.addListener(new AnimatorListenerAdapter() { @Override @@ -837,6 +852,21 @@ public class StatusBarIconView extends AnimatedImageView { mLayoutRunnable = runnable; } + public void setDismissed() { + mDismissed = true; + if (mOnDismissListener != null) { + mOnDismissListener.run(); + } + } + + public boolean isDismissed() { + return mDismissed; + } + + public void setOnDismissListener(Runnable onDismissListener) { + mOnDismissListener = onDismissListener; + } + public interface OnVisibilityChangedListener { void onVisibilityChanged(int newVisibility); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java index dfcd5e60719a..251e04b01afe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java @@ -52,6 +52,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { protected final ViewInvertHelper mInvertHelper; protected final ViewTransformationHelper mTransformationHelper; + private final int mTranslationForHeader; protected int mColor; private ImageView mIcon; @@ -63,6 +64,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private boolean mIsLowPriority; private boolean mTransformLowPriorityTitle; private boolean mShowExpandButtonAtEnd; + protected float mHeaderTranslation; protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { super(ctx, view, row); @@ -99,6 +101,10 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { resolveHeaderViews(); updateInvertHelper(); addAppOpsOnClickListener(row); + mTranslationForHeader = ctx.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_content_margin) + - ctx.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.notification_content_margin_top); } @Override @@ -243,6 +249,19 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } @Override + public void setHeaderVisibleAmount(float headerVisibleAmount) { + super.setHeaderVisibleAmount(headerVisibleAmount); + mNotificationHeader.setAlpha(headerVisibleAmount); + mHeaderTranslation = (1.0f - headerVisibleAmount) * mTranslationForHeader; + mView.setTranslationY(mHeaderTranslation); + } + + @Override + public int getHeaderTranslation() { + return (int) mHeaderTranslation; + } + + @Override public NotificationHeaderView getNotificationHeader() { return mNotificationHeader; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java index f9671182ab8c..f5110a2dbc2d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java @@ -179,6 +179,9 @@ public class NotificationInflater { : builder.makeAmbientNotification(); } result.packageContext = packageContext; + result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */); + result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText( + true /* showingPublic */); return result; } @@ -456,6 +459,8 @@ public class NotificationInflater { } entry.cachedAmbientContentView = result.newAmbientView; } + entry.headsUpStatusBarText = result.headsUpStatusBarText; + entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic; if (endListener != null) { endListener.onAsyncInflationFinished(row.getEntry()); } @@ -665,6 +670,8 @@ public class NotificationInflater { private View inflatedExpandedView; private View inflatedAmbientView; private View inflatedPublicView; + private CharSequence headsUpStatusBarText; + private CharSequence headsUpStatusBarTextPublic; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java index d463eae6e43f..28beb21dba4b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java @@ -278,7 +278,10 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp if (mActionsContainer != null) { // We should never push the actions higher than they are in the headsup view. int constrainedContentHeight = Math.max(mContentHeight, mMinHeightHint); - mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight()); + + // We also need to compensate for any header translation, since we're always at the end. + mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight() + - getHeaderTranslation()); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java index 17eb4c110031..873f088f8431 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java @@ -133,6 +133,10 @@ public abstract class NotificationViewWrapper implements TransformableView { return null; } + public int getHeaderTranslation() { + return 0; + } + @Override public TransformState getCurrentState(int fadingView) { return null; @@ -198,4 +202,7 @@ public abstract class NotificationViewWrapper implements TransformableView { public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) { return false; } + + public void setHeaderVisibleAmount(float headerVisibleAmount) { + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index fb3adf45df31..07b79a209b82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import android.content.Context; +import android.os.PowerManager; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; @@ -24,6 +25,7 @@ import android.text.TextUtils; import android.util.MathUtils; import android.util.SparseBooleanArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -36,19 +38,35 @@ public class DozeParameters implements TunerService.Tunable { private static final int MAX_DURATION = 60 * 1000; public static final String DOZE_SENSORS_WAKE_UP_FULLY = "doze_sensors_wake_up_fully"; + private static IntInOutMatcher sPickupSubtypePerformsProxMatcher; + private static DozeParameters sInstance; + private final Context mContext; private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; + private final PowerManager mPowerManager; - private static IntInOutMatcher sPickupSubtypePerformsProxMatcher; private final AlwaysOnDisplayPolicy mAlwaysOnPolicy; private boolean mDozeAlwaysOn; + private boolean mControlScreenOffAnimation; + + public static DozeParameters getInstance(Context context) { + if (sInstance == null) { + sInstance = new DozeParameters(context); + } + return sInstance; + } - public DozeParameters(Context context) { + @VisibleForTesting + protected DozeParameters(Context context) { mContext = context; mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext); mAlwaysOnPolicy = new AlwaysOnDisplayPolicy(context); + mControlScreenOffAnimation = !getDisplayNeedsBlanking(); + mPowerManager = mContext.getSystemService(PowerManager.class); + mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation); + Dependency.get(TunerService.class).addTunable(this, Settings.Secure.DOZE_ALWAYS_ON, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); } @@ -165,15 +183,21 @@ public class DozeParameters implements TunerService.Tunable { com.android.internal.R.bool.config_displayBlanksAfterDoze); } - /** - * Whether we can implement our own screen off animation or if we need - * to rely on DisplayPowerManager to dim the display. - * - * @return {@code true} if SystemUI can control the screen off animation. - */ - public boolean getCanControlScreenOffAnimation() { - return !mContext.getResources().getBoolean( - com.android.internal.R.bool.config_dozeAfterScreenOff); + public boolean shouldControlScreenOff() { + return mControlScreenOffAnimation; + } + + public void setControlScreenOffAnimation(boolean controlScreenOffAnimation) { + if (mControlScreenOffAnimation == controlScreenOffAnimation) { + return; + } + mControlScreenOffAnimation = controlScreenOffAnimation; + getPowerManager().setDozeAfterScreenOff(!controlScreenOffAnimation); + } + + @VisibleForTesting + protected PowerManager getPowerManager() { + return mPowerManager; } private boolean getBoolean(String propName, int resId) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java index 1011383b72e1..afd64f373c58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -78,9 +78,10 @@ public class DozeScrimController { } }; - public DozeScrimController(ScrimController scrimController, Context context) { + public DozeScrimController(ScrimController scrimController, Context context, + DozeParameters dozeParameters) { mScrimController = scrimController; - mDozeParameters = new DozeParameters(context); + mDozeParameters = dozeParameters; } public void setDozing(boolean dozing) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java new file mode 100644 index 000000000000..c57680151151 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2018 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.phone; + +import android.graphics.Rect; +import android.view.View; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.statusbar.CrossFadeHelper; +import com.android.systemui.statusbar.ExpandableNotificationRow; +import com.android.systemui.statusbar.HeadsUpStatusBarView; +import com.android.systemui.statusbar.NotificationData; +import com.android.systemui.statusbar.policy.DarkIconDispatcher; +import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; + +/** + * Controls the appearance of heads up notifications in the icon area and the header itself. + */ +class HeadsUpAppearanceController implements OnHeadsUpChangedListener, + DarkIconDispatcher.DarkReceiver { + public static final int CONTENT_FADE_DURATION = 110; + public static final int CONTENT_FADE_DELAY = 100; + private final NotificationIconAreaController mNotificationIconAreaController; + private final HeadsUpManagerPhone mHeadsUpManager; + private final NotificationStackScrollLayout mStackScroller; + private final HeadsUpStatusBarView mHeadsUpStatusBarView; + private final View mClockView; + private final DarkIconDispatcher mDarkIconDispatcher; + private float mExpandedHeight; + private boolean mIsExpanded; + private float mExpandFraction; + private ExpandableNotificationRow mTrackedChild; + private boolean mShown; + + public HeadsUpAppearanceController( + NotificationIconAreaController notificationIconAreaController, + HeadsUpManagerPhone headsUpManager, + View statusbarView) { + this(notificationIconAreaController, headsUpManager, + statusbarView.findViewById(R.id.heads_up_status_bar_view), + statusbarView.findViewById(R.id.notification_stack_scroller), + statusbarView.findViewById(R.id.notification_panel), + statusbarView.findViewById(R.id.clock)); + } + + @VisibleForTesting + public HeadsUpAppearanceController( + NotificationIconAreaController notificationIconAreaController, + HeadsUpManagerPhone headsUpManager, + HeadsUpStatusBarView headsUpStatusBarView, + NotificationStackScrollLayout stackScroller, + NotificationPanelView panelView, + View clockView) { + mNotificationIconAreaController = notificationIconAreaController; + mHeadsUpManager = headsUpManager; + mHeadsUpManager.addListener(this); + mHeadsUpStatusBarView = headsUpStatusBarView; + headsUpStatusBarView.setOnDrawingRectChangedListener( + () -> updateIsolatedIconLocation(true /* requireUpdate */)); + mStackScroller = stackScroller; + panelView.addTrackingHeadsUpListener(this::setTrackingHeadsUp); + panelView.setVerticalTranslationListener(this::updatePanelTranslation); + panelView.setHeadsUpAppearanceController(this); + mStackScroller.addOnExpandedHeightListener(this::setExpandedHeight); + mStackScroller.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) + -> updatePanelTranslation()); + mClockView = clockView; + mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class); + mDarkIconDispatcher.addDarkReceiver(this); + } + + private void updateIsolatedIconLocation(boolean requireStateUpdate) { + mNotificationIconAreaController.setIsolatedIconLocation( + mHeadsUpStatusBarView.getIconDrawingRect(), requireStateUpdate); + } + + @Override + public void onHeadsUpPinned(ExpandableNotificationRow headsUp) { + updateTopEntry(); + updateHeader(headsUp.getEntry()); + } + + public void updatePanelTranslation() { + float newTranslation = mStackScroller.getLeft() + mStackScroller.getTranslationX(); + mHeadsUpStatusBarView.setTranslationX(newTranslation); + } + + private void updateTopEntry() { + NotificationData.Entry newEntry = null; + if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) { + newEntry = mHeadsUpManager.getTopEntry(); + } + NotificationData.Entry previousEntry = mHeadsUpStatusBarView.getShowingEntry(); + mHeadsUpStatusBarView.setEntry(newEntry); + if (newEntry != previousEntry) { + boolean animateIsolation = false; + if (newEntry == null) { + // no heads up anymore, lets start the disappear animation + + setShown(false); + animateIsolation = !mIsExpanded; + } else if (previousEntry == null) { + // We now have a headsUp and didn't have one before. Let's start the disappear + // animation + setShown(true); + animateIsolation = !mIsExpanded; + } + updateIsolatedIconLocation(false /* requireUpdate */); + mNotificationIconAreaController.showIconIsolated(newEntry == null ? null + : newEntry.icon, animateIsolation); + } + } + + private void setShown(boolean isShown) { + if (mShown != isShown) { + mShown = isShown; + if (isShown) { + mHeadsUpStatusBarView.setVisibility(View.VISIBLE); + CrossFadeHelper.fadeIn(mHeadsUpStatusBarView, CONTENT_FADE_DURATION /* duration */, + CONTENT_FADE_DELAY /* delay */); + CrossFadeHelper.fadeOut(mClockView, CONTENT_FADE_DURATION/* duration */, + 0 /* delay */, () -> mClockView.setVisibility(View.INVISIBLE)); + } else { + CrossFadeHelper.fadeIn(mClockView, CONTENT_FADE_DURATION /* duration */, + CONTENT_FADE_DELAY /* delay */); + CrossFadeHelper.fadeOut(mHeadsUpStatusBarView, CONTENT_FADE_DURATION/* duration */, + 0 /* delay */, () -> mHeadsUpStatusBarView.setVisibility(View.GONE)); + + } + } + } + + @VisibleForTesting + public boolean isShown() { + return mShown; + } + + /** + * Should the headsup status bar view be visible right now? This may be different from isShown, + * since the headsUp manager might not have notified us yet of the state change. + * + * @return if the heads up status bar view should be shown + */ + public boolean shouldBeVisible() { + return !mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp(); + } + + @Override + public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) { + updateTopEntry(); + updateHeader(headsUp.getEntry()); + } + + public void setExpandedHeight(float expandedHeight, float appearFraction) { + boolean changedHeight = expandedHeight != mExpandedHeight; + mExpandedHeight = expandedHeight; + mExpandFraction = appearFraction; + boolean isExpanded = expandedHeight > 0; + if (changedHeight) { + updateHeadsUpHeaders(); + } + if (isExpanded != mIsExpanded) { + mIsExpanded = isExpanded; + updateTopEntry(); + } + } + + /** + * Set a headsUp to be tracked, meaning that it is currently being pulled down after being + * in a pinned state on the top. The expand animation is different in that case and we need + * to update the header constantly afterwards. + * + * @param trackedChild the tracked headsUp or null if it's not tracking anymore. + */ + public void setTrackingHeadsUp(ExpandableNotificationRow trackedChild) { + ExpandableNotificationRow previousTracked = mTrackedChild; + mTrackedChild = trackedChild; + if (previousTracked != null) { + updateHeader(previousTracked.getEntry()); + } + } + + private void updateHeadsUpHeaders() { + mHeadsUpManager.getAllEntries().forEach(entry -> { + updateHeader(entry); + }); + } + + private void updateHeader(NotificationData.Entry entry) { + ExpandableNotificationRow row = entry.row; + float headerVisibleAmount = 1.0f; + if (row.isPinned() || row == mTrackedChild) { + headerVisibleAmount = mExpandFraction; + } + row.setHeaderVisibleAmount(headerVisibleAmount); + } + + @Override + public void onDarkChanged(Rect area, float darkIntensity, int tint) { + mHeadsUpStatusBarView.onDarkChanged(area, darkIntensity, tint); + } + + public void setPublicMode(boolean publicMode) { + mHeadsUpStatusBarView.setPublicMode(publicMode); + updateTopEntry(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index d2cdc27d982c..fa0a774c249e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -29,6 +29,7 @@ import android.view.ViewTreeObserver; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; +import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.StatusBarState; @@ -52,12 +53,13 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, private static final boolean DEBUG = false; private final View mStatusBarWindowView; - private int mStatusBarHeight; private final NotificationGroupManager mGroupManager; private final StatusBar mBar; private final VisualStabilityManager mVisualStabilityManager; - private boolean mReleaseOnExpandFinish; + + private int mStatusBarHeight; + private int mHeadsUpInset; private boolean mTrackingHeadsUp; private HashSet<String> mSwipedOutKeys = new HashSet<>(); private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>(); @@ -101,9 +103,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, mBar = bar; mVisualStabilityManager = visualStabilityManager; - Resources resources = mContext.getResources(); - mStatusBarHeight = resources.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); + initResources(); addListener(new OnHeadsUpChangedListener() { @Override @@ -114,6 +114,20 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, }); } + private void initResources() { + Resources resources = mContext.getResources(); + mStatusBarHeight = resources.getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height); + mHeadsUpInset = mStatusBarHeight + resources.getDimensionPixelSize( + R.dimen.heads_up_status_bar_padding); + } + + @Override + public void onDensityOrFontScaleChanged() { + super.onDensityOrFontScaleChanged(); + initResources(); + } + /////////////////////////////////////////////////////////////////////////////////////////////// // Public methods: @@ -283,10 +297,10 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, topEntry.getLocationOnScreen(mTmpTwoArray); int minX = mTmpTwoArray[0]; int maxX = mTmpTwoArray[0] + topEntry.getWidth(); - int maxY = topEntry.getIntrinsicHeight(); + int height = topEntry.getIntrinsicHeight(); info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); - info.touchableRegion.set(minX, 0, maxX, maxY); + info.touchableRegion.set(minX, 0, maxX, mHeadsUpInset + height); } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) { info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java index 2bfdefe39017..903b81339869 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java @@ -23,7 +23,6 @@ import android.view.ViewConfiguration; import com.android.systemui.Gefingerpoken; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; /** @@ -102,10 +101,11 @@ public class HeadsUpTouchHelper implements Gefingerpoken { mCollapseSnoozes = h < 0; mInitialTouchX = x; mInitialTouchY = y; - int expandedHeight = mPickedChild.getActualHeight(); - mPanel.setPanelScrimMinFraction((float) expandedHeight + int startHeight = (int) (mPickedChild.getActualHeight() + + mPickedChild.getTranslationY()); + mPanel.setPanelScrimMinFraction((float) startHeight / mPanel.getMaxPanelHeight()); - mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight); + mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight); mPanel.startExpandingFromPeek(); // This call needs to be after the expansion start otherwise we will get a // flicker of one frame as it's not expanded yet. @@ -135,7 +135,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken { private void setTrackingHeadsUp(boolean tracking) { mTrackingHeadsUp = tracking; mHeadsUpManager.setTrackingHeadsUp(tracking); - mPanel.setTrackingHeadsUp(tracking); + mPanel.setTrackedHeadsUp(tracking ? mPickedChild : null); } public void notifyFling(boolean collapse) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index b493d7a0e7ed..a39800d156c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -464,7 +464,10 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private void onRotationSuggestionsDisabled() { // Immediately hide the rotate button and clear any planned removal setRotateSuggestionButtonState(false, true); - getView().removeCallbacks(mRemoveRotationProposal); + + // This method can be called before view setup is done, ensure getView isn't null + final View v = getView(); + if (v != null) v.removeCallbacks(mRemoveRotationProposal); } private void showAndLogRotationSuggestion() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index e1aed7a04c35..9063dea584b1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -12,9 +12,11 @@ import android.widget.FrameLayout; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.NotificationColorUtil; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationData; +import com.android.systemui.statusbar.NotificationEntryManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.NotificationUtils; @@ -31,6 +33,8 @@ import java.util.function.Function; */ public class NotificationIconAreaController implements DarkReceiver { private final NotificationColorUtil mNotificationColorUtil; + private final NotificationEntryManager mEntryManager; + private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons; private int mIconSize; private int mIconHPadding; @@ -48,6 +52,7 @@ public class NotificationIconAreaController implements DarkReceiver { mStatusBar = statusBar; mNotificationColorUtil = NotificationColorUtil.getInstance(context); mContext = context; + mEntryManager = Dependency.get(NotificationEntryManager.class); initializeNotificationAreaViews(context); } @@ -129,8 +134,8 @@ public class NotificationIconAreaController implements DarkReceiver { } protected boolean shouldShowNotificationIcon(NotificationData.Entry entry, - NotificationData notificationData, boolean showAmbient) { - if (notificationData.isAmbient(entry.key) && !showAmbient) { + boolean showAmbient, boolean hideDismissed) { + if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) { return false; } if (!StatusBar.isTopLevelChild(entry)) { @@ -139,9 +144,13 @@ public class NotificationIconAreaController implements DarkReceiver { if (entry.row.getVisibility() == View.GONE) { return false; } + if (entry.row.isDismissed() && hideDismissed) { + return false; + } // showAmbient == show in shade but not shelf - if (!showAmbient && notificationData.shouldSuppressStatusBar(entry.key)) { + if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar( + entry.key)) { return false; } @@ -151,28 +160,30 @@ public class NotificationIconAreaController implements DarkReceiver { /** * Updates the notifications with the given list of notifications to display. */ - public void updateNotificationIcons(NotificationData notificationData) { + public void updateNotificationIcons() { - updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons, - false /* showAmbient */); - updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons, - NotificationShelf.SHOW_AMBIENT_ICONS); + updateStatusBarIcons(); + updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons, + NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */); applyNotificationIconsTint(); } + private void updateStatusBarIcons() { + updateIconsForLayout(entry -> entry.icon, mNotificationIcons, + false /* showAmbient */, true /* hideDismissed */); + } + /** * Updates the notification icons for a host layout. This will ensure that the notification * host layout will have the same icons like the ones in here. - * - * @param notificationData the notification data to look up which notifications are relevant * @param function A function to look up an icon view based on an entry * @param hostLayout which layout should be updated * @param showAmbient should ambient notification icons be shown + * @param hideDismissed should dismissed icons be hidden */ - private void updateIconsForLayout(NotificationData notificationData, - Function<NotificationData.Entry, StatusBarIconView> function, - NotificationIconContainer hostLayout, boolean showAmbient) { + private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function, + NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed) { ArrayList<StatusBarIconView> toShow = new ArrayList<>( mNotificationScrollLayout.getChildCount()); @@ -181,7 +192,7 @@ public class NotificationIconAreaController implements DarkReceiver { View view = mNotificationScrollLayout.getChildAt(i); if (view instanceof ExpandableNotificationRow) { NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry(); - if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) { + if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed)) { toShow.add(function.apply(ent)); } } @@ -243,10 +254,13 @@ public class NotificationIconAreaController implements DarkReceiver { final FrameLayout.LayoutParams params = generateIconLayoutParams(); for (int i = 0; i < toShow.size(); i++) { - View v = toShow.get(i); + StatusBarIconView v = toShow.get(i); // The view might still be transiently added if it was just removed and added again hostLayout.removeTransientView(v); if (v.getParent() == null) { + if (hideDismissed) { + v.setOnDismissListener(mUpdateStatusBarIcons); + } hostLayout.addView(v, i, params); } } @@ -296,4 +310,12 @@ public class NotificationIconAreaController implements DarkReceiver { mNotificationIcons.setDark(dark, false, 0); mShelfIcons.setDark(dark, false, 0); } + + public void showIconIsolated(StatusBarIconView icon, boolean animated) { + mNotificationIcons.showIconIsolated(icon, animated); + } + + public void setIsolatedIconLocation(Rect iconDrawingRect, boolean requireStateUpdate) { + mNotificationIcons.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index b29ac9067556..55174349cc53 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -16,17 +16,17 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DURATION; +import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DELAY; + import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Rect; import android.graphics.drawable.Icon; -import android.os.AsyncTask; -import android.os.VibrationEffect; -import android.os.Vibrator; import android.support.v4.util.ArrayMap; -import android.support.v4.util.ArraySet; import android.util.AttributeSet; import android.view.View; @@ -100,6 +100,33 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } }.setDuration(200).setDelay(50); + /** + * The animation property used for all icons that were not isolated, when the isolation ends. + * This just fades the alpha and doesn't affect the movement and has a delay. + */ + private static final AnimationProperties UNISOLATION_PROPERTY_OTHERS + = new AnimationProperties() { + private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); + + @Override + public AnimationFilter getAnimationFilter() { + return mAnimationFilter; + } + }.setDuration(CONTENT_FADE_DURATION); + + /** + * The animation property used for the icon when its isolation ends. + * This animates the translation back to the right position. + */ + private static final AnimationProperties UNISOLATION_PROPERTY = new AnimationProperties() { + private AnimationFilter mAnimationFilter = new AnimationFilter().animateX(); + + @Override + public AnimationFilter getAnimationFilter() { + return mAnimationFilter; + } + }.setDuration(CONTENT_FADE_DURATION); + public static final int MAX_VISIBLE_ICONS_WHEN_DARK = 5; public static final int MAX_STATIC_ICONS = 4; private static final int MAX_DOTS = 3; @@ -127,6 +154,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private float mVisualOverflowStart; // Keep track of overflow in range [0, 3] private int mNumDots; + private StatusBarIconView mIsolatedIcon; + private Rect mIsolatedIconLocation; + private int[] mAbsolutePosition = new int[2]; + private View mIsolatedIconForAnimation; public NotificationIconContainer(Context context, AttributeSet attrs) { @@ -196,13 +227,18 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mIconSize = child.getWidth(); } } + getLocationOnScreen(mAbsolutePosition); if (mIsStaticLayout) { - resetViewStates(); - calculateIconTranslations(); - applyIconStates(); + updateState(); } } + private void updateState() { + resetViewStates(); + calculateIconTranslations(); + applyIconStates(); + } + public void applyIconStates() { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); @@ -214,6 +250,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mAddAnimationStartIndex = -1; mCannedAnimationStartIndex = -1; mDisallowNextAnimation = false; + mIsolatedIconForAnimation = null; } @Override @@ -281,8 +318,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mIconStates.remove(child); if (!isReplacingIcon) { addTransientView(icon, 0); + boolean isIsolatedIcon = child == mIsolatedIcon; icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */, - () -> removeTransientView(icon)); + () -> removeTransientView(icon), + isIsolatedIcon ? CONTENT_FADE_DURATION : 0); } } } @@ -306,7 +345,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { View view = getChildAt(i); ViewState iconState = mIconStates.get(view); iconState.initFrom(view); - iconState.alpha = 1.0f; + iconState.alpha = mIsolatedIcon == null || view == mIsolatedIcon ? 1.0f : 0.0f; iconState.hidden = false; } } @@ -402,6 +441,16 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { iconState.xTranslation = getWidth() - iconState.xTranslation - view.getWidth(); } } + if (mIsolatedIcon != null) { + IconState iconState = mIconStates.get(mIsolatedIcon); + if (iconState != null) { + // Most of the time the icon isn't yet added when this is called but only happening + // later + iconState.xTranslation = mIsolatedIconLocation.left - mAbsolutePosition[0] + - (1 - mIsolatedIcon.getIconScale()) * mIsolatedIcon.getWidth() / 2.0f; + iconState.visibleState = StatusBarIconView.STATE_ICON; + } + } } private float getLayoutEnd() { @@ -573,6 +622,21 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mReplacingIcons = replacingIcons; } + public void showIconIsolated(StatusBarIconView icon, boolean animated) { + if (animated) { + mIsolatedIconForAnimation = icon != null ? icon : mIsolatedIcon; + } + mIsolatedIcon = icon; + updateState(); + } + + public void setIsolatedIconLocation(Rect isolatedIconLocation, boolean requireUpdate) { + mIsolatedIconLocation = isolatedIconLocation; + if (requireUpdate) { + updateState(); + } + } + public class IconState extends ViewState { public static final int NO_VALUE = NotificationIconContainer.NO_VALUE; public float iconAppearAmount = 1.0f; @@ -646,6 +710,18 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { animationProperties.setDuration(CANNED_ANIMATION_DURATION); animate = true; } + if (mIsolatedIconForAnimation != null) { + if (view == mIsolatedIconForAnimation) { + animationProperties = UNISOLATION_PROPERTY; + animationProperties.setDelay( + mIsolatedIcon != null ? CONTENT_FADE_DELAY : 0); + } else { + animationProperties = UNISOLATION_PROPERTY_OTHERS; + animationProperties.setDelay( + mIsolatedIcon == null ? CONTENT_FADE_DELAY : 0); + } + animate = true; + } } icon.setVisibleState(visibleState, animationsAllowed); icon.setIconColor(iconColor, needsCannedAnimation && animationsAllowed); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 2711d7ac7a89..6852df65918c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -74,7 +74,9 @@ import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.StackStateAnimator; +import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; public class NotificationPanelView extends PanelView implements ExpandableView.OnHeightChangedListener, @@ -243,6 +245,10 @@ public class NotificationPanelView extends PanelView implements private float mExpandOffset; private boolean mHideIconsDuringNotificationLaunch = true; private int mStackScrollerMeasuringPass; + private ArrayList<Consumer<ExpandableNotificationRow>> mTrackingHeadsUpListeners + = new ArrayList<>(); + private Runnable mVerticalTranslationListener; + private HeadsUpAppearanceController mHeadsUpAppearanceController; public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); @@ -269,6 +275,7 @@ public class NotificationPanelView extends PanelView implements mNotificationStackScroller.setOnHeightChangedListener(this); mNotificationStackScroller.setOverscrollTopChangedListener(this); mNotificationStackScroller.setOnEmptySpaceClickListener(this); + addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp); mKeyguardBottomArea = findViewById(R.id.keyguard_bottom_area); mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim); mLastOrientation = getResources().getConfiguration().orientation; @@ -1139,6 +1146,14 @@ public class NotificationPanelView extends PanelView implements @Override public void run() { mKeyguardStatusViewAnimating = false; + mKeyguardStatusView.setVisibility(View.INVISIBLE); + } + }; + + private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() { + @Override + public void run() { + mKeyguardStatusViewAnimating = false; mKeyguardStatusView.setVisibility(View.GONE); } }; @@ -1227,16 +1242,17 @@ public class NotificationPanelView extends PanelView implements private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway, boolean goingToFullShade) { + mKeyguardStatusView.animate().cancel(); + mKeyguardStatusViewAnimating = false; if ((!keyguardFadingAway && mStatusBarState == StatusBarState.KEYGUARD && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) { - mKeyguardStatusView.animate().cancel(); mKeyguardStatusViewAnimating = true; mKeyguardStatusView.animate() .alpha(0f) .setStartDelay(0) .setDuration(160) .setInterpolator(Interpolators.ALPHA_OUT) - .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable); + .withEndAction(mAnimateKeyguardStatusViewGoneEndRunnable); if (keyguardFadingAway) { mKeyguardStatusView.animate() .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay()) @@ -1245,7 +1261,6 @@ public class NotificationPanelView extends PanelView implements } } else if (mStatusBarState == StatusBarState.SHADE_LOCKED && statusBarState == StatusBarState.KEYGUARD) { - mKeyguardStatusView.animate().cancel(); mKeyguardStatusView.setVisibility(View.VISIBLE); mKeyguardStatusViewAnimating = true; mKeyguardStatusView.setAlpha(0f); @@ -1256,13 +1271,21 @@ public class NotificationPanelView extends PanelView implements .setInterpolator(Interpolators.ALPHA_IN) .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable); } else if (statusBarState == StatusBarState.KEYGUARD) { - mKeyguardStatusView.animate().cancel(); - mKeyguardStatusViewAnimating = false; - mKeyguardStatusView.setVisibility(View.VISIBLE); - mKeyguardStatusView.setAlpha(1f); + if (keyguardFadingAway) { + mKeyguardStatusViewAnimating = true; + mKeyguardStatusView.animate() + .alpha(0) + .translationYBy(-getHeight() * 0.05f) + .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN) + .setDuration(125) + .setStartDelay(0) + .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable) + .start(); + } else { + mKeyguardStatusView.setVisibility(View.VISIBLE); + mKeyguardStatusView.setAlpha(1f); + } } else { - mKeyguardStatusView.animate().cancel(); - mKeyguardStatusViewAnimating = false; mKeyguardStatusView.setVisibility(View.GONE); mKeyguardStatusView.setAlpha(1f); } @@ -1780,11 +1803,19 @@ public class NotificationPanelView extends PanelView implements mQsExpandImmediate = false; mTwoFingerQsExpandPossible = false; mIsExpansionFromHeadsUp = false; - mNotificationStackScroller.setTrackingHeadsUp(false); + notifyListenersTrackingHeadsUp(null); mExpandingFromHeadsUp = false; setPanelScrimMinFraction(0.0f); } + private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) { + for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) { + Consumer<ExpandableNotificationRow> listener + = mTrackingHeadsUpListeners.get(i); + listener.accept(pickedChild); + } + } + private void setListening(boolean listening) { mKeyguardStatusBar.setListening(listening); if (mQs == null) return; @@ -2197,14 +2228,6 @@ public class NotificationPanelView extends PanelView implements return (1 - t) * start + t * end; } - public void setDozing(boolean dozing, boolean animate) { - if (dozing == mDozing) return; - mDozing = dozing; - if (mStatusBarState == StatusBarState.KEYGUARD) { - updateDozingVisibilities(animate); - } - } - private void updateDozingVisibilities(boolean animate) { if (mDozing) { mKeyguardStatusBar.setVisibility(View.INVISIBLE); @@ -2351,9 +2374,9 @@ public class NotificationPanelView extends PanelView implements this); } - public void setTrackingHeadsUp(boolean tracking) { - if (tracking) { - mNotificationStackScroller.setTrackingHeadsUp(true); + public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) { + if (pickedChild != null) { + notifyListenersTrackingHeadsUp(pickedChild); mExpandingFromHeadsUp = true; } // otherwise we update the state when the expansion is finished @@ -2400,6 +2423,9 @@ public class NotificationPanelView extends PanelView implements protected void setVerticalPanelTranslation(float translation) { mNotificationStackScroller.setTranslationX(translation); mQsFrame.setTranslationX(translation); + if (mVerticalTranslationListener != null) { + mVerticalTranslationListener.run(); + } } protected void updateExpandedHeight(float expandedHeight) { @@ -2555,6 +2581,10 @@ public class NotificationPanelView extends PanelView implements if (mLaunchingNotification) { return mHideIconsDuringNotificationLaunch; } + if (mHeadsUpAppearanceController != null + && mHeadsUpAppearanceController.shouldBeVisible()) { + return false; + } return !isFullWidth() || !mShowIconsWhenExpanded; } @@ -2600,11 +2630,16 @@ public class NotificationPanelView extends PanelView implements } } - public void setDark(boolean dark, boolean animate) { - float darkAmount = dark ? 1 : 0; - if (mDarkAmount == darkAmount) { - return; + public void setDozing(boolean dozing, boolean animate) { + if (dozing == mDozing) return; + mDozing = dozing; + + if (mStatusBarState == StatusBarState.KEYGUARD + || mStatusBarState == StatusBarState.SHADE_LOCKED) { + updateDozingVisibilities(animate); } + + final float darkAmount = dozing ? 1 : 0; if (mDarkAnimator != null && mDarkAnimator.isRunning()) { if (animate && mDarkAmountTarget == darkAmount) { return; @@ -2696,4 +2731,26 @@ public class NotificationPanelView extends PanelView implements } } } + + public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) { + mTrackingHeadsUpListeners.add(listener); + } + + public void setVerticalTranslationListener(Runnable verticalTranslationListener) { + mVerticalTranslationListener = verticalTranslationListener; + } + + public void setHeadsUpAppearanceController( + HeadsUpAppearanceController headsUpAppearanceController) { + mHeadsUpAppearanceController = headsUpAppearanceController; + } + + /** + * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the + * security view of the bouncer. + */ + public void onBouncerPreHideAnimation() { + setKeyguardStatusViewVisibility(mStatusBarState, true /* keyguardFadingAway */, + false /* goingToFullShade */); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 739d8d5c2c67..cfc0cc6207c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -49,7 +49,6 @@ import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.ScrimView; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.stack.ViewState; import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.wakelock.DelayedWakeLock; @@ -63,8 +62,8 @@ import java.util.function.Consumer; * Controls both the scrim behind the notifications and in front of the notifications (when a * security method gets shown). */ -public class ScrimController implements ViewTreeObserver.OnPreDrawListener, - OnHeadsUpChangedListener, OnColorsChangedListener, Dumpable { +public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener, + Dumpable { private static final String TAG = "ScrimController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -106,7 +105,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private final Context mContext; protected final ScrimView mScrimBehind; protected final ScrimView mScrimInFront; - private final View mHeadsUpScrim; private final LightBarController mLightBarController; private final UnlockMethodCache mUnlockMethodCache; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -140,9 +138,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private int mCurrentInFrontTint; private int mCurrentBehindTint; private boolean mWallpaperVisibilityTimedOut; - private int mPinnedHeadsUpCount; - private float mTopHeadsUpDragAmount; - private View mDraggedHeadsUpView; private int mScrimsVisibility; private final Consumer<Integer> mScrimVisibleListener; private boolean mBlankScreen; @@ -161,11 +156,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private boolean mKeyguardOccluded; public ScrimController(LightBarController lightBarController, ScrimView scrimBehind, - ScrimView scrimInFront, View headsUpScrim, Consumer<Integer> scrimVisibleListener, + ScrimView scrimInFront, Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters, AlarmManager alarmManager) { mScrimBehind = scrimBehind; mScrimInFront = scrimInFront; - mHeadsUpScrim = headsUpScrim; mScrimVisibleListener = scrimVisibleListener; mContext = scrimBehind.getContext(); mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); @@ -196,7 +190,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } mState = ScrimState.UNINITIALIZED; - updateHeadsUpScrim(false); updateScrims(); } @@ -285,10 +278,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, // with too many things at this case, in order to not skip the initial frames. mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16); mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY; - } else if (!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD) { - // Execute first frame immediately when display was completely off. - // Scheduling a frame isn't enough because the system may aggressively enter doze, - // delaying callbacks or never triggering them until the power button is pressed. + } else if (!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD + || (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) { + // Scheduling a frame isn't enough when: + // • Leaving doze and we need to modify scrim color immediately + // • ColorFade will not kick-in and scrim cannot wait for pre-draw. onPreDraw(); } else { scheduleUpdate(); @@ -363,10 +357,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, return; } - if (mPinnedHeadsUpCount != 0) { - updateHeadsUpScrim(false); - } - setOrAdaptCurrentAnimation(mScrimBehind); setOrAdaptCurrentAnimation(mScrimInFront); } @@ -610,8 +600,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, return mCurrentInFrontAlpha; } else if (scrim == mScrimBehind) { return mCurrentBehindAlpha; - } else if (scrim == mHeadsUpScrim) { - return calculateHeadsUpAlpha(); } else { throw new IllegalArgumentException("Unknown scrim view"); } @@ -622,8 +610,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, return mCurrentInFrontTint; } else if (scrim == mScrimBehind) { return mCurrentBehindTint; - } else if (scrim == mHeadsUpScrim) { - return Color.TRANSPARENT; } else { throw new IllegalArgumentException("Unknown scrim view"); } @@ -670,40 +656,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mScrimBehind.setDrawAsSrc(asSrc); } - @Override - public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { - } - - @Override - public void onHeadsUpPinned(ExpandableNotificationRow headsUp) { - mPinnedHeadsUpCount++; - updateHeadsUpScrim(true); - } - - @Override - public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) { - mPinnedHeadsUpCount--; - if (headsUp == mDraggedHeadsUpView) { - mDraggedHeadsUpView = null; - mTopHeadsUpDragAmount = 0.0f; - } - updateHeadsUpScrim(true); - } - - @Override - public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { - } - - private void updateHeadsUpScrim(boolean animate) { - if (animate) { - mAnimationDuration = ANIMATION_DURATION; - cancelAnimator((ValueAnimator) mHeadsUpScrim.getTag(TAG_KEY_ANIM)); - startScrimAnimation(mHeadsUpScrim, mHeadsUpScrim.getAlpha()); - } else { - setOrAdaptCurrentAnimation(mHeadsUpScrim); - } - } - @VisibleForTesting void setOnAnimationFinished(Runnable onAnimationFinished) { mOnAnimationFinished = onAnimationFinished; @@ -810,33 +762,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, return Handler.getMain(); } - /** - * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means - * the heads up is in its resting space and 1 means it's fully dragged out. - * - * @param draggedHeadsUpView the dragged view - * @param topHeadsUpDragAmount how far is it dragged - */ - public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) { - mTopHeadsUpDragAmount = topHeadsUpDragAmount; - mDraggedHeadsUpView = draggedHeadsUpView; - updateHeadsUpScrim(false); - } - - private float calculateHeadsUpAlpha() { - float alpha; - if (mPinnedHeadsUpCount >= 2) { - alpha = 1.0f; - } else if (mPinnedHeadsUpCount == 0) { - alpha = 0.0f; - } else { - alpha = 1.0f - mTopHeadsUpDragAmount; - } - float expandFactor = (1.0f - mExpansionFraction); - expandFactor = Math.max(expandFactor, 0.0f); - return alpha * expandFactor; - } - public void setExcludedBackgroundArea(Rect area) { mScrimBehind.setExcludedArea(area); } @@ -851,13 +776,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mScrimBehind.setChangeRunnable(changeRunnable); } - public void onDensityOrFontScaleChanged() { - ViewGroup.LayoutParams layoutParams = mHeadsUpScrim.getLayoutParams(); - layoutParams.height = mHeadsUpScrim.getResources().getDimensionPixelSize( - R.dimen.heads_up_scrim_height); - mHeadsUpScrim.setLayoutParams(layoutParams); - } - public void setCurrentUser(int currentUser) { // Don't care in the base class. } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 55e8714f796f..5b734ebf5d1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -105,8 +105,7 @@ public enum ScrimState { @Override public void prepare(ScrimState previousState) { final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn(); - final boolean wasPulsing = previousState == ScrimState.PULSING; - mBlankScreen = wasPulsing && !mCanControlScreenOff; + mBlankScreen = mDisplayRequiresBlanking; mCurrentBehindAlpha = mWallpaperSupportsAmbientMode && !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f; mCurrentInFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f; @@ -114,7 +113,7 @@ public enum ScrimState { mCurrentBehindTint = Color.BLACK; // DisplayPowerManager will blank the screen for us, we just need // to set our state. - mAnimateChange = mCanControlScreenOff; + mAnimateChange = !mDisplayRequiresBlanking; } @Override @@ -178,7 +177,6 @@ public enum ScrimState { ScrimView mScrimBehind; DozeParameters mDozeParameters; boolean mDisplayRequiresBlanking; - boolean mCanControlScreenOff; boolean mWallpaperSupportsAmbientMode; KeyguardUpdateMonitor mKeyguardUpdateMonitor; int mIndex; @@ -192,7 +190,6 @@ public enum ScrimState { mScrimBehind = scrimBehind; mDozeParameters = dozeParameters; mDisplayRequiresBlanking = dozeParameters.getDisplayNeedsBlanking(); - mCanControlScreenOff = dozeParameters.getCanControlScreenOffAnimation(); mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(scrimInFront.getContext()); } 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 7422a4343cb4..feb7dc360ad6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -189,6 +189,7 @@ import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.GestureRecorder; +import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.KeyboardShortcuts; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationData; @@ -602,6 +603,7 @@ public class StatusBar extends SystemUI implements DemoMode, private NavigationBarFragment mNavigationBar; private View mNavigationBarView; protected ActivityLaunchAnimator mActivityLaunchAnimator; + private HeadsUpAppearanceController mHeadsUpAppearanceController; @Override public void start() { @@ -807,6 +809,8 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarView.setPanel(mNotificationPanel); mStatusBarView.setScrimController(mScrimController); mStatusBarView.setBouncerShowing(mBouncerShowing); + mHeadsUpAppearanceController = new HeadsUpAppearanceController( + mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow); setAreThereNotifications(); checkBarModes(); }).getFragmentManager() @@ -899,14 +903,14 @@ public class StatusBar extends SystemUI implements DemoMode, ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind); ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front); - View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim); mScrimController = SystemUIFactory.getInstance().createScrimController(mLightBarController, - scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper, + scrimBehind, scrimInFront, mLockscreenWallpaper, scrimsVisible -> { if (mStatusBarWindowManager != null) { mStatusBarWindowManager.setScrimsVisibility(scrimsVisible); } - }, new DozeParameters(mContext), mContext.getSystemService(AlarmManager.class)); + }, DozeParameters.getInstance(mContext), + mContext.getSystemService(AlarmManager.class)); if (mScrimSrcModeEnabled) { Runnable runnable = () -> { boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE; @@ -916,9 +920,9 @@ public class StatusBar extends SystemUI implements DemoMode, mBackdrop.setOnVisibilityChangedRunnable(runnable); runnable.run(); } - mHeadsUpManager.addListener(mScrimController); mStackScroller.setScrimController(mScrimController); - mDozeScrimController = new DozeScrimController(mScrimController, context); + mDozeScrimController = new DozeScrimController(mScrimController, context, + DozeParameters.getInstance(context)); // Other icons mVolumeComponent = getComponent(VolumeComponent.class); @@ -1073,7 +1077,6 @@ public class StatusBar extends SystemUI implements DemoMode, mReinflateNotificationsOnUserSwitched = true; } // end old BaseStatusBar.onDensityOrFontScaleChanged(). - mScrimController.onDensityOrFontScaleChanged(); // TODO: Remove this. if (mBrightnessMirrorController != null) { mBrightnessMirrorController.onDensityOrFontScaleChanged(); @@ -1087,6 +1090,7 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardUserSwitcher.onDensityOrFontScaleChanged(); } mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext); + mHeadsUpManager.onDensityOrFontScaleChanged(); reevaluateStyles(); } @@ -1384,8 +1388,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateQsExpansionEnabled(); // Let's also update the icons - mNotificationIconAreaController.updateNotificationIcons( - mEntryManager.getNotificationData()); + mNotificationIconAreaController.updateNotificationIcons(); } @Override @@ -1867,7 +1870,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public boolean isDozing() { - return mDozing; + return mDozing && mStackScroller.isFullyDark(); } @Override @@ -1991,7 +1994,7 @@ public class StatusBar extends SystemUI implements DemoMode, mVisualStabilityManager.setPanelExpanded(isExpanded); if (isExpanded && getBarState() != StatusBarState.KEYGUARD) { if (DEBUG) { - Log.v(TAG, "clearing notification effects from setPanelExpanded"); + Log.v(TAG, "clearing notification effects from setExpandedHeight"); } clearNotificationEffects(); } @@ -2813,7 +2816,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive) { mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive); - entry.row.updateMaxHeights(); + entry.row.notifyHeightChanged(true /* needsAnimation */); } public void lockScrollTo(NotificationData.Entry entry) { mStackScroller.lockScrollTo(entry.row); @@ -3817,13 +3820,16 @@ public class StatusBar extends SystemUI implements DemoMode, private void updateDozingState() { Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0); Trace.beginSection("StatusBar#updateDozingState"); + + boolean sleepingFromKeyguard = + mStatusBarKeyguardViewManager.isGoingToSleepVisibleNotOccluded(); boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup()) - || (mDozing && mDozeServiceHost.shouldAnimateScreenOff()); - mNotificationPanel.setDozing(mDozing, animate); + || (mDozing && mDozeServiceHost.shouldAnimateScreenOff() && sleepingFromKeyguard); + mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation); mDozeScrimController.setDozing(mDozing); mKeyguardIndicationController.setDozing(mDozing); - mNotificationPanel.setDark(mDozing, animate); + mNotificationPanel.setDozing(mDozing, animate); updateQsExpansionEnabled(); mViewHierarchyManager.updateRowStates(); Trace.endSection(); @@ -3833,6 +3839,9 @@ public class StatusBar extends SystemUI implements DemoMode, if (mStackScroller == null) return; boolean onKeyguard = mState == StatusBarState.KEYGUARD; boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode(); + if (mHeadsUpAppearanceController != null) { + mHeadsUpAppearanceController.setPublicMode(publicMode); + } mStackScroller.setHideSensitive(publicMode, goingToFullShade); mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */); mStackScroller.setExpandingEnabled(!onKeyguard); @@ -4739,6 +4748,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (mDozingRequested) { mDozingRequested = false; DozeLog.traceDozing(mContext, mDozing); + mWakefulnessLifecycle.dispatchStartedWakingUp(); updateDozing(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index b26b7c950695..a9c467ece194 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -95,6 +95,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb protected boolean mLastRemoteInputActive; private boolean mLastDozing; private int mLastFpMode; + private boolean mGoingToSleepVisibleNotOccluded; private OnDismissAction mAfterKeyguardGoneAction; private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>(); @@ -262,11 +263,16 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } + public boolean isGoingToSleepVisibleNotOccluded() { + return mGoingToSleepVisibleNotOccluded; + } + public void onStartedGoingToSleep() { - // TODO: remove + mGoingToSleepVisibleNotOccluded = isShowing() && !isOccluded(); } public void onFinishedGoingToSleep() { + mGoingToSleepVisibleNotOccluded = false; mBouncer.onScreenTurnedOff(); } @@ -371,6 +377,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void startPreHideAnimation(Runnable finishRunnable) { if (mBouncer.isShowing()) { mBouncer.startPreHideAnimation(finishRunnable); + mNotificationPanelView.onBouncerPreHideAnimation(); } else if (finishRunnable != null) { finishRunnable.run(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java index defb46ce27b2..309a1a7abe4d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java @@ -73,7 +73,7 @@ public class StatusBarWindowManager implements RemoteInputController.Callback, D mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mActivityManager = ActivityManager.getService(); mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation(); - mDozeParameters = new DozeParameters(mContext); + mDozeParameters = DozeParameters.getInstance(mContext); mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze(); } @@ -141,11 +141,9 @@ public class StatusBarWindowManager implements RemoteInputController.Callback, D mLpChanged.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; } - final boolean showWallpaperOnAod = mDozeParameters.getAlwaysOn() && - state.wallpaperSupportsAmbientMode && - state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_OPAQUE; - if (state.keyguardShowing && !state.backdropShowing && - (!state.dozing || showWallpaperOnAod)) { + final boolean scrimsOccludingWallpaper = + state.scrimsVisibility == ScrimController.VISIBILITY_FULLY_OPAQUE; + if (state.keyguardShowing && !state.backdropShowing && !scrimsOccludingWallpaper) { mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; } else { mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index 040d7ec32ecc..aeda55a8bf8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -443,6 +443,9 @@ public class HeadsUpManager { entry.reset(); } + public void onDensityOrFontScaleChanged() { + } + /** * This represents a notification and how long it is in a heads up mode. It also manages its * lifecycle automatically when created. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index 0f637fb5a40a..7c1c566a57bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -70,6 +70,8 @@ public class AmbientState { private int mIntrinsicPadding; private int mExpandAnimationTopChange; private ExpandableNotificationRow mExpandingNotification; + private boolean mFullyDark; + private int mDarkTopPadding; public AmbientState(Context context) { reload(context); @@ -409,4 +411,26 @@ public class AmbientState { public int getExpandAnimationTopChange() { return mExpandAnimationTopChange; } + + /** + * {@see isFullyDark} + */ + public void setFullyDark(boolean fullyDark) { + mFullyDark = fullyDark; + } + + /** + * @return {@code true } when shade is completely dark: in AOD or ambient display. + */ + public boolean isFullyDark() { + return mFullyDark; + } + + public void setDarkTopPadding(int darkTopPadding) { + mDarkTopPadding = darkTopPadding; + } + + public int getDarkTopPadding() { + return mDarkTopPadding; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java index 53377d955722..c26568ea00b3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java @@ -21,12 +21,12 @@ import android.util.Property; import android.view.View; import java.util.ArrayList; -import java.util.Set; /** * Filters the animations for only a certain type of properties. */ public class AnimationFilter { + public static final int NO_DELAY = -1; boolean animateAlpha; boolean animateX; boolean animateY; @@ -40,7 +40,7 @@ public class AnimationFilter { public boolean animateShadowAlpha; boolean hasDelays; boolean hasGoToFullShadeEvent; - boolean hasHeadsUpDisappearClickEvent; + long customDelay; private ArraySet<Property> mAnimatedProperties = new ArraySet<>(); public AnimationFilter animateAlpha() { @@ -129,8 +129,14 @@ public class AnimationFilter { hasGoToFullShadeEvent = true; } if (ev.animationType == NotificationStackScrollLayout.AnimationEvent + .ANIMATION_TYPE_HEADS_UP_DISAPPEAR) { + customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP; + } else if (ev.animationType == NotificationStackScrollLayout.AnimationEvent .ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) { - hasHeadsUpDisappearClickEvent = true; + // We need both timeouts when clicking, one to delay it and one for the animation + // to look nice + customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP_CLICKED + + StackStateAnimator.ANIMATION_DELAY_HEADS_UP; } } } @@ -165,7 +171,7 @@ public class AnimationFilter { animateHideSensitive = false; hasDelays = false; hasGoToFullShadeEvent = false; - hasHeadsUpDisappearClickEvent = false; + customDelay = NO_DELAY; mAnimatedProperties.clear(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java index 3bf7d892ea0e..a7925aa003e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java @@ -233,7 +233,8 @@ public class ExpandableViewState extends ViewState { expandableView.setDark(this.dark, animationFilter.animateDark, properties.delay); if (properties.wasAdded(child) && !hidden) { - expandableView.performAddAnimation(properties.delay, properties.duration); + expandableView.performAddAnimation(properties.delay, properties.duration, + false /* isHeadsUpAppear */); } if (!expandableView.isInShelf() && this.inShelf) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java index 05c0099359ab..59ce0ca3b3cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java @@ -23,6 +23,11 @@ import android.view.animation.PathInterpolator; * An interpolator specifically designed for the appear animation of heads up notifications. */ public class HeadsUpAppearInterpolator extends PathInterpolator { + + private static float X1 = 250f; + private static float X2 = 200f; + private static float XTOT = (X1 + X2);; + public HeadsUpAppearInterpolator() { super(getAppearPath()); } @@ -30,22 +35,18 @@ public class HeadsUpAppearInterpolator extends PathInterpolator { private static Path getAppearPath() { Path path = new Path(); path.moveTo(0, 0); - float x1 = 250f; - float x2 = 150f; - float x3 = 100f; float y1 = 90f; - float y2 = 78f; - float y3 = 80f; - float xTot = (x1 + x2 + x3); - path.cubicTo(x1 * 0.9f / xTot, 0f, - x1 * 0.8f / xTot, y1 / y3, - x1 / xTot , y1 / y3); - path.cubicTo((x1 + x2 * 0.4f) / xTot, y1 / y3, - (x1 + x2 * 0.2f) / xTot, y2 / y3, - (x1 + x2) / xTot, y2 / y3); - path.cubicTo((x1 + x2 + x3 * 0.4f) / xTot, y2 / y3, - (x1 + x2 + x3 * 0.2f) / xTot, 1f, - 1f, 1f); + float y2 = 80f; + path.cubicTo(X1 * 0.8f / XTOT, y1 / y2, + X1 * 0.8f / XTOT, y1 / y2, + X1 / XTOT, y1 / y2); + path.cubicTo((X1 + X2 * 0.4f) / XTOT, y1 / y2, + (X1 + X2 * 0.2f) / XTOT, 1.0f, + 1.0f , 1.0f); return path; } + + public static float getFractionUntilOvershoot() { + return X1 / XTOT; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java index ac2a1e1e041f..e5ab712e9bdd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java @@ -102,6 +102,9 @@ public class NotificationChildrenContainer extends ViewGroup { private boolean mShowDividersWhenExpanded; private boolean mHideDividersDuringExpand; + private int mTranslationForHeader; + private int mCurrentHeaderTranslation = 0; + private float mHeaderVisibleAmount = 1.0f; public NotificationChildrenContainer(Context context) { this(context, null); @@ -142,6 +145,9 @@ public class NotificationChildrenContainer extends ViewGroup { res.getBoolean(R.bool.config_showDividersWhenGroupNotificationExpanded); mHideDividersDuringExpand = res.getBoolean(R.bool.config_hideDividersDuringExpand); + mTranslationForHeader = res.getDimensionPixelSize( + com.android.internal.R.dimen.notification_content_margin) + - mNotificationHeaderMargin; } @Override @@ -486,7 +492,7 @@ public class NotificationChildrenContainer extends ViewGroup { if (showingAsLowPriority()) { return mNotificationHeaderLowPriority.getHeight(); } - int intrinsicHeight = mNotificationHeaderMargin; + int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation; int visibleChildren = 0; int childCount = mChildren.size(); boolean firstChild = true; @@ -541,7 +547,7 @@ public class NotificationChildrenContainer extends ViewGroup { public void getState(StackScrollState resultState, ExpandableViewState parentState, AmbientState ambientState) { int childCount = mChildren.size(); - int yPosition = mNotificationHeaderMargin; + int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation; boolean firstChild = true; int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(); int lastVisibleIndex = maxAllowedVisibleChildren - 1; @@ -645,6 +651,11 @@ public class NotificationChildrenContainer extends ViewGroup { mHeaderViewState.zTranslation = childrenExpandedAndNotAnimating ? parentState.zTranslation : 0; + mHeaderViewState.yTranslation = mCurrentHeaderTranslation; + mHeaderViewState.alpha = mHeaderVisibleAmount; + // The hiding is done automatically by the alpha, otherwise we'll pick it up again + // in the next frame with the initFrom call above and have an invisible header + mHeaderViewState.hidden = false; } } @@ -1009,7 +1020,8 @@ public class NotificationChildrenContainer extends ViewGroup { return getMinHeight(NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED, true /* likeHighPriority */); } - int maxContentHeight = mNotificationHeaderMargin + mNotificatonTopPadding; + int maxContentHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation + + mNotificatonTopPadding; int visibleChildren = 0; int childCount = mChildren.size(); for (int i = 0; i < childCount; i++) { @@ -1071,7 +1083,8 @@ public class NotificationChildrenContainer extends ViewGroup { } private int getVisibleChildrenExpandHeight() { - int intrinsicHeight = mNotificationHeaderMargin + mNotificatonTopPadding + mDividerHeight; + int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation + + mNotificatonTopPadding + mDividerHeight; int visibleChildren = 0; int childCount = mChildren.size(); int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */); @@ -1110,7 +1123,7 @@ public class NotificationChildrenContainer extends ViewGroup { if (!likeHighPriority && showingAsLowPriority()) { return mNotificationHeaderLowPriority.getHeight(); } - int minExpandHeight = mNotificationHeaderMargin; + int minExpandHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation; int visibleChildren = 0; boolean firstChild = true; int childCount = mChildren.size(); @@ -1190,7 +1203,8 @@ public class NotificationChildrenContainer extends ViewGroup { } public int getPositionInLinearLayout(View childInGroup) { - int position = mNotificationHeaderMargin + mNotificatonTopPadding; + int position = mNotificationHeaderMargin + mCurrentHeaderTranslation + + mNotificatonTopPadding; for (int i = 0; i < mChildren.size(); i++) { ExpandableNotificationRow child = mChildren.get(i); @@ -1281,4 +1295,9 @@ public class NotificationChildrenContainer extends ViewGroup { last = false; } } + + public void setHeaderVisibleAmount(float headerVisibleAmount) { + mHeaderVisibleAmount = headerVisibleAmount; + mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java new file mode 100644 index 000000000000..a36c966a3310 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2018 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.stack; + +import android.view.View; + +import com.android.systemui.statusbar.ActivatableNotificationView; +import com.android.systemui.statusbar.ExpandableNotificationRow; +import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; + +import java.util.HashSet; + +/** + * A class that manages the roundness for notification views + */ +class NotificationRoundnessManager implements OnHeadsUpChangedListener { + + private boolean mExpanded; + private ActivatableNotificationView mFirst; + private ActivatableNotificationView mLast; + private HashSet<View> mAnimatedChildren; + private Runnable mRoundingChangedCallback; + private ExpandableNotificationRow mTrackedHeadsUp; + private float mAppearFraction; + + @Override + public void onHeadsUpPinned(ExpandableNotificationRow headsUp) { + updateRounding(headsUp, false /* animate */); + } + + @Override + public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) { + updateRounding(headsUp, true /* animate */); + } + + private void updateRounding(ActivatableNotificationView view, boolean animate) { + float topRoundness = getRoundness(view, true /* top */); + float bottomRoundness = getRoundness(view, false /* top */); + boolean firstChanged = view.setTopRoundness(topRoundness, animate); + boolean secondChanged = view.setBottomRoundness(bottomRoundness, animate); + if ((view == mFirst || view == mLast) && (firstChanged || secondChanged)) { + mRoundingChangedCallback.run(); + } + } + + private float getRoundness(ActivatableNotificationView view, boolean top) { + if ((view.isPinned() || view.isHeadsUpAnimatingAway()) && !mExpanded) { + return 1.0f; + } + if (view == mFirst && top) { + return 1.0f; + } + if (view == mLast && !top) { + return 1.0f; + } + if (view == mTrackedHeadsUp && mAppearFraction <= 0.0f) { + // If we're pushing up on a headsup the appear fraction is < 0 and it needs to still be + // rounded. + return 1.0f; + } + return 0.0f; + } + + public void setExpanded(float expandedHeight, float appearFraction) { + mExpanded = expandedHeight != 0.0f; + mAppearFraction = appearFraction; + if (mTrackedHeadsUp != null) { + updateRounding(mTrackedHeadsUp, true); + } + } + + public void setFirstAndLastBackgroundChild(ActivatableNotificationView first, + ActivatableNotificationView last) { + boolean firstChanged = mFirst != first; + boolean lastChanged = mLast != last; + if (!firstChanged && !lastChanged) { + return; + } + ActivatableNotificationView oldFirst = mFirst; + ActivatableNotificationView oldLast = mLast; + mFirst = first; + mLast = last; + if (firstChanged && oldFirst != null && !oldFirst.isRemoved()) { + updateRounding(oldFirst, oldFirst.isShown()); + } + if (lastChanged && oldLast != null && !oldLast.isRemoved()) { + updateRounding(oldLast, oldLast.isShown()); + } + if (mFirst != null) { + updateRounding(mFirst, mFirst.isShown() && !mAnimatedChildren.contains(mFirst)); + } + if (mLast != null) { + updateRounding(mLast, mLast.isShown() && !mAnimatedChildren.contains(mLast)); + } + mRoundingChangedCallback.run(); + } + + public void setAnimatedChildren(HashSet<View> animatedChildren) { + mAnimatedChildren = animatedChildren; + } + + public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) { + mRoundingChangedCallback = roundingChangedCallback; + } + + public void setTrackingHeadsUp(ExpandableNotificationRow row) { + mTrackedHeadsUp = row; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index a85f4e2b53a5..a572450c3329 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -94,8 +94,8 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.VisibilityLocationProvider; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.ScrollAdapter; @@ -108,6 +108,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.function.BiConsumer; /** * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack. @@ -289,6 +290,7 @@ public class NotificationStackScrollLayout extends ViewGroup private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations = new HashSet<>(); private HeadsUpManagerPhone mHeadsUpManager; + private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager(); private boolean mTrackingHeadsUp; private ScrimController mScrimController; private boolean mForceNoOverlappingRendering; @@ -400,9 +402,11 @@ public class NotificationStackScrollLayout extends ViewGroup private int mSidePaddings; private final int mSeparatorWidth; private final int mSeparatorThickness; - private final Rect mTmpRect = new Rect(); + private final Rect mBackgroundAnimationRect = new Rect(); private int mClockBottom; private int mAntiBurnInOffsetX; + private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>(); + private int mHeadsUpInset; public NotificationStackScrollLayout(Context context) { this(context, null); @@ -440,6 +444,9 @@ public class NotificationStackScrollLayout extends ViewGroup mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width); mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness); mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding); + mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated); + mRoundnessManager.setOnRoundingChangedCallback(this::invalidate); + addOnExpandedHeightListener(mRoundnessManager::setExpanded); updateWillNotDraw(); mBackgroundPaint.setAntiAlias(true); @@ -515,26 +522,29 @@ public class NotificationStackScrollLayout extends ViewGroup final int darkBottom = darkTop + mSeparatorThickness; if (mAmbientState.hasPulsingNotifications()) { - // TODO draw divider between notification and shelf - } else if (mAmbientState.isDark()) { + // No divider, we have a notification icon instead + } else if (mAmbientState.isFullyDark()) { // Only draw divider on AOD if we actually have notifications if (mFirstVisibleBackgroundChild != null) { canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint); } - setClipBounds(null); } else { float animProgress = Interpolators.FAST_OUT_SLOW_IN .getInterpolation(1f - mDarkAmount); float sidePaddingsProgress = Interpolators.FAST_OUT_SLOW_IN .getInterpolation((1f - mDarkAmount) * 2); - mTmpRect.set((int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress), + mBackgroundAnimationRect.set( + (int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress), (int) MathUtils.lerp(darkTop, lockScreenTop, animProgress), (int) MathUtils.lerp(darkRight, lockScreenRight, sidePaddingsProgress), (int) MathUtils.lerp(darkBottom, lockScreenBottom, animProgress)); - canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom, - mCornerRadius, mCornerRadius, mBackgroundPaint); - setClipBounds(animProgress == 1 ? null : mTmpRect); + if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) { + canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top, + mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom, + mCornerRadius, mCornerRadius, mBackgroundPaint); + } } + updateClipping(); } private void updateBackgroundDimming() { @@ -582,13 +592,15 @@ public class NotificationStackScrollLayout extends ViewGroup res.getDimensionPixelSize(R.dimen.notification_divider_height_increased); mMinTopOverScrollToEscape = res.getDimensionPixelSize( R.dimen.min_top_overscroll_to_qs); - mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height); + mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height); mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom); mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings); mMinInteractionHeight = res.getDimensionPixelSize( R.dimen.notification_min_interaction_height); mCornerRadius = res.getDimensionPixelSize( Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius)); + mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize( + R.dimen.heads_up_status_bar_padding); } public void setDrawBackgroundAsSrc(boolean asSrc) { @@ -693,7 +705,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (mPulsing) { mTopPadding = mClockBottom; } else { - mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding; + mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount); } mAmbientState.setLayoutHeight(getLayoutHeight()); updateAlgorithmLayoutMinHeight(); @@ -701,7 +713,8 @@ public class NotificationStackScrollLayout extends ViewGroup } private void updateAlgorithmLayoutMinHeight() { - mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() ? getLayoutMinHeight() : 0); + mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() || isHeadsUpTransition() + ? getLayoutMinHeight() : 0); } /** @@ -820,6 +833,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (mRegularTopPadding != topPadding) { mRegularTopPadding = topPadding; mDarkTopPadding = topPadding + mDarkSeparatorPadding; + mAmbientState.setDarkTopPadding(mDarkTopPadding); updateAlgorithmHeightAndPadding(); updateContentHeight(); if (animate && mAnimationsEnabled && mIsExpanded) { @@ -854,11 +868,12 @@ public class NotificationStackScrollLayout extends ViewGroup float translationY; float appearEndPosition = getAppearEndPosition(); float appearStartPosition = getAppearStartPosition(); + float appearFraction = 1.0f; if (height >= appearEndPosition) { translationY = 0; stackHeight = (int) height; } else { - float appearFraction = getAppearFraction(height); + appearFraction = getAppearFraction(height); if (appearFraction >= 0) { translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0, appearFraction); @@ -867,7 +882,12 @@ public class NotificationStackScrollLayout extends ViewGroup // start translationY = height - appearStartPosition + getExpandTranslationStart(); } - stackHeight = (int) (height - translationY); + if (isHeadsUpTransition()) { + stackHeight = mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight(); + translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction); + } else { + stackHeight = (int) (height - translationY); + } } if (stackHeight != mCurrentStackHeight) { mCurrentStackHeight = stackHeight; @@ -875,6 +895,10 @@ public class NotificationStackScrollLayout extends ViewGroup requestChildrenUpdate(); } setStackTranslation(translationY); + for (int i = 0; i < mExpandedHeightListeners.size(); i++) { + BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i); + listener.accept(mExpandedHeight, appearFraction); + } } private void setRequestedClipBounds(Rect clipRect) { @@ -883,13 +907,17 @@ public class NotificationStackScrollLayout extends ViewGroup } public void updateClipping() { + boolean animatingClipping = mDarkAmount > 0 && mDarkAmount < 1; boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode && !mHeadsUpAnimatingAway; if (mIsClipped != clipped) { mIsClipped = clipped; updateFadingState(); } - if (clipped) { + + if (animatingClipping) { + setClipBounds(mBackgroundAnimationRect); + } else if (clipped) { setClipBounds(mRequestedClipBounds); } else { setClipBounds(null); @@ -909,12 +937,8 @@ public class NotificationStackScrollLayout extends ViewGroup * Measured in absolute height. */ private float getAppearStartPosition() { - if (mTrackingHeadsUp && mFirstVisibleBackgroundChild != null) { - if (mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild)) { - // If we ever expanded beyond the first notification, it's allowed to merge into - // the shelf - return mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight(); - } + if (isHeadsUpTransition()) { + return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight(); } return getMinExpansionHeight(); } @@ -948,17 +972,14 @@ public class NotificationStackScrollLayout extends ViewGroup int appearPosition; int notGoneChildCount = getNotGoneChildCount(); if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) { - int minNotificationsForShelf = 1; - if (mTrackingHeadsUp + if (isHeadsUpTransition() || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) { appearPosition = getTopHeadsUpPinnedHeight(); - minNotificationsForShelf = 2; } else { appearPosition = 0; - } - if (notGoneChildCount >= minNotificationsForShelf - && mShelf.getVisibility() != GONE) { - appearPosition += mShelf.getIntrinsicHeight(); + if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) { + appearPosition += mShelf.getIntrinsicHeight(); + } } } else { appearPosition = mEmptyShadeView.getHeight(); @@ -966,6 +987,11 @@ public class NotificationStackScrollLayout extends ViewGroup return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding); } + private boolean isHeadsUpTransition() { + return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null + && mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild); + } + /** * @param height the height of the panel * @return the fraction of the appear animation that has been performed @@ -1076,10 +1102,6 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) { - if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) { - mScrimController.setTopHeadsUpDragAmount(animView, - Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f)); - } // Returning true prevents alpha fading. return !mFadeNotificationsOnDismiss; } @@ -2075,7 +2097,7 @@ public class NotificationStackScrollLayout extends ViewGroup float previousPaddingAmount = 0.0f; int numShownItems = 0; boolean finish = false; - int maxDisplayedNotifications = mAmbientState.isDark() + int maxDisplayedNotifications = mAmbientState.isFullyDark() ? (hasPulsingNotifications() ? 1 : 0) : mMaxDisplayedNotifications; @@ -2085,7 +2107,7 @@ public class NotificationStackScrollLayout extends ViewGroup && !expandableView.hasNoContentHeight()) { boolean limitReached = maxDisplayedNotifications != -1 && numShownItems >= maxDisplayedNotifications; - boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isDark() + boolean notificationOnAmbientThatIsNotPulsing = mAmbientState.isFullyDark() && hasPulsingNotifications() && expandableView instanceof ExpandableNotificationRow && !isPulsing(((ExpandableNotificationRow) expandableView).getEntry()); @@ -2168,7 +2190,7 @@ public class NotificationStackScrollLayout extends ViewGroup private void updateBackground() { // No need to update the background color if it's not being drawn. - if (!mShouldDrawNotificationBackground || mAmbientState.isDark()) { + if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) { return; } @@ -2521,6 +2543,9 @@ public class NotificationStackScrollLayout extends ViewGroup } public int getLayoutMinHeight() { + if (isHeadsUpTransition()) { + return getTopHeadsUpPinnedHeight(); + } return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight(); } @@ -2929,42 +2954,18 @@ public class NotificationStackScrollLayout extends ViewGroup private void updateFirstAndLastBackgroundViews() { ActivatableNotificationView firstChild = getFirstChildWithBackground(); ActivatableNotificationView lastChild = getLastChildWithBackground(); - boolean firstChanged = firstChild != mFirstVisibleBackgroundChild; - boolean lastChanged = lastChild != mLastVisibleBackgroundChild; if (mAnimationsEnabled && mIsExpanded) { - mAnimateNextBackgroundTop = firstChanged; - mAnimateNextBackgroundBottom = lastChanged; + mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild; + mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild; } else { mAnimateNextBackgroundTop = false; mAnimateNextBackgroundBottom = false; } - if (firstChanged && mFirstVisibleBackgroundChild != null - && !mFirstVisibleBackgroundChild.isRemoved()) { - mFirstVisibleBackgroundChild.setTopRoundness(0.0f, - mFirstVisibleBackgroundChild.isShown()); - } - if (lastChanged && mLastVisibleBackgroundChild != null - && !mLastVisibleBackgroundChild.isRemoved()) { - mLastVisibleBackgroundChild.setBottomRoundness(0.0f, - mLastVisibleBackgroundChild.isShown()); - } mFirstVisibleBackgroundChild = firstChild; mLastVisibleBackgroundChild = lastChild; mAmbientState.setLastVisibleBackgroundChild(lastChild); - applyRoundedNess(); - } - - private void applyRoundedNess() { - if (mFirstVisibleBackgroundChild != null) { - mFirstVisibleBackgroundChild.setTopRoundness(1.0f, - mFirstVisibleBackgroundChild.isShown() - && !mChildrenToAddAnimated.contains(mFirstVisibleBackgroundChild)); - } - if (mLastVisibleBackgroundChild != null) { - mLastVisibleBackgroundChild.setBottomRoundness(1.0f, - mLastVisibleBackgroundChild.isShown() - && !mChildrenToAddAnimated.contains(mLastVisibleBackgroundChild)); - } + mRoundnessManager.setFirstAndLastBackgroundChild(mFirstVisibleBackgroundChild, + mLastVisibleBackgroundChild); invalidate(); } @@ -3298,7 +3299,7 @@ public class NotificationStackScrollLayout extends ViewGroup .animateY(mShelf)); ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex; mAnimationEvents.add(ev); - startBackgroundFadeIn(); + startBackgroundFade(); } mDarkNeedsAnimation = false; } @@ -3889,7 +3890,6 @@ public class NotificationStackScrollLayout extends ViewGroup requestChildrenUpdate(); applyCurrentBackgroundBounds(); updateWillNotDraw(); - updateContentHeight(); updateAntiBurnInTranslation(); notifyHeightChangeListener(mShelf); } @@ -3910,6 +3910,11 @@ public class NotificationStackScrollLayout extends ViewGroup private void setDarkAmount(float darkAmount) { mDarkAmount = darkAmount; + final boolean fullyDark = darkAmount == 1; + if (mAmbientState.isFullyDark() != fullyDark) { + mAmbientState.setFullyDark(fullyDark); + updateContentHeight(); + } updateBackgroundDimming(); } @@ -3917,8 +3922,9 @@ public class NotificationStackScrollLayout extends ViewGroup return mDarkAmount; } - private void startBackgroundFadeIn() { - ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount, 0f); + private void startBackgroundFade() { + ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount, + mAmbientState.isDark() ? 1f : 0); fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP); fadeAnimator.setInterpolator(Interpolators.ALPHA_IN); fadeAnimator.start(); @@ -4288,6 +4294,7 @@ public class NotificationStackScrollLayout extends ViewGroup public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { mHeadsUpManager = headsUpManager; mAmbientState.setHeadsUpManager(headsUpManager); + mHeadsUpManager.addListener(mRoundnessManager); } public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) { @@ -4319,8 +4326,9 @@ public class NotificationStackScrollLayout extends ViewGroup requestChildrenUpdate(); } - public void setTrackingHeadsUp(boolean trackingHeadsUp) { - mTrackingHeadsUp = trackingHeadsUp; + public void setTrackingHeadsUp(ExpandableNotificationRow row) { + mTrackingHeadsUp = row != null; + mRoundnessManager.setTrackingHeadsUp(row); } public void setScrimController(ScrimController scrimController) { @@ -4502,6 +4510,20 @@ public class NotificationStackScrollLayout extends ViewGroup mAmbientState.getScrollY())); } + public boolean isFullyDark() { + return mAmbientState.isFullyDark(); + } + + /** + * Add a listener whenever the expanded height changes. The first value passed as an argument + * is the expanded height and the second one is the appearFraction. + * + * @param listener the listener to notify. + */ + public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) { + mExpandedHeightListeners.add(listener); + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ @@ -4880,7 +4902,8 @@ public class NotificationStackScrollLayout extends ViewGroup .animateHeight() .animateTopInset() .animateY() - .animateZ(), + .animateZ() + .hasDelays(), // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK new AnimationFilter() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 51737a863748..7c8e0fc4886b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -50,6 +50,8 @@ public class StackScrollAlgorithm { private boolean mIsExpanded; private boolean mClipNotificationScrollToTop; private int mStatusBarHeight; + private float mHeadsUpInset; + private int mPinnedZTranslationExtra; public StackScrollAlgorithm(Context context) { initView(context); @@ -68,6 +70,10 @@ public class StackScrollAlgorithm { mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height); mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height); mClipNotificationScrollToTop = res.getBoolean(R.bool.config_clipNotificationScrollToTop); + mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize( + R.dimen.heads_up_status_bar_padding); + mPinnedZTranslationExtra = res.getDimensionPixelSize( + R.dimen.heads_up_pinned_elevation); } public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) { @@ -184,7 +190,7 @@ public class StackScrollAlgorithm { private void updateDimmedActivatedHideSensitive(AmbientState ambientState, StackScrollState resultState, StackScrollAlgorithmState algorithmState) { boolean dimmed = ambientState.isDimmed(); - boolean dark = ambientState.isDark(); + boolean dark = ambientState.isFullyDark(); boolean hideSensitive = ambientState.isHideSensitive(); View activatedChild = ambientState.getActivatedChild(); int childCount = algorithmState.visibleChildren.size(); @@ -457,7 +463,7 @@ public class StackScrollAlgorithm { } } if (row.isPinned()) { - childState.yTranslation = Math.max(childState.yTranslation, 0); + childState.yTranslation = Math.max(childState.yTranslation, mHeadsUpInset); childState.height = Math.max(row.getIntrinsicHeight(), childState.height); childState.hidden = false; ExpandableViewState topState = resultState.getViewStateForView(topHeadsUpEntry); @@ -522,9 +528,6 @@ public class StackScrollAlgorithm { childViewState.inShelf = true; childViewState.headsUpIsVisible = false; } - if (!ambientState.isShadeExpanded()) { - childViewState.height = (int) (mStatusBarHeight - childViewState.yTranslation); - } } protected int getMaxAllowedChildHeight(View child) { @@ -592,6 +595,13 @@ public class StackScrollAlgorithm { } else { childViewState.zTranslation = baseZ; } + + // We need to scrim the notification more from its surrounding content when we are pinned, + // and we therefore elevate it higher. + // We can use the headerVisibleAmount for this, since the value nicely goes from 0 to 1 when + // expanding after which we have a normal elevation again. + childViewState.zTranslation += (1.0f - child.getHeaderVisibleAmount()) + * mPinnedZTranslationExtra; return childrenOnTop; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 236c348e539b..d48ae76495f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -29,6 +29,7 @@ import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.NotificationShelf; +import com.android.systemui.statusbar.StatusBarIconView; import java.util.ArrayList; import java.util.HashSet; @@ -45,13 +46,17 @@ public class StackStateAnimator { public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464; public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220; public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150; - public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 650; - public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 230; + public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 550; + public static final int ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED + = (int) (ANIMATION_DURATION_HEADS_UP_APPEAR + * HeadsUpAppearInterpolator.getFractionUntilOvershoot()); + public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300; public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80; public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32; public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48; public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2; public static final int ANIMATION_DELAY_HEADS_UP = 120; + public static final int ANIMATION_DELAY_HEADS_UP_CLICKED= 120; private final int mGoToFullShadeAppearingTranslation; private final ExpandableViewState mTmpState = new ExpandableViewState(); @@ -74,8 +79,9 @@ public class StackStateAnimator { private ValueAnimator mBottomOverScrollAnimator; private int mHeadsUpAppearHeightBottom; private boolean mShadeExpanded; - private ArrayList<View> mChildrenToClearFromOverlay = new ArrayList<>(); private NotificationShelf mShelf; + private float mStatusBarIconLocation; + private int[] mTmpLocation = new int[2]; public StackStateAnimator(NotificationStackScrollLayout hostLayout) { mHostLayout = hostLayout; @@ -222,8 +228,8 @@ public class StackStateAnimator { if (mAnimationFilter.hasGoToFullShadeEvent) { return calculateDelayGoToFullShade(viewState); } - if (mAnimationFilter.hasHeadsUpDisappearClickEvent) { - return ANIMATION_DELAY_HEADS_UP; + if (mAnimationFilter.customDelay != AnimationFilter.NO_DELAY) { + return mAnimationFilter.customDelay; } long minDelay = 0; for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) { @@ -327,10 +333,6 @@ public class StackStateAnimator { private void onAnimationFinished() { mHostLayout.onChildAnimationFinished(); - for (View v : mChildrenToClearFromOverlay) { - removeFromOverlay(v); - } - mChildrenToClearFromOverlay.clear(); } /** @@ -396,13 +398,14 @@ public class StackStateAnimator { } changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR, - translationDirection, new Runnable() { + 0 /* delay */, translationDirection, false /* isHeadsUpAppear */, + 0, new Runnable() { @Override public void run() { // remove the temporary overlay removeFromOverlay(changingView); } - }); + }, null); } else if (event.animationType == NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) { // A race condition can trigger the view to be added to the overlay even though @@ -424,7 +427,9 @@ public class StackStateAnimator { if (event.headsUpFromBottom) { mTmpState.yTranslation = mHeadsUpAppearHeightBottom; } else { - mTmpState.yTranslation = -mTmpState.height; + mTmpState.yTranslation = 0; + changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED, + true /* isHeadsUpAppear */); } mHeadsUpAppearChildren.add(changingView); mTmpState.applyToView(changingView); @@ -433,22 +438,56 @@ public class StackStateAnimator { event.animationType == NotificationStackScrollLayout .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) { mHeadsUpDisappearChildren.add(changingView); + Runnable endRunnable = null; + // We need some additional delay in case we were removed to make sure we're not + // lagging + int extraDelay = event.animationType == NotificationStackScrollLayout + .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK + ? ANIMATION_DELAY_HEADS_UP_CLICKED + : 0; if (changingView.getParent() == null) { // This notification was actually removed, so we need to add it to the overlay mHostLayout.getOverlay().add(changingView); mTmpState.initFrom(changingView); - mTmpState.yTranslation = -changingView.getActualHeight(); + mTmpState.yTranslation = 0; // We temporarily enable Y animations, the real filter will be combined // afterwards anyway mAnimationFilter.animateY = true; - mAnimationProperties.delay = - event.animationType == NotificationStackScrollLayout - .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK - ? ANIMATION_DELAY_HEADS_UP - : 0; + mAnimationProperties.delay = extraDelay + ANIMATION_DELAY_HEADS_UP; mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR; mTmpState.animateTo(changingView, mAnimationProperties); - mChildrenToClearFromOverlay.add(changingView); + endRunnable = () -> { + // remove the temporary overlay + removeFromOverlay(changingView); + }; + } + float targetLocation = 0; + boolean needsAnimation = true; + if (changingView instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) changingView; + if (row.isDismissed()) { + needsAnimation = false; + } + StatusBarIconView icon = row.getEntry().icon; + if (icon.getParent() != null) { + icon.getLocationOnScreen(mTmpLocation); + float iconPosition = mTmpLocation[0] - icon.getTranslationX() + + ViewState.getFinalTranslationX(icon) + icon.getWidth() * 0.25f; + mHostLayout.getLocationOnScreen(mTmpLocation); + targetLocation = iconPosition - mTmpLocation[0]; + } + } + + if (needsAnimation) { + // We need to add the global animation listener, since once no animations are + // running anymore, the panel will instantly hide itself. We need to wait until + // the animation is fully finished for this though. + changingView.performRemoveAnimation(ANIMATION_DURATION_HEADS_UP_DISAPPEAR + + ANIMATION_DELAY_HEADS_UP, extraDelay, 0.0f, + true /* isHeadsUpAppear */, targetLocation, endRunnable, + getGlobalAnimationFinishedListener()); + } else if (endRunnable != null) { + endRunnable.run(); } } mNewEvents.add(event); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java index 04a7bd79c6ca..4b3643f620a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java @@ -641,6 +641,22 @@ public class ViewState { } /** + * Get the end value of the xTranslation animation running on a view or the xTranslation + * if no animation is running. + */ + public static float getFinalTranslationX(View view) { + if (view == null) { + return 0; + } + ValueAnimator xAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X); + if (xAnimator == null) { + return view.getTranslationX(); + } else { + return getChildTag(view, TAG_END_TRANSLATION_X); + } + } + + /** * Get the end value of the yTranslation animation running on a view or the yTranslation * if no animation is running. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java index 8c4fd7386d6d..66836365705e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java @@ -28,6 +28,9 @@ import static com.android.systemui.utils.os.FakeHandler.Mode.QUEUEING; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.os.Looper; @@ -37,6 +40,7 @@ import android.view.Display; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.util.wakelock.WakeLock; import com.android.systemui.utils.os.FakeHandler; import org.junit.Before; @@ -54,14 +58,17 @@ public class DozeScreenStateTest extends SysuiTestCase { FakeHandler mHandlerFake; @Mock DozeParameters mDozeParameters; + @Mock + WakeLock mWakeLock; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true); + when(mDozeParameters.getAlwaysOn()).thenReturn(true); mServiceFake = new DozeServiceFake(); mHandlerFake = new FakeHandler(Looper.getMainLooper()); - mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters); + mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters, mWakeLock); } @Test @@ -142,4 +149,23 @@ public class DozeScreenStateTest extends SysuiTestCase { assertFalse(mServiceFake.screenStateSet); } + @Test + public void test_holdsWakeLockWhenGoingToLowPowerDelayed() { + // Transition to low power mode will be delayed to let + // animations play at 60 fps. + when(mDozeParameters.shouldControlScreenOff()).thenReturn(true); + mHandlerFake.setMode(QUEUEING); + + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mHandlerFake.dispatchQueuedMessages(); + reset(mWakeLock); + + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + verify(mWakeLock).acquire(); + verify(mWakeLock, never()).release(); + + mHandlerFake.dispatchQueuedMessages(); + verify(mWakeLock).release(); + } + }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java index 75ade9d603e5..0d8d9526f607 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java @@ -28,15 +28,20 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AlarmManager; import android.os.Handler; import android.os.HandlerThread; +import android.os.PowerManager; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.wakelock.WakeLockFake; @@ -46,33 +51,39 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) @SmallTest public class DozeUiTest extends SysuiTestCase { + @Mock private AlarmManager mAlarmManager; + @Mock private DozeMachine mMachine; + @Mock + private DozeParameters mDozeParameters; + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + private DozeHost mHost; private WakeLockFake mWakeLock; - private DozeHostFake mHost; private Handler mHandler; private HandlerThread mHandlerThread; private DozeUi mDozeUi; @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mHandlerThread = new HandlerThread("DozeUiTest"); mHandlerThread.start(); - mAlarmManager = mock(AlarmManager.class); - mMachine = mock(DozeMachine.class); mWakeLock = new WakeLockFake(); - mHost = new DozeHostFake(); mHandler = mHandlerThread.getThreadHandler(); - DozeParameters params = mock(DozeParameters.class); - when(params.getCanControlScreenOffAnimation()).thenReturn(true); - when(params.getDisplayNeedsBlanking()).thenReturn(false); - mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, params); + mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, + mDozeParameters, mKeyguardUpdateMonitor); } @After @@ -96,18 +107,69 @@ public class DozeUiTest extends SysuiTestCase { } @Test - public void propagatesAnimateScreenOff() { - Assert.assertTrue("animateScreenOff should be true", mHost.animateScreenOff); + public void propagatesAnimateScreenOff_noAlwaysOn() { + reset(mHost); + when(mDozeParameters.getAlwaysOn()).thenReturn(false); + when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false); + + mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false); + verify(mHost).setAnimateScreenOff(eq(false)); + } + + @Test + public void propagatesAnimateScreenOff_alwaysOn() { + reset(mHost); + when(mDozeParameters.getAlwaysOn()).thenReturn(true); + when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false); + + // Take over when the keyguard is visible. + mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true); + verify(mHost).setAnimateScreenOff(eq(true)); + + // Do not animate screen-off when keyguard isn't visible - PowerManager will do it. + mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false); + verify(mHost).setAnimateScreenOff(eq(false)); + } + + @Test + public void neverAnimateScreenOff_whenNotSupported() { + // Re-initialize DozeParameters saying that the display requires blanking. + reset(mDozeParameters); + reset(mHost); + when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true); + mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, + mDozeParameters, mKeyguardUpdateMonitor); + + // Never animate if display doesn't support it. + mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true); + mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false); + verify(mHost, never()).setAnimateScreenOff(eq(false)); + } - DozeParameters params = mock(DozeParameters.class); - new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, params); - Assert.assertFalse("animateScreenOff should be false", mHost.animateScreenOff); + @Test + public void transitionSetsAnimateWakeup_alwaysOn() { + when(mDozeParameters.getAlwaysOn()).thenReturn(true); + when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false); + mDozeUi.transitionTo(UNINITIALIZED, DOZE); + verify(mHost).setAnimateWakeup(eq(true)); + } + + @Test + public void keyguardVisibility_changesControlScreenOffAnimation() { + // Pre-condition + reset(mDozeParameters); + when(mDozeParameters.getAlwaysOn()).thenReturn(true); + when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false); + + mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false); + verify(mDozeParameters).setControlScreenOffAnimation(eq(false)); + mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true); + verify(mDozeParameters).setControlScreenOffAnimation(eq(true)); } @Test - public void transitionSetsAnimateWakeup() { - mHost.animateWakeup = false; + public void transitionSetsAnimateWakeup_noAlwaysOn() { mDozeUi.transitionTo(UNINITIALIZED, DOZE); - Assert.assertTrue("animateScreenOff should be true", mHost.animateWakeup); + verify(mHost).setAnimateWakeup(eq(false)); } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java index 2705bca042b4..5c80bb5ded9d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java @@ -24,7 +24,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.IWallpaperManager; -import android.os.Handler; import android.os.RemoteException; import android.support.test.filters.SmallTest; @@ -37,7 +36,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @RunWith(JUnit4.class) @@ -77,7 +75,7 @@ public class DozeWallpaperStateTest extends SysuiTestCase { public void testAnimates_whenSupported() throws RemoteException { // Pre-conditions when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false); - when(mDozeParameters.getCanControlScreenOffAnimation()).thenReturn(true); + when(mDozeParameters.shouldControlScreenOff()).thenReturn(true); when(mDozeParameters.getAlwaysOn()).thenReturn(true); mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED, @@ -92,8 +90,8 @@ public class DozeWallpaperStateTest extends SysuiTestCase { public void testDoesNotAnimate_whenNotSupported() throws RemoteException { // Pre-conditions when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true); - when(mDozeParameters.getCanControlScreenOffAnimation()).thenReturn(false); when(mDozeParameters.getAlwaysOn()).thenReturn(true); + when(mDozeParameters.shouldControlScreenOff()).thenReturn(false); mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.DOZE_AOD); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java index e15e0b4184f3..9eba9b894064 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java @@ -18,6 +18,7 @@ package com.android.systemui.keyguard; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -99,6 +100,23 @@ public class WakefulnessLifecycleTest extends SysuiTestCase { } @Test + public void doesNotDispatchTwice() throws Exception { + mWakefulness.dispatchStartedWakingUp(); + mWakefulness.dispatchStartedWakingUp(); + mWakefulness.dispatchFinishedWakingUp(); + mWakefulness.dispatchFinishedWakingUp(); + mWakefulness.dispatchStartedGoingToSleep(); + mWakefulness.dispatchStartedGoingToSleep(); + mWakefulness.dispatchFinishedGoingToSleep(); + mWakefulness.dispatchFinishedGoingToSleep(); + + verify(mWakefulnessObserver, times(1)).onStartedGoingToSleep(); + verify(mWakefulnessObserver, times(1)).onFinishedGoingToSleep(); + verify(mWakefulnessObserver, times(1)).onStartedWakingUp(); + verify(mWakefulnessObserver, times(1)).onFinishedWakingUp(); + } + + @Test public void dump() throws Exception { mWakefulness.dump(null, new PrintWriter(new ByteArrayOutputStream()), new String[0]); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java index b1e1c02a035f..2000bff7e99a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java @@ -60,6 +60,7 @@ public class NotificationDataTest extends SysuiTestCase { private static final int UID_NORMAL = 123; private static final int UID_ALLOW_DURING_SETUP = 456; + private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey"; private final StatusBarNotification mMockStatusBarNotification = mock(StatusBarNotification.class); @@ -247,6 +248,22 @@ public class NotificationDataTest extends SysuiTestCase { assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification)); } + @Test + public void testShouldFilterHiddenNotifications() { + // setup + when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false); + when(mFsc.isSystemAlertNotification(any())).thenReturn(false); + + // test should filter out hidden notifications: + // hidden + when(mMockStatusBarNotification.getKey()).thenReturn(TEST_HIDDEN_NOTIFICATION_KEY); + assertTrue(mNotificationData.shouldFilterOut(mMockStatusBarNotification)); + + // not hidden + when(mMockStatusBarNotification.getKey()).thenReturn("not hidden"); + assertFalse(mNotificationData.shouldFilterOut(mMockStatusBarNotification)); + } + private void initStatusBarNotification(boolean allowDuringSetup) { Bundle bundle = new Bundle(); bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup); @@ -269,6 +286,21 @@ public class NotificationDataTest extends SysuiTestCase { @Override protected boolean getRanking(String key, NotificationListenerService.Ranking outRanking) { super.getRanking(key, outRanking); + if (key.equals(TEST_HIDDEN_NOTIFICATION_KEY)) { + outRanking.populate(key, outRanking.getRank(), + outRanking.matchesInterruptionFilter(), + outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(), + outRanking.getImportance(), outRanking.getImportanceExplanation(), + outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null, + outRanking.canShowBadge(), outRanking.getUserSentiment(), true); + } else { + outRanking.populate(key, outRanking.getRank(), + outRanking.matchesInterruptionFilter(), + outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(), + outRanking.getImportance(), outRanking.getImportanceExplanation(), + outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null, + outRanking.canShowBadge(), outRanking.getUserSentiment(), false); + } return true; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java index 7e5db344629c..3703d6ad2b83 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java @@ -140,7 +140,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { 0, NotificationManager.IMPORTANCE_DEFAULT, null, null, - null, null, null, true, sentiment); + null, null, null, true, sentiment, false); return true; }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java index e89ff9733388..550a35dc9ffc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java @@ -16,11 +16,15 @@ package com.android.systemui.statusbar.phone; +import android.content.Context; +import android.os.PowerManager; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.phone.DozeParameters.IntInOutMatcher; + +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -28,6 +32,14 @@ import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + @SmallTest @RunWith(AndroidJUnit4.class) public class DozeParametersTest extends SysuiTestCase { @@ -186,4 +198,38 @@ public class DozeParametersTest extends SysuiTestCase { } } + @Test + public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_false() { + TestableDozeParameters dozeParameters = new TestableDozeParameters(getContext()); + PowerManager mockedPowerManager = dozeParameters.getPowerManager(); + dozeParameters.setControlScreenOffAnimation(true); + reset(mockedPowerManager); + dozeParameters.setControlScreenOffAnimation(false); + verify(mockedPowerManager).setDozeAfterScreenOff(eq(true)); + } + + @Test + public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_true() { + TestableDozeParameters dozeParameters = new TestableDozeParameters(getContext()); + PowerManager mockedPowerManager = dozeParameters.getPowerManager(); + dozeParameters.setControlScreenOffAnimation(false); + reset(mockedPowerManager); + dozeParameters.setControlScreenOffAnimation(true); + verify(dozeParameters.getPowerManager()).setDozeAfterScreenOff(eq(false)); + } + + private class TestableDozeParameters extends DozeParameters { + private PowerManager mPowerManager; + + TestableDozeParameters(Context context) { + super(context); + mPowerManager = mock(PowerManager.class); + } + + @Override + protected PowerManager getPowerManager() { + return mPowerManager; + } + } + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java index ca2f713d2b6f..203ebe6d5132 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java @@ -34,18 +34,23 @@ import com.android.systemui.doze.DozeHost; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest public class DozeScrimControllerTest extends SysuiTestCase { + @Mock private ScrimController mScrimController; + @Mock + private DozeParameters mDozeParameters; private DozeScrimController mDozeScrimController; @Before public void setup() { - mScrimController = mock(ScrimController.class); + MockitoAnnotations.initMocks(this); // Make sure callbacks will be invoked to complete the lifecycle. doAnswer(invocationOnMock -> { ScrimController.Callback callback = invocationOnMock.getArgument(1); @@ -56,7 +61,8 @@ public class DozeScrimControllerTest extends SysuiTestCase { }).when(mScrimController).transitionTo(any(ScrimState.class), any(ScrimController.Callback.class)); - mDozeScrimController = new DozeScrimController(mScrimController, getContext()); + mDozeScrimController = new DozeScrimController(mScrimController, getContext(), + mDozeParameters); mDozeScrimController.setDozing(true); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java new file mode 100644 index 000000000000..c904ef30dd16 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2018 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.phone; + +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.View; +import android.widget.TextView; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.TestableDependency; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.ExpandableNotificationRow; +import com.android.systemui.statusbar.HeadsUpStatusBarView; +import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.policy.DarkIconDispatcher; +import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashSet; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class HeadsUpAppearanceControllerTest extends SysuiTestCase { + + private HeadsUpAppearanceController mHeadsUpAppearanceController; + private ExpandableNotificationRow mFirst; + private HeadsUpStatusBarView mHeadsUpStatusBarView; + private HeadsUpManagerPhone mHeadsUpManager; + + @Before + public void setUp() throws Exception { + NotificationTestHelper testHelper = new NotificationTestHelper(getContext()); + mFirst = testHelper.createRow(); + mDependency.injectMockDependency(DarkIconDispatcher.class); + mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class), + mock(TextView.class)); + mHeadsUpManager = mock(HeadsUpManagerPhone.class); + mHeadsUpAppearanceController = new HeadsUpAppearanceController( + mock(NotificationIconAreaController.class), + mHeadsUpManager, + mHeadsUpStatusBarView, + mock(NotificationStackScrollLayout.class), + mock(NotificationPanelView.class), + new View(mContext)); + mHeadsUpAppearanceController.setExpandedHeight(0.0f, 0.0f); + } + + @Test + public void testShowinEntryUpdated() { + mFirst.setPinned(true); + when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); + when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry()); + mHeadsUpAppearanceController.onHeadsUpPinned(mFirst); + Assert.assertEquals(mFirst.getEntry(), mHeadsUpStatusBarView.getShowingEntry()); + + mFirst.setPinned(false); + when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); + mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst); + Assert.assertEquals(null, mHeadsUpStatusBarView.getShowingEntry()); + } + + @Test + public void testShownUpdated() { + mFirst.setPinned(true); + when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); + when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry()); + mHeadsUpAppearanceController.onHeadsUpPinned(mFirst); + Assert.assertTrue(mHeadsUpAppearanceController.isShown()); + + mFirst.setPinned(false); + when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); + mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst); + Assert.assertFalse(mHeadsUpAppearanceController.isShown()); + } + + @Test + public void testHeaderUpdated() { + mFirst.setPinned(true); + when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); + when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry()); + mHeadsUpAppearanceController.onHeadsUpPinned(mFirst); + Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 0.0f, 0.0f); + + mFirst.setPinned(false); + when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); + mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst); + Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 1.0f, 0.0f); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index d32c9a8e5649..45845fc147a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -64,7 +64,6 @@ public class ScrimControllerTest extends SysuiTestCase { private SynchronousScrimController mScrimController; private ScrimView mScrimBehind; private ScrimView mScrimInFront; - private View mHeadsUpScrim; private Consumer<Integer> mScrimVisibilityCallback; private int mScrimVisibility; private LightBarController mLightBarController; @@ -78,7 +77,6 @@ public class ScrimControllerTest extends SysuiTestCase { mLightBarController = mock(LightBarController.class); mScrimBehind = new ScrimView(getContext()); mScrimInFront = new ScrimView(getContext()); - mHeadsUpScrim = new View(getContext()); mWakeLock = mock(WakeLock.class); mAlarmManager = mock(AlarmManager.class); mAlwaysOnEnabled = true; @@ -87,7 +85,7 @@ public class ScrimControllerTest extends SysuiTestCase { when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled); when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true); mScrimController = new SynchronousScrimController(mLightBarController, mScrimBehind, - mScrimInFront, mHeadsUpScrim, mScrimVisibilityCallback, mDozeParamenters, + mScrimInFront, mScrimVisibilityCallback, mDozeParamenters, mAlarmManager); } @@ -349,7 +347,7 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test - public void testWillHideAoDWallpaper() { + public void testWillHideAodWallpaper() { mScrimController.setWallpaperSupportsAmbientMode(true); mScrimController.transitionTo(ScrimState.AOD); verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any()); @@ -415,56 +413,6 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test - public void testHeadsUpScrimOpacity() { - mScrimController.setPanelExpansion(0f); - mScrimController.onHeadsUpPinned(null /* row */); - mScrimController.finishAnimationsImmediately(); - - Assert.assertNotEquals("Heads-up scrim should be visible", 0f, - mHeadsUpScrim.getAlpha(), 0.01f); - - mScrimController.onHeadsUpUnPinned(null /* row */); - mScrimController.finishAnimationsImmediately(); - - Assert.assertEquals("Heads-up scrim should have disappeared", 0f, - mHeadsUpScrim.getAlpha(), 0.01f); - } - - @Test - public void testHeadsUpScrimCounting() { - mScrimController.setPanelExpansion(0f); - mScrimController.onHeadsUpPinned(null /* row */); - mScrimController.onHeadsUpPinned(null /* row */); - mScrimController.onHeadsUpPinned(null /* row */); - mScrimController.finishAnimationsImmediately(); - - Assert.assertNotEquals("Heads-up scrim should be visible", 0f, - mHeadsUpScrim.getAlpha(), 0.01f); - - mScrimController.onHeadsUpUnPinned(null /* row */); - mScrimController.finishAnimationsImmediately(); - - Assert.assertEquals("Heads-up scrim should only disappear when counter reaches 0", 1f, - mHeadsUpScrim.getAlpha(), 0.01f); - - mScrimController.onHeadsUpUnPinned(null /* row */); - mScrimController.onHeadsUpUnPinned(null /* row */); - mScrimController.finishAnimationsImmediately(); - Assert.assertEquals("Heads-up scrim should have disappeared", 0f, - mHeadsUpScrim.getAlpha(), 0.01f); - } - - @Test - public void testNoHeadsUpScrimExpanded() { - mScrimController.setPanelExpansion(1f); - mScrimController.onHeadsUpPinned(null /* row */); - mScrimController.finishAnimationsImmediately(); - - Assert.assertEquals("Heads-up scrim should not be visible when shade is expanded", 0f, - mHeadsUpScrim.getAlpha(), 0.01f); - } - - @Test public void testScrimFocus() { mScrimController.transitionTo(ScrimState.AOD); Assert.assertFalse("Should not be focusable on AOD", mScrimBehind.isFocusable()); @@ -540,16 +488,23 @@ public class ScrimControllerTest extends SysuiTestCase { private FakeHandler mHandler; private boolean mAnimationCancelled; + boolean mOnPreDrawCalled; SynchronousScrimController(LightBarController lightBarController, - ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim, + ScrimView scrimBehind, ScrimView scrimInFront, Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters, AlarmManager alarmManager) { - super(lightBarController, scrimBehind, scrimInFront, headsUpScrim, + super(lightBarController, scrimBehind, scrimInFront, scrimVisibleListener, dozeParameters, alarmManager); mHandler = new FakeHandler(Looper.myLooper()); } + @Override + public boolean onPreDraw() { + mOnPreDrawCalled = true; + return super.onPreDraw(); + } + void finishAnimationsImmediately() { boolean[] animationFinished = {false}; setOnAnimationFinished(()-> animationFinished[0] = true); @@ -562,7 +517,6 @@ public class ScrimControllerTest extends SysuiTestCase { // Force finish all animations. endAnimation(mScrimBehind, TAG_KEY_ANIM); endAnimation(mScrimInFront, TAG_KEY_ANIM); - endAnimation(mHeadsUpScrim, TAG_KEY_ANIM); if (!animationFinished[0]) { throw new IllegalStateException("Animation never finished"); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java new file mode 100644 index 000000000000..1d2c01dbd564 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2018 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.stack; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import android.support.test.annotation.UiThreadTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.View; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.ActivatableNotificationView; +import com.android.systemui.statusbar.ExpandableNotificationRow; +import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.StatusBar; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashSet; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class NotificationRoundnessManagerTest extends SysuiTestCase { + + private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager(); + private HashSet<View> mAnimatedChildren = new HashSet<>(); + private Runnable mRoundnessCallback = mock(Runnable.class); + private ExpandableNotificationRow mFirst; + private ExpandableNotificationRow mSecond; + + @Before + public void setUp() throws Exception { + NotificationTestHelper testHelper = new NotificationTestHelper(getContext()); + mFirst = testHelper.createRow(); + mSecond = testHelper.createRow(); + mRoundnessManager.setOnRoundingChangedCallback(mRoundnessCallback); + mRoundnessManager.setAnimatedChildren(mAnimatedChildren); + mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mFirst); + mRoundnessManager.setExpanded(1.0f, 1.0f); + } + + @Test + public void testCallbackCalledWhenSecondChanged() { + mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mSecond); + verify(mRoundnessCallback, atLeast(1)).run(); + } + + @Test + public void testCallbackCalledWhenFirstChanged() { + mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mFirst); + verify(mRoundnessCallback, atLeast(1)).run(); + } + + @Test + public void testRoundnessSetOnLast() { + mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mSecond); + Assert.assertEquals(1.0f, mSecond.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(0.0f, mSecond.getCurrentTopRoundness(), 0.0f); + } + + @Test + public void testRoundnessSetOnNew() { + mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, null); + Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f); + } + + @Test + public void testCompleteReplacement() { + mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond); + Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f); + } + + @Test + public void testNotCalledWhenRemoved() { + mFirst.setRemoved(); + mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond); + Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f); + } + + @Test + public void testRoundedWhenPinnedAndCollapsed() { + mFirst.setPinned(true); + mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */); + mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond); + Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f); + } + + @Test + public void testRoundedWhenGoingAwayAndCollapsed() { + mFirst.setHeadsUpAnimatingAway(true); + mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */); + mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond); + Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f); + } + + @Test + public void testRoundedNormalRoundingWhenExpanded() { + mFirst.setHeadsUpAnimatingAway(true); + mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.0f /* appearFraction */); + mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond); + Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f); + } + + @Test + public void testTrackingHeadsUpRoundedIfPushingUp() { + mRoundnessManager.setExpanded(1.0f /* expandedHeight */, -0.5f /* appearFraction */); + mRoundnessManager.setTrackingHeadsUp(mFirst); + mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond); + Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f); + } + + @Test + public void testTrackingHeadsUpNotRoundedIfPushingDown() { + mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.5f /* appearFraction */); + mRoundnessManager.setTrackingHeadsUp(mFirst); + mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond); + Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f); + Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f); + } +} diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index d6f6c6cf1fc3..4cac70721128 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -215,13 +215,6 @@ public class BackupManagerService implements BackupManagerServiceInterface { // Timeout interval for deciding that a bind or clear-data has taken too long private static final long TIMEOUT_INTERVAL = 10 * 1000; - // Timeout intervals for agent backup & restore operations - public static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000; - public static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000; - public static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000; - public static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000; - public static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000; - // User confirmation timeout for a full backup/restore operation. It's this long in // order to give them time to enter the backup password. private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000; @@ -232,6 +225,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours private BackupManagerConstants mConstants; + private BackupAgentTimeoutParameters mAgentTimeoutParameters; private Context mContext; private PackageManager mPackageManager; private IPackageManager mPackageManagerBinder; @@ -315,6 +309,10 @@ public class BackupManagerService implements BackupManagerServiceInterface { return mConstants; } + public BackupAgentTimeoutParameters getAgentTimeoutParameters() { + return mAgentTimeoutParameters; + } + public Context getContext() { return mContext; } @@ -856,6 +854,10 @@ public class BackupManagerService implements BackupManagerServiceInterface { // require frequent starting and stopping. mConstants.start(); + mAgentTimeoutParameters = new + BackupAgentTimeoutParameters(mBackupHandler, mContext.getContentResolver()); + mAgentTimeoutParameters.start(); + // Set up the various sorts of package tracking we do mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule"); initPackageTracking(); @@ -3407,7 +3409,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { } mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport); mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, - TIMEOUT_RESTORE_INTERVAL); + mAgentTimeoutParameters.getRestoreAgentTimeoutMillis()); } return mActiveRestoreSession; } diff --git a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java index 7b021c64eaf8..aabe7f611845 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java +++ b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java @@ -191,4 +191,7 @@ public interface BackupManagerServiceInterface { void dump(FileDescriptor fd, PrintWriter pw, String[] args); IBackupManager getBackupManagerBinder(); + + // Gets access to the backup/restore agent timeout parameters. + BackupAgentTimeoutParameters getAgentTimeoutParameters(); } diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java index 47558775d19e..f08c65597a4b 100644 --- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java +++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java @@ -4,8 +4,8 @@ import static android.os.ParcelFileDescriptor.MODE_CREATE; import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; import static android.os.ParcelFileDescriptor.MODE_TRUNCATE; + import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT; -import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL; import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; @@ -59,6 +59,7 @@ public class KeyValueAdbBackupEngine { private ParcelFileDescriptor mSavedState; private ParcelFileDescriptor mBackupData; private ParcelFileDescriptor mNewState; + private final BackupAgentTimeoutParameters mAgentTimeoutParameters; public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo, BackupManagerServiceInterface backupManagerService, PackageManager packageManager, @@ -81,6 +82,7 @@ public class KeyValueAdbBackupEngine { pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX); mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME); + mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters(); } public void backupOnePackage() throws IOException { @@ -148,8 +150,9 @@ public class KeyValueAdbBackupEngine { // Return true on backup success, false otherwise private boolean invokeAgentForAdbBackup(String packageName, IBackupAgent agent) { int token = mBackupManagerService.generateRandomIntegerToken(); + long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis(); try { - mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null, + mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null, OP_TYPE_BACKUP_WAIT); // Start backup and wait for BackupManagerService to get callback for success or timeout @@ -231,14 +234,14 @@ public class KeyValueAdbBackupEngine { } private void writeBackupData() throws IOException { - int token = mBackupManagerService.generateRandomIntegerToken(); + long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis(); ParcelFileDescriptor[] pipes = null; try { pipes = ParcelFileDescriptor.createPipe(); - mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null, + mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null, OP_TYPE_BACKUP_WAIT); // We will have to create a runnable that will read the manifest and backup data we diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java index 0582abac91d4..597da21c7204 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java +++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java @@ -25,9 +25,6 @@ import static com.android.server.backup.BackupManagerService.MORE_DEBUG; import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT; import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; import static com.android.server.backup.BackupManagerService.TAG; -import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL; -import static com.android.server.backup.BackupManagerService - .TIMEOUT_SHARED_BACKUP_INTERVAL; import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; @@ -45,8 +42,9 @@ import android.util.Slog; import android.util.StringBuilderPrinter; import com.android.server.AppWidgetBackupBridge; -import com.android.server.backup.BackupRestoreTask; +import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupManagerService; +import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.utils.FullBackupUtils; import java.io.BufferedOutputStream; @@ -75,6 +73,7 @@ public class FullBackupEngine { private final long mQuota; private final int mOpToken; private final int mTransportFlags; + private final BackupAgentTimeoutParameters mAgentTimeoutParameters; class FullBackupRunner implements Runnable { @@ -137,8 +136,8 @@ public class FullBackupEngine { final boolean isSharedStorage = mPackage.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE); final long timeout = isSharedStorage ? - TIMEOUT_SHARED_BACKUP_INTERVAL : - TIMEOUT_FULL_BACKUP_INTERVAL; + mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() : + mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis(); if (DEBUG) { Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName); @@ -180,6 +179,7 @@ public class FullBackupEngine { mQuota = quota; mOpToken = opToken; mTransportFlags = transportFlags; + mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters(); } public int preflightCheck() throws RemoteException { diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java index 40b6967a729a..d441cf6a78e1 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java +++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java @@ -19,7 +19,6 @@ package com.android.server.backup.fullbackup; import static com.android.server.backup.BackupManagerService.MORE_DEBUG; import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT; import static com.android.server.backup.BackupManagerService.TAG; -import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL; import android.app.backup.IBackupManager; import android.content.ComponentName; @@ -33,6 +32,7 @@ import android.os.UserHandle; import android.util.Slog; import com.android.internal.backup.IObbBackupService; +import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupManagerService; import com.android.server.backup.utils.FullBackupUtils; @@ -46,10 +46,12 @@ public class FullBackupObbConnection implements ServiceConnection { private BackupManagerService backupManagerService; volatile IObbBackupService mService; + private final BackupAgentTimeoutParameters mAgentTimeoutParameters; public FullBackupObbConnection(BackupManagerService backupManagerService) { this.backupManagerService = backupManagerService; mService = null; + mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters(); } public void establish() { @@ -75,8 +77,10 @@ public class FullBackupObbConnection implements ServiceConnection { try { pipes = ParcelFileDescriptor.createPipe(); int token = backupManagerService.generateRandomIntegerToken(); + long fullBackupAgentTimeoutMillis = + mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis(); backupManagerService.prepareOperationTimeout( - token, TIMEOUT_FULL_BACKUP_INTERVAL, null, OP_TYPE_BACKUP_WAIT); + token, fullBackupAgentTimeoutMillis, null, OP_TYPE_BACKUP_WAIT); mService.backupObbs(pkg.packageName, pipes[1], token, backupManagerService.getBackupManagerBinder()); FullBackupUtils.routeSocketDataToOutput(pipes[0], out); diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java index 2c2dd8528cb1..1ea3eb5fc2e8 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java +++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java @@ -22,7 +22,6 @@ import static com.android.server.backup.BackupManagerService.MORE_DEBUG; import static com.android.server.backup.BackupManagerService.OP_PENDING; import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP; import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT; -import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL; import android.annotation.Nullable; import android.app.IBackupAgent; @@ -44,6 +43,7 @@ import android.util.Slog; import com.android.internal.backup.IBackupTransport; import com.android.server.EventLogTags; +import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.FullBackupJob; import com.android.server.backup.BackupManagerService; @@ -146,6 +146,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba private volatile boolean mIsDoingBackup; private volatile boolean mCancelAll; private final int mCurrentOpToken; + private final BackupAgentTimeoutParameters mAgentTimeoutParameters; public PerformFullTransportBackupTask(BackupManagerService backupManagerService, TransportClient transportClient, @@ -167,6 +168,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba mUserInitiated = userInitiated; mCurrentOpToken = backupManagerService.generateRandomIntegerToken(); mBackupRunnerOpToken = backupManagerService.generateRandomIntegerToken(); + mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters(); if (backupManagerService.isBackupOperationInProgress()) { if (DEBUG) { @@ -698,9 +700,11 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba @Override public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) { int result; + long fullBackupAgentTimeoutMillis = + mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis(); try { backupManagerService.prepareOperationTimeout( - mCurrentOpToken, TIMEOUT_FULL_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT); + mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT); backupManagerService.addBackupTrace("preflighting"); if (MORE_DEBUG) { Slog.d(TAG, "Preflighting full payload of " + pkg.packageName); @@ -713,7 +717,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba // timeout had been produced. In case of a real backstop timeout, mResult // will still contain the value it was constructed with, AGENT_ERROR, which // intentionaly falls into the "just report failure" code. - mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); + mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS); long totalSize = mResult.get(); // If preflight timed out, mResult will contain error code as int. @@ -769,8 +773,10 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba @Override public long getExpectedSizeOrErrorCode() { + long fullBackupAgentTimeoutMillis = + mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis(); try { - mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); + mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS); return mResult.get(); } catch (InterruptedException e) { return BackupTransport.NO_MORE_DATA; @@ -863,8 +869,10 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba // If preflight succeeded, returns positive number - preflight size, // otherwise return negative error code. long getPreflightResultBlocking() { + long fullBackupAgentTimeoutMillis = + mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis(); try { - mPreflightLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); + mPreflightLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS); if (mIsCancelled) { return BackupManager.ERROR_BACKUP_CANCELLED; } @@ -879,8 +887,10 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } int getBackupResultBlocking() { + long fullBackupAgentTimeoutMillis = + mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis(); try { - mBackupLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); + mBackupLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS); if (mIsCancelled) { return BackupManager.ERROR_BACKUP_CANCELLED; } diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java index 136fada43b1f..58868627ee2b 100644 --- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java +++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java @@ -19,7 +19,6 @@ package com.android.server.backup.internal; import static com.android.server.backup.BackupManagerService.DEBUG; import static com.android.server.backup.BackupManagerService.MORE_DEBUG; import static com.android.server.backup.BackupManagerService.TAG; -import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL; import android.app.backup.RestoreSet; import android.content.Intent; @@ -34,6 +33,7 @@ import android.util.Slog; import com.android.internal.backup.IBackupTransport; import com.android.server.EventLogTags; +import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupManagerService; import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.DataChangedJournal; @@ -81,10 +81,12 @@ public class BackupHandler extends Handler { public static final int MSG_OP_COMPLETE = 21; private final BackupManagerService backupManagerService; + private final BackupAgentTimeoutParameters mAgentTimeoutParameters; public BackupHandler(BackupManagerService backupManagerService, Looper looper) { super(looper); this.backupManagerService = backupManagerService; + mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters(); } public void handleMessage(Message msg) { @@ -322,7 +324,8 @@ public class BackupHandler extends Handler { // Done: reset the session timeout clock removeMessages(MSG_RESTORE_SESSION_TIMEOUT); - sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, TIMEOUT_RESTORE_INTERVAL); + sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, + mAgentTimeoutParameters.getRestoreAgentTimeoutMillis()); params.listener.onFinished(callerLogString); } diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java index 11394e66a0f0..0313066656d8 100644 --- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java +++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java @@ -24,7 +24,6 @@ import static com.android.server.backup.BackupManagerService.OP_PENDING; import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP; import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT; import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL; -import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL; import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT; import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP; @@ -57,6 +56,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.backup.IBackupTransport; import com.android.server.AppWidgetBackupBridge; import com.android.server.EventLogTags; +import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.DataChangedJournal; import com.android.server.backup.KeyValueBackupJob; @@ -142,6 +142,7 @@ public class PerformBackupTask implements BackupRestoreTask { private boolean mFinished; private final boolean mUserInitiated; private final boolean mNonIncremental; + private final BackupAgentTimeoutParameters mAgentTimeoutParameters; private volatile boolean mCancelAll; @@ -162,6 +163,7 @@ public class PerformBackupTask implements BackupRestoreTask { mPendingFullBackups = pendingFullBackups; mUserInitiated = userInitiated; mNonIncremental = nonIncremental; + mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters(); mStateDir = new File(backupManagerService.getBaseStateDir(), dirName); mCurrentOpToken = backupManagerService.generateRandomIntegerToken(); @@ -711,8 +713,10 @@ public class PerformBackupTask implements BackupRestoreTask { // Initiate the target's backup pass backupManagerService.addBackupTrace("setting timeout"); + long kvBackupAgentTimeoutMillis = + mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis(); backupManagerService.prepareOperationTimeout( - mEphemeralOpToken, TIMEOUT_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT); + mEphemeralOpToken, kvBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT); backupManagerService.addBackupTrace("calling agent doBackup()"); agent.doBackup( diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java index e4f3a9d5cd0a..6175629d7451 100644 --- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java +++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java @@ -18,10 +18,10 @@ package com.android.server.backup.restore; import static com.android.server.backup.BackupManagerService.DEBUG; import static com.android.server.backup.BackupManagerService.MORE_DEBUG; -import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL; import android.util.Slog; +import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupManagerService; import com.android.server.backup.BackupRestoreTask; @@ -37,18 +37,22 @@ public class AdbRestoreFinishedLatch implements BackupRestoreTask { private BackupManagerService backupManagerService; final CountDownLatch mLatch; private final int mCurrentOpToken; + private final BackupAgentTimeoutParameters mAgentTimeoutParameters; public AdbRestoreFinishedLatch(BackupManagerService backupManagerService, int currentOpToken) { this.backupManagerService = backupManagerService; mLatch = new CountDownLatch(1); mCurrentOpToken = currentOpToken; + mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters(); } void await() { boolean latched = false; + long fullBackupAgentTimeoutMillis = + mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis(); try { - latched = mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS); + latched = mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Slog.w(TAG, "Interrupted!"); } diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index c1a1c1dc10e7..f168afed55ea 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -23,9 +23,6 @@ import static com.android.server.backup.BackupManagerService.MORE_DEBUG; import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT; import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; import static com.android.server.backup.BackupManagerService.TAG; -import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL; -import static com.android.server.backup.BackupManagerService - .TIMEOUT_SHARED_BACKUP_INTERVAL; import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT; import android.app.ApplicationThreadConstants; @@ -43,10 +40,11 @@ import android.os.RemoteException; import android.util.Slog; import com.android.server.LocalServices; +import com.android.server.backup.BackupAgentTimeoutParameters; +import com.android.server.backup.BackupManagerService; import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.FileMetadata; import com.android.server.backup.KeyValueAdbRestoreEngine; -import com.android.server.backup.BackupManagerService; import com.android.server.backup.fullbackup.FullBackupObbConnection; import com.android.server.backup.utils.BytesReadListener; import com.android.server.backup.utils.FullBackupRestoreObserverUtils; @@ -121,6 +119,8 @@ public class FullRestoreEngine extends RestoreEngine { final int mEphemeralOpToken; + private final BackupAgentTimeoutParameters mAgentTimeoutParameters; + public FullRestoreEngine(BackupManagerService backupManagerService, BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer, IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks, @@ -135,6 +135,7 @@ public class FullRestoreEngine extends RestoreEngine { mAllowObbs = allowObbs; mBuffer = new byte[32 * 1024]; mBytes = 0; + mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters(); } public IBackupAgent getAgent() { @@ -381,8 +382,8 @@ public class FullRestoreEngine extends RestoreEngine { long toCopy = info.size; final boolean isSharedStorage = pkg.equals(SHARED_BACKUP_AGENT_PACKAGE); final long timeout = isSharedStorage ? - TIMEOUT_SHARED_BACKUP_INTERVAL : - TIMEOUT_RESTORE_INTERVAL; + mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() : + mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(); try { mBackupManagerService.prepareOperationTimeout(token, timeout, diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java index dacde0b9af68..221637cae191 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java @@ -16,8 +16,6 @@ package com.android.server.backup.restore; -import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT; -import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK; import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC; import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION; import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME; @@ -28,8 +26,8 @@ import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAI import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE; import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; import static com.android.server.backup.BackupManagerService.TAG; -import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL; -import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL; +import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT; +import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK; import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT; import android.app.ApplicationThreadConstants; @@ -49,6 +47,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; +import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupManagerService; import com.android.server.backup.FileMetadata; import com.android.server.backup.KeyValueAdbRestoreEngine; @@ -101,6 +100,7 @@ public class PerformAdbRestoreTask implements Runnable { private byte[] mWidgetData = null; private long mBytes; + private final BackupAgentTimeoutParameters mAgentTimeoutParameters; // Runner that can be placed on a separate thread to do in-process invocation // of the "restore finished" API asynchronously. Used by adb restore. @@ -155,6 +155,7 @@ public class PerformAdbRestoreTask implements Runnable { mAgentPackage = null; mTargetApp = null; mObbConnection = new FullBackupObbConnection(backupManagerService); + mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters(); // Which packages we've already wiped data on. We prepopulate this // with a whitelist of packages known to be unclearable. @@ -643,9 +644,11 @@ public class PerformAdbRestoreTask implements Runnable { if (okay) { boolean agentSuccess = true; long toCopy = info.size; + long restoreAgentTimeoutMillis = + mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(); try { mBackupManagerService.prepareOperationTimeout( - token, TIMEOUT_RESTORE_INTERVAL, null, OP_TYPE_RESTORE_WAIT); + token, restoreAgentTimeoutMillis, null, OP_TYPE_RESTORE_WAIT); if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) { if (DEBUG) { @@ -820,10 +823,12 @@ public class PerformAdbRestoreTask implements Runnable { // In the adb restore case, we do restore-finished here if (doRestoreFinished) { final int token = mBackupManagerService.generateRandomIntegerToken(); + long fullBackupAgentTimeoutMillis = + mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis(); final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch( mBackupManagerService, token); mBackupManagerService.prepareOperationTimeout( - token, TIMEOUT_FULL_BACKUP_INTERVAL, latch, OP_TYPE_RESTORE_WAIT); + token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT); if (mTargetApp.processName.equals("system")) { if (MORE_DEBUG) { Slog.d(TAG, "system agent - restoreFinished on thread"); diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 4b467e5a0399..069e3b6c8247 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -23,9 +23,6 @@ import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAI import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL; import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE; import static com.android.server.backup.BackupManagerService.TAG; -import static com.android.server.backup.BackupManagerService - .TIMEOUT_RESTORE_FINISHED_INTERVAL; -import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL; import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP; import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT; import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT; @@ -59,6 +56,7 @@ import com.android.internal.backup.IBackupTransport; import com.android.server.AppWidgetBackupBridge; import com.android.server.EventLogTags; import com.android.server.LocalServices; +import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.BackupUtils; import com.android.server.backup.PackageManagerBackupAgent; @@ -160,6 +158,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { ParcelFileDescriptor mNewState; private final int mEphemeralOpToken; + private final BackupAgentTimeoutParameters mAgentTimeoutParameters; // This task can assume that the wakelock is properly held for it and doesn't have to worry // about releasing it. @@ -190,6 +189,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { mFinished = false; mDidLaunch = false; mListener = listener; + mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters(); if (targetPackage != null) { // Single package restore @@ -760,8 +760,9 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // Kick off the restore, checking for hung agents. The timeout or // the operationComplete() callback will schedule the next step, // so we do not do that here. + long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(); backupManagerService.prepareOperationTimeout( - mEphemeralOpToken, TIMEOUT_RESTORE_INTERVAL, this, OP_TYPE_RESTORE_WAIT); + mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT); mAgent.doRestore(mBackupData, appVersionCode, mNewState, mEphemeralOpToken, backupManagerService.getBackupManagerBinder()); } catch (Exception e) { @@ -813,9 +814,11 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { Slog.d(TAG, "restoreFinished packageName=" + mCurrentPackage.packageName); } try { + long restoreAgentFinishedTimeoutMillis = + mAgentTimeoutParameters.getRestoreAgentFinishedTimeoutMillis(); backupManagerService .prepareOperationTimeout(mEphemeralOpToken, - TIMEOUT_RESTORE_FINISHED_INTERVAL, this, + restoreAgentFinishedTimeoutMillis, this, OP_TYPE_RESTORE_WAIT); mAgent.doRestoreFinished(mEphemeralOpToken, backupManagerService.getBackupManagerBinder()); @@ -1109,9 +1112,10 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { } else { // We were invoked via an active restore session, not by the Package // Manager, so start up the session timeout again. + long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(); backupManagerService.getBackupHandler().sendEmptyMessageDelayed( MSG_RESTORE_SESSION_TIMEOUT, - TIMEOUT_RESTORE_INTERVAL); + restoreAgentTimeoutMillis); } // Kick off any work that may be needed regarding app widget restores diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 45a4dfb91bbf..f1f251f42524 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -36,6 +36,7 @@ import android.net.IpSecTransform; import android.net.IpSecTransformResponse; import android.net.IpSecTunnelInterfaceResponse; import android.net.IpSecUdpEncapResponse; +import android.net.LinkAddress; import android.net.Network; import android.net.NetworkUtils; import android.net.TrafficStats; @@ -618,10 +619,8 @@ public class IpSecService extends IIpSecService.Stub { spi, mConfig.getMarkValue(), mConfig.getMarkMask()); - } catch (ServiceSpecificException e) { - // FIXME: get the error code and throw is at an IOException from Errno Exception - } catch (RemoteException e) { - Log.e(TAG, "Failed to delete SA with ID: " + mResourceId); + } catch (RemoteException | ServiceSpecificException e) { + Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e); } getResourceTracker().give(); @@ -681,10 +680,8 @@ public class IpSecService extends IIpSecService.Stub { .getNetdInstance() .ipSecDeleteSecurityAssociation( mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0); - } catch (ServiceSpecificException e) { - // FIXME: get the error code and throw is at an IOException from Errno Exception - } catch (RemoteException e) { - Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId); + } catch (ServiceSpecificException | RemoteException e) { + Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e); } mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX; @@ -829,15 +826,13 @@ public class IpSecService extends IIpSecService.Stub { 0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff); } } - } catch (ServiceSpecificException e) { - // FIXME: get the error code and throw is at an IOException from Errno Exception - } catch (RemoteException e) { + } catch (ServiceSpecificException | RemoteException e) { Log.e( TAG, "Failed to delete VTI with interface name: " + mInterfaceName + " and id: " - + mResourceId); + + mResourceId, e); } getResourceTracker().give(); @@ -1319,7 +1314,9 @@ public class IpSecService extends IIpSecService.Stub { * from multiple local IP addresses over the same tunnel. */ @Override - public synchronized void addAddressToTunnelInterface(int tunnelResourceId, String localAddr) { + public synchronized void addAddressToTunnelInterface( + int tunnelResourceId, LinkAddress localAddr) { + enforceNetworkStackPermission(); UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); // Get tunnelInterface record; if no such interface is found, will throw @@ -1327,8 +1324,21 @@ public class IpSecService extends IIpSecService.Stub { TunnelInterfaceRecord tunnelInterfaceInfo = userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId); - // TODO: Add calls to netd: - // Add address to TunnelInterface + try { + // We can assume general validity of the IP address, since we get them as a + // LinkAddress, which does some validation. + mSrvConfig + .getNetdInstance() + .interfaceAddAddress( + tunnelInterfaceInfo.mInterfaceName, + localAddr.getAddress().getHostAddress(), + localAddr.getPrefixLength()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (ServiceSpecificException e) { + // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw. + throw new IllegalArgumentException(e); + } } /** @@ -1337,7 +1347,8 @@ public class IpSecService extends IIpSecService.Stub { */ @Override public synchronized void removeAddressFromTunnelInterface( - int tunnelResourceId, String localAddr) { + int tunnelResourceId, LinkAddress localAddr) { + enforceNetworkStackPermission(); UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); // Get tunnelInterface record; if no such interface is found, will throw @@ -1345,8 +1356,21 @@ public class IpSecService extends IIpSecService.Stub { TunnelInterfaceRecord tunnelInterfaceInfo = userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId); - // TODO: Add calls to netd: - // Remove address from TunnelInterface + try { + // We can assume general validity of the IP address, since we get them as a + // LinkAddress, which does some validation. + mSrvConfig + .getNetdInstance() + .interfaceDelAddress( + tunnelInterfaceInfo.mInterfaceName, + localAddr.getAddress().getHostAddress(), + localAddr.getPrefixLength()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (ServiceSpecificException e) { + // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw. + throw new IllegalArgumentException(e); + } } /** @@ -1467,6 +1491,13 @@ public class IpSecService extends IIpSecService.Stub { IpSecAlgorithm crypt = c.getEncryption(); IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption(); + String cryptName; + if (crypt == null) { + cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : ""; + } else { + cryptName = crypt.getName(); + } + mSrvConfig .getNetdInstance() .ipSecAddSecurityAssociation( @@ -1481,7 +1512,7 @@ public class IpSecService extends IIpSecService.Stub { (auth != null) ? auth.getName() : "", (auth != null) ? auth.getKey() : new byte[] {}, (auth != null) ? auth.getTruncationLengthBits() : 0, - (crypt != null) ? crypt.getName() : "", + cryptName, (crypt != null) ? crypt.getKey() : new byte[] {}, (crypt != null) ? crypt.getTruncationLengthBits() : 0, (authCrypt != null) ? authCrypt.getName() : "", diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 305c1d0eb7b6..fb8f7491e726 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -187,6 +187,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMI import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME; import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY; import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; @@ -755,21 +756,6 @@ public class ActivityManagerService extends IActivityManager.Stub private final RecentTasks mRecentTasks; /** - * For addAppTask: cached of the last activity component that was added. - */ - ComponentName mLastAddedTaskComponent; - - /** - * For addAppTask: cached of the last activity uid that was added. - */ - int mLastAddedTaskUid; - - /** - * For addAppTask: cached of the last ActivityInfo that was added. - */ - ActivityInfo mLastAddedTaskActivity; - - /** * The package name of the DeviceOwner. This package is not permitted to have its data cleared. */ String mDeviceOwnerName; @@ -10611,27 +10597,24 @@ public class ActivityManagerService extends IActivityManager.Stub intent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS); } } - if (!comp.equals(mLastAddedTaskComponent) || callingUid != mLastAddedTaskUid) { - mLastAddedTaskActivity = null; - } - ActivityInfo ainfo = mLastAddedTaskActivity; - if (ainfo == null) { - ainfo = mLastAddedTaskActivity = AppGlobals.getPackageManager().getActivityInfo( - comp, 0, UserHandle.getUserId(callingUid)); - if (ainfo.applicationInfo.uid != callingUid) { - throw new SecurityException( - "Can't add task for another application: target uid=" - + ainfo.applicationInfo.uid + ", calling uid=" + callingUid); - } + final ActivityInfo ainfo = AppGlobals.getPackageManager().getActivityInfo(comp, 0, + UserHandle.getUserId(callingUid)); + if (ainfo.applicationInfo.uid != callingUid) { + throw new SecurityException( + "Can't add task for another application: target uid=" + + ainfo.applicationInfo.uid + ", calling uid=" + callingUid); } - TaskRecord task = TaskRecord.create(this, - mStackSupervisor.getNextTaskIdForUserLocked(r.userId), - ainfo, intent, description); + final ActivityStack stack = r.getStack(); + final TaskRecord task = stack.createTaskRecord( + mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent, + null /* voiceSession */, null /* voiceInteractor */, !ON_TOP); if (!mRecentTasks.addToBottom(task)) { + // The app has too many tasks already and we can't add any more + stack.removeTask(task, "addAppTask", REMOVE_TASK_MODE_DESTROYING); return INVALID_TASK_ID; } - r.getStack().addTask(task, !ON_TOP, "addAppTask"); + task.lastTaskDescription.copyFrom(description); // TODO: Send the thumbnail to WM to store it. @@ -21770,6 +21753,54 @@ public class ActivityManagerService extends IActivityManager.Stub // INSTRUMENTATION // ========================================================= + private static String[] HIDDENAPI_EXEMPT_PACKAGES = { + "com.android.bluetooth.tests", + "com.android.managedprovisioning.tests", + "com.android.frameworks.coretests", + "com.android.frameworks.coretests.binderproxycountingtestapp", + "com.android.frameworks.coretests.binderproxycountingtestservice", + "com.android.frameworks.tests.net", + "com.android.frameworks.tests.uiservices", + "com.android.coretests.apps.bstatstestapp", + "com.android.servicestests.apps.conntestapp", + "com.android.frameworks.servicestests", + "com.android.frameworks.utiltests", + "com.android.mtp.tests", + "android.mtp", + "com.android.documentsui.tests", + "com.android.shell.tests", + "com.android.systemui.tests", + "com.android.testables", + "android.net.wifi.test", + "com.android.server.wifi.test", + "com.android.frameworks.telephonytests", + "com.android.providers.contacts.tests", + "com.android.providers.contacts.tests2", + "com.android.settings.tests.unit", + "com.android.server.telecom.tests", + "com.android.vcard.tests", + "com.android.providers.blockednumber.tests", + "android.settings.functional", + "com.android.notification.functional", + "com.android.frameworks.dexloggertest", + "com.android.server.usb", + "com.android.providers.downloads.tests", + "com.android.emergency.tests.unit", + "com.android.providers.calendar.tests", + "com.android.settingslib", + "com.android.rs.test", + "com.android.printspooler.outofprocess.tests", + "com.android.cellbroadcastreceiver.tests.unit", + "com.android.providers.telephony.tests", + "com.android.carrierconfig.tests", + "com.android.phone.tests", + "com.android.service.ims.presence.tests", + "com.android.providers.setting.test", + "com.android.frameworks.locationtests", + "com.android.frameworks.coretests.privacy", + "com.android.settings.ui", + }; + public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection, @@ -21852,6 +21883,14 @@ public class ActivityManagerService extends IActivityManager.Stub } boolean disableHiddenApiChecks = (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0; + + // TODO: Temporary whitelist of packages which need to be exempt from hidden API + // checks. Remove this as soon as the testing infrastructure allows to set + // the flag in AndroidTest.xml. + if (Arrays.asList(HIDDENAPI_EXEMPT_PACKAGES).contains(ai.packageName)) { + disableHiddenApiChecks = true; + } + ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, abiOverride); app.instr = activeInstr; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 19fffbb73149..e4695b64a070 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -643,7 +643,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D void setWindowManager(WindowManagerService wm) { synchronized (mService) { mWindowManager = wm; - mKeyguardController.setWindowManager(wm); + getKeyguardController().setWindowManager(wm); mDisplayManager = (DisplayManager)mService.mContext.getSystemService(Context.DISPLAY_SERVICE); @@ -1312,7 +1312,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D r.setProcess(app); - if (mKeyguardController.isKeyguardLocked()) { + if (getKeyguardController().isKeyguardLocked()) { r.notifyUnknownVisibilityLaunched(); } @@ -3377,7 +3377,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } else { stack.awakeFromSleepingLocked(); if (isFocusedStack(stack) - && !mKeyguardController.isKeyguardShowing(display.mDisplayId)) { + && !getKeyguardController().isKeyguardShowing(display.mDisplayId)) { // If the keyguard is unlocked - resume immediately. // It is possible that the display will not be awake at the time we // process the keyguard going away, which can happen before the sleep token @@ -3501,7 +3501,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges, boolean preserveWindows) { - mKeyguardController.beginActivityVisibilityUpdate(); + getKeyguardController().beginActivityVisibilityUpdate(); try { // First the front stacks. In case any are not fullscreen and are in front of home. for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { @@ -3512,7 +3512,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } } finally { - mKeyguardController.endActivityVisibilityUpdate(); + getKeyguardController().endActivityVisibilityUpdate(); } } @@ -3799,7 +3799,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D pw.print(prefix); pw.print("isHomeRecentsComponent="); pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser)); - mKeyguardController.dump(pw, prefix); + getKeyguardController().dump(pw, prefix); mService.mLockTaskController.dump(pw, prefix); } @@ -3810,7 +3810,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx); activityDisplay.writeToProto(proto, DISPLAYS); } - mKeyguardController.writeToProto(proto, KEYGUARD_CONTROLLER); + getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER); if (mFocusedStack != null) { proto.write(FOCUSED_STACK_ID, mFocusedStack.mStackId); ActivityRecord focusedActivity = getResumedActivityLocked(); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index bd53eac7fee7..a30a944b44f1 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1834,7 +1834,8 @@ class ActivityStarter { mNoAnimation, mOptions, mStartActivity.appTimeTracker, "bringToFrontInsteadOfAdjacentLaunch"); } - mMovedToFront = true; + mMovedToFront = launchStack != launchStack.getDisplay() + .getTopStackInWindowingMode(launchStack.getWindowingMode()); } else if (launchStack.mDisplayId != mTargetStack.mDisplayId) { // Target and computed stacks are on different displays and we've // found a matching task - move the existing instance to that display and @@ -2393,6 +2394,11 @@ class ActivityStarter { return this; } + @VisibleForTesting + Intent getIntent() { + return mRequest.intent; + } + ActivityStarter setReason(String reason) { mRequest.reason = reason; return this; diff --git a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java index 690d985ef096..5bf5020c99ee 100644 --- a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java @@ -43,7 +43,7 @@ class GlobalSettingsToPropertiesMapper { {Settings.Global.FPS_DEVISOR, ThreadedRenderer.DEBUG_FPS_DIVISOR}, {Settings.Global.DISPLAY_PANEL_LPM, "sys.display_panel_lpm"}, {Settings.Global.SYS_UIDCPUPOWER, "sys.uidcpupower"}, - {Settings.Global.SYS_TRACED, "persist.traced.enable"}, + {Settings.Global.SYS_TRACED, "sys.traced.enable_override"}, }; diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index cb53521c5e7f..5e1afeb571de 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -282,6 +282,10 @@ class AutomaticBrightnessController { return mBrightnessMapper.isDefaultConfig(); } + public BrightnessConfiguration getDefaultConfig() { + return mBrightnessMapper.getDefaultConfig(); + } + private boolean setDisplayPolicy(int policy) { if (mDisplayPolicy == policy) { return false; diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index c0d259992a46..4313d1724214 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -206,6 +206,8 @@ public abstract class BrightnessMappingStrategy { /** @return true if the current brightness config is the default one */ public abstract boolean isDefaultConfig(); + public abstract BrightnessConfiguration getDefaultConfig(); + public abstract void dump(PrintWriter pw); private static float normalizeAbsoluteBrightness(int brightness) { @@ -406,6 +408,9 @@ public abstract class BrightnessMappingStrategy { } @Override + public BrightnessConfiguration getDefaultConfig() { return null; } + + @Override public void dump(PrintWriter pw) { pw.println("SimpleMappingStrategy"); pw.println(" mSpline=" + mSpline); @@ -533,6 +538,9 @@ public abstract class BrightnessMappingStrategy { } @Override + public BrightnessConfiguration getDefaultConfig() { return mDefaultConfig; } + + @Override public void dump(PrintWriter pw) { pw.println("PhysicalMappingStrategy"); pw.println(" mConfig=" + mConfig); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 9861ea735570..c4b2b5ed3ccf 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1878,6 +1878,48 @@ public final class DisplayManagerService extends SystemService { } @Override // Binder call + public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS, + "Permission required to read the display's brightness configuration"); + if (userId != UserHandle.getCallingUserId()) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS, + "Permission required to read the display brightness" + + " configuration of another user"); + } + final long token = Binder.clearCallingIdentity(); + try { + final int userSerial = getUserManager().getUserSerialNumber(userId); + synchronized (mSyncRoot) { + BrightnessConfiguration config = + mPersistentDataStore.getBrightnessConfiguration(userSerial); + if (config == null) { + config = mDisplayPowerController.getDefaultBrightnessConfiguration(); + } + return config; + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public BrightnessConfiguration getDefaultBrightnessConfiguration() { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS, + "Permission required to read the display's default brightness configuration"); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + return mDisplayPowerController.getDefaultBrightnessConfiguration(); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call public void setTemporaryBrightness(int brightness) { mContext.enforceCallingOrSelfPermission( Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS, diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index fa39ce4f8503..ff8b88bfa79c 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -567,6 +567,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + public BrightnessConfiguration getDefaultBrightnessConfiguration() { + return mAutomaticBrightnessController.getDefaultConfig(); + } + private void sendUpdatePowerState() { synchronized (mLock) { sendUpdatePowerStateLocked(); diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/fingerprint/ClientMonitor.java index 3eae157c53aa..4100a9a5f71c 100644 --- a/services/core/java/com/android/server/fingerprint/ClientMonitor.java +++ b/services/core/java/com/android/server/fingerprint/ClientMonitor.java @@ -21,6 +21,7 @@ import android.content.Context; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IFingerprintServiceReceiver; +import android.media.AudioAttributes; import android.os.IBinder; import android.os.RemoteException; import android.os.VibrationEffect; @@ -39,6 +40,11 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient { protected static final int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. See errno.h. protected static final boolean DEBUG = FingerprintService.DEBUG; private static final long[] DEFAULT_SUCCESS_VIBRATION_PATTERN = new long[] {0, 30}; + private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES = + new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) + .build(); private final Context mContext; private final long mHalDeviceId; private final int mTargetUserId; @@ -223,14 +229,14 @@ public abstract class ClientMonitor implements IBinder.DeathRecipient { public final void vibrateSuccess() { Vibrator vibrator = mContext.getSystemService(Vibrator.class); if (vibrator != null) { - vibrator.vibrate(mSuccessVibrationEffect); + vibrator.vibrate(mSuccessVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES); } } public final void vibrateError() { Vibrator vibrator = mContext.getSystemService(Vibrator.class); if (vibrator != null) { - vibrator.vibrate(mErrorVibrationEffect); + vibrator.vibrate(mErrorVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES); } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 729ac0c68662..e02feecbc288 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -1721,7 +1721,9 @@ public class GnssLocationProvider implements LocationProviderInterface { if (mItarSpeedLimitExceeded) { Log.i(TAG, "Hal reported a speed in excess of ITAR limit." + " GPS/GNSS Navigation output blocked."); - mGnssMetrics.logReceivedLocationStatus(false); + if (mStarted) { + mGnssMetrics.logReceivedLocationStatus(false); + } return; // No output of location allowed } @@ -1738,14 +1740,16 @@ public class GnssLocationProvider implements LocationProviderInterface { Log.e(TAG, "RemoteException calling reportLocation"); } - mGnssMetrics.logReceivedLocationStatus(hasLatLong); - if (hasLatLong) { - if (location.hasAccuracy()) { - mGnssMetrics.logPositionAccuracyMeters(location.getAccuracy()); - } - if (mTimeToFirstFix > 0) { - int timeBetweenFixes = (int) (SystemClock.elapsedRealtime() - mLastFixTime); - mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes); + if (mStarted) { + mGnssMetrics.logReceivedLocationStatus(hasLatLong); + if (hasLatLong) { + if (location.hasAccuracy()) { + mGnssMetrics.logPositionAccuracyMeters(location.getAccuracy()); + } + if (mTimeToFirstFix > 0) { + int timeBetweenFixes = (int) (SystemClock.elapsedRealtime() - mLastFixTime); + mGnssMetrics.logMissedReports(mFixInterval, timeBetweenFixes); + } } } @@ -1754,7 +1758,9 @@ public class GnssLocationProvider implements LocationProviderInterface { if (mTimeToFirstFix == 0 && hasLatLong) { mTimeToFirstFix = (int) (mLastFixTime - mFixRequestTime); if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix); - mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix); + if (mStarted) { + mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix); + } // notify status listeners mListenerHelper.onFirstFix(mTimeToFirstFix); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 74ebf3e44616..a87a11330ab6 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2051,11 +2051,13 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public byte[] startRecoverySessionWithCertPath(@NonNull String sessionId, - @NonNull RecoveryCertPath verifierCertPath, @NonNull byte[] vaultParams, - @NonNull byte[] vaultChallenge, @NonNull List<KeyChainProtectionParams> secrets) + @NonNull String rootCertificateAlias, @NonNull RecoveryCertPath verifierCertPath, + @NonNull byte[] vaultParams, @NonNull byte[] vaultChallenge, + @NonNull List<KeyChainProtectionParams> secrets) throws RemoteException { return mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( - sessionId, verifierCertPath, vaultParams, vaultChallenge, secrets); + sessionId, rootCertificateAlias, verifierCertPath, vaultParams, vaultChallenge, + secrets); } public void closeSession(@NonNull String sessionId) throws RemoteException { @@ -2063,11 +2065,19 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override + public Map<String, String> recoverKeyChainSnapshot( + @NonNull String sessionId, + @NonNull byte[] recoveryKeyBlob, + @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException { + return mRecoverableKeyStoreManager.recoverKeyChainSnapshot( + sessionId, recoveryKeyBlob, applicationKeys); + } + + @Override public Map<String, byte[]> recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException { - return mRecoverableKeyStoreManager.recoverKeys( - sessionId, recoveryKeyBlob, applicationKeys); + return mRecoverableKeyStoreManager.recoverKeys(sessionId, recoveryKeyBlob, applicationKeys); } @Override diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index 20f3403c56c5..5b10add4ecfa 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -31,6 +31,7 @@ import android.annotation.Nullable; import android.app.PendingIntent; import android.content.Context; import android.os.Binder; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; @@ -38,8 +39,10 @@ import android.security.keystore.recovery.KeyChainProtectionParams; import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.RecoveryCertPath; import android.security.keystore.recovery.RecoveryController; +import android.security.keystore.recovery.TrustedRootCertificates; import android.security.keystore.recovery.WrappedApplicationKey; import android.security.KeyStore; +import android.util.ArrayMap; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -50,7 +53,6 @@ import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKe import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException; import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException; import com.android.server.locksettings.recoverablekeystore.certificate.CertXml; -import com.android.server.locksettings.recoverablekeystore.certificate.TrustedRootCert; import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb; import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage; import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage; @@ -64,6 +66,7 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertPath; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; @@ -200,15 +203,19 @@ public class RecoverableKeyStoreManager { } Log.i(TAG, "Updating the certificate with the new serial number " + newSerial); + // Randomly choose and validate an endpoint certificate from the list CertPath certPath; + X509Certificate rootCert = getRootCertificate(rootCertificateAlias); try { Log.d(TAG, "Getting and validating a random endpoint certificate"); - certPath = certXml.getRandomEndpointCert(TrustedRootCert.TRUSTED_ROOT_CERT); + certPath = certXml.getRandomEndpointCert(rootCert); } catch (CertValidationException e) { Log.e(TAG, "Invalid endpoint cert", e); throw new ServiceSpecificException( ERROR_INVALID_CERTIFICATE, "Failed to validate certificate."); } + + // Save the chosen and validated certificate into database try { Log.d(TAG, "Saving the randomly chosen endpoint certificate to database"); if (mDatabase.setRecoveryServiceCertPath(userId, uid, certPath) > 0) { @@ -253,8 +260,9 @@ public class RecoverableKeyStoreManager { ERROR_BAD_CERTIFICATE_FORMAT, "Failed to parse the sig file."); } + X509Certificate rootCert = getRootCertificate(rootCertificateAlias); try { - sigXml.verifyFileSignature(TrustedRootCert.TRUSTED_ROOT_CERT, recoveryServiceCertFile); + sigXml.verifyFileSignature(rootCert, recoveryServiceCertFile); } catch (CertValidationException e) { Log.d(TAG, "The signature over the cert file is invalid." + " Cert: " + HexDump.toHexString(recoveryServiceCertFile) @@ -479,6 +487,7 @@ public class RecoverableKeyStoreManager { */ public @NonNull byte[] startRecoverySessionWithCertPath( @NonNull String sessionId, + @NonNull String rootCertificateAlias, @NonNull RecoveryCertPath verifierCertPath, @NonNull byte[] vaultParams, @NonNull byte[] vaultChallenge, @@ -495,11 +504,10 @@ public class RecoverableKeyStoreManager { } try { - CertUtils.validateCertPath(TrustedRootCert.TRUSTED_ROOT_CERT, certPath); + CertUtils.validateCertPath(getRootCertificate(rootCertificateAlias), certPath); } catch (CertValidationException e) { Log.e(TAG, "Failed to validate the given cert path", e); - // TODO: Change this to ERROR_INVALID_CERTIFICATE once ag/3666620 is submitted - throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, e.getMessage()); + throw new ServiceSpecificException(ERROR_INVALID_CERTIFICATE, e.getMessage()); } byte[] verifierPublicKey = certPath.getCertificates().get(0).getPublicKey().getEncoded(); @@ -550,6 +558,78 @@ public class RecoverableKeyStoreManager { } /** + * Invoked by a recovery agent after a successful recovery claim is sent to the remote vault + * service. + * + * @param sessionId The session ID used to generate the claim. See + * {@link #startRecoverySession(String, byte[], byte[], byte[], List)}. + * @param encryptedRecoveryKey The encrypted recovery key blob returned by the remote vault + * service. + * @param applicationKeys The encrypted key blobs returned by the remote vault service. These + * were wrapped with the recovery key. + * @throws RemoteException if an error occurred recovering the keys. + */ + public Map<String, String> recoverKeyChainSnapshot( + @NonNull String sessionId, + @NonNull byte[] encryptedRecoveryKey, + @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException { + checkRecoverKeyStorePermission(); + int userId = UserHandle.getCallingUserId(); + int uid = Binder.getCallingUid(); + RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId); + if (sessionEntry == null) { + throw new ServiceSpecificException(ERROR_SESSION_EXPIRED, + String.format(Locale.US, + "Application uid=%d does not have pending session '%s'", + uid, + sessionId)); + } + + try { + byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey); + Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys); + return importKeyMaterials(userId, uid, keysByAlias); + } catch (KeyStoreException e) { + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); + } finally { + sessionEntry.destroy(); + mRecoverySessionStorage.remove(uid); + } + } + + /** + * Imports the key materials, returning a map from alias to grant alias for the calling user. + * + * @param userId The calling user ID. + * @param uid The calling uid. + * @param keysByAlias The key materials, keyed by alias. + * @throws KeyStoreException if an error occurs importing the key or getting the grant. + */ + private Map<String, String> importKeyMaterials( + int userId, int uid, Map<String, byte[]> keysByAlias) throws KeyStoreException { + ArrayMap<String, String> grantAliasesByAlias = new ArrayMap<>(keysByAlias.size()); + for (String alias : keysByAlias.keySet()) { + mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keysByAlias.get(alias)); + String grantAlias = getAlias(userId, uid, alias); + Log.i(TAG, String.format(Locale.US, "Import %s -> %s", alias, grantAlias)); + grantAliasesByAlias.put(alias, grantAlias); + } + return grantAliasesByAlias; + } + + /** + * Returns an alias for the key. + * + * @param userId The user ID of the calling process. + * @param uid The uid of the calling process. + * @param alias The alias of the key. + * @return The alias in the calling process's keystore. + */ + private String getAlias(int userId, int uid, String alias) { + return mApplicationKeyStorage.getGrantAlias(userId, uid, alias); + } + + /** * Deprecated * Generates a key named {@code alias} in the recoverable store for the calling uid. Then * returns the raw key material. @@ -626,7 +706,7 @@ public class RecoverableKeyStoreManager { byte[] secretKey = mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias); mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey); - return mApplicationKeyStorage.getGrantAlias(userId, uid, alias); + return getAlias(userId, uid, alias); } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } @@ -677,7 +757,7 @@ public class RecoverableKeyStoreManager { // Import the key to Android KeyStore and get grant mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes); - return mApplicationKeyStorage.getGrantAlias(userId, uid, alias); + return getAlias(userId, uid, alias); } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } @@ -691,8 +771,7 @@ public class RecoverableKeyStoreManager { public String getKey(@NonNull String alias) throws RemoteException { int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); - String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias); - return grantAlias; + return getAlias(userId, uid, alias); } private byte[] decryptRecoveryKey( @@ -837,6 +916,21 @@ public class RecoverableKeyStoreManager { } } + private X509Certificate getRootCertificate(String rootCertificateAlias) throws RemoteException { + if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) { + // Use the default Google Key Vault Service CA certificate if the alias is not provided + rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS; + } + + X509Certificate rootCertificate = + TrustedRootCertificates.getRootCertificate(rootCertificateAlias); + if (rootCertificate == null) { + throw new ServiceSpecificException( + ERROR_INVALID_CERTIFICATE, "The provided root certificate alias is invalid"); + } + return rootCertificate; + } + private void checkRecoverKeyStorePermission() { mContext.enforceCallingOrSelfPermission( Manifest.permission.RECOVER_KEYSTORE, diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java deleted file mode 100644 index 7195d628e4bd..000000000000 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.locksettings.recoverablekeystore.certificate; - -import java.security.cert.X509Certificate; - -/** - * Holds the X509 certificate of the trusted root CA cert for the recoverable key store service. - * - * TODO: Read the certificate from a PEM file directly and remove this class. - */ -public final class TrustedRootCert { - - private static final String TRUSTED_ROOT_CERT_BASE64 = "" - + "MIIFJjCCAw6gAwIBAgIJAIobXsJlzhNdMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV" - + "BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAyMDIxOTM5MTRaFw0zODAx" - + "MjgxOTM5MTRaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCAiIw" - + "DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2OT5i40/H7LINg/lq/0G0hR65P" - + "Q4Mud3OnuVt6UIYV2T18+v6qW1yJd5FcnND/ZKPau4aUAYklqJuSVjOXQD0BjgS2" - + "98Xa4dSn8Ci1rUR+5tdmrxqbYUdT2ZvJIUMMR6fRoqi+LlAbKECrV+zYQTyLU68w" - + "V66hQpAButjJKiZzkXjmKLfJ5IWrNEn17XM988rk6qAQn/BYCCQGf3rQuJeksGmA" - + "N1lJOwNYxmWUyouVwqwZthNEWqTuEyBFMkAT+99PXW7oVDc7oU5cevuihxQWNTYq" - + "viGB8cck6RW3cmqrDSaJF/E+N0cXFKyYC7FDcggt6k3UrxNKTuySdDEa8+2RTQqU" - + "Y9npxBlQE+x9Ig56OI1BG3bSBsGdPgjpyHadZeh2tgk+oqlGsSsum24YxaxuSysT" - + "Qfcu/XhyfUXavfmGrBOXerTzIl5oBh/F5aHTV85M2tYEG0qsPPvSpZAWtdJ/2rca" - + "OxvhwOL+leZKr8McjXVR00lBsRuKXX4nTUMwya09CO3QHFPFZtZvqjy2HaMOnVLQ" - + "I6b6dHEfmsHybzVOe3yPEoFQSU9UhUdmi71kwwoanPD3j9fJHmXTx4PzYYBRf1ZE" - + "o+uPgMPk7CDKQFZLjnR40z1uzu3O8aZ3AKZzP+j7T4XQKJLQLmllKtPgLgNdJyib" - + "2Glg7QhXH/jBTL6hAgMBAAGjYzBhMB0GA1UdDgQWBBSbZfrqOYH54EJpkdKMZjMc" - + "z/Hp+DAfBgNVHSMEGDAWgBSbZfrqOYH54EJpkdKMZjMcz/Hp+DAPBgNVHRMBAf8E" - + "BTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQ0FAAOCAgEAKh9nm/vW" - + "glMWp3vcCwWwJW286ecREDlI+CjGh5h+f2N4QRrXd/tKE3qQJWCqGx8sFfIUjmI7" - + "KYdsC2gyQ2cA2zl0w7pB2QkuqE6zVbnh1D17Hwl19IMyAakFaM9ad4/EoH7oQmqX" - + "nF/f5QXGZw4kf1HcgKgoCHWXjqR8MqHOcXR8n6WFqxjzJf1jxzi6Yo2dZ7PJbnE6" - + "+kHIJuiCpiHL75v5g1HM41gT3ddFFSrn88ThNPWItT5Z8WpFjryVzank2Yt02LLl" - + "WqZg9IC375QULc5B58NMnaiVJIDJQ8zoNgj1yaxqtUMnJX570lotO2OXe4ec9aCQ" - + "DIJ84YLM/qStFdeZ9416E80dchskbDG04GuVJKlzWjxAQNMRFhyaPUSBTLLg+kwP" - + "t9+AMmc+A7xjtFQLZ9fBYHOBsndJOmeSQeYeckl+z/1WQf7DdwXn/yijon7mxz4z" - + "cCczfKwTJTwBh3wR5SQr2vQm7qaXM87qxF8PCAZrdZaw5I80QwkgTj0WTZ2/GdSw" - + "d3o5SyzzBAjpwtG+4bO/BD9h9wlTsHpT6yWOZs4OYAKU5ykQrncI8OyavMggArh3" - + "/oM58v0orUWINtIc2hBlka36PhATYQiLf+AiWKnwhCaaHExoYKfQlMtXBodNvOK8" - + "xqx69x05q/qbHKEcTHrsss630vxrp1niXvA="; - - /** - * The X509 certificate of the trusted root CA cert for the recoverable key store service. - * - * TODO: Change it to the production certificate root CA before the final launch. - */ - public static final X509Certificate TRUSTED_ROOT_CERT; - - static { - try { - TRUSTED_ROOT_CERT = CertUtils.decodeCert( - CertUtils.decodeBase64(TRUSTED_ROOT_CERT_BASE64)); - } catch (CertParsingException e) { - // Should not happen - throw new RuntimeException(e); - } - } -} diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java index 3d9762337312..84ddbf778c70 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java @@ -24,6 +24,7 @@ import android.security.Credentials; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; import android.security.KeyStore; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.server.locksettings.recoverablekeystore.KeyStoreProxy; @@ -31,6 +32,8 @@ import com.android.server.locksettings.recoverablekeystore.KeyStoreProxyImpl; import java.security.KeyStore.SecretKeyEntry; import java.security.KeyStoreException; +import java.util.Locale; + import javax.crypto.spec.SecretKeySpec; /** @@ -40,11 +43,13 @@ import javax.crypto.spec.SecretKeySpec; * revealing key material */ public class ApplicationKeyStorage { + private static final String TAG = "RecoverableAppKeyStore"; + private static final String APPLICATION_KEY_ALIAS_PREFIX = "com.android.server.locksettings.recoverablekeystore/application/"; - KeyStoreProxy mKeyStore; - KeyStore mKeystoreService; + private final KeyStoreProxy mKeyStore; + private final KeyStore mKeystoreService; public static ApplicationKeyStorage getInstance(KeyStore keystoreService) throws KeyStoreException { @@ -65,12 +70,15 @@ public class ApplicationKeyStorage { public @Nullable String getGrantAlias(int userId, int uid, String alias) { // Aliases used by {@link KeyStore} are different than used by public API. // {@code USER_PRIVATE_KEY} prefix is used secret keys. + Log.i(TAG, String.format(Locale.US, "Get %d/%d/%s", userId, uid, alias)); String keystoreAlias = Credentials.USER_PRIVATE_KEY + getInternalAlias(userId, uid, alias); return mKeystoreService.grant(keystoreAlias, uid); } public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey) throws KeyStoreException { + Log.i(TAG, String.format(Locale.US, "Set %d/%d/%s: %d bytes of key material", + userId, uid, alias, secretKey.length)); try { mKeyStore.setEntry( getInternalAlias(userId, uid, alias), @@ -87,6 +95,7 @@ public class ApplicationKeyStorage { } public void deleteEntry(int userId, int uid, String alias) { + Log.i(TAG, String.format(Locale.US, "Del %d/%d/%s", userId, uid, alias)); try { mKeyStore.deleteEntry(getInternalAlias(userId, uid, alias)); } catch (KeyStoreException e) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index cd19eb81aeed..27eeb93fa3f5 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -959,6 +959,8 @@ public class NotificationManagerService extends SystemService { boolean queryRemove = false; boolean packageChanged = false; boolean cancelNotifications = true; + boolean hideNotifications = false; + boolean unhideNotifications = false; int reason = REASON_PACKAGE_CHANGED; if (action.equals(Intent.ACTION_PACKAGE_ADDED) @@ -967,7 +969,8 @@ public class NotificationManagerService extends SystemService { || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED)) || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE) - || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) { + || action.equals(Intent.ACTION_PACKAGES_SUSPENDED) + || action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED)) { int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL); String pkgList[] = null; @@ -980,7 +983,12 @@ public class NotificationManagerService extends SystemService { uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST); } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); - reason = REASON_PACKAGE_SUSPENDED; + cancelNotifications = false; + hideNotifications = true; + } else if (action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED)) { + pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + cancelNotifications = false; + unhideNotifications = true; } else if (queryRestart) { pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)}; @@ -1022,9 +1030,15 @@ public class NotificationManagerService extends SystemService { if (cancelNotifications) { cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0, !queryRestart, changeUserId, reason, null); + } else if (hideNotifications) { + hideNotificationsForPackages(pkgList); + } else if (unhideNotifications) { + unhideNotificationsForPackages(pkgList); } + } } + mListeners.onPackagesChanged(removingPackage, pkgList, uidList); mAssistants.onPackagesChanged(removingPackage, pkgList, uidList); mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList); @@ -1479,6 +1493,7 @@ public class NotificationManagerService extends SystemService { IntentFilter suspendedPkgFilter = new IntentFilter(); suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); + suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, suspendedPkgFilter, null, null); @@ -2486,6 +2501,7 @@ public class NotificationManagerService extends SystemService { try { synchronized (mNotificationLock) { final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + if (keys != null) { final int N = keys.length; for (int i = 0; i < N; i++) { @@ -4271,6 +4287,14 @@ public class NotificationManagerService extends SystemService { } } + @GuardedBy("mNotificationLock") + private boolean isPackageSuspendedLocked(NotificationRecord r) { + final String pkg = r.sbn.getPackageName(); + final int callingUid = r.sbn.getUid(); + + return isPackageSuspendedForUser(pkg, callingUid); + } + protected class PostNotificationRunnable implements Runnable { private final String key; @@ -4295,6 +4319,8 @@ public class NotificationManagerService extends SystemService { Slog.i(TAG, "Cannot find enqueued record for key: " + key); return; } + + r.setHidden(isPackageSuspendedLocked(r)); NotificationRecord old = mNotificationsByKey.get(key); final StatusBarNotification n = r.sbn; final Notification notification = n.getNotification(); @@ -4347,7 +4373,7 @@ public class NotificationManagerService extends SystemService { } else { Slog.e(TAG, "Not posting notification without small icon: " + notification); if (old != null && !old.isCanceled) { - mListeners.notifyRemovedLocked(n, + mListeners.notifyRemovedLocked(r, NotificationListenerService.REASON_ERROR, null); mHandler.post(new Runnable() { @Override @@ -4363,7 +4389,9 @@ public class NotificationManagerService extends SystemService { + n.getPackageName()); } - buzzBeepBlinkLocked(r); + if (!r.isHidden()) { + buzzBeepBlinkLocked(r); + } maybeRecordInterruptionLocked(r); } finally { int N = mEnqueuedNotifications.size(); @@ -5022,7 +5050,7 @@ public class NotificationManagerService extends SystemService { private void handleSendRankingUpdate() { synchronized (mNotificationLock) { - mListeners.notifyRankingUpdateLocked(); + mListeners.notifyRankingUpdateLocked(null); } } @@ -5207,7 +5235,7 @@ public class NotificationManagerService extends SystemService { if (reason != REASON_SNOOZED) { r.isCanceled = true; } - mListeners.notifyRemovedLocked(r.sbn, reason, r.getStats()); + mListeners.notifyRemovedLocked(r, reason, r.getStats()); mHandler.post(new Runnable() { @Override public void run() { @@ -5316,6 +5344,7 @@ public class NotificationManagerService extends SystemService { final String pkg, final String tag, final int id, final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, final int userId, final int reason, final ManagedServiceInfo listener) { + // In enqueueNotificationInternal notifications are added by scheduling the // work on the worker handler. Hence, we also schedule the cancel on this // handler to avoid a scenario where an add notification call followed by a @@ -5707,6 +5736,42 @@ public class NotificationManagerService extends SystemService { return -1; } + @VisibleForTesting + protected void hideNotificationsForPackages(String[] pkgs) { + synchronized (mNotificationLock) { + List<String> pkgList = Arrays.asList(pkgs); + List<NotificationRecord> changedNotifications = new ArrayList<>(); + int numNotifications = mNotificationList.size(); + for (int i = 0; i < numNotifications; i++) { + NotificationRecord rec = mNotificationList.get(i); + if (pkgList.contains(rec.sbn.getPackageName())) { + rec.setHidden(true); + changedNotifications.add(rec); + } + } + + mListeners.notifyHiddenLocked(changedNotifications); + } + } + + @VisibleForTesting + protected void unhideNotificationsForPackages(String[] pkgs) { + synchronized (mNotificationLock) { + List<String> pkgList = Arrays.asList(pkgs); + List<NotificationRecord> changedNotifications = new ArrayList<>(); + int numNotifications = mNotificationList.size(); + for (int i = 0; i < numNotifications; i++) { + NotificationRecord rec = mNotificationList.get(i); + if (pkgList.contains(rec.sbn.getPackageName())) { + rec.setHidden(false); + changedNotifications.add(rec); + } + } + + mListeners.notifyUnhiddenLocked(changedNotifications); + } + } + private void updateNotificationPulse() { synchronized (mNotificationLock) { updateLightsLocked(); @@ -5826,6 +5891,7 @@ public class NotificationManagerService extends SystemService { Bundle snoozeCriteria = new Bundle(); Bundle showBadge = new Bundle(); Bundle userSentiment = new Bundle(); + Bundle hidden = new Bundle(); for (int i = 0; i < N; i++) { NotificationRecord record = mNotificationList.get(i); if (!isVisibleToListener(record.sbn, info)) { @@ -5852,6 +5918,7 @@ public class NotificationManagerService extends SystemService { snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria()); showBadge.putBoolean(key, record.canShowBadge()); userSentiment.putInt(key, record.getUserSentiment()); + hidden.putBoolean(key, record.isHidden()); } final int M = keys.size(); String[] keysAr = keys.toArray(new String[M]); @@ -5862,7 +5929,7 @@ public class NotificationManagerService extends SystemService { } return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides, suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys, - channels, overridePeople, snoozeCriteria, showBadge, userSentiment); + channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden); } boolean hasCompanionDevice(ManagedServiceInfo info) { @@ -6151,6 +6218,16 @@ public class NotificationManagerService extends SystemService { */ @GuardedBy("mNotificationLock") public void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn) { + notifyPostedLocked(r, oldSbn, true); + } + + /** + * @param notifyAllListeners notifies all listeners if true, else only notifies listeners + * targetting <= O_MR1 + */ + @GuardedBy("mNotificationLock") + private void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn, + boolean notifyAllListeners) { // Lazily initialized snapshots of the notification. StatusBarNotification sbn = r.sbn; TrimCache trimCache = new TrimCache(sbn); @@ -6164,6 +6241,21 @@ public class NotificationManagerService extends SystemService { if (!oldSbnVisible && !sbnVisible) { continue; } + + // If the notification is hidden, don't notifyPosted listeners targeting < P. + // Instead, those listeners will receive notifyPosted when the notification is + // unhidden. + if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) { + continue; + } + + // If we shouldn't notify all listeners, this means the hidden state of + // a notification was changed. Don't notifyPosted listeners targeting >= P. + // Instead, those listeners will receive notifyRankingUpdate. + if (!notifyAllListeners && info.targetSdkVersion >= Build.VERSION_CODES.P) { + continue; + } + final NotificationRankingUpdate update = makeRankingUpdateLocked(info); // This notification became invisible -> remove the old one. @@ -6218,8 +6310,9 @@ public class NotificationManagerService extends SystemService { * asynchronously notify all listeners about a removed notification */ @GuardedBy("mNotificationLock") - public void notifyRemovedLocked(StatusBarNotification sbn, int reason, + public void notifyRemovedLocked(NotificationRecord r, int reason, NotificationStats notificationStats) { + final StatusBarNotification sbn = r.sbn; // make a copy in case changes are made to the underlying Notification object // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the // notification @@ -6228,6 +6321,21 @@ public class NotificationManagerService extends SystemService { if (!isVisibleToListener(sbn, info)) { continue; } + + // don't notifyRemoved for listeners targeting < P + // if not for reason package suspended + if (r.isHidden() && reason != REASON_PACKAGE_SUSPENDED + && info.targetSdkVersion < Build.VERSION_CODES.P) { + continue; + } + + // don't notifyRemoved for listeners targeting >= P + // if the reason is package suspended + if (reason == REASON_PACKAGE_SUSPENDED + && info.targetSdkVersion >= Build.VERSION_CODES.P) { + continue; + } + // Only assistants can get stats final NotificationStats stats = mAssistants.isServiceTokenValidLocked(info.service) ? notificationStats : null; @@ -6242,21 +6350,44 @@ public class NotificationManagerService extends SystemService { } /** - * asynchronously notify all listeners about a reordering of notifications + * Asynchronously notify all listeners about a reordering of notifications + * unless changedHiddenNotifications is populated. + * If changedHiddenNotifications is populated, there was a change in the hidden state + * of the notifications. In this case, we only send updates to listeners that + * target >= P. */ @GuardedBy("mNotificationLock") - public void notifyRankingUpdateLocked() { + public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) { + boolean isHiddenRankingUpdate = changedHiddenNotifications != null + && changedHiddenNotifications.size() > 0; + for (final ManagedServiceInfo serviceInfo : getServices()) { if (!serviceInfo.isEnabledForCurrentProfiles()) { continue; } - final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo); - mHandler.post(new Runnable() { - @Override - public void run() { - notifyRankingUpdate(serviceInfo, update); + + boolean notifyThisListener = false; + if (isHiddenRankingUpdate && serviceInfo.targetSdkVersion >= + Build.VERSION_CODES.P) { + for (NotificationRecord rec : changedHiddenNotifications) { + if (isVisibleToListener(rec.sbn, serviceInfo)) { + notifyThisListener = true; + break; + } } - }); + } + + if (notifyThisListener || !isHiddenRankingUpdate) { + final NotificationRankingUpdate update = makeRankingUpdateLocked( + serviceInfo); + + mHandler.post(new Runnable() { + @Override + public void run() { + notifyRankingUpdate(serviceInfo, update); + } + }); + } } } @@ -6275,6 +6406,52 @@ public class NotificationManagerService extends SystemService { } } + /** + * asynchronously notify relevant listeners their notification is hidden + * NotificationListenerServices that target P+: + * NotificationListenerService#notifyRankingUpdateLocked() + * NotificationListenerServices that target <= P: + * NotificationListenerService#notifyRemovedLocked() with REASON_PACKAGE_SUSPENDED. + */ + @GuardedBy("mNotificationLock") + public void notifyHiddenLocked(List<NotificationRecord> changedNotifications) { + if (changedNotifications == null || changedNotifications.size() == 0) { + return; + } + + notifyRankingUpdateLocked(changedNotifications); + + // for listeners that target < P, notifyRemoveLocked + int numChangedNotifications = changedNotifications.size(); + for (int i = 0; i < numChangedNotifications; i++) { + NotificationRecord rec = changedNotifications.get(i); + mListeners.notifyRemovedLocked(rec, REASON_PACKAGE_SUSPENDED, rec.getStats()); + } + } + + /** + * asynchronously notify relevant listeners their notification is unhidden + * NotificationListenerServices that target P+: + * NotificationListenerService#notifyRankingUpdateLocked() + * NotificationListenerServices that target <= P: + * NotificationListeners#notifyPostedLocked() + */ + @GuardedBy("mNotificationLock") + public void notifyUnhiddenLocked(List<NotificationRecord> changedNotifications) { + if (changedNotifications == null || changedNotifications.size() == 0) { + return; + } + + notifyRankingUpdateLocked(changedNotifications); + + // for listeners that target < P, notifyPostedLocked + int numChangedNotifications = changedNotifications.size(); + for (int i = 0; i < numChangedNotifications; i++) { + NotificationRecord rec = changedNotifications.get(i); + mListeners.notifyPostedLocked(rec, rec.sbn, false); + } + } + public void notifyInterruptionFilterChanged(final int interruptionFilter) { for (final ManagedServiceInfo serviceInfo : getServices()) { if (!serviceInfo.isEnabledForCurrentProfiles()) { @@ -6503,6 +6680,22 @@ public class NotificationManagerService extends SystemService { } } + @VisibleForTesting + protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) { + // only use for testing: mimic receive broadcast that package is (un)suspended + // but does not actually (un)suspend the package + final Bundle extras = new Bundle(); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, + new String[]{pkg}); + + final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED + : Intent.ACTION_PACKAGES_UNSUSPENDED; + final Intent intent = new Intent(action); + intent.putExtras(extras); + + mPackageIntentReceiver.onReceive(getContext(), intent); + } + /** * Wrapper for a StatusBarNotification object that allows transfer across a oneway * binder without sending large amounts of data over a oneway transaction. @@ -6531,7 +6724,9 @@ public class NotificationManagerService extends SystemService { + "allow_assistant COMPONENT\n" + "remove_assistant COMPONENT\n" + "allow_dnd PACKAGE\n" - + "disallow_dnd PACKAGE"; + + "disallow_dnd PACKAGE\n" + + "suspend_package PACKAGE\n" + + "unsuspend_package PACKAGE"; @Override public int onCommand(String cmd) { @@ -6600,7 +6795,16 @@ public class NotificationManagerService extends SystemService { getBinderService().setNotificationAssistantAccessGranted(cn, false); } break; - + case "suspend_package": { + // only use for testing + simulatePackageSuspendBroadcast(true, getNextArgRequired()); + } + break; + case "unsuspend_package": { + // only use for testing + simulatePackageSuspendBroadcast(false, getNextArgRequired()); + } + break; default: return handleDefaultCommands(cmd); } diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index f1908bff2d94..c88708551662 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -103,6 +103,9 @@ public final class NotificationRecord { // is this notification currently being intercepted by Zen Mode? private boolean mIntercept; + // is this notification hidden since the app pkg is suspended? + private boolean mHidden; + // The timestamp used for ranking. private long mRankingTimeMs; @@ -353,6 +356,7 @@ public final class NotificationRecord { mPackagePriority = previous.mPackagePriority; mPackageVisibility = previous.mPackageVisibility; mIntercept = previous.mIntercept; + mHidden = previous.mHidden; mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs()); mCreationTimeMs = previous.mCreationTimeMs; mVisibleSinceMs = previous.mVisibleSinceMs; @@ -498,6 +502,7 @@ public final class NotificationRecord { + NotificationListenerService.Ranking.importanceToString(mImportance)); pw.println(prefix + "mImportanceExplanation=" + mImportanceExplanation); pw.println(prefix + "mIntercept=" + mIntercept); + pw.println(prefix + "mHidden==" + mHidden); pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey); pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs); pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs); @@ -702,6 +707,15 @@ public final class NotificationRecord { return mIntercept; } + public void setHidden(boolean hidden) { + mHidden = hidden; + } + + public boolean isHidden() { + return mHidden; + } + + public void setSuppressedVisualEffects(int effects) { mSuppressedVisualEffects = effects; } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 6cd60e6ae1f6..61b1eb4d4d83 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -999,7 +999,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.POLICY_CONTROL), false, this, UserHandle.USER_ALL); - resolver.registerContentObserver(Settings.Global.getUriFor( + resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED), false, this, UserHandle.USER_ALL); updateSettings(); diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 055e6ea3f4a4..adbf7ed866ee 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -69,7 +69,6 @@ import android.service.dreams.DreamManagerInternal; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.util.KeyValueListParser; -import android.util.MathUtils; import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; @@ -408,7 +407,7 @@ public final class PowerManagerService extends SystemService private boolean mDreamsActivateOnDockSetting; // True if doze should not be started until after the screen off transition. - private boolean mDozeAfterScreenOffConfig; + private boolean mDozeAfterScreenOff; // The minimum screen off timeout, in milliseconds. private long mMinimumScreenOffTimeoutConfig; @@ -896,7 +895,7 @@ public final class PowerManagerService extends SystemService com.android.internal.R.integer.config_dreamsBatteryLevelMinimumWhenNotPowered); mDreamsBatteryLevelDrainCutoffConfig = resources.getInteger( com.android.internal.R.integer.config_dreamsBatteryLevelDrainCutoff); - mDozeAfterScreenOffConfig = resources.getBoolean( + mDozeAfterScreenOff = resources.getBoolean( com.android.internal.R.bool.config_dozeAfterScreenOff); mMinimumScreenOffTimeoutConfig = resources.getInteger( com.android.internal.R.integer.config_minimumScreenOffTimeout); @@ -1724,7 +1723,7 @@ public final class PowerManagerService extends SystemService // Update wireless dock detection state. final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update( - mIsPowered, mPlugType, mBatteryLevel); + mIsPowered, mPlugType); // Treat plugging and unplugging the devices as a user activity. // Users find it disconcerting when they plug or unplug the device @@ -2507,7 +2506,7 @@ public final class PowerManagerService extends SystemService if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) { return DisplayPowerRequest.POLICY_DOZE; } - if (mDozeAfterScreenOffConfig) { + if (mDozeAfterScreenOff) { return DisplayPowerRequest.POLICY_OFF; } // Fall through and preserve the current screen policy if not configured to @@ -3094,6 +3093,12 @@ public final class PowerManagerService extends SystemService light.setFlashing(color, Light.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); } + private void setDozeAfterScreenOffInternal(boolean on) { + synchronized (mLock) { + mDozeAfterScreenOff = on; + } + } + private void boostScreenBrightnessInternal(long eventTime, int uid) { synchronized (mLock) { if (!mSystemReady || mWakefulness == WAKEFULNESS_ASLEEP @@ -3372,7 +3377,7 @@ public final class PowerManagerService extends SystemService pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting); pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting); pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting); - pw.println(" mDozeAfterScreenOffConfig=" + mDozeAfterScreenOffConfig); + pw.println(" mDozeAfterScreenOff=" + mDozeAfterScreenOff); pw.println(" mLowPowerModeSetting=" + mLowPowerModeSetting); pw.println(" mAutoLowPowerModeConfigured=" + mAutoLowPowerModeConfigured); pw.println(" mAutoLowPowerModeSnoozing=" + mAutoLowPowerModeSnoozing); @@ -3656,7 +3661,7 @@ public final class PowerManagerService extends SystemService mDreamsActivateOnDockSetting); proto.write( PowerServiceSettingsAndConfigurationDumpProto.IS_DOZE_AFTER_SCREEN_OFF_CONFIG, - mDozeAfterScreenOffConfig); + mDozeAfterScreenOff); proto.write( PowerServiceSettingsAndConfigurationDumpProto.IS_LOW_POWER_MODE_SETTING, mLowPowerModeSetting); @@ -4603,6 +4608,19 @@ public final class PowerManagerService extends SystemService } @Override // Binder call + public void setDozeAfterScreenOff(boolean on) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, null); + + final long ident = Binder.clearCallingIdentity(); + try { + setDozeAfterScreenOffInternal(on); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call public void boostScreenBrightness(long eventTime) { if (eventTime > SystemClock.uptimeMillis()) { throw new IllegalArgumentException("event time must not be in the future"); diff --git a/services/core/java/com/android/server/power/WirelessChargerDetector.java b/services/core/java/com/android/server/power/WirelessChargerDetector.java index 54487e39d82f..18e5ce465df6 100644 --- a/services/core/java/com/android/server/power/WirelessChargerDetector.java +++ b/services/core/java/com/android/server/power/WirelessChargerDetector.java @@ -84,10 +84,6 @@ final class WirelessChargerDetector { // The minimum number of samples that must be collected. private static final int MIN_SAMPLES = 3; - // Upper bound on the battery charge percentage in order to consider turning - // the screen on when the device starts charging wirelessly. - private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95; - // To detect movement, we compute the angle between the gravity vector // at rest and the current gravity vector. This field specifies the // cosine of the maximum angle variance that we tolerate while at rest. @@ -214,11 +210,10 @@ final class WirelessChargerDetector { * * @param isPowered True if the device is powered. * @param plugType The current plug type. - * @param batteryLevel The current battery level. * @return True if the device is determined to have just been docked on a wireless * charger, after suppressing spurious docking or undocking signals. */ - public boolean update(boolean isPowered, int plugType, int batteryLevel) { + public boolean update(boolean isPowered, int plugType) { synchronized (mLock) { final boolean wasPoweredWirelessly = mPoweredWirelessly; @@ -249,13 +244,9 @@ final class WirelessChargerDetector { } // Report that the device has been docked only if the device just started - // receiving power wirelessly, has a high enough battery level that we - // can be assured that charging was not delayed due to the battery previously - // having been full, and the device is not known to already be at rest + // receiving power wirelessly and the device is not known to already be at rest // on the wireless charger from earlier. - return mPoweredWirelessly && !wasPoweredWirelessly - && batteryLevel < WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT - && !mAtRest; + return mPoweredWirelessly && !wasPoweredWirelessly && !mAtRest; } } diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index a7dfd35acf1b..0b7d9d02f1cb 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -16,6 +16,8 @@ package com.android.server.slice; +import static android.app.usage.UsageEvents.Event.SLICE_PINNED; +import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV; import static android.content.ContentProvider.getUriWithoutUserId; import static android.content.ContentProvider.getUserIdFromUri; import static android.content.ContentProvider.maybeAddUserId; @@ -31,6 +33,7 @@ import android.app.IActivityManager; import android.app.slice.ISliceManager; import android.app.slice.SliceManager; import android.app.slice.SliceSpec; +import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -95,6 +98,7 @@ public class SliceManagerService extends ISliceManager.Stub { private final AtomicFile mSliceAccessFile; @GuardedBy("mAccessList") private final SliceFullAccessList mAccessList; + private final UsageStatsManagerInternal mAppUsageStats; public SliceManagerService(Context context) { this(context, createHandler().getLooper()); @@ -112,6 +116,7 @@ public class SliceManagerService extends ISliceManager.Stub { final File systemDir = new File(Environment.getDataDirectory(), "system"); mSliceAccessFile = new AtomicFile(new File(systemDir, "slice_access.xml")); mAccessList = new SliceFullAccessList(mContext); + mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); synchronized (mSliceAccessFile) { if (!mSliceAccessFile.exists()) return; @@ -166,8 +171,19 @@ public class SliceManagerService extends ISliceManager.Stub { public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token) throws RemoteException { verifyCaller(pkg); enforceAccess(pkg, uri); - uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier()); + int user = Binder.getCallingUserHandle().getIdentifier(); + uri = maybeAddUserId(uri, user); getOrCreatePinnedSlice(uri, pkg).pin(pkg, specs, token); + + Uri finalUri = uri; + mHandler.post(() -> { + String slicePkg = getProviderPkg(finalUri, user); + if (slicePkg != null && !Objects.equals(pkg, slicePkg)) { + mAppUsageStats.reportEvent(slicePkg, user, + isAssistant(pkg, user) || isDefaultHomeApp(pkg, user) + ? SLICE_PINNED_PRIV : SLICE_PINNED); + } + }); } @Override @@ -352,36 +368,43 @@ public class SliceManagerService extends ISliceManager.Stub { if (getContext().checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PERMISSION_GRANTED) { // Last fallback (if the calling app owns the authority, then it can have access). - long ident = Binder.clearCallingIdentity(); + if (!Objects.equals(getProviderPkg(uri, user), pkg)) { + return PERMISSION_DENIED; + } + } + } + return PERMISSION_GRANTED; + } + + private String getProviderPkg(Uri uri, int user) { + long ident = Binder.clearCallingIdentity(); + try { + IBinder token = new Binder(); + IActivityManager activityManager = ActivityManager.getService(); + ContentProviderHolder holder = null; + String providerName = getUriWithoutUserId(uri).getAuthority(); + try { try { - IBinder token = new Binder(); - IActivityManager activityManager = ActivityManager.getService(); - ContentProviderHolder holder = null; - String providerName = getUriWithoutUserId(uri).getAuthority(); - try { - try { - holder = activityManager.getContentProviderExternal( - providerName, getUserIdFromUri(uri, user), token); - if (holder == null || holder.info == null - || !Objects.equals(holder.info.packageName, pkg)) { - return PERMISSION_DENIED; - } - } finally { - if (holder != null && holder.provider != null) { - activityManager.removeContentProviderExternal(providerName, token); - } - } - } catch (RemoteException e) { - // Can't happen. - e.rethrowAsRuntimeException(); + holder = activityManager.getContentProviderExternal( + providerName, getUserIdFromUri(uri, user), token); + if (holder != null && holder.info != null) { + return holder.info.packageName; + } else { + return null; } } finally { - // I know, the double finally seems ugly, but seems safest for the identity. - Binder.restoreCallingIdentity(ident); + if (holder != null && holder.provider != null) { + activityManager.removeContentProviderExternal(providerName, token); + } } + } catch (RemoteException e) { + // Can't happen. + throw e.rethrowAsRuntimeException(); } + } finally { + // I know, the double finally seems ugly, but seems safest for the identity. + Binder.restoreCallingIdentity(ident); } - return PERMISSION_GRANTED; } private void enforceCrossUser(String pkg, Uri uri) { diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java index 60535122929e..6df7092cf811 100644 --- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java +++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java @@ -16,7 +16,9 @@ package com.android.server.textclassifier; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -25,12 +27,14 @@ import android.content.pm.PackageManager; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.util.Slog; -import android.service.textclassifier.ITextClassifierService; +import android.os.UserHandle; import android.service.textclassifier.ITextClassificationCallback; +import android.service.textclassifier.ITextClassifierService; import android.service.textclassifier.ITextLinksCallback; import android.service.textclassifier.ITextSelectionCallback; import android.service.textclassifier.TextClassifierService; +import android.util.Slog; +import android.util.SparseArray; import android.view.textclassifier.SelectionEvent; import android.view.textclassifier.TextClassification; import android.view.textclassifier.TextClassifier; @@ -38,12 +42,13 @@ import android.view.textclassifier.TextLinks; import android.view.textclassifier.TextSelection; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.FunctionalUtils; +import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.internal.util.Preconditions; import com.android.server.SystemService; -import java.util.LinkedList; +import java.util.ArrayDeque; import java.util.Queue; -import java.util.concurrent.Callable; /** * A manager for TextClassifier services. @@ -73,58 +78,44 @@ public final class TextClassificationManagerService extends ITextClassifierServi Slog.e(LOG_TAG, "Could not start the TextClassificationManagerService.", t); } } - } - private final Context mContext; - private final Intent mServiceIntent; - private final ServiceConnection mConnection; - private final Object mLock; - @GuardedBy("mLock") - private final Queue<PendingRequest> mPendingRequests; + @Override + public void onStartUser(int userId) { + processAnyPendingWork(userId); + } - @GuardedBy("mLock") - private ITextClassifierService mService; - @GuardedBy("mLock") - private boolean mBinding; + @Override + public void onUnlockUser(int userId) { + // Rebind if we failed earlier due to locked encrypted user + processAnyPendingWork(userId); + } - private TextClassificationManagerService(Context context) { - mContext = Preconditions.checkNotNull(context); - mServiceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE) - .setComponent(TextClassifierService.getServiceComponentName(mContext)); - mConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - synchronized (mLock) { - mService = ITextClassifierService.Stub.asInterface(service); - setBindingLocked(false); - handlePendingRequestsLocked(); - } + private void processAnyPendingWork(int userId) { + synchronized (mManagerService.mLock) { + mManagerService.getUserStateLocked(userId).bindIfHasPendingRequestsLocked(); } + } - @Override - public void onServiceDisconnected(ComponentName name) { - cleanupService(); + @Override + public void onStopUser(int userId) { + synchronized (mManagerService.mLock) { + UserState userState = mManagerService.peekUserStateLocked(userId); + if (userState != null) { + userState.mConnection.cleanupService(); + mManagerService.mUserStates.remove(userId); + } } + } - @Override - public void onBindingDied(ComponentName name) { - cleanupService(); - } + } - @Override - public void onNullBinding(ComponentName name) { - cleanupService(); - } + private final Context mContext; + private final Object mLock; + @GuardedBy("mLock") + final SparseArray<UserState> mUserStates = new SparseArray<>(); - private void cleanupService() { - synchronized (mLock) { - mService = null; - setBindingLocked(false); - handlePendingRequestsLocked(); - } - } - }; - mPendingRequests = new LinkedList<>(); + private TextClassificationManagerService(Context context) { + mContext = Preconditions.checkNotNull(context); mLock = new Object(); } @@ -133,30 +124,20 @@ public final class TextClassificationManagerService extends ITextClassifierServi CharSequence text, int selectionStartIndex, int selectionEndIndex, TextSelection.Options options, ITextSelectionCallback callback) throws RemoteException { - // TODO(b/72481438): All remote calls need to take userId. validateInput(text, selectionStartIndex, selectionEndIndex, callback); - if (!bind()) { - callback.onFailure(); - return; - } - synchronized (mLock) { - if (isBoundLocked()) { - mService.onSuggestSelection( + UserState userState = getCallingUserStateLocked(); + if (!userState.bindLocked()) { + callback.onFailure(); + } else if (userState.isBoundLocked()) { + userState.mService.onSuggestSelection( text, selectionStartIndex, selectionEndIndex, options, callback); } else { - final Callable<Void> request = () -> { - onSuggestSelection( - text, selectionStartIndex, selectionEndIndex, - options, callback); - return null; - }; - final Callable<Void> onServiceFailure = () -> { - callback.onFailure(); - return null; - }; - enqueueRequestLocked(request, onServiceFailure, callback.asBinder()); + userState.mPendingRequests.add(new PendingRequest( + () -> onSuggestSelection( + text, selectionStartIndex, selectionEndIndex, options, callback), + callback::onFailure, callback.asBinder(), this, userState)); } } } @@ -168,24 +149,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi throws RemoteException { validateInput(text, startIndex, endIndex, callback); - if (!bind()) { - callback.onFailure(); - return; - } - synchronized (mLock) { - if (isBoundLocked()) { - mService.onClassifyText(text, startIndex, endIndex, options, callback); + UserState userState = getCallingUserStateLocked(); + if (!userState.bindLocked()) { + callback.onFailure(); + } else if (userState.isBoundLocked()) { + userState.mService.onClassifyText(text, startIndex, endIndex, options, callback); } else { - final Callable<Void> request = () -> { - onClassifyText(text, startIndex, endIndex, options, callback); - return null; - }; - final Callable<Void> onServiceFailure = () -> { - callback.onFailure(); - return null; - }; - enqueueRequestLocked(request, onServiceFailure, callback.asBinder()); + userState.mPendingRequests.add(new PendingRequest( + () -> onClassifyText(text, startIndex, endIndex, options, callback), + callback::onFailure, callback.asBinder(), this, userState)); } } } @@ -196,24 +169,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi throws RemoteException { validateInput(text, callback); - if (!bind()) { - callback.onFailure(); - return; - } - synchronized (mLock) { - if (isBoundLocked()) { - mService.onGenerateLinks(text, options, callback); + UserState userState = getCallingUserStateLocked(); + if (!userState.bindLocked()) { + callback.onFailure(); + } else if (userState.isBoundLocked()) { + userState.mService.onGenerateLinks(text, options, callback); } else { - final Callable<Void> request = () -> { - onGenerateLinks(text, options, callback); - return null; - }; - final Callable<Void> onServiceFailure = () -> { - callback.onFailure(); - return null; - }; - enqueueRequestLocked(request, onServiceFailure, callback.asBinder()); + userState.mPendingRequests.add(new PendingRequest( + () -> onGenerateLinks(text, options, callback), + callback::onFailure, callback.asBinder(), this, userState)); } } } @@ -223,99 +188,63 @@ public final class TextClassificationManagerService extends ITextClassifierServi validateInput(event, mContext); synchronized (mLock) { - if (isBoundLocked()) { - mService.onSelectionEvent(event); + UserState userState = getCallingUserStateLocked(); + if (userState.isBoundLocked()) { + userState.mService.onSelectionEvent(event); } else { - final Callable<Void> request = () -> { - onSelectionEvent(event); - return null; - }; - enqueueRequestLocked(request, null /* onServiceFailure */, null /* binder */); - } - } - } - - /** - * @return true if the service is bound or in the process of being bound. - * Returns false otherwise. - */ - private boolean bind() { - synchronized (mLock) { - if (isBoundLocked() || isBindingLocked()) { - return true; - } - - // TODO: Handle bind timeout. - final boolean willBind; - final long identity = Binder.clearCallingIdentity(); - try { - Slog.d(LOG_TAG, "Binding to " + mServiceIntent.getComponent()); - willBind = mContext.bindServiceAsUser( - mServiceIntent, mConnection, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, - Binder.getCallingUserHandle()); - setBindingLocked(willBind); - } finally { - Binder.restoreCallingIdentity(identity); + userState.mPendingRequests.add(new PendingRequest( + () -> onSelectionEvent(event), + null /* onServiceFailure */, null /* binder */, this, userState)); } - return willBind; } } - @GuardedBy("mLock") - private boolean isBoundLocked() { - return mService != null; - } - - @GuardedBy("mLock") - private boolean isBindingLocked() { - return mBinding; + private UserState getCallingUserStateLocked() { + return getUserStateLocked(UserHandle.getCallingUserId()); } - @GuardedBy("mLock") - private void setBindingLocked(boolean binding) { - mBinding = binding; + private UserState getUserStateLocked(int userId) { + UserState result = mUserStates.get(userId); + if (result == null) { + result = new UserState(userId, mContext, mLock); + mUserStates.put(userId, result); + } + return result; } - @GuardedBy("mLock") - private void enqueueRequestLocked( - Callable<Void> request, Callable<Void> onServiceFailure, IBinder binder) { - mPendingRequests.add(new PendingRequest(request, onServiceFailure, binder)); + UserState peekUserStateLocked(int userId) { + return mUserStates.get(userId); } - @GuardedBy("mLock") - private void handlePendingRequestsLocked() { - // TODO(b/72481146): Implement PendingRequest similar to that in RemoteFillService. - final PendingRequest[] pendingRequests = - mPendingRequests.toArray(new PendingRequest[mPendingRequests.size()]); - for (PendingRequest pendingRequest : pendingRequests) { - if (isBoundLocked()) { - pendingRequest.executeLocked(); - } else { - pendingRequest.notifyServiceFailureLocked(); - } - } - } + private static final class PendingRequest implements IBinder.DeathRecipient { - private final class PendingRequest implements IBinder.DeathRecipient { - - private final Callable<Void> mRequest; - @Nullable private final Callable<Void> mOnServiceFailure; @Nullable private final IBinder mBinder; + @NonNull private final Runnable mRequest; + @Nullable private final Runnable mOnServiceFailure; + @GuardedBy("mLock") + @NonNull private final UserState mOwningUser; + @NonNull private final TextClassificationManagerService mService; /** * Initializes a new pending request. - * * @param request action to perform when the service is bound * @param onServiceFailure action to perform when the service dies or disconnects * @param binder binder to the process that made this pending request + * @param service + * @param owningUser */ PendingRequest( - Callable<Void> request, @Nullable Callable<Void> onServiceFailure, - @Nullable IBinder binder) { - mRequest = Preconditions.checkNotNull(request); - mOnServiceFailure = onServiceFailure; + @NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure, + @Nullable IBinder binder, + TextClassificationManagerService service, + UserState owningUser) { + mRequest = + logOnFailure(Preconditions.checkNotNull(request), "handling pending request"); + mOnServiceFailure = + logOnFailure(onServiceFailure, "notifying callback of service failure"); mBinder = binder; + mService = service; + mOwningUser = owningUser; if (mBinder != null) { try { mBinder.linkToDeath(this, 0); @@ -325,32 +254,9 @@ public final class TextClassificationManagerService extends ITextClassifierServi } } - @GuardedBy("mLock") - void executeLocked() { - removeLocked(); - try { - mRequest.call(); - } catch (Exception e) { - Slog.d(LOG_TAG, "Error handling pending request: " + e.getMessage()); - } - } - - @GuardedBy("mLock") - void notifyServiceFailureLocked() { - removeLocked(); - if (mOnServiceFailure != null) { - try { - mOnServiceFailure.call(); - } catch (Exception e) { - Slog.d(LOG_TAG, "Error notifying callback of service failure: " - + e.getMessage()); - } - } - } - @Override public void binderDied() { - synchronized (mLock) { + synchronized (mService.mLock) { // No need to handle this pending request anymore. Remove. removeLocked(); } @@ -358,13 +264,19 @@ public final class TextClassificationManagerService extends ITextClassifierServi @GuardedBy("mLock") private void removeLocked() { - mPendingRequests.remove(this); + mOwningUser.mPendingRequests.remove(this); if (mBinder != null) { mBinder.unlinkToDeath(this, 0); } } } + private static Runnable logOnFailure(@Nullable ThrowingRunnable r, String opDesc) { + if (r == null) return null; + return FunctionalUtils.handleExceptions(r, + e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage())); + } + private static void validateInput( CharSequence text, int startIndex, int endIndex, Object callback) throws RemoteException { @@ -396,4 +308,119 @@ public final class TextClassificationManagerService extends ITextClassifierServi throw new RemoteException(e.getMessage()); } } + + private static final class UserState { + @UserIdInt final int mUserId; + final TextClassifierServiceConnection mConnection = new TextClassifierServiceConnection(); + @GuardedBy("mLock") + final Queue<PendingRequest> mPendingRequests = new ArrayDeque<>(); + @GuardedBy("mLock") + ITextClassifierService mService; + @GuardedBy("mLock") + boolean mBinding; + + private final Context mContext; + private final Object mLock; + + private UserState(int userId, Context context, Object lock) { + mUserId = userId; + mContext = Preconditions.checkNotNull(context); + mLock = Preconditions.checkNotNull(lock); + } + + @GuardedBy("mLock") + boolean isBoundLocked() { + return mService != null; + } + + @GuardedBy("mLock") + private void handlePendingRequestsLocked() { + // TODO(b/72481146): Implement PendingRequest similar to that in RemoteFillService. + PendingRequest request; + while ((request = mPendingRequests.poll()) != null) { + if (isBoundLocked()) { + request.mRequest.run(); + } else { + if (request.mOnServiceFailure != null) { + request.mOnServiceFailure.run(); + } + } + + if (request.mBinder != null) { + request.mBinder.unlinkToDeath(request, 0); + } + } + } + + private boolean bindIfHasPendingRequestsLocked() { + return !mPendingRequests.isEmpty() && bindLocked(); + } + + /** + * @return true if the service is bound or in the process of being bound. + * Returns false otherwise. + */ + private boolean bindLocked() { + if (isBoundLocked() || mBinding) { + return true; + } + + // TODO: Handle bind timeout. + final boolean willBind; + final long identity = Binder.clearCallingIdentity(); + try { + ComponentName componentName = + TextClassifierService.getServiceComponentName(mContext); + if (componentName == null) { + // Might happen if the storage is encrypted and the user is not unlocked + return false; + } + Intent serviceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE) + .setComponent(componentName); + Slog.d(LOG_TAG, "Binding to " + serviceIntent.getComponent()); + willBind = mContext.bindServiceAsUser( + serviceIntent, mConnection, + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + UserHandle.of(mUserId)); + mBinding = willBind; + } finally { + Binder.restoreCallingIdentity(identity); + } + return willBind; + } + + private final class TextClassifierServiceConnection implements ServiceConnection { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + init(ITextClassifierService.Stub.asInterface(service)); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + cleanupService(); + } + + @Override + public void onBindingDied(ComponentName name) { + cleanupService(); + } + + @Override + public void onNullBinding(ComponentName name) { + cleanupService(); + } + + void cleanupService() { + init(null); + } + + private void init(@Nullable ITextClassifierService service) { + synchronized (mLock) { + mService = service; + mBinding = false; + handlePendingRequestsLocked(); + } + } + } + } } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index c31cdec0a0fc..641a1ba68648 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -1096,10 +1096,7 @@ final class AccessibilityController { // Add windows of certain types not covered by modal windows. if (isReportedWindowType(windowState.mAttrs.type)) { // Add the window to the ones to be reported. - WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen); - window.layer = addedWindows.size(); - addedWindows.add(window.token); - windows.add(window); + addPopulatedWindowInfo(windowState, boundsInScreen, windows, addedWindows); if (windowState.isFocused()) { focusedWindowAdded = true; } @@ -1150,10 +1147,8 @@ final class AccessibilityController { computeWindowBoundsInScreen(windowState, boundsInScreen); // Add the window to the ones to be reported. - WindowInfo window = obtainPopulatedWindowInfo(windowState, - boundsInScreen); - addedWindows.add(window.token); - windows.add(window); + addPopulatedWindowInfo( + windowState, boundsInScreen, windows, addedWindows); break; } } @@ -1244,11 +1239,14 @@ final class AccessibilityController { (int) windowFrame.right, (int) windowFrame.bottom); } - private static WindowInfo obtainPopulatedWindowInfo( - WindowState windowState, Rect boundsInScreen) { + private static void addPopulatedWindowInfo( + WindowState windowState, Rect boundsInScreen, + List<WindowInfo> out, Set<IBinder> tokenOut) { final WindowInfo window = windowState.getWindowInfo(); window.boundsInScreen.set(boundsInScreen); - return window; + window.layer = tokenOut.size(); + out.add(window); + tokenOut.add(window.token); } private void cacheWindows(List<WindowInfo> windows) { diff --git a/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java b/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java new file mode 100644 index 000000000000..ae343da30c74 --- /dev/null +++ b/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2018 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.util.ArrayMap; +import android.util.ArraySet; + +import java.util.ArrayList; + +/** + * Keeps track of all {@link AppWindowToken} that are animating and makes sure all animations are + * finished at the same time such that we don't run into issues with z-ordering: An activity A + * that has a shorter animation that is above another activity B with a longer animation in the same + * task, the animation layer would put the B on top of A, but from the hierarchy, A needs to be on + * top of B. Thus, we defer reparenting A to the original hierarchy such that it stays on top of B + * until B finishes animating. + */ +class AnimatingAppWindowTokenRegistry { + + private ArraySet<AppWindowToken> mAnimatingTokens = new ArraySet<>(); + private ArrayMap<AppWindowToken, Runnable> mFinishedTokens = new ArrayMap<>(); + + private ArrayList<Runnable> mTmpRunnableList = new ArrayList<>(); + + /** + * Notifies that an {@link AppWindowToken} has started animating. + */ + void notifyStarting(AppWindowToken token) { + mAnimatingTokens.add(token); + } + + /** + * Notifies that an {@link AppWindowToken} has finished animating. + */ + void notifyFinished(AppWindowToken token) { + mAnimatingTokens.remove(token); + mFinishedTokens.remove(token); + } + + /** + * Called when an {@link AppWindowToken} is about to finish animating. + * + * @param endDeferFinishCallback Callback to run when defer finish should be ended. + * @return {@code true} if finishing the animation should be deferred, {@code false} otherwise. + */ + boolean notifyAboutToFinish(AppWindowToken token, Runnable endDeferFinishCallback) { + final boolean removed = mAnimatingTokens.remove(token); + if (!removed) { + return false; + } + + if (mAnimatingTokens.isEmpty()) { + + // If no animations are animating anymore, finish all others. + endDeferringFinished(); + return false; + } else { + + // Otherwise let's put it into the pending list of to be finished animations. + mFinishedTokens.put(token, endDeferFinishCallback); + return true; + } + } + + private void endDeferringFinished() { + // Copy it into a separate temp list to avoid modifying the collection while iterating as + // calling the callback may call back into notifyFinished. + for (int i = mFinishedTokens.size() - 1; i >= 0; i--) { + mTmpRunnableList.add(mFinishedTokens.valueAt(i)); + } + mFinishedTokens.clear(); + for (int i = mTmpRunnableList.size() - 1; i >= 0; i--) { + mTmpRunnableList.get(i).run(); + } + mTmpRunnableList.clear(); + } +} diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index d2ddf551fc8d..f52f91cd6a3b 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -251,6 +251,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree private final Point mTmpPoint = new Point(); private final Rect mTmpRect = new Rect(); private RemoteAnimationDefinition mRemoteAnimationDefinition; + private AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry; AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, @@ -780,6 +781,16 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree task.mStack.mExitingAppTokens.remove(this); } } + final TaskStack stack = getStack(); + + // If we reparent, make sure to remove ourselves from the old animation registry. + if (mAnimatingAppWindowTokenRegistry != null) { + mAnimatingAppWindowTokenRegistry.notifyFinished(this); + } + mAnimatingAppWindowTokenRegistry = stack != null + ? stack.getAnimatingAppWindowTokenRegistry() + : null; + mLastParent = task; } @@ -1471,11 +1482,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree void layoutLetterbox(WindowState winHint) { final WindowState w = findMainWindow(); - if (w != winHint && winHint != null && w != null) { + if (w == null || winHint != null && w != winHint) { return; } - final boolean needsLetterbox = w != null && w.isLetterboxedAppWindow() - && fillsParent() && w.hasDrawnLw(); + final boolean surfaceReady = w.hasDrawnLw() // Regular case + || w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready. + || w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface. + final boolean needsLetterbox = w.isLetterboxedAppWindow() && fillsParent() && surfaceReady; if (needsLetterbox) { if (mLetterbox == null) { mLetterbox = new Letterbox(() -> makeChildSurface(null)); @@ -1784,6 +1797,21 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } @Override + public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { + return mAnimatingAppWindowTokenRegistry != null + && mAnimatingAppWindowTokenRegistry.notifyAboutToFinish( + this, endDeferFinishCallback); + } + + @Override + public void onAnimationLeashDestroyed(Transaction t) { + super.onAnimationLeashDestroyed(t); + if (mAnimatingAppWindowTokenRegistry != null) { + mAnimatingAppWindowTokenRegistry.notifyFinished(this); + } + } + + @Override protected void setLayer(Transaction t, int layer) { if (!mSurfaceAnimator.hasLeash()) { t.setLayer(mSurfaceControl, layer); @@ -1825,6 +1853,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final DisplayContent dc = getDisplayContent(); dc.assignStackOrdering(t); + if (mAnimatingAppWindowTokenRegistry != null) { + mAnimatingAppWindowTokenRegistry.notifyStarting(this); + } } /** diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c35c05dc7212..e2e169003913 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -305,6 +305,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo int pendingLayoutChanges; // TODO(multi-display): remove some of the usages. boolean isDefaultDisplay; + /** + * Flag indicating whether WindowManager should override info for this display in + * DisplayManager. + */ + boolean mShouldOverrideDisplayConfiguration = true; /** Window tokens that are in the process of exiting, but still on screen for animations. */ final ArrayList<WindowToken> mExitingTokens = new ArrayList<>(); @@ -1177,8 +1182,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDisplayInfo.flags &= ~Display.FLAG_SCALING_DISABLED; } + // We usually set the override info in DisplayManager so that we get consistent display + // metrics values when displays are changing and don't send out new values until WM is aware + // of them. However, we don't do this for displays that serve as containers for ActivityView + // because we don't want letter-/pillar-boxing during resize. + final DisplayInfo overrideDisplayInfo = mShouldOverrideDisplayConfiguration + ? mDisplayInfo : null; mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId, - mDisplayInfo); + overrideDisplayInfo); mBaseDisplayRect.set(0, 0, dw, dh); diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index c06caaf9e900..f10ff8c1dd81 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -81,9 +81,14 @@ class SurfaceAnimator { if (anim != mAnimation) { return; } - reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */); - if (animationFinishedCallback != null) { - animationFinishedCallback.run(); + final Runnable resetAndInvokeFinish = () -> { + reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */); + if (animationFinishedCallback != null) { + animationFinishedCallback.run(); + } + }; + if (!mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)) { + resetAndInvokeFinish.run(); } } }; @@ -407,5 +412,17 @@ class SurfaceAnimator { * @return The height of the surface to be animated. */ int getSurfaceHeight(); + + /** + * Gets called when the animation is about to finish and gives the client the opportunity to + * defer finishing the animation, i.e. it keeps the leash around until the client calls + * {@link #cancelAnimation}. + * + * @param endDeferFinishCallback The callback to call when defer finishing should be ended. + * @return Whether the client would like to defer the animation finish. + */ + default boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { + return false; + } } } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index b5d00a75d7e6..460edece0f61 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -155,6 +155,9 @@ public class TaskStack extends WindowContainer<Task> implements final Rect mTmpDimBoundsRect = new Rect(); private final Point mLastSurfaceSize = new Point(); + private final AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry = + new AnimatingAppWindowTokenRegistry(); + TaskStack(WindowManagerService service, int stackId, StackWindowController controller) { super(service); mStackId = stackId; @@ -1782,4 +1785,8 @@ public class TaskStack extends WindowContainer<Task> implements outPos.x -= outset; outPos.y -= outset; } + + AnimatingAppWindowTokenRegistry getAnimatingAppWindowTokenRegistry() { + return mAnimatingAppWindowTokenRegistry; + } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 5ae4dc5636f0..27c0b3b39dfc 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -285,7 +285,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } if (mSurfaceControl != null) { - getPendingTransaction().destroy(mSurfaceControl); + mPendingTransaction.destroy(mSurfaceControl); + + // Merge to parent transaction to ensure the transactions on this WindowContainer are + // applied in native even if WindowContainer is removed. + if (mParent != null) { + mParent.getPendingTransaction().merge(mPendingTransaction); + } + mSurfaceControl = null; scheduleAnimation(); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 9f8de58b16cf..5f0769d19b2c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1117,17 +1117,7 @@ public class WindowManagerService extends IWindowManager.Stub throw new IllegalStateException("Display has not been initialialized"); } - DisplayContent displayContent = mRoot.getDisplayContent(displayId); - - // Adding a window is an exception where the WindowManagerService can create the - // display instead of waiting for the ActivityManagerService to drive creation. - if (displayContent == null) { - final Display display = mDisplayManager.getDisplay(displayId); - - if (display != null) { - displayContent = mRoot.createDisplayContent(display, null /* controller */); - } - } + final DisplayContent displayContent = getDisplayContentOrCreate(displayId); if (displayContent == null) { Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: " @@ -1493,6 +1483,32 @@ public class WindowManagerService extends IWindowManager.Stub return res; } + /** + * Get existing {@link DisplayContent} or create a new one if the display is registered in + * DisplayManager. + * + * NOTE: This should only be used in cases when there is a chance that a {@link DisplayContent} + * that corresponds to a display just added to DisplayManager has not yet been created. This + * usually means that the call of this method was initiated from outside of Activity or Window + * Manager. In most cases the regular getter should be used. + * @see RootWindowContainer#getDisplayContent(int) + */ + private DisplayContent getDisplayContentOrCreate(int displayId) { + DisplayContent displayContent = mRoot.getDisplayContent(displayId); + + // Create an instance if possible instead of waiting for the ActivityManagerService to drive + // the creation. + if (displayContent == null) { + final Display display = mDisplayManager.getDisplay(displayId); + + if (display != null) { + displayContent = mRoot.createDisplayContent(display, null /* controller */); + } + } + + return displayContent; + } + private boolean doesAddToastWindowRequireToken(String packageName, int callingUid, WindowState attachedWindow) { // Try using the target SDK of the root window @@ -6987,6 +7003,24 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void dontOverrideDisplayInfo(int displayId) { + synchronized (mWindowMap) { + final DisplayContent dc = getDisplayContentOrCreate(displayId); + if (dc == null) { + throw new IllegalArgumentException( + "Trying to register a non existent display."); + } + // We usually set the override info in DisplayManager so that we get consistent + // values when displays are changing. However, we don't do this for displays that + // serve as containers for ActivityViews because we don't want letter-/pillar-boxing + // during resize. + dc.mShouldOverrideDisplayConfiguration = false; + mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(displayId, + null /* info */); + } + } + + @Override public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver) throws RemoteException { if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS, "registerShortcutKey")) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index c10051153ac3..eebf2fd1b356 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1032,15 +1032,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mAttrs.type == TYPE_DOCK_DIVIDER) { // For the docked divider, we calculate the stable insets like a full-screen window // so it can use it to calculate the snap positions. - mStableInsets.set(Math.max(mStableFrame.left - mDisplayFrame.left, 0), - Math.max(mStableFrame.top - mDisplayFrame.top, 0), - Math.max(mDisplayFrame.right - mStableFrame.right, 0), - Math.max(mDisplayFrame.bottom - mStableFrame.bottom, 0)); + final WmDisplayCutout c = displayCutout.calculateRelativeTo(mDisplayFrame); + mTmpRect.set(mDisplayFrame); + mTmpRect.inset(c.getDisplayCutout().getSafeInsets()); + mTmpRect.intersectUnchecked(mStableFrame); + + mStableInsets.set(Math.max(mTmpRect.left - mDisplayFrame.left, 0), + Math.max(mTmpRect.top - mDisplayFrame.top, 0), + Math.max(mDisplayFrame.right - mTmpRect.right, 0), + Math.max(mDisplayFrame.bottom - mTmpRect.bottom, 0)); // The divider doesn't care about insets in any case, so set it to empty so we don't // trigger a relayout when moving it. mContentInsets.setEmpty(); mVisibleInsets.setEmpty(); + displayCutout = WmDisplayCutout.NO_CUTOUT; } else { getDisplayContent().getBounds(mTmpRect); // Override right and/or bottom insets in case if the frame doesn't fit the screen in @@ -4525,8 +4531,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mAttrs.type == TYPE_NAVIGATION_BAR || // It's tempting to wonder: Have we forgotten the rounded corners overlay? // worry not: it's a fake TYPE_NAVIGATION_BAR_PANEL - mAttrs.type == TYPE_NAVIGATION_BAR_PANEL || - mAttrs.type == TYPE_STATUS_BAR) { + mAttrs.type == TYPE_NAVIGATION_BAR_PANEL) { return false; } return true; diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 410eddfeb71b..0c2b0757df97 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -225,6 +225,11 @@ class WindowStateAnimator { // case we need to give the client a new Surface if it lays back out to a visible state. boolean mChildrenDetached = false; + // Set to true after the first frame of the Pinned stack animation + // and reset after the last to ensure we only reset mForceScaleUntilResize + // once per animation. + boolean mPipAnimationStarted = false; + WindowStateAnimator(final WindowState win) { final WindowManagerService service = win.mService; @@ -512,7 +517,7 @@ class WindowStateAnimator { mDrawState = NO_SURFACE; return null; } catch (Exception e) { - Slog.e(TAG, "Exception creating surface", e); + Slog.e(TAG, "Exception creating surface (parent dead?)", e); mDrawState = NO_SURFACE; return null; } @@ -983,8 +988,13 @@ class WindowStateAnimator { // As we are in SCALING_MODE_SCALE_TO_WINDOW, SurfaceFlinger will // then take over the scaling until the new buffer arrives, and things // will be seamless. - mForceScaleUntilResize = true; + if (mPipAnimationStarted == false) { + mForceScaleUntilResize = true; + mPipAnimationStarted = true; + } } else { + mPipAnimationStarted = false; + if (!w.mSeamlesslyRotated) { mSurfaceController.setPositionInTransaction(mXOffset, mYOffset, recoveringMemory); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 70abf8054dbb..d165a45f0b01 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -196,6 +196,8 @@ public final class SystemServer { "com.android.server.search.SearchManagerService$Lifecycle"; private static final String THERMAL_OBSERVER_CLASS = "com.google.android.clockwork.ThermalObserver"; + private static final String WEAR_CONFIG_SERVICE_CLASS = + "com.google.android.clockwork.WearConfigManagerService"; private static final String WEAR_CONNECTIVITY_SERVICE_CLASS = "com.android.clockwork.connectivity.WearConnectivityService"; private static final String WEAR_SIDEKICK_SERVICE_CLASS = @@ -1543,6 +1545,10 @@ public final class SystemServer { } if (isWatch) { + traceBeginAndSlog("StartWearConfigService"); + mSystemServiceManager.startService(WEAR_CONFIG_SERVICE_CLASS); + traceEnd(); + traceBeginAndSlog("StartWearConnectivityService"); mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS); traceEnd(); diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java index f603a09baa37..d0398ad539ac 100644 --- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java +++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java @@ -148,16 +148,22 @@ public class PerformBackupTaskTest { Looper backupLooper = startBackupThreadAndGetLooper(); mShadowBackupLooper = shadowOf(backupLooper); mBackupHandler = new BackupHandler(mBackupManagerService, backupLooper); + Handler mainHandler = new Handler(Looper.getMainLooper()); mBackupManager = spy(FakeIBackupManager.class); + BackupAgentTimeoutParameters agentTimeoutParameters = + new BackupAgentTimeoutParameters(mainHandler, application.getContentResolver()); + agentTimeoutParameters.start(); + setUpBackupManagerServiceBasics( mBackupManagerService, application, mTransportManager, packageManager, mBackupHandler, - mWakeLock); + mWakeLock, + agentTimeoutParameters); when(mBackupManagerService.getBaseStateDir()).thenReturn(mBaseStateDir); when(mBackupManagerService.getDataDir()).thenReturn(dataDir); when(mBackupManagerService.getBackupManagerBinder()).thenReturn(mBackupManager); diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java index 03792b1d40ba..869c50b55fe8 100644 --- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java +++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java @@ -41,12 +41,14 @@ import android.app.backup.IRestoreSession; import android.app.backup.RestoreSet; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; +import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import com.android.server.EventLogTags; +import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupManagerService; import com.android.server.backup.TransportManager; import com.android.server.backup.internal.BackupHandler; @@ -115,6 +117,15 @@ public class ActiveRestoreSessionTest { Looper backupLooper = startBackupThreadAndGetLooper(); mShadowBackupLooper = shadowOf(backupLooper); + + Handler mainHandler = new Handler(Looper.getMainLooper()); + BackupAgentTimeoutParameters agentTimeoutParameters = + new BackupAgentTimeoutParameters(mainHandler, application.getContentResolver()); + agentTimeoutParameters.start(); + + // We need to mock BMS timeout parameters before initializing the BackupHandler since + // the constructor of BackupHandler relies on the timeout parameters. + when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters); BackupHandler backupHandler = new BackupHandler(mBackupManagerService, backupLooper); mWakeLock = createBackupWakeLock(application); @@ -125,7 +136,8 @@ public class ActiveRestoreSessionTest { mTransportManager, application.getPackageManager(), backupHandler, - mWakeLock); + mWakeLock, + agentTimeoutParameters); when(mBackupManagerService.getPendingRestores()).thenReturn(new ArrayDeque<>()); } diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java index c210fdea6e89..5a886e33622f 100644 --- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java +++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java @@ -28,6 +28,7 @@ import android.os.Looper; import android.os.PowerManager; import android.util.SparseArray; +import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupManagerService; import com.android.server.backup.TransportManager; import com.android.server.backup.internal.BackupHandler; @@ -43,7 +44,8 @@ public class BackupManagerServiceTestUtils { TransportManager transportManager, PackageManager packageManager, BackupHandler backupHandler, - PowerManager.WakeLock wakeLock) { + PowerManager.WakeLock wakeLock, + BackupAgentTimeoutParameters agentTimeoutParameters) { when(backupManagerService.getContext()).thenReturn(context); when(backupManagerService.getTransportManager()).thenReturn(transportManager); when(backupManagerService.getPackageManager()).thenReturn(packageManager); @@ -53,6 +55,7 @@ public class BackupManagerServiceTestUtils { when(backupManagerService.getCurrentOperations()).thenReturn(new SparseArray<>()); when(backupManagerService.getActivityManager()).thenReturn(mock(IActivityManager.class)); when(backupManagerService.getWakelock()).thenReturn(wakeLock); + when(backupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters); } public static PowerManager.WakeLock createBackupWakeLock(Application application) { diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java index 2378e6d49b88..8721d9c28e0e 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java @@ -26,6 +26,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import android.app.ActivityManager; import android.app.WaitResult; @@ -180,4 +186,56 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { assertEquals(deliverToTopWait.who, firstActivity.realActivity); } } + + @Test + public void testApplySleepTokensLocked() throws Exception { + final ActivityDisplay display = mSupervisor.getDefaultDisplay(); + final KeyguardController keyguard = mSupervisor.getKeyguardController(); + final ActivityStack stack = mock(ActivityStack.class); + display.addChild(stack, 0 /* position */); + + // Make sure we wake and resume in the case the display is turning on and the keyguard is + // not showing. + verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, + false /* displayShouldSleep */, true /* isFocusedStack */, + false /* keyguardShowing */, true /* expectWakeFromSleep */, + true /* expectResumeTopActivity */); + + // Make sure we wake and don't resume when the display is turning on and the keyguard is + // showing. + verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, + false /* displayShouldSleep */, true /* isFocusedStack */, + true /* keyguardShowing */, true /* expectWakeFromSleep */, + false /* expectResumeTopActivity */); + + // Make sure we wake and don't resume when the display is turning on and the keyguard is + // not showing as unfocused. + verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, + false /* displayShouldSleep */, false /* isFocusedStack */, + false /* keyguardShowing */, true /* expectWakeFromSleep */, + false /* expectResumeTopActivity */); + + // Should not do anything if the display state hasn't changed. + verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/, + false /* displayShouldSleep */, true /* isFocusedStack */, + false /* keyguardShowing */, false /* expectWakeFromSleep */, + false /* expectResumeTopActivity */); + } + + private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard, + ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep, + boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep, + boolean expectResumeTopActivity) { + reset(stack); + + doReturn(displayShouldSleep).when(display).shouldSleep(); + doReturn(displaySleeping).when(display).isSleeping(); + doReturn(keyguardShowing).when(keyguard).isKeyguardShowing(anyInt()); + + mSupervisor.mFocusedStack = isFocusedStack ? stack : null; + mSupervisor.applySleepTokensLocked(true); + verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked(); + verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked( + null /* target */, null /* targetOptions */); + } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java index 5906db306f11..8ff3e4587d00 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java @@ -18,13 +18,19 @@ package com.android.server.am; import static android.app.ActivityManager.START_ABORTED; import static android.app.ActivityManager.START_CLASS_NOT_FOUND; +import static android.app.ActivityManager.START_DELIVERED_TO_TOP; import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT; +import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED; import static android.app.ActivityManager.START_NOT_VOICE_COMPATIBLE; +import static android.app.ActivityManager.START_PERMISSION_DENIED; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_SWITCHES_CANCELED; +import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 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 android.app.ActivityOptions; import android.app.IApplicationThread; @@ -45,6 +51,7 @@ import android.view.Gravity; import org.junit.runner.RunWith; import org.junit.Test; +import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED; import static com.android.server.am.ActivityManagerService.ANIMATE; import static org.junit.Assert.assertEquals; @@ -62,9 +69,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.times; -import static android.app.ActivityManager.START_PERMISSION_DENIED; -import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED; - import com.android.internal.os.BatteryStatsImpl; import com.android.server.am.ActivityStarter.Factory; import com.android.server.am.LaunchParamsController.LaunchParamsModifier; @@ -290,7 +294,7 @@ public class ActivityStarterTests extends ActivityTestsBase { } } - private ActivityStarter prepareStarter() { + private ActivityStarter prepareStarter(int launchFlags) { // always allow test to start activity. doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission( any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), @@ -325,8 +329,20 @@ public class ActivityStarterTests extends ActivityTestsBase { // ignore requests to create window container. doNothing().when(task).createWindowContainer(anyBoolean(), anyBoolean()); + + final Intent intent = new Intent(); + intent.addFlags(launchFlags); + intent.setComponent(ActivityBuilder.getDefaultComponent()); + + final ActivityInfo info = new ActivityInfo(); + + info.applicationInfo = new ApplicationInfo(); + info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName(); + return new ActivityStarter(mController, mService, - mService.mStackSupervisor, mock(ActivityStartInterceptor.class)); + mService.mStackSupervisor, mock(ActivityStartInterceptor.class)) + .setIntent(intent) + .setActivityInfo(info); } /** @@ -342,9 +358,6 @@ public class ActivityStarterTests extends ActivityTestsBase { // add custom values to activity info to make unique. final ActivityInfo info = new ActivityInfo(); final Rect launchBounds = new Rect(0, 0, 20, 30); - final Intent intent = new Intent(); - - intent.setComponent(ActivityBuilder.getDefaultComponent()); final WindowLayout windowLayout = new WindowLayout(10, .5f, 20, 1.0f, Gravity.NO_GRAVITY, 1, 1); @@ -354,14 +367,13 @@ public class ActivityStarterTests extends ActivityTestsBase { info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName(); // create starter. - final ActivityStarter optionStarter = prepareStarter(); + final ActivityStarter optionStarter = prepareStarter(0 /* launchFlags */); final ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchBounds(launchBounds); // run starter. optionStarter - .setIntent(intent) .setReason("testCreateTaskLayout") .setActivityInfo(info) .setActivityOptions(new SafeActivityOptions(options)) @@ -371,4 +383,69 @@ public class ActivityStarterTests extends ActivityTestsBase { verify(modifier, times(1)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options), any(), any()); } + + /** + * This test ensures that if the intent is being delivered to a + */ + @Test + public void testSplitScreenDeliverToTop() { + final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + + final ActivityRecord focusActivity = new ActivityBuilder(mService) + .setCreateTask(true) + .build(); + + focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + + final ActivityRecord reusableActivity = new ActivityBuilder(mService) + .setCreateTask(true) + .build(); + + // Create reusable activity after entering split-screen so that it is the top secondary + // stack. + reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + + // Set focus back to primary. + mService.mStackSupervisor.setFocusStackUnchecked("testSplitScreenDeliverToTop", + focusActivity.getStack()); + + doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt()); + + final int result = starter.setReason("testSplitScreenDeliverToTop").execute(); + + // Ensure result is delivering intent to top. + assertEquals(result, START_DELIVERED_TO_TOP); + } + + /** + * This test ensures that if the intent is being delivered to a split-screen unfocused task + * reports it is brought to front instead of delivering to top. + */ + @Test + public void testSplitScreenTaskToFront() { + final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + + // Create reusable activity here first. Setting the windowing mode of the primary stack + // will move the existing standard full screen stack to secondary, putting this one on the + // bottom. + final ActivityRecord reusableActivity = new ActivityBuilder(mService) + .setCreateTask(true) + .build(); + + reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + + final ActivityRecord focusActivity = new ActivityBuilder(mService) + .setCreateTask(true) + .build(); + + // Enter split-screen. Primary stack should have focus. + focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + + doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt()); + + final int result = starter.setReason("testSplitScreenMoveToFront").execute(); + + // Ensure result is moving task to front. + assertEquals(result, START_TASK_TO_FRONT); + } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index c130592b4cd3..6fb1b2e21c47 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -353,22 +353,29 @@ public class ActivityTestsBase { */ protected static class TestActivityStackSupervisor extends ActivityStackSupervisor { private ActivityDisplay mDisplay; + private KeyguardController mKeyguardController; public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) { super(service, looper); mDisplayManager = (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE); mWindowManager = prepareMockWindowManager(); + mKeyguardController = mock(KeyguardController.class); } @Override public void initialize() { super.initialize(); - mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY); + mDisplay = spy(new TestActivityDisplay(this, DEFAULT_DISPLAY)); attachDisplay(mDisplay); } @Override + public KeyguardController getKeyguardController() { + return mKeyguardController; + } + + @Override ActivityDisplay getDefaultDisplay() { return mDisplay; } diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java index 613d0afea183..10a21fdf6f3e 100644 --- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java +++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java @@ -437,6 +437,11 @@ public class PackageManagerStub extends PackageManager { } @Override + public ResolveInfo resolveServiceAsUser(Intent intent, int flags, int userId) { + return null; + } + + @Override public List<ResolveInfo> queryIntentServices(Intent intent, int flags) { return null; } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index e8170ee4ad39..d2fb1cab3479 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -4738,7 +4738,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testOverrideApnAPIsFailWithPO() throws Exception { setupProfileOwner(); - ApnSetting apn = (new ApnSetting.Builder()).build(); + ApnSetting apn = (new ApnSetting.Builder()) + .setApnName("test") + .setEntryName("test") + .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT) + .build(); assertExpectException(SecurityException.class, null, () -> dpm.addOverrideApn(admin1, apn)); assertExpectException(SecurityException.class, null, () -> diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java index 0ceb55862738..260bb0a2171f 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java @@ -40,6 +40,7 @@ import android.os.Binder; import android.os.ServiceSpecificException; import android.os.UserHandle; import android.security.KeyStore; +import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.AndroidKeyStoreSecretKey; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; @@ -68,6 +69,7 @@ import org.mockito.MockitoAnnotations; import java.io.File; import java.nio.charset.StandardCharsets; +import java.security.UnrecoverableKeyException; import java.security.cert.CertPath; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; @@ -76,8 +78,10 @@ import java.util.concurrent.Executors; import java.util.Map; import java.util.Random; +import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; @SmallTest @@ -85,7 +89,7 @@ import javax.crypto.spec.SecretKeySpec; public class RecoverableKeyStoreManagerTest { private static final String DATABASE_FILE_NAME = "recoverablekeystore.db"; - private static final String ROOT_CERTIFICATE_ALIAS = "put_default_alias_here"; + private static final String ROOT_CERTIFICATE_ALIAS = ""; private static final String TEST_SESSION_ID = "karlin"; private static final byte[] TEST_PUBLIC_KEY = new byte[] { (byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a, @@ -139,12 +143,12 @@ public class RecoverableKeyStoreManagerTest { private static final String KEY_ALGORITHM = "AES"; private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey"; + private static final String TEST_ROOT_CERT_ALIAS = ""; @Mock private Context mMockContext; @Mock private RecoverySnapshotListenersStorage mMockListenersStorage; @Mock private KeyguardManager mKeyguardManager; @Mock private PlatformKeyManager mPlatformKeyManager; - @Mock private KeyStore mKeyStore; @Mock private ApplicationKeyStorage mApplicationKeyStorage; private RecoverableKeyStoreDb mRecoverableKeyStoreDb; @@ -175,7 +179,7 @@ public class RecoverableKeyStoreManagerTest { mRecoverableKeyStoreManager = new RecoverableKeyStoreManager( mMockContext, - mKeyStore, + KeyStore.getInstance(), mRecoverableKeyStoreDb, mRecoverySessionStorage, Executors.newSingleThreadExecutor(), @@ -219,6 +223,7 @@ public class RecoverableKeyStoreManagerTest { assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull(); assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); + // TODO(76083050) Test the grant mechanism for the keys. } @Test @@ -449,10 +454,13 @@ public class RecoverableKeyStoreManagerTest { eq(Manifest.permission.RECOVER_KEYSTORE), any()); } + // TODO: Add tests for non-existing cert alias + @Test public void startRecoverySessionWithCertPath_storesTheSessionInfo() throws Exception { mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( TEST_SESSION_ID, + TEST_ROOT_CERT_ALIAS, RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, @@ -474,6 +482,7 @@ public class RecoverableKeyStoreManagerTest { public void startRecoverySessionWithCertPath_checksPermissionFirst() throws Exception { mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( TEST_SESSION_ID, + TEST_ROOT_CERT_ALIAS, RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, @@ -591,6 +600,7 @@ public class RecoverableKeyStoreManagerTest { try { mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( TEST_SESSION_ID, + TEST_ROOT_CERT_ALIAS, RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, @@ -609,6 +619,7 @@ public class RecoverableKeyStoreManagerTest { try { mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( TEST_SESSION_ID, + TEST_ROOT_CERT_ALIAS, RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1), vaultParams, TEST_VAULT_CHALLENGE, @@ -631,6 +642,7 @@ public class RecoverableKeyStoreManagerTest { try { mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( TEST_SESSION_ID, + TEST_ROOT_CERT_ALIAS, RecoveryCertPath.createRecoveryCertPath(emptyCertPath), TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, @@ -655,6 +667,7 @@ public class RecoverableKeyStoreManagerTest { try { mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( TEST_SESSION_ID, + TEST_ROOT_CERT_ALIAS, RecoveryCertPath.createRecoveryCertPath(shortCertPath), TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, @@ -671,9 +684,9 @@ public class RecoverableKeyStoreManagerTest { } @Test - public void recoverKeys_throwsIfNoSessionIsPresent() throws Exception { + public void recoverKeyChainSnapshot_throwsIfNoSessionIsPresent() throws Exception { try { - mRecoverableKeyStoreManager.recoverKeys( + mRecoverableKeyStoreManager.recoverKeyChainSnapshot( TEST_SESSION_ID, /*recoveryKeyBlob=*/ randomBytes(32), /*applicationKeys=*/ ImmutableList.of( @@ -686,7 +699,7 @@ public class RecoverableKeyStoreManagerTest { } @Test - public void recoverKeys_throwsIfRecoveryClaimCannotBeDecrypted() throws Exception { + public void recoverKeyChainSnapshot_throwsIfRecoveryClaimCannotBeDecrypted() throws Exception { mRecoverableKeyStoreManager.startRecoverySession( TEST_SESSION_ID, TEST_PUBLIC_KEY, @@ -699,7 +712,7 @@ public class RecoverableKeyStoreManagerTest { TEST_SECRET))); try { - mRecoverableKeyStoreManager.recoverKeys( + mRecoverableKeyStoreManager.recoverKeyChainSnapshot( TEST_SESSION_ID, /*encryptedRecoveryKey=*/ randomBytes(60), /*applicationKeys=*/ ImmutableList.of()); @@ -710,7 +723,7 @@ public class RecoverableKeyStoreManagerTest { } @Test - public void recoverKeys_throwsIfFailedToDecryptAllApplicationKeys() throws Exception { + public void recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys() throws Exception { mRecoverableKeyStoreManager.startRecoverySession( TEST_SESSION_ID, TEST_PUBLIC_KEY, @@ -731,7 +744,7 @@ public class RecoverableKeyStoreManagerTest { encryptedApplicationKey(randomRecoveryKey(), randomBytes(32))); try { - mRecoverableKeyStoreManager.recoverKeys( + mRecoverableKeyStoreManager.recoverKeyChainSnapshot( TEST_SESSION_ID, /*encryptedRecoveryKey=*/ encryptedClaimResponse, /*applicationKeys=*/ ImmutableList.of(badApplicationKey)); @@ -742,7 +755,8 @@ public class RecoverableKeyStoreManagerTest { } @Test - public void recoverKeys_doesNotThrowIfNoApplicationKeysToBeDecrypted() throws Exception { + public void recoverKeyChainSnapshot_doesNotThrowIfNoApplicationKeysToBeDecrypted() + throws Exception { mRecoverableKeyStoreManager.startRecoverySession( TEST_SESSION_ID, TEST_PUBLIC_KEY, @@ -759,14 +773,14 @@ public class RecoverableKeyStoreManagerTest { byte[] encryptedClaimResponse = encryptClaimResponse( keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey); - mRecoverableKeyStoreManager.recoverKeys( + mRecoverableKeyStoreManager.recoverKeyChainSnapshot( TEST_SESSION_ID, /*encryptedRecoveryKey=*/ encryptedClaimResponse, /*applicationKeys=*/ ImmutableList.of()); } @Test - public void recoverKeys_returnsDecryptedKeys() throws Exception { + public void recoverKeyChainSnapshot_returnsDecryptedKeys() throws Exception { mRecoverableKeyStoreManager.startRecoverySession( TEST_SESSION_ID, TEST_PUBLIC_KEY, @@ -787,17 +801,18 @@ public class RecoverableKeyStoreManagerTest { TEST_ALIAS, encryptedApplicationKey(recoveryKey, applicationKeyBytes)); - Map<String, byte[]> recoveredKeys = mRecoverableKeyStoreManager.recoverKeys( + Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot( TEST_SESSION_ID, encryptedClaimResponse, ImmutableList.of(applicationKey)); assertThat(recoveredKeys).hasSize(1); - assertThat(recoveredKeys.get(TEST_ALIAS)).isEqualTo(applicationKeyBytes); + assertThat(recoveredKeys).containsKey(TEST_ALIAS); + // TODO(76083050) Test the grant mechanism for the keys. } @Test - public void recoverKeys_worksOnOtherApplicationKeysIfOneDecryptionFails() throws Exception { + public void recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails() throws Exception { mRecoverableKeyStoreManager.startRecoverySession( TEST_SESSION_ID, TEST_PUBLIC_KEY, @@ -825,13 +840,14 @@ public class RecoverableKeyStoreManagerTest { TEST_ALIAS2, encryptedApplicationKey(recoveryKey, applicationKeyBytes2)); - Map<String, byte[]> recoveredKeys = mRecoverableKeyStoreManager.recoverKeys( + Map<String, String> recoveredKeys = mRecoverableKeyStoreManager.recoverKeyChainSnapshot( TEST_SESSION_ID, encryptedClaimResponse, ImmutableList.of(applicationKey1, applicationKey2)); assertThat(recoveredKeys).hasSize(1); - assertThat(recoveredKeys.get(TEST_ALIAS2)).isEqualTo(applicationKeyBytes2); + assertThat(recoveredKeys).containsKey(TEST_ALIAS2); + // TODO(76083050) Test the grant mechanism for the keys. } @Test diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index edf1f746fb4b..552c915f8480 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -17,6 +17,8 @@ package com.android.server.usage; import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN; +import static android.app.usage.UsageEvents.Event.SLICE_PINNED; +import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV; import static android.app.usage.UsageEvents.Event.USER_INTERACTION; import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT; import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED; @@ -406,6 +408,30 @@ public class AppStandbyControllerTests { } @Test + public void testSlicePinnedEvent() throws Exception { + setChargingState(mController, false); + + reportEvent(mController, USER_INTERACTION, 0); + assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); + mInjector.mElapsedRealtime = 1; + reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime); + assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); + + mController.forceIdleState(PACKAGE_1, USER_ID, true); + reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime); + assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController)); + } + + @Test + public void testSlicePinnedPrivEvent() throws Exception { + setChargingState(mController, false); + + mController.forceIdleState(PACKAGE_1, USER_ID, true); + reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime); + assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); + } + + @Test public void testPredictionTimedout() throws Exception { setChargingState(mController, false); // Set it to timeout or usage, so that prediction can override it diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java new file mode 100644 index 000000000000..8b78f10b0b5b --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2018 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 android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.FlakyTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for the {@link TaskStack} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:com.android.server.wm.AnimatingAppWindowTokenRegistryTest + */ +@SmallTest +@Presubmit +@FlakyTest(detail = "Promote once confirmed non-flaky") +@RunWith(AndroidJUnit4.class) +public class AnimatingAppWindowTokenRegistryTest extends WindowTestsBase { + + @Mock + AnimationAdapter mAdapter; + + @Mock + Runnable mMockEndDeferFinishCallback1; + @Mock + Runnable mMockEndDeferFinishCallback2; + @Before + public void setUp() throws Exception { + super.setUp(); + MockitoAnnotations.initMocks(this); + } + + @Test + public void testDeferring() throws Exception { + final AppWindowToken window1 = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD, + "window2").mAppToken; + final AnimatingAppWindowTokenRegistry registry = + window1.getStack().getAnimatingAppWindowTokenRegistry(); + + window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */); + window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */); + assertTrue(window1.isSelfAnimating()); + assertTrue(window2.isSelfAnimating()); + + // Make sure that first animation finish is deferred, second one is not deferred, and first + // one gets cancelled. + assertTrue(registry.notifyAboutToFinish(window1, mMockEndDeferFinishCallback1)); + assertFalse(registry.notifyAboutToFinish(window2, mMockEndDeferFinishCallback2)); + verify(mMockEndDeferFinishCallback1).run(); + verifyZeroInteractions(mMockEndDeferFinishCallback2); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java index 4f49a4ae6467..b645700abb79 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -31,7 +31,10 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import org.junit.Test; import org.junit.runner.RunWith; @@ -457,6 +460,18 @@ public class DisplayContentTests extends WindowTestsBase { SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); } + @Test + public void testDisableDisplayInfoOverrideFromWindowManager() { + final DisplayContent dc = createNewDisplay(); + + assertTrue(dc.mShouldOverrideDisplayConfiguration); + sWm.dontOverrideDisplayInfo(dc.getDisplayId()); + + assertFalse(dc.mShouldOverrideDisplayConfiguration); + verify(sWm.mDisplayManagerInternal, times(1)) + .setDisplayInfoOverrideFromWindowManager(dc.getDisplayId(), null); + } + private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth, int expectedBaseHeight, int expectedBaseDensity) { assertEquals(displayContent.mBaseDisplayWidth, expectedBaseWidth); diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java index a120eba623a3..650687245858 100644 --- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import android.platform.test.annotations.Presubmit; +import android.support.test.filters.FlakyTest; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.view.SurfaceControl; @@ -64,6 +65,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase { private SurfaceSession mSession = new SurfaceSession(); private MyAnimatable mAnimatable; private MyAnimatable mAnimatable2; + private DeferFinishAnimatable mDeferFinishAnimatable; @Before public void setUp() throws Exception { @@ -71,6 +73,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase { MockitoAnnotations.initMocks(this); mAnimatable = new MyAnimatable(); mAnimatable2 = new MyAnimatable(); + mDeferFinishAnimatable = new DeferFinishAnimatable(); } @Test @@ -166,6 +169,29 @@ public class SurfaceAnimatorTest extends WindowTestsBase { verify(mTransaction).destroy(eq(leash)); } + @Test + @FlakyTest(detail = "Promote once confirmed non-flaky") + public void testDeferFinish() throws Exception { + + // Start animation + mDeferFinishAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, + true /* hidden */); + final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass( + OnAnimationFinishedCallback.class); + assertAnimating(mDeferFinishAnimatable); + verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); + + // Finish the animation but then make sure we are deferring. + callbackCaptor.getValue().onAnimationFinished(mSpec); + assertAnimating(mDeferFinishAnimatable); + + // Now end defer finishing. + mDeferFinishAnimatable.endDeferFinishCallback.run(); + assertNotAnimating(mAnimatable2); + assertTrue(mDeferFinishAnimatable.mFinishedCallbackCalled); + verify(mTransaction).destroy(eq(mDeferFinishAnimatable.mLeash)); + } + private void assertAnimating(MyAnimatable animatable) { assertTrue(animatable.mSurfaceAnimator.isAnimating()); assertNotNull(animatable.mSurfaceAnimator.getAnimation()); @@ -254,4 +280,15 @@ public class SurfaceAnimatorTest extends WindowTestsBase { private final Runnable mFinishedCallback = () -> mFinishedCallbackCalled = true; } + + private class DeferFinishAnimatable extends MyAnimatable { + + Runnable endDeferFinishCallback; + + @Override + public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { + this.endDeferFinishCallback = endDeferFinishCallback; + return true; + } + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index f4313b80c520..181fcebbb536 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -70,6 +70,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertEquals(getSnoozeCriteria(key, i), ranking.getSnoozeCriteria()); assertEquals(getShowBadge(i), ranking.canShowBadge()); assertEquals(getUserSentiment(i), ranking.getUserSentiment()); + assertEquals(getHidden(i), ranking.isSuspended()); } } @@ -85,6 +86,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { Bundle showBadge = new Bundle(); int[] importance = new int[mKeys.length]; Bundle userSentiment = new Bundle(); + Bundle mHidden = new Bundle(); for (int i = 0; i < mKeys.length; i++) { String key = mKeys[i]; @@ -101,11 +103,12 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { snoozeCriteria.putParcelableArrayList(key, getSnoozeCriteria(key, i)); showBadge.putBoolean(key, getShowBadge(i)); userSentiment.putInt(key, getUserSentiment(i)); + mHidden.putBoolean(key, getHidden(i)); } NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys, interceptedKeys.toArray(new String[0]), visibilityOverrides, suppressedVisualEffects, importance, explanation, overrideGroupKeys, - channels, overridePeople, snoozeCriteria, showBadge, userSentiment); + channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden); return update; } @@ -153,6 +156,10 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { return USER_SENTIMENT_NEUTRAL; } + private boolean getHidden(int index) { + return index % 2 == 0; + } + private ArrayList<String> getPeople(String key, int index) { ArrayList<String> people = new ArrayList<>(); for (int i = 0; i < index; i++) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 9afaa249e9d4..b82becd64f65 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -2739,4 +2739,44 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertFalse(mService.isVisuallyInterruptive(r1, r2)); } + + @Test + public void testHideAndUnhideNotificationsOnSuspendedPackageBroadcast() { + // post 2 notification from this package + final NotificationRecord notif1 = generateNotificationRecord( + mTestNotificationChannel, 1, null, true); + final NotificationRecord notif2 = generateNotificationRecord( + mTestNotificationChannel, 2, null, false); + mService.addNotification(notif1); + mService.addNotification(notif2); + + // on broadcast, hide the 2 notifications + mService.simulatePackageSuspendBroadcast(true, PKG); + ArgumentCaptor<List> captorHide = ArgumentCaptor.forClass(List.class); + verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture()); + assertEquals(2, captorHide.getValue().size()); + + // on broadcast, unhide the 2 notifications + mService.simulatePackageSuspendBroadcast(false, PKG); + ArgumentCaptor<List> captorUnhide = ArgumentCaptor.forClass(List.class); + verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture()); + assertEquals(2, captorUnhide.getValue().size()); + } + + @Test + public void testNoNotificationsHiddenOnSuspendedPackageBroadcast() { + // post 2 notification from this package + final NotificationRecord notif1 = generateNotificationRecord( + mTestNotificationChannel, 1, null, true); + final NotificationRecord notif2 = generateNotificationRecord( + mTestNotificationChannel, 2, null, false); + mService.addNotification(notif1); + mService.addNotification(notif2); + + // on broadcast, nothing is hidden since no notifications are of package "test_package" + mService.simulatePackageSuspendBroadcast(true, "test_package"); + ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); + verify(mListeners, times(1)).notifyHiddenLocked(captor.capture()); + assertEquals(0, captor.getValue().size()); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java index 4f446a9afb98..1073a800527a 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.app.slice.SliceSpec; +import android.app.usage.UsageStatsManagerInternal; import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.Binder; @@ -66,6 +67,8 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @Before public void setup() { LocalServices.addService(PackageManagerInternal.class, mock(PackageManagerInternal.class)); + LocalServices.addService(UsageStatsManagerInternal.class, + mock(UsageStatsManagerInternal.class)); mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class)); mContext.getTestablePermissions().setPermission(TEST_URI, PERMISSION_GRANTED); @@ -77,6 +80,7 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @After public void teardown() { LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.removeServiceForTest(UsageStatsManagerInternal.class); } @Test diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index e836677e38fc..1af5f469c0f0 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -36,6 +36,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; + import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; @@ -43,8 +44,8 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.usage.AppStandbyInfo; -import android.app.usage.UsageStatsManager.StandbyBuckets; import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManager.StandbyBuckets; import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; @@ -100,7 +101,6 @@ import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -690,7 +690,9 @@ public class AppStandbyController { || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION || event.mEventType == UsageEvents.Event.USER_INTERACTION - || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN)) { + || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN + || event.mEventType == UsageEvents.Event.SLICE_PINNED + || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV)) { final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory( event.mPackage, userId, elapsedRealtime); @@ -699,7 +701,8 @@ public class AppStandbyController { final long nextCheckTime; final int subReason = usageEventToSubReason(event.mEventType); final int reason = REASON_MAIN_USAGE | subReason; - if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) { + if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN + || event.mEventType == UsageEvents.Event.SLICE_PINNED) { // Mild usage elevates to WORKING_SET but doesn't change usage time. mAppIdleHistory.reportUsage(appHistory, event.mPackage, STANDBY_BUCKET_WORKING_SET, subReason, diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index d9742825ac70..c2e38f2f0bd8 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -761,6 +761,10 @@ class UserUsageStatsService { return "STANDBY_BUCKET_CHANGED"; case UsageEvents.Event.NOTIFICATION_INTERRUPTION: return "NOTIFICATION_INTERRUPTION"; + case UsageEvents.Event.SLICE_PINNED: + return "SLICE_PINNED"; + case UsageEvents.Event.SLICE_PINNED_PRIV: + return "SLICE_PINNED_PRIV"; default: return "UNKNOWN"; } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index cd3fdeefee13..11609435205f 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -15,36 +15,68 @@ */ package com.android.server.soundtrigger; + +import static android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE; +import static android.content.Context.BIND_AUTO_CREATE; +import static android.content.Context.BIND_FOREGROUND_SERVICE; +import static android.content.pm.PackageManager.GET_META_DATA; +import static android.content.pm.PackageManager.GET_SERVICES; +import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR; import static android.hardware.soundtrigger.SoundTrigger.STATUS_OK; +import static android.provider.Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY; +import static android.provider.Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT; + +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.PendingIntent; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.content.pm.PackageManager; -import android.Manifest; +import android.content.pm.ResolveInfo; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.SoundTrigger; -import android.hardware.soundtrigger.SoundTrigger.SoundModel; import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel; import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel; import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; +import android.hardware.soundtrigger.SoundTrigger.SoundModel; +import android.media.soundtrigger.ISoundTriggerDetectionService; +import android.media.soundtrigger.ISoundTriggerDetectionServiceClient; +import android.media.soundtrigger.SoundTriggerDetectionService; import android.media.soundtrigger.SoundTriggerManager; +import android.os.Binder; import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; import android.os.Parcel; import android.os.ParcelUuid; import android.os.PowerManager; import android.os.RemoteException; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; -import com.android.server.SystemService; +import com.android.internal.annotations.GuardedBy; import com.android.internal.app.ISoundTriggerService; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.Preconditions; +import com.android.server.SystemService; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.TreeMap; import java.util.UUID; +import java.util.concurrent.TimeUnit; /** * A single SystemService to manage all sound/voice-based sound models on the DSP. @@ -67,9 +99,13 @@ public class SoundTriggerService extends SystemService { private SoundTriggerHelper mSoundTriggerHelper; private final TreeMap<UUID, SoundModel> mLoadedModels; private Object mCallbacksLock; - private final TreeMap<UUID, LocalSoundTriggerRecognitionStatusCallback> mIntentCallbacks; + private final TreeMap<UUID, IRecognitionStatusCallback> mCallbacks; private PowerManager.WakeLock mWakelock; + /** Number of ops run by the {@link RemoteSoundTriggerDetectionService} per package name */ + @GuardedBy("mLock") + private final ArrayMap<String, NumOps> mNumOpsPerPackage = new ArrayMap<>(); + public SoundTriggerService(Context context) { super(context); mContext = context; @@ -77,7 +113,7 @@ public class SoundTriggerService extends SystemService { mLocalSoundTriggerService = new LocalSoundTriggerService(context); mLoadedModels = new TreeMap<UUID, SoundModel>(); mCallbacksLock = new Object(); - mIntentCallbacks = new TreeMap<UUID, LocalSoundTriggerRecognitionStatusCallback>(); + mCallbacks = new TreeMap<>(); mLock = new Object(); } @@ -214,7 +250,7 @@ public class SoundTriggerService extends SystemService { if (oldModel != null && !oldModel.equals(soundModel)) { mSoundTriggerHelper.unloadGenericSoundModel(soundModel.uuid); synchronized (mCallbacksLock) { - mIntentCallbacks.remove(soundModel.uuid); + mCallbacks.remove(soundModel.uuid); } } mLoadedModels.put(soundModel.uuid, soundModel); @@ -245,7 +281,7 @@ public class SoundTriggerService extends SystemService { if (oldModel != null && !oldModel.equals(soundModel)) { mSoundTriggerHelper.unloadKeyphraseSoundModel(soundModel.keyphrases[0].id); synchronized (mCallbacksLock) { - mIntentCallbacks.remove(soundModel.uuid); + mCallbacks.remove(soundModel.uuid); } } mLoadedModels.put(soundModel.uuid, soundModel); @@ -254,8 +290,28 @@ public class SoundTriggerService extends SystemService { } @Override + public int startRecognitionForService(ParcelUuid soundModelId, Bundle params, + ComponentName detectionService, SoundTrigger.RecognitionConfig config) { + Preconditions.checkNotNull(soundModelId); + Preconditions.checkNotNull(detectionService); + Preconditions.checkNotNull(config); + + return startRecognitionForInt(soundModelId, + new RemoteSoundTriggerDetectionService(soundModelId.getUuid(), + params, detectionService, Binder.getCallingUserHandle(), config), config); + + } + + @Override public int startRecognitionForIntent(ParcelUuid soundModelId, PendingIntent callbackIntent, SoundTrigger.RecognitionConfig config) { + return startRecognitionForInt(soundModelId, + new LocalSoundTriggerRecognitionStatusIntentCallback(soundModelId.getUuid(), + callbackIntent, config), config); + } + + private int startRecognitionForInt(ParcelUuid soundModelId, + IRecognitionStatusCallback callback, SoundTrigger.RecognitionConfig config) { enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER); if (!isInitialized()) return STATUS_ERROR; if (DEBUG) { @@ -268,27 +324,25 @@ public class SoundTriggerService extends SystemService { Slog.e(TAG, soundModelId + " is not loaded"); return STATUS_ERROR; } - LocalSoundTriggerRecognitionStatusCallback callback = null; + IRecognitionStatusCallback existingCallback = null; synchronized (mCallbacksLock) { - callback = mIntentCallbacks.get(soundModelId.getUuid()); + existingCallback = mCallbacks.get(soundModelId.getUuid()); } - if (callback != null) { + if (existingCallback != null) { Slog.e(TAG, soundModelId + " is already running"); return STATUS_ERROR; } - callback = new LocalSoundTriggerRecognitionStatusCallback(soundModelId.getUuid(), - callbackIntent, config); int ret; switch (soundModel.type) { case SoundModel.TYPE_KEYPHRASE: { KeyphraseSoundModel keyphraseSoundModel = (KeyphraseSoundModel) soundModel; ret = mSoundTriggerHelper.startKeyphraseRecognition( - keyphraseSoundModel.keyphrases[0].id, keyphraseSoundModel, callback, - config); + keyphraseSoundModel.keyphrases[0].id, keyphraseSoundModel, callback, + config); } break; case SoundModel.TYPE_GENERIC_SOUND: ret = mSoundTriggerHelper.startGenericRecognition(soundModel.uuid, - (GenericSoundModel) soundModel, callback, config); + (GenericSoundModel) soundModel, callback, config); break; default: Slog.e(TAG, "Unknown model type"); @@ -300,7 +354,7 @@ public class SoundTriggerService extends SystemService { return ret; } synchronized (mCallbacksLock) { - mIntentCallbacks.put(soundModelId.getUuid(), callback); + mCallbacks.put(soundModelId.getUuid(), callback); } } return STATUS_OK; @@ -320,9 +374,9 @@ public class SoundTriggerService extends SystemService { Slog.e(TAG, soundModelId + " is not loaded"); return STATUS_ERROR; } - LocalSoundTriggerRecognitionStatusCallback callback = null; + IRecognitionStatusCallback callback = null; synchronized (mCallbacksLock) { - callback = mIntentCallbacks.get(soundModelId.getUuid()); + callback = mCallbacks.get(soundModelId.getUuid()); } if (callback == null) { Slog.e(TAG, soundModelId + " is not running"); @@ -347,7 +401,7 @@ public class SoundTriggerService extends SystemService { return ret; } synchronized (mCallbacksLock) { - mIntentCallbacks.remove(soundModelId.getUuid()); + mCallbacks.remove(soundModelId.getUuid()); } } return STATUS_OK; @@ -394,8 +448,7 @@ public class SoundTriggerService extends SystemService { enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER); if (!isInitialized()) return false; synchronized (mCallbacksLock) { - LocalSoundTriggerRecognitionStatusCallback callback = - mIntentCallbacks.get(parcelUuid.getUuid()); + IRecognitionStatusCallback callback = mCallbacks.get(parcelUuid.getUuid()); if (callback == null) { return false; } @@ -404,13 +457,13 @@ public class SoundTriggerService extends SystemService { } } - private final class LocalSoundTriggerRecognitionStatusCallback + private final class LocalSoundTriggerRecognitionStatusIntentCallback extends IRecognitionStatusCallback.Stub { private UUID mUuid; private PendingIntent mCallbackIntent; private RecognitionConfig mRecognitionConfig; - public LocalSoundTriggerRecognitionStatusCallback(UUID modelUuid, + public LocalSoundTriggerRecognitionStatusIntentCallback(UUID modelUuid, PendingIntent callbackIntent, RecognitionConfig config) { mUuid = modelUuid; @@ -528,7 +581,7 @@ public class SoundTriggerService extends SystemService { private void removeCallback(boolean releaseWakeLock) { mCallbackIntent = null; synchronized (mCallbacksLock) { - mIntentCallbacks.remove(mUuid); + mCallbacks.remove(mUuid); if (releaseWakeLock) { mWakelock.release(); } @@ -536,6 +589,478 @@ public class SoundTriggerService extends SystemService { } } + /** + * Counts the number of operations added in the last 24 hours. + */ + private static class NumOps { + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private int[] mNumOps = new int[24]; + @GuardedBy("mLock") + private long mLastOpsHourSinceBoot; + + /** + * Clear buckets of new hours that have elapsed since last operation. + * + * <p>I.e. when the last operation was triggered at 1:40 and the current operation was + * triggered at 4:03, the buckets "2, 3, and 4" are cleared. + * + * @param currentTime Current elapsed time since boot in ns + */ + void clearOldOps(long currentTime) { + synchronized (mLock) { + long numHoursSinceBoot = TimeUnit.HOURS.convert(currentTime, TimeUnit.NANOSECONDS); + + // Clear buckets of new hours that have elapsed since last operation + // I.e. when the last operation was triggered at 1:40 and the current + // operation was triggered at 4:03, the bucket "2, 3, and 4" is cleared + if (mLastOpsHourSinceBoot != 0) { + for (long hour = mLastOpsHourSinceBoot + 1; hour <= numHoursSinceBoot; hour++) { + mNumOps[(int) (hour % 24)] = 0; + } + } + } + } + + /** + * Add a new operation. + * + * @param currentTime Current elapsed time since boot in ns + */ + void addOp(long currentTime) { + synchronized (mLock) { + long numHoursSinceBoot = TimeUnit.HOURS.convert(currentTime, TimeUnit.NANOSECONDS); + + mNumOps[(int) (numHoursSinceBoot % 24)]++; + mLastOpsHourSinceBoot = numHoursSinceBoot; + } + } + + /** + * Get the total operations added in the last 24 hours. + * + * @return The total number of operations added in the last 24 hours + */ + int getOpsAdded() { + synchronized (mLock) { + int totalOperationsInLastDay = 0; + for (int i = 0; i < 24; i++) { + totalOperationsInLastDay += mNumOps[i]; + } + + return totalOperationsInLastDay; + } + } + } + + private interface Operation { + void run(int opId, ISoundTriggerDetectionService service) throws RemoteException; + } + + /** + * Local end for a {@link SoundTriggerDetectionService}. Operations are queued up and executed + * when the service connects. + * + * <p>If operations take too long they are forcefully aborted. + * + * <p>This also limits the amount of operations in 24 hours. + */ + private class RemoteSoundTriggerDetectionService + extends IRecognitionStatusCallback.Stub implements ServiceConnection { + private static final int MSG_STOP_ALL_PENDING_OPERATIONS = 1; + + private final Object mRemoteServiceLock = new Object(); + + /** UUID of the model the service is started for */ + private final @NonNull ParcelUuid mPuuid; + /** Params passed into the start method for the service */ + private final @Nullable Bundle mParams; + /** Component name passed when starting the service */ + private final @NonNull ComponentName mServiceName; + /** User that started the service */ + private final @NonNull UserHandle mUser; + /** Configuration of the recognition the service is handling */ + private final @NonNull RecognitionConfig mRecognitionConfig; + /** Wake lock keeping the remote service alive */ + private final @NonNull PowerManager.WakeLock mRemoteServiceWakeLock; + + private final @NonNull Handler mHandler; + + /** Callbacks that are called by the service */ + private final @NonNull ISoundTriggerDetectionServiceClient mClient; + + /** Operations that are pending because the service is not yet connected */ + @GuardedBy("mRemoteServiceLock") + private final ArrayList<Operation> mPendingOps = new ArrayList<>(); + /** Operations that have been send to the service but have no yet finished */ + @GuardedBy("mRemoteServiceLock") + private final ArraySet<Integer> mRunningOpIds = new ArraySet<>(); + /** The number of operations executed in each of the last 24 hours */ + private final NumOps mNumOps; + + /** The service binder if connected */ + @GuardedBy("mRemoteServiceLock") + private @Nullable ISoundTriggerDetectionService mService; + /** Whether the service has been bound */ + @GuardedBy("mRemoteServiceLock") + private boolean mIsBound; + /** Whether the service has been destroyed */ + @GuardedBy("mRemoteServiceLock") + private boolean mIsDestroyed; + /** + * Set once a final op is scheduled. No further ops can be added and the service is + * destroyed once the op finishes. + */ + @GuardedBy("mRemoteServiceLock") + private boolean mDestroyOnceRunningOpsDone; + + /** Total number of operations performed by this service */ + @GuardedBy("mRemoteServiceLock") + private int mNumTotalOpsPerformed; + + /** + * Create a new remote sound trigger detection service. This only binds to the service when + * operations are in flight. Each operation has a certain time it can run. Once no + * operations are allowed to run anymore, {@link #stopAllPendingOperations() all operations + * are aborted and stopped} and the service is disconnected. + * + * @param modelUuid The UUID of the model the recognition is for + * @param params The params passed to each method of the service + * @param serviceName The component name of the service + * @param user The user of the service + * @param config The configuration of the recognition + */ + public RemoteSoundTriggerDetectionService(@NonNull UUID modelUuid, + @Nullable Bundle params, @NonNull ComponentName serviceName, @NonNull UserHandle user, + @NonNull RecognitionConfig config) { + mPuuid = new ParcelUuid(modelUuid); + mParams = params; + mServiceName = serviceName; + mUser = user; + mRecognitionConfig = config; + mHandler = new Handler(Looper.getMainLooper()); + + PowerManager pm = ((PowerManager) mContext.getSystemService(Context.POWER_SERVICE)); + mRemoteServiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + "RemoteSoundTriggerDetectionService " + mServiceName.getPackageName() + ":" + + mServiceName.getClassName()); + + synchronized (mLock) { + NumOps numOps = mNumOpsPerPackage.get(mServiceName.getPackageName()); + if (numOps == null) { + numOps = new NumOps(); + mNumOpsPerPackage.put(mServiceName.getPackageName(), numOps); + } + mNumOps = numOps; + } + + mClient = new ISoundTriggerDetectionServiceClient.Stub() { + @Override + public void onOpFinished(int opId) { + long token = Binder.clearCallingIdentity(); + try { + synchronized (mRemoteServiceLock) { + mRunningOpIds.remove(opId); + + if (mRunningOpIds.isEmpty() && mPendingOps.isEmpty()) { + if (mDestroyOnceRunningOpsDone) { + destroy(); + } else { + disconnectLocked(); + } + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + } + + @Override + public boolean pingBinder() { + return !(mIsDestroyed || mDestroyOnceRunningOpsDone); + } + + /** + * Disconnect from the service, but allow to re-connect when new operations are triggered. + */ + private void disconnectLocked() { + if (mService != null) { + try { + mService.removeClient(mPuuid); + } catch (Exception e) { + Slog.e(TAG, mPuuid + ": Cannot remove client", e); + } + + mService = null; + } + + if (mIsBound) { + mContext.unbindService(RemoteSoundTriggerDetectionService.this); + mIsBound = false; + + synchronized (mCallbacksLock) { + mRemoteServiceWakeLock.release(); + } + } + } + + /** + * Disconnect, do not allow to reconnect to the service. All further operations will be + * dropped. + */ + private void destroy() { + if (DEBUG) Slog.v(TAG, mPuuid + ": destroy"); + + synchronized (mRemoteServiceLock) { + disconnectLocked(); + + mIsDestroyed = true; + } + + // The callback is removed before the flag is set + if (!mDestroyOnceRunningOpsDone) { + synchronized (mCallbacksLock) { + mCallbacks.remove(mPuuid.getUuid()); + } + } + } + + /** + * Stop all pending operations and then disconnect for the service. + */ + private void stopAllPendingOperations() { + synchronized (mRemoteServiceLock) { + if (mIsDestroyed) { + return; + } + + if (mService != null) { + int numOps = mRunningOpIds.size(); + for (int i = 0; i < numOps; i++) { + try { + mService.onStopOperation(mPuuid, mRunningOpIds.valueAt(i)); + } catch (Exception e) { + Slog.e(TAG, mPuuid + ": Could not stop operation " + + mRunningOpIds.valueAt(i), e); + } + } + + mRunningOpIds.clear(); + } + + disconnectLocked(); + } + } + + /** + * Verify that the service has the expected properties and then bind to the service + */ + private void bind() { + long token = Binder.clearCallingIdentity(); + try { + Intent i = new Intent(); + i.setComponent(mServiceName); + + ResolveInfo ri = mContext.getPackageManager().resolveServiceAsUser(i, + GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING, + mUser.getIdentifier()); + + if (ri == null) { + Slog.w(TAG, mPuuid + ": " + mServiceName + " not found"); + return; + } + + if (!BIND_SOUND_TRIGGER_DETECTION_SERVICE + .equals(ri.serviceInfo.permission)) { + Slog.w(TAG, mPuuid + ": " + mServiceName + " does not require " + + BIND_SOUND_TRIGGER_DETECTION_SERVICE); + return; + } + + mIsBound = mContext.bindServiceAsUser(i, this, + BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE, mUser); + + if (mIsBound) { + mRemoteServiceWakeLock.acquire(); + } else { + Slog.w(TAG, mPuuid + ": Could not bind to " + mServiceName); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Run an operation (i.e. send it do the service). If the service is not connected, this + * binds the service and then runs the operation once connected. + * + * @param op The operation to run + */ + private void runOrAddOperation(Operation op) { + synchronized (mRemoteServiceLock) { + if (mIsDestroyed || mDestroyOnceRunningOpsDone) { + return; + } + + if (mService == null) { + mPendingOps.add(op); + + if (!mIsBound) { + bind(); + } + } else { + long currentTime = System.nanoTime(); + mNumOps.clearOldOps(currentTime); + + // Drop operation if too many were executed in the last 24 hours. + int opsAllowed = Settings.Global.getInt(mContext.getContentResolver(), + MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY, + Integer.MAX_VALUE); + + int opsAdded = mNumOps.getOpsAdded(); + if (mNumOps.getOpsAdded() >= opsAllowed) { + if (DEBUG || opsAllowed + 10 > opsAdded) { + Slog.w(TAG, mPuuid + ": Dropped operation as too many operations were " + + "run in last 24 hours"); + } + return; + } + + mNumOps.addOp(currentTime); + + // Find a free opID + int opId = mNumTotalOpsPerformed; + do { + mNumTotalOpsPerformed++; + } while (mRunningOpIds.contains(opId)); + + // Run OP + try { + if (DEBUG) Slog.v(TAG, mPuuid + ": runOp " + opId); + + op.run(opId, mService); + mRunningOpIds.add(opId); + } catch (Exception e) { + Slog.e(TAG, mPuuid + ": Could not run operation " + opId, e); + } + + // Unbind from service if no operations are left (i.e. if the operation failed) + if (mPendingOps.isEmpty() && mRunningOpIds.isEmpty()) { + if (mDestroyOnceRunningOpsDone) { + destroy(); + } else { + disconnectLocked(); + } + } else { + mHandler.removeMessages(MSG_STOP_ALL_PENDING_OPERATIONS); + mHandler.sendMessageDelayed(obtainMessage( + RemoteSoundTriggerDetectionService::stopAllPendingOperations, this) + .setWhat(MSG_STOP_ALL_PENDING_OPERATIONS), + Settings.Global.getLong(mContext.getContentResolver(), + SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT, + Long.MAX_VALUE)); + } + } + } + } + + @Override + public void onKeyphraseDetected(SoundTrigger.KeyphraseRecognitionEvent event) { + Slog.w(TAG, mPuuid + "->" + mServiceName + ": IGNORED onKeyphraseDetected(" + event + + ")"); + } + + @Override + public void onGenericSoundTriggerDetected(SoundTrigger.GenericRecognitionEvent event) { + if (DEBUG) Slog.v(TAG, mPuuid + ": Generic sound trigger event: " + event); + + runOrAddOperation((opId, service) -> { + if (!mRecognitionConfig.allowMultipleTriggers) { + synchronized (mCallbacksLock) { + mCallbacks.remove(mPuuid.getUuid()); + } + mDestroyOnceRunningOpsDone = true; + } + + service.onGenericRecognitionEvent(mPuuid, opId, event); + }); + } + + @Override + public void onError(int status) { + if (DEBUG) Slog.v(TAG, mPuuid + ": onError: " + status); + + runOrAddOperation((opId, service) -> { + synchronized (mCallbacksLock) { + mCallbacks.remove(mPuuid.getUuid()); + } + mDestroyOnceRunningOpsDone = true; + + service.onError(mPuuid, opId, status); + }); + } + + @Override + public void onRecognitionPaused() { + Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionPaused"); + } + + @Override + public void onRecognitionResumed() { + Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionResumed"); + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceConnected(" + service + ")"); + + synchronized (mRemoteServiceLock) { + mService = ISoundTriggerDetectionService.Stub.asInterface(service); + + try { + mService.setClient(mPuuid, mParams, mClient); + } catch (Exception e) { + Slog.e(TAG, mPuuid + ": Could not init " + mServiceName, e); + return; + } + + while (!mPendingOps.isEmpty()) { + runOrAddOperation(mPendingOps.remove(0)); + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceDisconnected"); + + synchronized (mRemoteServiceLock) { + mService = null; + } + } + + @Override + public void onBindingDied(ComponentName name) { + if (DEBUG) Slog.v(TAG, mPuuid + ": onBindingDied"); + + synchronized (mRemoteServiceLock) { + destroy(); + } + } + + @Override + public void onNullBinding(ComponentName name) { + Slog.w(TAG, name + " for model " + mPuuid + " returned a null binding"); + + synchronized (mRemoteServiceLock) { + disconnectLocked(); + } + } + } + private void grabWakeLock() { synchronized (mCallbacksLock) { if (mWakelock == null) { diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java index 992545d62408..7475c74ea70c 100644 --- a/telephony/java/android/telephony/CellIdentityTdscdma.java +++ b/telephony/java/android/telephony/CellIdentityTdscdma.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.Nullable; import android.os.Parcel; import android.text.TextUtils; @@ -34,6 +35,10 @@ public final class CellIdentityTdscdma extends CellIdentity { private final int mCid; // 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown. private final int mCpid; + // long alpha Operator Name String or Enhanced Operator Name String + private final String mAlphaLong; + // short alpha Operator Name String or Enhanced Operator Name String + private final String mAlphaShort; /** * @hide @@ -43,6 +48,8 @@ public final class CellIdentityTdscdma extends CellIdentity { mLac = Integer.MAX_VALUE; mCid = Integer.MAX_VALUE; mCpid = Integer.MAX_VALUE; + mAlphaLong = null; + mAlphaShort = null; } /** @@ -55,7 +62,7 @@ public final class CellIdentityTdscdma extends CellIdentity { * @hide */ public CellIdentityTdscdma(int mcc, int mnc, int lac, int cid, int cpid) { - this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid); + this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid, null, null); } /** @@ -65,6 +72,7 @@ public final class CellIdentityTdscdma extends CellIdentity { * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown * + * FIXME: This is a temporary constructor to facilitate migration. * @hide */ public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) { @@ -72,10 +80,34 @@ public final class CellIdentityTdscdma extends CellIdentity { mLac = lac; mCid = cid; mCpid = cpid; + mAlphaLong = null; + mAlphaShort = null; + } + + /** + * @param mcc 3-digit Mobile Country Code in string format + * @param mnc 2 or 3-digit Mobile Network Code in string format + * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown + * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown + * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown + * @param alphal long alpha Operator Name String or Enhanced Operator Name String + * @param alphas short alpha Operator Name String or Enhanced Operator Name String + * + * @hide + */ + public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid, + String alphal, String alphas) { + super(TAG, TYPE_TDSCDMA, mcc, mnc); + mLac = lac; + mCid = cid; + mCpid = cpid; + mAlphaLong = alphal; + mAlphaShort = alphas; } private CellIdentityTdscdma(CellIdentityTdscdma cid) { - this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid, cid.mCpid); + this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid, + cid.mCpid, cid.mAlphaLong, cid.mAlphaShort); } CellIdentityTdscdma copy() { @@ -119,9 +151,31 @@ public final class CellIdentityTdscdma extends CellIdentity { return mCpid; } + /** + * @return The long alpha tag associated with the current scan result (may be the operator + * name string or extended operator name string). May be null if unknown. + * + * @hide + */ + @Nullable + public CharSequence getOperatorAlphaLong() { + return mAlphaLong; + } + + /** + * @return The short alpha tag associated with the current scan result (may be the operator + * name string or extended operator name string). May be null if unknown. + * + * @hide + */ + @Nullable + public CharSequence getOperatorAlphaShort() { + return mAlphaShort; + } + @Override public int hashCode() { - return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid); + return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid, mAlphaLong, mAlphaShort); } @Override @@ -139,7 +193,9 @@ public final class CellIdentityTdscdma extends CellIdentity { && TextUtils.equals(mMncStr, o.mMncStr) && mLac == o.mLac && mCid == o.mCid - && mCpid == o.mCpid; + && mCpid == o.mCpid + && mAlphaLong == o.mAlphaLong + && mAlphaShort == o.mAlphaShort; } @Override @@ -150,6 +206,8 @@ public final class CellIdentityTdscdma extends CellIdentity { .append(" mLac=").append(mLac) .append(" mCid=").append(mCid) .append(" mCpid=").append(mCpid) + .append(" mAlphaLong=").append(mAlphaLong) + .append(" mAlphaShort=").append(mAlphaShort) .append("}").toString(); } @@ -161,6 +219,8 @@ public final class CellIdentityTdscdma extends CellIdentity { dest.writeInt(mLac); dest.writeInt(mCid); dest.writeInt(mCpid); + dest.writeString(mAlphaLong); + dest.writeString(mAlphaShort); } /** Construct from Parcel, type has already been processed */ @@ -169,6 +229,8 @@ public final class CellIdentityTdscdma extends CellIdentity { mLac = in.readInt(); mCid = in.readInt(); mCpid = in.readInt(); + mAlphaLong = in.readString(); + mAlphaShort = in.readString(); if (DBG) log(toString()); } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index c1300b3892ff..0b15191951da 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -5458,7 +5458,10 @@ public class TelephonyManager { * app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @param request Contains all the RAT with bands/channels that need to be scanned. - * @param executor The executor through which the callback should be invoked. + * @param executor The executor through which the callback should be invoked. Since the scan + * request may trigger multiple callbacks and they must be invoked in the same order as + * they are received by the platform, the user should provide an executor which executes + * tasks one at a time in serial order. For example AsyncTask.SERIAL_EXECUTOR. * @param callback Returns network scan results or errors. * @return A NetworkScan obj which contains a callback which can be used to stop the scan. */ @@ -5484,7 +5487,7 @@ public class TelephonyManager { @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public NetworkScan requestNetworkScan( NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) { - return requestNetworkScan(request, AsyncTask.THREAD_POOL_EXECUTOR, callback); + return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback); } /** diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java index 99e2db883e52..96ff33255b53 100644 --- a/telephony/java/android/telephony/TelephonyScanManager.java +++ b/telephony/java/android/telephony/TelephonyScanManager.java @@ -137,8 +137,10 @@ public final class TelephonyScanManager { for (int i = 0; i < parcelables.length; i++) { ci[i] = (CellInfo) parcelables[i]; } - executor.execute(() -> - callback.onResults((List<CellInfo>) Arrays.asList(ci))); + executor.execute(() ->{ + Rlog.d(TAG, "onResults: " + ci.toString()); + callback.onResults((List<CellInfo>) Arrays.asList(ci)); + }); } catch (Exception e) { Rlog.e(TAG, "Exception in networkscan callback onResults", e); } @@ -146,14 +148,20 @@ public final class TelephonyScanManager { case CALLBACK_SCAN_ERROR: try { final int errorCode = message.arg1; - executor.execute(() -> callback.onError(errorCode)); + executor.execute(() -> { + Rlog.d(TAG, "onError: " + errorCode); + callback.onError(errorCode); + }); } catch (Exception e) { Rlog.e(TAG, "Exception in networkscan callback onError", e); } break; case CALLBACK_SCAN_COMPLETE: try { - executor.execute(() -> callback.onComplete()); + executor.execute(() -> { + Rlog.d(TAG, "onComplete"); + callback.onComplete(); + }); mScanInfo.remove(message.arg2); } catch (Exception e) { Rlog.e(TAG, "Exception in networkscan callback onComplete", e); diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 73a05af8e56e..145ed7eeabdd 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -17,10 +17,10 @@ package android.telephony.data; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.StringDef; import android.content.ContentValues; import android.database.Cursor; import android.hardware.radio.V1_0.ApnTypes; +import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.provider.Telephony; @@ -28,17 +28,15 @@ import android.telephony.Rlog; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.Log; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.InetAddress; -import java.net.MalformedURLException; -import java.net.URL; import java.net.UnknownHostException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -46,25 +44,184 @@ import java.util.Objects; */ public class ApnSetting implements Parcelable { - static final String LOG_TAG = "ApnSetting"; + private static final String LOG_TAG = "ApnSetting"; private static final boolean VDBG = false; + private static final Map<String, Integer> APN_TYPE_STRING_MAP; + private static final Map<Integer, String> APN_TYPE_INT_MAP; + private static final Map<String, Integer> PROTOCOL_STRING_MAP; + private static final Map<Integer, String> PROTOCOL_INT_MAP; + private static final Map<String, Integer> MVNO_TYPE_STRING_MAP; + private static final Map<Integer, String> MVNO_TYPE_INT_MAP; + private static final int NOT_IN_MAP_INT = -1; + private static final int NO_PORT_SPECIFIED = -1; + + /** All APN types except IA. */ + private static final int TYPE_ALL_BUT_IA = ApnTypes.ALL & (~ApnTypes.IA); + + /** APN type for default data traffic and HiPri traffic. */ + public static final int TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI; + /** APN type for MMS traffic. */ + public static final int TYPE_MMS = ApnTypes.MMS; + /** APN type for SUPL assisted GPS. */ + public static final int TYPE_SUPL = ApnTypes.SUPL; + /** APN type for DUN traffic. */ + public static final int TYPE_DUN = ApnTypes.DUN; + /** APN type for HiPri traffic. */ + public static final int TYPE_HIPRI = ApnTypes.HIPRI; + /** APN type for accessing the carrier's FOTA portal, used for over the air updates. */ + public static final int TYPE_FOTA = ApnTypes.FOTA; + /** APN type for IMS. */ + public static final int TYPE_IMS = ApnTypes.IMS; + /** APN type for CBS. */ + public static final int TYPE_CBS = ApnTypes.CBS; + /** APN type for IA Initial Attach APN. */ + public static final int TYPE_IA = ApnTypes.IA; + /** + * APN type for Emergency PDN. This is not an IA apn, but is used + * for access to carrier services in an emergency call situation. + */ + public static final int TYPE_EMERGENCY = ApnTypes.EMERGENCY; + + /** @hide */ + @IntDef(flag = true, prefix = { "TYPE_" }, value = { + TYPE_DEFAULT, + TYPE_MMS, + TYPE_SUPL, + TYPE_DUN, + TYPE_HIPRI, + TYPE_FOTA, + TYPE_IMS, + TYPE_CBS, + TYPE_IA, + TYPE_EMERGENCY + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ApnType {} + + // Possible values for authentication types. + /** No authentication type. */ + public static final int AUTH_TYPE_NONE = 0; + /** Authentication type for PAP. */ + public static final int AUTH_TYPE_PAP = 1; + /** Authentication type for CHAP. */ + public static final int AUTH_TYPE_CHAP = 2; + /** Authentication type for PAP or CHAP. */ + public static final int AUTH_TYPE_PAP_OR_CHAP = 3; + + /** @hide */ + @IntDef(prefix = { "AUTH_TYPE_" }, value = { + AUTH_TYPE_NONE, + AUTH_TYPE_PAP, + AUTH_TYPE_CHAP, + AUTH_TYPE_PAP_OR_CHAP, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AuthType {} + + // Possible values for protocol. + /** Protocol type for IP. */ + public static final int PROTOCOL_IP = 0; + /** Protocol type for IPV6. */ + public static final int PROTOCOL_IPV6 = 1; + /** Protocol type for IPV4V6. */ + public static final int PROTOCOL_IPV4V6 = 2; + /** Protocol type for PPP. */ + public static final int PROTOCOL_PPP = 3; + + /** @hide */ + @IntDef(prefix = { "PROTOCOL_" }, value = { + PROTOCOL_IP, + PROTOCOL_IPV6, + PROTOCOL_IPV4V6, + PROTOCOL_PPP, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ProtocolType {} + + // Possible values for MVNO type. + /** MVNO type for service provider name. */ + public static final int MVNO_TYPE_SPN = 0; + /** MVNO type for IMSI. */ + public static final int MVNO_TYPE_IMSI = 1; + /** MVNO type for group identifier level 1. */ + public static final int MVNO_TYPE_GID = 2; + /** MVNO type for ICCID. */ + public static final int MVNO_TYPE_ICCID = 3; + + /** @hide */ + @IntDef(prefix = { "MVNO_TYPE_" }, value = { + MVNO_TYPE_SPN, + MVNO_TYPE_IMSI, + MVNO_TYPE_GID, + MVNO_TYPE_ICCID, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MvnoType {} + + static { + APN_TYPE_STRING_MAP = new ArrayMap<String, Integer>(); + APN_TYPE_STRING_MAP.put("*", TYPE_ALL_BUT_IA); + APN_TYPE_STRING_MAP.put("default", TYPE_DEFAULT); + APN_TYPE_STRING_MAP.put("mms", TYPE_MMS); + APN_TYPE_STRING_MAP.put("supl", TYPE_SUPL); + APN_TYPE_STRING_MAP.put("dun", TYPE_DUN); + APN_TYPE_STRING_MAP.put("hipri", TYPE_HIPRI); + APN_TYPE_STRING_MAP.put("fota", TYPE_FOTA); + APN_TYPE_STRING_MAP.put("ims", TYPE_IMS); + APN_TYPE_STRING_MAP.put("cbs", TYPE_CBS); + APN_TYPE_STRING_MAP.put("ia", TYPE_IA); + APN_TYPE_STRING_MAP.put("emergency", TYPE_EMERGENCY); + APN_TYPE_INT_MAP = new ArrayMap<Integer, String>(); + APN_TYPE_INT_MAP.put(TYPE_DEFAULT, "default"); + APN_TYPE_INT_MAP.put(TYPE_MMS, "mms"); + APN_TYPE_INT_MAP.put(TYPE_SUPL, "supl"); + APN_TYPE_INT_MAP.put(TYPE_DUN, "dun"); + APN_TYPE_INT_MAP.put(TYPE_HIPRI, "hipri"); + APN_TYPE_INT_MAP.put(TYPE_FOTA, "fota"); + APN_TYPE_INT_MAP.put(TYPE_IMS, "ims"); + APN_TYPE_INT_MAP.put(TYPE_CBS, "cbs"); + APN_TYPE_INT_MAP.put(TYPE_IA, "ia"); + APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, "emergency"); + + PROTOCOL_STRING_MAP = new ArrayMap<String, Integer>(); + PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP); + PROTOCOL_STRING_MAP.put("IPV6", PROTOCOL_IPV6); + PROTOCOL_STRING_MAP.put("IPV4V6", PROTOCOL_IPV4V6); + PROTOCOL_STRING_MAP.put("PPP", PROTOCOL_PPP); + PROTOCOL_INT_MAP = new ArrayMap<Integer, String>(); + PROTOCOL_INT_MAP.put(PROTOCOL_IP, "IP"); + PROTOCOL_INT_MAP.put(PROTOCOL_IPV6, "IPV6"); + PROTOCOL_INT_MAP.put(PROTOCOL_IPV4V6, "IPV4V6"); + PROTOCOL_INT_MAP.put(PROTOCOL_PPP, "PPP"); + + MVNO_TYPE_STRING_MAP = new ArrayMap<String, Integer>(); + MVNO_TYPE_STRING_MAP.put("spn", MVNO_TYPE_SPN); + MVNO_TYPE_STRING_MAP.put("imsi", MVNO_TYPE_IMSI); + MVNO_TYPE_STRING_MAP.put("gid", MVNO_TYPE_GID); + MVNO_TYPE_STRING_MAP.put("iccid", MVNO_TYPE_ICCID); + MVNO_TYPE_INT_MAP = new ArrayMap<Integer, String>(); + MVNO_TYPE_INT_MAP.put(MVNO_TYPE_SPN, "spn"); + MVNO_TYPE_INT_MAP.put(MVNO_TYPE_IMSI, "imsi"); + MVNO_TYPE_INT_MAP.put(MVNO_TYPE_GID, "gid"); + MVNO_TYPE_INT_MAP.put(MVNO_TYPE_ICCID, "iccid"); + } + private final String mEntryName; private final String mApnName; - private final InetAddress mProxy; - private final int mPort; - private final URL mMmsc; - private final InetAddress mMmsProxy; - private final int mMmsPort; + private final InetAddress mProxyAddress; + private final int mProxyPort; + private final Uri mMmsc; + private final InetAddress mMmsProxyAddress; + private final int mMmsProxyPort; private final String mUser; private final String mPassword; private final int mAuthType; - private final List<String> mTypes; - private final int mTypesBitmap; + private final int mApnTypeBitmask; private final int mId; private final String mOperatorNumeric; - private final String mProtocol; - private final String mRoamingProtocol; + private final int mProtocol; + private final int mRoamingProtocol; private final int mMtu; private final boolean mCarrierEnabled; @@ -78,22 +235,12 @@ public class ApnSetting implements Parcelable { private final int mWaitTime; private final int mMaxConnsTime; - private final String mMvnoType; + private final int mMvnoType; private final String mMvnoMatchData; private boolean mPermanentFailed = false; /** - * Returns the types bitmap of the APN. - * - * @return types bitmap of the APN - * @hide - */ - public int getTypesBitmap() { - return mTypesBitmap; - } - - /** * Returns the MTU size of the mobile interface to which the APN connected. * * @return the MTU size of the APN @@ -211,8 +358,8 @@ public class ApnSetting implements Parcelable { * * @return proxy address. */ - public InetAddress getProxy() { - return mProxy; + public InetAddress getProxyAddress() { + return mProxyAddress; } /** @@ -220,15 +367,15 @@ public class ApnSetting implements Parcelable { * * @return proxy port */ - public int getPort() { - return mPort; + public int getProxyPort() { + return mProxyPort; } /** - * Returns the MMSC URL of the APN. + * Returns the MMSC Uri of the APN. * - * @return MMSC URL. + * @return MMSC Uri. */ - public URL getMmsc() { + public Uri getMmsc() { return mMmsc; } @@ -237,8 +384,8 @@ public class ApnSetting implements Parcelable { * * @return MMS proxy address. */ - public InetAddress getMmsProxy() { - return mMmsProxy; + public InetAddress getMmsProxyAddress() { + return mMmsProxyAddress; } /** @@ -246,8 +393,8 @@ public class ApnSetting implements Parcelable { * * @return MMS proxy port */ - public int getMmsPort() { - return mMmsPort; + public int getMmsProxyPort() { + return mMmsProxyPort; } /** @@ -268,21 +415,9 @@ public class ApnSetting implements Parcelable { return mPassword; } - /** @hide */ - @IntDef({ - AUTH_TYPE_NONE, - AUTH_TYPE_PAP, - AUTH_TYPE_CHAP, - AUTH_TYPE_PAP_OR_CHAP, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface AuthType {} - /** * Returns the authentication type of the APN. * - * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}. - * * @return authentication type */ @AuthType @@ -290,32 +425,20 @@ public class ApnSetting implements Parcelable { return mAuthType; } - /** @hide */ - @StringDef({ - TYPE_DEFAULT, - TYPE_MMS, - TYPE_SUPL, - TYPE_DUN, - TYPE_HIPRI, - TYPE_FOTA, - TYPE_IMS, - TYPE_CBS, - TYPE_IA, - TYPE_EMERGENCY - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ApnType {} - /** - * Returns the list of APN types of the APN. + * Returns the bitmask of APN types. * - * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}. + * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple + * APN types, eg, a single APN may service regular internet traffic ("default") as well as + * MMS-specific connections. * - * @return the list of APN types + * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}. + * + * @see Builder#setApnTypeBitmask(int) + * @return a bitmask describing the types of the APN */ - @ApnType - public List<String> getTypes() { - return mTypes; + public @ApnType int getApnTypeBitmask() { + return mApnTypeBitmask; } /** @@ -328,7 +451,7 @@ public class ApnSetting implements Parcelable { } /** - * Returns the numeric operator ID for the APN. Usually + * Returns the numeric operator ID for the APN. Numeric operator ID is defined as * {@link android.provider.Telephony.Carriers#MCC} + * {@link android.provider.Telephony.Carriers#MNC}. * @@ -338,37 +461,29 @@ public class ApnSetting implements Parcelable { return mOperatorNumeric; } - /** @hide */ - @StringDef({ - PROTOCOL_IP, - PROTOCOL_IPV6, - PROTOCOL_IPV4V6, - PROTOCOL_PPP, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ProtocolType {} - /** * Returns the protocol to use to connect to this APN. * - * One of the {@code PDP_type} values in TS 27.007 section 10.1.1. - * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}. + * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1. * + * @see Builder#setProtocol(int) * @return the protocol */ @ProtocolType - public String getProtocol() { + public int getProtocol() { return mProtocol; } /** - * Returns the protocol to use to connect to this APN when roaming. + * Returns the protocol to use to connect to this APN while the device is roaming. * - * The syntax is the same as {@link android.provider.Telephony.Carriers#PROTOCOL}. + * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1. * + * @see Builder#setRoamingProtocol(int) * @return the roaming protocol */ - public String getRoamingProtocol() { + @ProtocolType + public int getRoamingProtocol() { return mRoamingProtocol; } @@ -398,41 +513,29 @@ public class ApnSetting implements Parcelable { return mNetworkTypeBitmask; } - /** @hide */ - @StringDef({ - MVNO_TYPE_SPN, - MVNO_TYPE_IMSI, - MVNO_TYPE_GID, - MVNO_TYPE_ICCID, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface MvnoType {} - /** * Returns the MVNO match type for this APN. * - * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}. - * + * @see Builder#setMvnoType(int) * @return the MVNO match type */ @MvnoType - public String getMvnoType() { + public int getMvnoType() { return mMvnoType; } private ApnSetting(Builder builder) { this.mEntryName = builder.mEntryName; this.mApnName = builder.mApnName; - this.mProxy = builder.mProxy; - this.mPort = builder.mPort; + this.mProxyAddress = builder.mProxyAddress; + this.mProxyPort = builder.mProxyPort; this.mMmsc = builder.mMmsc; - this.mMmsProxy = builder.mMmsProxy; - this.mMmsPort = builder.mMmsPort; + this.mMmsProxyAddress = builder.mMmsProxyAddress; + this.mMmsProxyPort = builder.mMmsProxyPort; this.mUser = builder.mUser; this.mPassword = builder.mPassword; this.mAuthType = builder.mAuthType; - this.mTypes = (builder.mTypes == null ? new ArrayList<String>() : builder.mTypes); - this.mTypesBitmap = builder.mTypesBitmap; + this.mApnTypeBitmask = builder.mApnTypeBitmask; this.mId = builder.mId; this.mOperatorNumeric = builder.mOperatorNumeric; this.mProtocol = builder.mProtocol; @@ -451,25 +554,25 @@ public class ApnSetting implements Parcelable { /** @hide */ public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName, - String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy, - int mmsPort, String user, String password, int authType, List<String> types, - String protocol, String roamingProtocol, boolean carrierEnabled, + String apnName, InetAddress proxy, int port, Uri mmsc, InetAddress mmsProxy, + int mmsPort, String user, String password, int authType, int mApnTypeBitmask, + int protocol, int roamingProtocol, boolean carrierEnabled, int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns, - int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) { + int waitTime, int maxConnsTime, int mtu, int mvnoType, String mvnoMatchData) { return new Builder() .setId(id) .setOperatorNumeric(operatorNumeric) .setEntryName(entryName) .setApnName(apnName) - .setProxy(proxy) - .setPort(port) + .setProxyAddress(proxy) + .setProxyPort(port) .setMmsc(mmsc) - .setMmsProxy(mmsProxy) - .setMmsPort(mmsPort) + .setMmsProxyAddress(mmsProxy) + .setMmsProxyPort(mmsPort) .setUser(user) .setPassword(password) .setAuthType(authType) - .setTypes(types) + .setApnTypeBitmask(mApnTypeBitmask) .setProtocol(protocol) .setRoamingProtocol(roamingProtocol) .setCarrierEnabled(carrierEnabled) @@ -487,7 +590,7 @@ public class ApnSetting implements Parcelable { /** @hide */ public static ApnSetting makeApnSetting(Cursor cursor) { - String[] types = parseTypes( + final int apnTypesBitmask = parseTypes( cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE))); int networkTypeBitmask = cursor.getInt( cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK)); @@ -507,7 +610,7 @@ public class ApnSetting implements Parcelable { cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))), portFromString(cursor.getString( cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))), - URLFromString(cursor.getString( + UriFromString(cursor.getString( cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))), inetAddressFromString(cursor.getString( cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))), @@ -516,10 +619,12 @@ public class ApnSetting implements Parcelable { cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)), cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)), cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)), - Arrays.asList(types), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)), - cursor.getString(cursor.getColumnIndexOrThrow( - Telephony.Carriers.ROAMING_PROTOCOL)), + apnTypesBitmask, + nullToNotInMapInt(PROTOCOL_STRING_MAP.get( + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)))), + nullToNotInMapInt(PROTOCOL_STRING_MAP.get( + cursor.getString(cursor.getColumnIndexOrThrow( + Telephony.Carriers.ROAMING_PROTOCOL)))), cursor.getInt(cursor.getColumnIndexOrThrow( Telephony.Carriers.CARRIER_ENABLED)) == 1, networkTypeBitmask, @@ -531,8 +636,9 @@ public class ApnSetting implements Parcelable { cursor.getInt(cursor.getColumnIndexOrThrow( Telephony.Carriers.MAX_CONNS_TIME)), cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)), - cursor.getString(cursor.getColumnIndexOrThrow( - Telephony.Carriers.MVNO_TYPE)), + nullToNotInMapInt(MVNO_TYPE_STRING_MAP.get( + cursor.getString(cursor.getColumnIndexOrThrow( + Telephony.Carriers.MVNO_TYPE)))), cursor.getString(cursor.getColumnIndexOrThrow( Telephony.Carriers.MVNO_MATCH_DATA))); } @@ -540,8 +646,8 @@ public class ApnSetting implements Parcelable { /** @hide */ public static ApnSetting makeApnSetting(ApnSetting apn) { return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName, - apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser, - apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol, + apn.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress, apn.mMmsProxyPort, apn.mUser, + apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask, apn.mProtocol, apn.mRoamingProtocol, apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId, apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu, apn.mMvnoType, apn.mMvnoMatchData); @@ -555,18 +661,14 @@ public class ApnSetting implements Parcelable { .append(", ").append(mId) .append(", ").append(mOperatorNumeric) .append(", ").append(mApnName) - .append(", ").append(inetAddressToString(mProxy)) - .append(", ").append(URLToString(mMmsc)) - .append(", ").append(inetAddressToString(mMmsProxy)) - .append(", ").append(portToString(mMmsPort)) - .append(", ").append(portToString(mPort)) + .append(", ").append(inetAddressToString(mProxyAddress)) + .append(", ").append(UriToString(mMmsc)) + .append(", ").append(inetAddressToString(mMmsProxyAddress)) + .append(", ").append(portToString(mMmsProxyPort)) + .append(", ").append(portToString(mProxyPort)) .append(", ").append(mAuthType).append(", "); - for (int i = 0; i < mTypes.size(); i++) { - sb.append(mTypes.get(i)); - if (i < mTypes.size() - 1) { - sb.append(" | "); - } - } + final String[] types = deParseTypes(mApnTypeBitmask).split(","); + sb.append(TextUtils.join(" | ", types)).append(", "); sb.append(", ").append(mProtocol); sb.append(", ").append(mRoamingProtocol); sb.append(", ").append(mCarrierEnabled); @@ -588,56 +690,37 @@ public class ApnSetting implements Parcelable { * @hide */ public boolean hasMvnoParams() { - return !TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData); + return (mMvnoType != NOT_IN_MAP_INT) && !TextUtils.isEmpty(mMvnoMatchData); } /** @hide */ - public boolean canHandleType(String type) { - if (!mCarrierEnabled) return false; - boolean wildcardable = true; - if (TYPE_IA.equalsIgnoreCase(type)) wildcardable = false; - for (String t : mTypes) { - // DEFAULT handles all, and HIPRI is handled by DEFAULT - if (t.equalsIgnoreCase(type) - || (wildcardable && t.equalsIgnoreCase(TYPE_ALL)) - || (t.equalsIgnoreCase(TYPE_DEFAULT) - && type.equalsIgnoreCase(TYPE_HIPRI))) { - return true; - } - } - return false; + public boolean canHandleType(@ApnType int type) { + return mCarrierEnabled && ((mApnTypeBitmask & type) == type); } // check whether the types of two APN same (even only one type of each APN is same) private boolean typeSameAny(ApnSetting first, ApnSetting second) { if (VDBG) { StringBuilder apnType1 = new StringBuilder(first.mApnName + ": "); - for (int index1 = 0; index1 < first.mTypes.size(); index1++) { - apnType1.append(first.mTypes.get(index1)); - apnType1.append(","); - } + apnType1.append(deParseTypes(first.mApnTypeBitmask)); StringBuilder apnType2 = new StringBuilder(second.mApnName + ": "); - for (int index1 = 0; index1 < second.mTypes.size(); index1++) { - apnType2.append(second.mTypes.get(index1)); - apnType2.append(","); - } + apnType2.append(deParseTypes(second.mApnTypeBitmask)); + Rlog.d(LOG_TAG, "APN1: is " + apnType1); Rlog.d(LOG_TAG, "APN2: is " + apnType2); } - for (int index1 = 0; index1 < first.mTypes.size(); index1++) { - for (int index2 = 0; index2 < second.mTypes.size(); index2++) { - if (first.mTypes.get(index1).equals(ApnSetting.TYPE_ALL) - || second.mTypes.get(index2).equals(ApnSetting.TYPE_ALL) - || first.mTypes.get(index1).equals(second.mTypes.get(index2))) { - if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true"); - return true; - } + if ((first.mApnTypeBitmask & second.mApnTypeBitmask) != 0) { + if (VDBG) { + Rlog.d(LOG_TAG, "typeSameAny: return true"); } + return true; } - if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false"); + if (VDBG) { + Rlog.d(LOG_TAG, "typeSameAny: return false"); + } return false; } @@ -655,16 +738,15 @@ public class ApnSetting implements Parcelable { && Objects.equals(mId, other.mId) && Objects.equals(mOperatorNumeric, other.mOperatorNumeric) && Objects.equals(mApnName, other.mApnName) - && Objects.equals(mProxy, other.mProxy) + && Objects.equals(mProxyAddress, other.mProxyAddress) && Objects.equals(mMmsc, other.mMmsc) - && Objects.equals(mMmsProxy, other.mMmsProxy) - && Objects.equals(mMmsPort, other.mMmsPort) - && Objects.equals(mPort,other.mPort) + && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress) + && Objects.equals(mMmsProxyPort, other.mMmsProxyPort) + && Objects.equals(mProxyPort,other.mProxyPort) && Objects.equals(mUser, other.mUser) && Objects.equals(mPassword, other.mPassword) && Objects.equals(mAuthType, other.mAuthType) - && Objects.equals(mTypes, other.mTypes) - && Objects.equals(mTypesBitmap, other.mTypesBitmap) + && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask) && Objects.equals(mProtocol, other.mProtocol) && Objects.equals(mRoamingProtocol, other.mRoamingProtocol) && Objects.equals(mCarrierEnabled, other.mCarrierEnabled) @@ -701,16 +783,15 @@ public class ApnSetting implements Parcelable { return mEntryName.equals(other.mEntryName) && Objects.equals(mOperatorNumeric, other.mOperatorNumeric) && Objects.equals(mApnName, other.mApnName) - && Objects.equals(mProxy, other.mProxy) + && Objects.equals(mProxyAddress, other.mProxyAddress) && Objects.equals(mMmsc, other.mMmsc) - && Objects.equals(mMmsProxy, other.mMmsProxy) - && Objects.equals(mMmsPort, other.mMmsPort) - && Objects.equals(mPort, other.mPort) + && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress) + && Objects.equals(mMmsProxyPort, other.mMmsProxyPort) + && Objects.equals(mProxyPort, other.mProxyPort) && Objects.equals(mUser, other.mUser) && Objects.equals(mPassword, other.mPassword) && Objects.equals(mAuthType, other.mAuthType) - && Objects.equals(mTypes, other.mTypes) - && Objects.equals(mTypesBitmap, other.mTypesBitmap) + && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask) && (isDataRoaming || Objects.equals(mProtocol,other.mProtocol)) && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol)) && Objects.equals(mCarrierEnabled, other.mCarrierEnabled) @@ -736,17 +817,17 @@ public class ApnSetting implements Parcelable { && !other.canHandleType(TYPE_DUN) && Objects.equals(this.mApnName, other.mApnName) && !typeSameAny(this, other) - && xorEqualsInetAddress(this.mProxy, other.mProxy) - && xorEqualsPort(this.mPort, other.mPort) + && xorEquals(this.mProxyAddress, other.mProxyAddress) + && xorEqualsPort(this.mProxyPort, other.mProxyPort) && xorEquals(this.mProtocol, other.mProtocol) && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol) && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled) && Objects.equals(this.mProfileId, other.mProfileId) && Objects.equals(this.mMvnoType, other.mMvnoType) && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData) - && xorEqualsURL(this.mMmsc, other.mMmsc) - && xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy) - && xorEqualsPort(this.mMmsPort, other.mMmsPort)) + && xorEquals(this.mMmsc, other.mMmsc) + && xorEquals(this.mMmsProxyAddress, other.mMmsProxyAddress) + && xorEqualsPort(this.mMmsProxyPort, other.mMmsProxyPort)) && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask); } @@ -757,42 +838,23 @@ public class ApnSetting implements Parcelable { || TextUtils.isEmpty(second)); } - // Equal or one is not specified. - private boolean xorEqualsInetAddress(InetAddress first, InetAddress second) { - return first == null || second == null || first.equals(second); - } - - // Equal or one is not specified. - private boolean xorEqualsURL(URL first, URL second) { + // Equal or one is not null. + private boolean xorEquals(Object first, Object second) { return first == null || second == null || first.equals(second); } // Equal or one is not specified. private boolean xorEqualsPort(int first, int second) { - return first == -1 || second == -1 || Objects.equals(first, second); - } - - // Helper function to convert APN string into a 32-bit bitmask. - private static int getApnBitmask(String apn) { - switch (apn) { - case TYPE_DEFAULT: return ApnTypes.DEFAULT; - case TYPE_MMS: return ApnTypes.MMS; - case TYPE_SUPL: return ApnTypes.SUPL; - case TYPE_DUN: return ApnTypes.DUN; - case TYPE_HIPRI: return ApnTypes.HIPRI; - case TYPE_FOTA: return ApnTypes.FOTA; - case TYPE_IMS: return ApnTypes.IMS; - case TYPE_CBS: return ApnTypes.CBS; - case TYPE_IA: return ApnTypes.IA; - case TYPE_EMERGENCY: return ApnTypes.EMERGENCY; - case TYPE_ALL: return ApnTypes.ALL; - default: return ApnTypes.NONE; - } + return first == NO_PORT_SPECIFIED || second == NO_PORT_SPECIFIED + || Objects.equals(first, second); } - private String deParseTypes(List<String> types) { - if (types == null) { - return null; + private String deParseTypes(int apnTypeBitmask) { + List<String> types = new ArrayList<>(); + for (Integer type : APN_TYPE_INT_MAP.keySet()) { + if ((apnTypeBitmask & type) == type) { + types.add(APN_TYPE_INT_MAP.get(type)); + } } return TextUtils.join(",", types); } @@ -808,21 +870,25 @@ public class ApnSetting implements Parcelable { apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric)); apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName)); apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName)); - apnValue.put(Telephony.Carriers.PROXY, mProxy == null ? "" : inetAddressToString(mProxy)); - apnValue.put(Telephony.Carriers.PORT, portToString(mPort)); - apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc)); - apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort)); - apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null - ? "" : inetAddressToString(mMmsProxy)); + apnValue.put(Telephony.Carriers.PROXY, mProxyAddress == null ? "" + : inetAddressToString(mProxyAddress)); + apnValue.put(Telephony.Carriers.PORT, portToString(mProxyPort)); + apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : UriToString(mMmsc)); + apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsProxyPort)); + apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxyAddress == null + ? "" : inetAddressToString(mMmsProxyAddress)); apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser)); apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword)); apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType); - String apnType = deParseTypes(mTypes); + String apnType = deParseTypes(mApnTypeBitmask); apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType)); - apnValue.put(Telephony.Carriers.PROTOCOL, nullToEmpty(mProtocol)); - apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, nullToEmpty(mRoamingProtocol)); + apnValue.put(Telephony.Carriers.PROTOCOL, + nullToEmpty(PROTOCOL_INT_MAP.get(mProtocol))); + apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, + nullToEmpty(PROTOCOL_INT_MAP.get(mRoamingProtocol))); apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled); - apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType)); + apnValue.put(Telephony.Carriers.MVNO_TYPE, + nullToEmpty(MVNO_TYPE_INT_MAP.get(mMvnoType))); apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask); return apnValue; @@ -830,32 +896,31 @@ public class ApnSetting implements Parcelable { /** * @param types comma delimited list of APN types - * @return array of APN types + * @return bitmask of APN types * @hide */ - public static String[] parseTypes(String types) { - String[] result; - // If unset, set to DEFAULT. + public static int parseTypes(String types) { + // If unset, set to ALL. if (TextUtils.isEmpty(types)) { - result = new String[1]; - result[0] = TYPE_ALL; + return TYPE_ALL_BUT_IA; } else { - result = types.split(","); + int result = 0; + for (String str : types.split(",")) { + Integer type = APN_TYPE_STRING_MAP.get(str); + if (type != null) { + result |= type; + } + } + return result; } - return result; } - private static URL URLFromString(String url) { - try { - return TextUtils.isEmpty(url) ? null : new URL(url); - } catch (MalformedURLException e) { - Log.e(LOG_TAG, "Can't parse URL from string."); - return null; - } + private static Uri UriFromString(String uri) { + return TextUtils.isEmpty(uri) ? null : Uri.parse(uri); } - private static String URLToString(URL url) { - return url == null ? "" : url.toString(); + private static String UriToString(Uri uri) { + return uri == null ? "" : uri.toString(); } private static InetAddress inetAddressFromString(String inetAddress) { @@ -887,7 +952,7 @@ public class ApnSetting implements Parcelable { } private static int portFromString(String strPort) { - int port = -1; + int port = NO_PORT_SPECIFIED; if (!TextUtils.isEmpty(strPort)) { try { port = Integer.parseInt(strPort); @@ -899,7 +964,7 @@ public class ApnSetting implements Parcelable { } private static String portToString(int port) { - return port == -1 ? "" : Integer.toString(port); + return port == NO_PORT_SPECIFIED ? "" : Integer.toString(port); } // Implement Parcelable. @@ -916,19 +981,19 @@ public class ApnSetting implements Parcelable { dest.writeString(mOperatorNumeric); dest.writeString(mEntryName); dest.writeString(mApnName); - dest.writeValue(mProxy); - dest.writeInt(mPort); + dest.writeValue(mProxyAddress); + dest.writeInt(mProxyPort); dest.writeValue(mMmsc); - dest.writeValue(mMmsProxy); - dest.writeInt(mMmsPort); + dest.writeValue(mMmsProxyAddress); + dest.writeInt(mMmsProxyPort); dest.writeString(mUser); dest.writeString(mPassword); dest.writeInt(mAuthType); - dest.writeStringArray(mTypes.toArray(new String[0])); - dest.writeString(mProtocol); - dest.writeString(mRoamingProtocol); + dest.writeInt(mApnTypeBitmask); + dest.writeInt(mProtocol); + dest.writeInt(mRoamingProtocol); dest.writeInt(mCarrierEnabled ? 1: 0); - dest.writeString(mMvnoType); + dest.writeInt(mMvnoType); dest.writeInt(mNetworkTypeBitmask); } @@ -939,23 +1004,23 @@ public class ApnSetting implements Parcelable { final String apnName = in.readString(); final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader()); final int port = in.readInt(); - final URL mmsc = (URL)in.readValue(URL.class.getClassLoader()); + final Uri mmsc = (Uri)in.readValue(Uri.class.getClassLoader()); final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader()); final int mmsPort = in.readInt(); final String user = in.readString(); final String password = in.readString(); final int authType = in.readInt(); - final List<String> types = Arrays.asList(in.readStringArray()); - final String protocol = in.readString(); - final String roamingProtocol = in.readString(); + final int apnTypesBitmask = in.readInt(); + final int protocol = in.readInt(); + final int roamingProtocol = in.readInt(); final boolean carrierEnabled = in.readInt() > 0; - final String mvnoType = in.readString(); + final int mvnoType = in.readInt(); final int networkTypeBitmask = in.readInt(); return makeApnSetting(id, operatorNumeric, entryName, apnName, - proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, types, protocol, - roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false, - 0, 0, 0, 0, mvnoType, null); + proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, apnTypesBitmask, + protocol, roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false, + 0, 0, 0, 0, mvnoType, null); } public static final Parcelable.Creator<ApnSetting> CREATOR = @@ -971,89 +1036,26 @@ public class ApnSetting implements Parcelable { } }; - /** - * APN types for data connections. These are usage categories for an APN - * entry. One APN entry may support multiple APN types, eg, a single APN - * may service regular internet traffic ("default") as well as MMS-specific - * connections.<br/> - * ALL is a special type to indicate that this APN entry can - * service all data connections. - */ - public static final String TYPE_ALL = "*"; - /** APN type for default data traffic */ - public static final String TYPE_DEFAULT = "default"; - /** APN type for MMS traffic */ - public static final String TYPE_MMS = "mms"; - /** APN type for SUPL assisted GPS */ - public static final String TYPE_SUPL = "supl"; - /** APN type for DUN traffic */ - public static final String TYPE_DUN = "dun"; - /** APN type for HiPri traffic */ - public static final String TYPE_HIPRI = "hipri"; - /** APN type for FOTA */ - public static final String TYPE_FOTA = "fota"; - /** APN type for IMS */ - public static final String TYPE_IMS = "ims"; - /** APN type for CBS */ - public static final String TYPE_CBS = "cbs"; - /** APN type for IA Initial Attach APN */ - public static final String TYPE_IA = "ia"; - /** APN type for Emergency PDN. This is not an IA apn, but is used - * for access to carrier services in an emergency call situation. */ - public static final String TYPE_EMERGENCY = "emergency"; - /** - * Array of all APN types - * - * @hide - */ - public static final String[] ALL_TYPES = { - TYPE_DEFAULT, - TYPE_MMS, - TYPE_SUPL, - TYPE_DUN, - TYPE_HIPRI, - TYPE_FOTA, - TYPE_IMS, - TYPE_CBS, - TYPE_IA, - TYPE_EMERGENCY - }; - - // Possible values for authentication types. - public static final int AUTH_TYPE_NONE = 0; - public static final int AUTH_TYPE_PAP = 1; - public static final int AUTH_TYPE_CHAP = 2; - public static final int AUTH_TYPE_PAP_OR_CHAP = 3; - - // Possible values for protocol. - public static final String PROTOCOL_IP = "IP"; - public static final String PROTOCOL_IPV6 = "IPV6"; - public static final String PROTOCOL_IPV4V6 = "IPV4V6"; - public static final String PROTOCOL_PPP = "PPP"; - - // Possible values for MVNO type. - public static final String MVNO_TYPE_SPN = "spn"; - public static final String MVNO_TYPE_IMSI = "imsi"; - public static final String MVNO_TYPE_GID = "gid"; - public static final String MVNO_TYPE_ICCID = "iccid"; + private static int nullToNotInMapInt(Integer value) { + return value == null ? NOT_IN_MAP_INT : value; + } public static class Builder{ private String mEntryName; private String mApnName; - private InetAddress mProxy; - private int mPort = -1; - private URL mMmsc; - private InetAddress mMmsProxy; - private int mMmsPort = -1; + private InetAddress mProxyAddress; + private int mProxyPort = NO_PORT_SPECIFIED; + private Uri mMmsc; + private InetAddress mMmsProxyAddress; + private int mMmsProxyPort = NO_PORT_SPECIFIED; private String mUser; private String mPassword; private int mAuthType; - private List<String> mTypes; - private int mTypesBitmap; + private int mApnTypeBitmask; private int mId; private String mOperatorNumeric; - private String mProtocol; - private String mRoamingProtocol; + private int mProtocol = NOT_IN_MAP_INT; + private int mRoamingProtocol = NOT_IN_MAP_INT; private int mMtu; private int mNetworkTypeBitmask; private boolean mCarrierEnabled; @@ -1062,7 +1064,7 @@ public class ApnSetting implements Parcelable { private int mMaxConns; private int mWaitTime; private int mMaxConnsTime; - private String mMvnoType; + private int mMvnoType = NOT_IN_MAP_INT; private String mMvnoMatchData; /** @@ -1182,8 +1184,8 @@ public class ApnSetting implements Parcelable { * * @param proxy the proxy address to set for the APN */ - public Builder setProxy(InetAddress proxy) { - this.mProxy = proxy; + public Builder setProxyAddress(InetAddress proxy) { + this.mProxyAddress = proxy; return this; } @@ -1192,17 +1194,17 @@ public class ApnSetting implements Parcelable { * * @param port the proxy port to set for the APN */ - public Builder setPort(int port) { - this.mPort = port; + public Builder setProxyPort(int port) { + this.mProxyPort = port; return this; } /** - * Sets the MMSC URL of the APN. + * Sets the MMSC Uri of the APN. * - * @param mmsc the MMSC URL to set for the APN + * @param mmsc the MMSC Uri to set for the APN */ - public Builder setMmsc(URL mmsc) { + public Builder setMmsc(Uri mmsc) { this.mMmsc = mmsc; return this; } @@ -1212,8 +1214,8 @@ public class ApnSetting implements Parcelable { * * @param mmsProxy the MMS proxy address to set for the APN */ - public Builder setMmsProxy(InetAddress mmsProxy) { - this.mMmsProxy = mmsProxy; + public Builder setMmsProxyAddress(InetAddress mmsProxy) { + this.mMmsProxyAddress = mmsProxy; return this; } @@ -1222,8 +1224,8 @@ public class ApnSetting implements Parcelable { * * @param mmsPort the MMS proxy port to set for the APN */ - public Builder setMmsPort(int mmsPort) { - this.mMmsPort = mmsPort; + public Builder setMmsProxyPort(int mmsPort) { + this.mMmsProxyPort = mmsPort; return this; } @@ -1251,8 +1253,6 @@ public class ApnSetting implements Parcelable { /** * Sets the authentication type of the APN. * - * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}. - * * @param authType the authentication type to set for the APN */ public Builder setAuthType(@AuthType int authType) { @@ -1261,25 +1261,25 @@ public class ApnSetting implements Parcelable { } /** - * Sets the list of APN types of the APN. + * Sets the bitmask of APN types. + * + * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple + * APN types, eg, a single APN may service regular internet traffic ("default") as well as + * MMS-specific connections. * - * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}. + * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}. * - * @param types the list of APN types to set for the APN + * @param apnTypeBitmask a bitmask describing the types of the APN */ - public Builder setTypes(@ApnType List<String> types) { - this.mTypes = types; - int apnBitmap = 0; - for (int i = 0; i < mTypes.size(); i++) { - mTypes.set(i, mTypes.get(i).toLowerCase()); - apnBitmap |= getApnBitmask(mTypes.get(i)); - } - this.mTypesBitmap = apnBitmap; + public Builder setApnTypeBitmask(@ApnType int apnTypeBitmask) { + this.mApnTypeBitmask = apnTypeBitmask; return this; } /** - * Set the numeric operator ID for the APN. + * Sets the numeric operator ID for the APN. Numeric operator ID is defined as + * {@link android.provider.Telephony.Carriers#MCC} + + * {@link android.provider.Telephony.Carriers#MNC}. * * @param operatorNumeric the numeric operator ID to set for this entry */ @@ -1291,22 +1291,23 @@ public class ApnSetting implements Parcelable { /** * Sets the protocol to use to connect to this APN. * - * One of the {@code PDP_type} values in TS 27.007 section 10.1.1. - * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}. + * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1. * * @param protocol the protocol to set to use to connect to this APN */ - public Builder setProtocol(@ProtocolType String protocol) { + public Builder setProtocol(@ProtocolType int protocol) { this.mProtocol = protocol; return this; } /** - * Sets the protocol to use to connect to this APN when roaming. + * Sets the protocol to use to connect to this APN when the device is roaming. + * + * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1. * * @param roamingProtocol the protocol to set to use to connect to this APN when roaming */ - public Builder setRoamingProtocol(String roamingProtocol) { + public Builder setRoamingProtocol(@ProtocolType int roamingProtocol) { this.mRoamingProtocol = roamingProtocol; return this; } @@ -1334,16 +1335,25 @@ public class ApnSetting implements Parcelable { /** * Sets the MVNO match type for this APN. * - * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}. - * * @param mvnoType the MVNO match type to set for this APN */ - public Builder setMvnoType(@MvnoType String mvnoType) { + public Builder setMvnoType(@MvnoType int mvnoType) { this.mMvnoType = mvnoType; return this; } + /** + * Builds {@link ApnSetting} from this builder. + * + * @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)} + * is empty, or {@link #setApnTypeBitmask(int)} doesn't contain a valid bit, + * {@link ApnSetting} built from this builder otherwise. + */ public ApnSetting build() { + if ((mApnTypeBitmask & ApnTypes.ALL) == 0 || TextUtils.isEmpty(mApnName) + || TextUtils.isEmpty(mEntryName)) { + return null; + } return new ApnSetting(this); } } diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index 1637c55c0adf..dff1c6fe35f6 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -15,11 +15,12 @@ */ package android.telephony.euicc; +import android.Manifest; import android.annotation.IntDef; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Activity; import android.app.PendingIntent; import android.content.Context; @@ -73,6 +74,7 @@ public class EuiccManager { */ @SystemApi @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public static final String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED"; @@ -301,6 +303,7 @@ public class EuiccManager { * @hide */ @SystemApi + @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus() { if (!isEnabled()) { return EUICC_OTA_STATUS_UNAVAILABLE; @@ -325,6 +328,7 @@ public class EuiccManager { * @param switchAfterDownload if true, the profile will be activated upon successful download. * @param callbackIntent a PendingIntent to launch when the operation completes. */ + @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void downloadSubscription(DownloadableSubscription subscription, boolean switchAfterDownload, PendingIntent callbackIntent) { if (!isEnabled()) { @@ -387,6 +391,7 @@ public class EuiccManager { * @hide */ @SystemApi + @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) { if (!isEnabled()) { PendingIntent callbackIntent = @@ -422,6 +427,7 @@ public class EuiccManager { * @hide */ @SystemApi + @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata( DownloadableSubscription subscription, PendingIntent callbackIntent) { if (!isEnabled()) { @@ -452,6 +458,7 @@ public class EuiccManager { * @hide */ @SystemApi + @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) { if (!isEnabled()) { sendUnavailableError(callbackIntent); @@ -496,6 +503,7 @@ public class EuiccManager { * @param subscriptionId the ID of the subscription to delete. * @param callbackIntent a PendingIntent to launch when the operation completes. */ + @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) { if (!isEnabled()) { sendUnavailableError(callbackIntent); @@ -523,6 +531,7 @@ public class EuiccManager { * current profile without activating another profile to replace it. * @param callbackIntent a PendingIntent to launch when the operation completes. */ + @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) { if (!isEnabled()) { sendUnavailableError(callbackIntent); @@ -548,6 +557,7 @@ public class EuiccManager { * @param callbackIntent a PendingIntent to launch when the operation completes. * @hide */ + @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void updateSubscriptionNickname( int subscriptionId, String nickname, PendingIntent callbackIntent) { if (!isEnabled()) { @@ -566,12 +576,13 @@ public class EuiccManager { * Erase all subscriptions and reset the eUICC. * * <p>Requires that the calling app has the - * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for - * internal system use only. + * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. * * @param callbackIntent a PendingIntent to launch when the operation completes. * @hide */ + @SystemApi + @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(PendingIntent callbackIntent) { if (!isEnabled()) { sendUnavailableError(callbackIntent); diff --git a/test-mock/api/android-test-mock-current.txt b/test-mock/api/android-test-mock-current.txt index 07acfef7ce6c..f3b253c0f460 100644 --- a/test-mock/api/android-test-mock-current.txt +++ b/test-mock/api/android-test-mock-current.txt @@ -278,6 +278,7 @@ package android.test.mock { method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int); method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int); method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int); + method public android.content.pm.ResolveInfo resolveServiceAsUser(android.content.Intent, int, int); method public void setApplicationCategoryHint(java.lang.String, int); method public void setApplicationEnabledSetting(java.lang.String, int, int); method public void setComponentEnabledSetting(android.content.ComponentName, int, int); diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java index 1af7c3a479b6..8ebb77d7a350 100644 --- a/test-mock/src/android/test/mock/MockPackageManager.java +++ b/test-mock/src/android/test/mock/MockPackageManager.java @@ -18,6 +18,7 @@ package android.test.mock; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.PackageInstallObserver; import android.content.ComponentName; import android.content.Intent; @@ -464,6 +465,11 @@ public class MockPackageManager extends PackageManager { } @Override + public ResolveInfo resolveServiceAsUser(Intent intent, int flags, int userId) { + throw new UnsupportedOperationException(); + } + + @Override public List<ResolveInfo> queryIntentServices(Intent intent, int flags) { throw new UnsupportedOperationException(); } diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java index cc3366fbc832..0ca20dee427f 100644 --- a/tests/net/java/android/net/IpSecManagerTest.java +++ b/tests/net/java/android/net/IpSecManagerTest.java @@ -50,13 +50,18 @@ public class IpSecManagerTest { private static final int TEST_UDP_ENCAP_PORT = 34567; private static final int DROID_SPI = 0xD1201D; + private static final int DUMMY_RESOURCE_ID = 0x1234; private static final InetAddress GOOGLE_DNS_4; + private static final String VTI_INTF_NAME = "ipsec_test"; + private static final InetAddress VTI_LOCAL_ADDRESS; + private static final LinkAddress VTI_INNER_ADDRESS = new LinkAddress("10.0.1.1/24"); static { try { // Google Public DNS Addresses; GOOGLE_DNS_4 = InetAddress.getByName("8.8.8.8"); + VTI_LOCAL_ADDRESS = InetAddress.getByName("8.8.4.4"); } catch (UnknownHostException e) { throw new RuntimeException("Could not resolve DNS Addresses", e); } @@ -77,9 +82,8 @@ public class IpSecManagerTest { */ @Test public void testAllocSpi() throws Exception { - int resourceId = 1; IpSecSpiResponse spiResp = - new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI); + new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI); when(mMockIpSecService.allocateSecurityParameterIndex( eq(GOOGLE_DNS_4.getHostAddress()), eq(DROID_SPI), @@ -92,14 +96,13 @@ public class IpSecManagerTest { droidSpi.close(); - verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId); + verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID); } @Test public void testAllocRandomSpi() throws Exception { - int resourceId = 1; IpSecSpiResponse spiResp = - new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI); + new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI); when(mMockIpSecService.allocateSecurityParameterIndex( eq(GOOGLE_DNS_4.getHostAddress()), eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX), @@ -113,7 +116,7 @@ public class IpSecManagerTest { randomSpi.close(); - verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId); + verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID); } /* @@ -165,11 +168,10 @@ public class IpSecManagerTest { @Test public void testOpenEncapsulationSocket() throws Exception { - int resourceId = 1; IpSecUdpEncapResponse udpEncapResp = new IpSecUdpEncapResponse( IpSecManager.Status.OK, - resourceId, + DUMMY_RESOURCE_ID, TEST_UDP_ENCAP_PORT, Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); when(mMockIpSecService.openUdpEncapsulationSocket(eq(TEST_UDP_ENCAP_PORT), anyObject())) @@ -182,16 +184,15 @@ public class IpSecManagerTest { encapSocket.close(); - verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId); + verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID); } @Test public void testOpenEncapsulationSocketOnRandomPort() throws Exception { - int resourceId = 1; IpSecUdpEncapResponse udpEncapResp = new IpSecUdpEncapResponse( IpSecManager.Status.OK, - resourceId, + DUMMY_RESOURCE_ID, TEST_UDP_ENCAP_PORT, Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); @@ -206,7 +207,7 @@ public class IpSecManagerTest { encapSocket.close(); - verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId); + verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID); } @Test @@ -219,4 +220,45 @@ public class IpSecManagerTest { } // TODO: add test when applicable transform builder interface is available -} + + private IpSecManager.IpSecTunnelInterface createAndValidateVti(int resourceId, String intfName) + throws Exception { + IpSecTunnelInterfaceResponse dummyResponse = + new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName); + when(mMockIpSecService.createTunnelInterface( + eq(VTI_LOCAL_ADDRESS.getHostAddress()), eq(GOOGLE_DNS_4.getHostAddress()), + anyObject(), anyObject())) + .thenReturn(dummyResponse); + + IpSecManager.IpSecTunnelInterface tunnelIntf = mIpSecManager.createIpSecTunnelInterface( + VTI_LOCAL_ADDRESS, GOOGLE_DNS_4, mock(Network.class)); + + assertNotNull(tunnelIntf); + return tunnelIntf; + } + + @Test + public void testCreateVti() throws Exception { + IpSecManager.IpSecTunnelInterface tunnelIntf = + createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME); + + assertEquals(VTI_INTF_NAME, tunnelIntf.getInterfaceName()); + + tunnelIntf.close(); + verify(mMockIpSecService).deleteTunnelInterface(eq(DUMMY_RESOURCE_ID)); + } + + @Test + public void testAddRemoveAddressesFromVti() throws Exception { + IpSecManager.IpSecTunnelInterface tunnelIntf = + createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME); + + tunnelIntf.addAddress(VTI_INNER_ADDRESS); + verify(mMockIpSecService) + .addAddressToTunnelInterface(eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS)); + + tunnelIntf.removeAddress(VTI_INNER_ADDRESS); + verify(mMockIpSecService) + .addAddressToTunnelInterface(eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS)); + } +}
\ No newline at end of file diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index 3e1ff6dd5f32..a5c55e8d844e 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -17,6 +17,7 @@ package com.android.server; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; @@ -32,6 +33,9 @@ import android.net.IpSecConfig; import android.net.IpSecManager; import android.net.IpSecSpiResponse; import android.net.IpSecTransformResponse; +import android.net.IpSecTunnelInterfaceResponse; +import android.net.LinkAddress; +import android.net.Network; import android.net.NetworkUtils; import android.os.Binder; import android.os.ParcelFileDescriptor; @@ -56,10 +60,15 @@ public class IpSecServiceParameterizedTest { private final String mDestinationAddr; private final String mSourceAddr; + private final LinkAddress mLocalInnerAddress; @Parameterized.Parameters public static Collection ipSecConfigs() { - return Arrays.asList(new Object[][] {{"1.2.3.4", "8.8.4.4"}, {"2601::2", "2601::10"}}); + return Arrays.asList( + new Object[][] { + {"1.2.3.4", "8.8.4.4", "10.0.1.1/24"}, + {"2601::2", "2601::10", "2001:db8::1/64"} + }); } private static final byte[] AEAD_KEY = { @@ -86,6 +95,7 @@ public class IpSecServiceParameterizedTest { INetd mMockNetd; IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; IpSecService mIpSecService; + Network fakeNetwork = new Network(0xAB); private static final IpSecAlgorithm AUTH_ALGO = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4); @@ -94,9 +104,11 @@ public class IpSecServiceParameterizedTest { private static final IpSecAlgorithm AEAD_ALGO = new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128); - public IpSecServiceParameterizedTest(String sourceAddr, String destAddr) { + public IpSecServiceParameterizedTest( + String sourceAddr, String destAddr, String localInnerAddr) { mSourceAddr = sourceAddr; mDestinationAddr = destAddr; + mLocalInnerAddress = new LinkAddress(localInnerAddr); } @Before @@ -406,4 +418,103 @@ public class IpSecServiceParameterizedTest { verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor()); } + + private IpSecTunnelInterfaceResponse createAndValidateTunnel( + String localAddr, String remoteAddr) { + IpSecTunnelInterfaceResponse createTunnelResp = + mIpSecService.createTunnelInterface( + mSourceAddr, mDestinationAddr, fakeNetwork, new Binder()); + + assertNotNull(createTunnelResp); + assertEquals(IpSecManager.Status.OK, createTunnelResp.status); + return createTunnelResp; + } + + @Test + public void testCreateTunnelInterface() throws Exception { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr); + + // Check that we have stored the tracking object, and retrieve it + IpSecService.UserRecord userRecord = + mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.RefcountedResource refcountedRecord = + userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( + createTunnelResp.resourceId); + + assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent); + verify(mMockNetd) + .addVirtualTunnelInterface( + eq(createTunnelResp.interfaceName), + eq(mSourceAddr), + eq(mDestinationAddr), + anyInt(), + anyInt()); + } + + @Test + public void testDeleteTunnelInterface() throws Exception { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr); + + IpSecService.UserRecord userRecord = + mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + + mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId); + + // Verify quota and RefcountedResource objects cleaned up + assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent); + verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName)); + try { + userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( + createTunnelResp.resourceId); + fail("Expected IllegalArgumentException on attempt to access deleted resource"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testTunnelInterfaceBinderDeath() throws Exception { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr); + + IpSecService.UserRecord userRecord = + mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.RefcountedResource refcountedRecord = + userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( + createTunnelResp.resourceId); + + refcountedRecord.binderDied(); + + // Verify quota and RefcountedResource objects cleaned up + assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent); + verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName)); + try { + userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( + createTunnelResp.resourceId); + fail("Expected IllegalArgumentException on attempt to access deleted resource"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testAddRemoveAddressFromTunnelInterface() throws Exception { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr); + + mIpSecService.addAddressToTunnelInterface(createTunnelResp.resourceId, mLocalInnerAddress); + verify(mMockNetd) + .interfaceAddAddress( + eq(createTunnelResp.interfaceName), + eq(mLocalInnerAddress.getAddress().getHostAddress()), + eq(mLocalInnerAddress.getPrefixLength())); + + mIpSecService.removeAddressFromTunnelInterface( + createTunnelResp.resourceId, mLocalInnerAddress); + verify(mMockNetd) + .interfaceDelAddress( + eq(createTunnelResp.interfaceName), + eq(mLocalInnerAddress.getAddress().getHostAddress()), + eq(mLocalInnerAddress.getPrefixLength())); + } } |