Merge "Make autofill save UI RTL compliant" into pi-dev
diff --git a/Android.bp b/Android.bp
index 1caa497..037c29c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -462,6 +462,8 @@
"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 e2f88e8..ee8fbe0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -861,39 +861,42 @@
# ==== 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 73e1724..ccbccca 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 @@
// 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 @@
// 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 414491c..2ca424c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5401,6 +5401,7 @@
method public android.app.Notification.Builder extend(android.app.Notification.Extender);
method public android.os.Bundle getExtras();
method public deprecated android.app.Notification getNotification();
+ method public android.app.Notification.Style getStyle();
method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
method public android.app.Notification.Builder setAutoCancel(boolean);
@@ -7192,6 +7193,7 @@
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";
@@ -7217,29 +7219,22 @@
}
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 {
@@ -7262,10 +7257,11 @@
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 {
@@ -7281,6 +7277,13 @@
field public static final java.lang.String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
}
+ public class SliceMetrics {
+ ctor public SliceMetrics(android.content.Context, android.net.Uri);
+ method public void logHidden();
+ method public void logTouch(android.net.Uri);
+ method public void logVisible();
+ }
+
public abstract class SliceProvider extends android.content.ContentProvider {
ctor public SliceProvider();
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
@@ -15804,6 +15807,7 @@
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;
@@ -15816,7 +15820,7 @@
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;
@@ -16274,6 +16278,7 @@
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;
@@ -16282,7 +16287,7 @@
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;
@@ -23783,10 +23788,8 @@
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";
@@ -23815,6 +23818,8 @@
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";
@@ -24064,12 +24069,16 @@
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;
@@ -29475,7 +29484,7 @@
field public static final java.lang.String EXTRA_ID = "android.nfc.extra.ID";
field public static final java.lang.String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
field public static final java.lang.String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
- field public static final java.lang.String EXTRA_SE_NAME = "android.nfc.extra.SE_NAME";
+ field public static final java.lang.String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
field public static final int FLAG_READER_NFC_A = 1; // 0x1
field public static final int FLAG_READER_NFC_B = 2; // 0x2
@@ -40048,6 +40057,7 @@
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
@@ -43344,21 +43354,21 @@
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);
@@ -43367,46 +43377,45 @@
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);
}
@@ -51990,6 +51999,7 @@
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 0e33c17..1e2fe3d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -32,6 +32,7 @@
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 @@
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,9 @@
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
}
public final class UsageStatsManager {
@@ -1232,7 +1237,9 @@
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);
}
@@ -2219,6 +2226,21 @@
}
+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 {
@@ -2734,6 +2756,16 @@
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();
@@ -2758,6 +2790,7 @@
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);
}
@@ -5213,10 +5246,10 @@
method public boolean handlePinMmi(java.lang.String);
method public boolean handlePinMmiForSubscriber(int, java.lang.String);
method public boolean isDataConnectivityPossible();
- method public deprecated boolean isIdle();
- method public deprecated boolean isOffhook();
- method public deprecated boolean isRadioOn();
- method public deprecated boolean isRinging();
+ method public boolean isIdle();
+ method public boolean isOffhook();
+ method public boolean isRadioOn();
+ method public boolean isRinging();
method public boolean isVideoCallingEnabled();
method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method public boolean needsOtaServiceProvisioning();
@@ -5442,6 +5475,7 @@
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 e8d27a1..70e3cf3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -370,7 +370,9 @@
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 5d6a1d1a..ca0611b 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -177,6 +177,7 @@
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 652ec9d..82274a6 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -112,8 +112,8 @@
}
void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
- if (android::util::kAtomsWithAttributionChain.find(event->GetTagId()) !=
- android::util::kAtomsWithAttributionChain.end()) {
+ if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(event->GetTagId()) !=
+ android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
for (auto& value : *(event->getMutableValues())) {
if (value.mField.getPosAtDepth(0) > kAttributionField) {
break;
@@ -123,12 +123,20 @@
updateUid(&value.mValue, hostUid);
}
}
- } else if (android::util::kAtomsWithUidField.find(event->GetTagId()) !=
- android::util::kAtomsWithUidField.end() &&
- event->getValues().size() > 0 && (event->getValues())[0].mValue.getType() == INT) {
- Value& value = (*event->getMutableValues())[0].mValue;
- const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
- updateUid(&value, hostUid);
+ } else {
+ auto it = android::util::AtomsInfo::kAtomsWithUidField.find(event->GetTagId());
+ if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
+ int uidField = it->second; // uidField is the field number in proto,
+ // starting from 1
+ if (uidField > 0 && (int)event->getValues().size() >= uidField &&
+ (event->getValues())[uidField - 1].mValue.getType() == INT) {
+ Value& value = (*event->getMutableValues())[uidField - 1].mValue;
+ const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
+ updateUid(&value, hostUid);
+ } else {
+ ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
+ }
+ }
}
}
@@ -231,14 +239,13 @@
}
}
+/*
+ * 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());
@@ -261,35 +268,14 @@
// 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();
@@ -307,6 +293,40 @@
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);
@@ -360,11 +380,17 @@
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 8b42146..1be4dc5 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -86,8 +86,8 @@
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 c5dfc44..b86646a 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -867,14 +867,11 @@
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 @@
}
}
+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 02b6124..0ec31ef 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 @@
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 @@
* 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/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 19d00b7..a2a03b1 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -67,4 +67,7 @@
extend google.protobuf.FieldOptions {
// Flags to decorate an atom that presents a state change.
optional StateAtomFieldOption stateFieldOption = 50000;
+
+ // Flags to decorate the uid fields in an atom.
+ optional bool is_uid = 50001 [default = false];
}
\ No newline at end of file
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f74188f..40eff4c 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -209,7 +209,7 @@
* frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
*/
message UidProcessStateChanged {
- optional int32 uid = 1 [(stateFieldOption).option = PRIMARY];
+ optional int32 uid = 1 [(stateFieldOption).option = PRIMARY, (is_uid) = true];
// The state, from frameworks/base/core/proto/android/app/enums.proto.
optional android.app.ProcessStateEnum state = 2 [(stateFieldOption).option = EXCLUSIVE];
@@ -223,7 +223,7 @@
*/
message ProcessLifeCycleStateChanged {
// TODO: should be a string tagged w/ uid annotation
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// The process name (usually same as the app name).
optional string name = 2;
@@ -593,7 +593,7 @@
*/
message MobileRadioPowerStateChanged {
// TODO: Add attribution instead of uid?
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// Power state, from frameworks/base/core/proto/android/telephony/enums.proto.
optional android.telephony.DataConnectionPowerStateEnum state = 2;
@@ -608,7 +608,7 @@
*/
message WifiRadioPowerStateChanged {
// TODO: Add attribution instead of uid?
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// Power state, from frameworks/base/core/proto/android/telephony/enums.proto.
optional android.telephony.DataConnectionPowerStateEnum state = 2;
@@ -1130,7 +1130,8 @@
*/
message DaveyOccurred {
// The UID that logged this atom.
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
+ ;
// Amount of time it took to render the frame. Should be >=700ms.
optional int64 jank_duration_millis = 2;
@@ -1150,7 +1151,7 @@
/**
* Logs that a setting was updated.
* Logged from:
- * frameworks/base/core/java/android/provider/Settings.java
+ * frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
* The tag and is_default allow resetting of settings to default values based on the specified
* tag. See Settings#putString(ContentResolver, String, String, String, boolean) for more details.
*/
@@ -1176,8 +1177,14 @@
// True if this setting with tag should be resettable.
optional bool is_default = 6;
- // The user ID associated. Defined in android/os/UserHandle.java
+ // The associated user (for multi-user feature). Defined in android/os/UserHandle.java
optional int32 user = 7;
+
+ enum ChangeReason {
+ UPDATED = 1; // Updated can be an insertion or an update.
+ DELETED = 2;
+ }
+ optional ChangeReason reason = 8;
}
/**
@@ -1187,7 +1194,7 @@
* frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
*/
message ActivityForegroundStateChanged {
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
optional string pkg_name = 2;
optional string class_name = 3;
@@ -1205,7 +1212,7 @@
*/
message DropboxErrorChanged {
// The uid if available. -1 means not available.
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// Tag used when recording this error to dropbox. Contains data_ or system_ prefix.
optional string tag = 2;
@@ -1236,7 +1243,7 @@
*/
message AppBreadcrumbReported {
// The uid of the application that sent this custom atom.
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// An arbitrary label chosen by the developer. For Android P, the label should be in [0, 16).
optional int32 label = 2;
@@ -1260,7 +1267,7 @@
*/
message AnomalyDetected {
// Uid that owns the config whose anomaly detection alert fired.
- optional int32 config_uid = 1;
+ optional int32 config_uid = 1 [(is_uid) = true];
// Id of the config whose anomaly detection alert fired.
optional int64 config_id = 2;
@@ -1271,7 +1278,7 @@
message AppStartChanged {
// The uid if available. -1 means not available.
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// The app package name.
optional string pkg_name = 2;
@@ -1318,7 +1325,7 @@
message AppStartCancelChanged {
// The uid if available. -1 means not available.
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// The app package name.
optional string pkg_name = 2;
@@ -1338,7 +1345,7 @@
message AppStartFullyDrawnChanged {
// The uid if available. -1 means not available.
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// The app package name.
optional string pkg_name = 2;
@@ -1369,7 +1376,7 @@
*/
message PictureInPictureStateChanged {
// -1 if it is not available
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
optional string short_name = 2;
@@ -1388,7 +1395,7 @@
* services/core/java/com/android/server/wm/Session.java
*/
message OverlayStateChanged {
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
optional string package_name = 2;
@@ -1409,7 +1416,7 @@
* //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
*/
message ForegroundServiceStateChanged {
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// package_name + "/" + class_name
optional string short_name = 2;
@@ -1429,6 +1436,7 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message IsolatedUidChanged {
+ // NOTE: DO NOT annotate uid field in this atom. This atom is specially handled in statsd.
// The host UID. Generally, we should attribute metrics from the isolated uid to the host uid.
optional int32 parent_uid = 1;
@@ -1450,7 +1458,7 @@
message PacketWakeupOccurred {
// The uid owning the socket into which the packet was delivered, or -1 if the packet was
// delivered nowhere.
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// The interface name on which the packet was received.
optional string iface = 2;
// The ethertype value of the packet.
@@ -1478,7 +1486,7 @@
*/
message AppStartMemoryStateCaptured {
// The uid if available. -1 means not available.
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// The process name.
optional string process_name = 2;
@@ -1524,7 +1532,7 @@
*/
message LmkKillOccurred {
// The uid if available. -1 means not available.
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// The process name.
optional string process_name = 2;
@@ -1556,7 +1564,7 @@
*/
message AppDied {
// timestamp(elapsedRealtime) of record creation
- optional uint64 timestamp_millis = 1;
+ optional uint64 timestamp_millis = 1 [(stateFieldOption).option = EXCLUSIVE];
}
//////////////////////////////////////////////////////////////////////
@@ -1570,7 +1578,7 @@
* StatsCompanionService (using BatteryStats to get which interfaces are wifi)
*/
message WifiBytesTransfer {
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
optional int64 rx_bytes = 2;
@@ -1588,9 +1596,10 @@
* StatsCompanionService (using BatteryStats to get which interfaces are wifi)
*/
message WifiBytesTransferByFgBg {
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
- // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
+ // 1 denotes foreground and 0 denotes background. This is called Set in
+ // NetworkStats.
optional int32 is_foreground = 2;
optional int64 rx_bytes = 3;
@@ -1609,7 +1618,7 @@
* StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
*/
message MobileBytesTransfer {
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
optional int64 rx_bytes = 2;
@@ -1627,9 +1636,10 @@
* StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
*/
message MobileBytesTransferByFgBg {
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
- // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
+ // 1 denotes foreground and 0 denotes background. This is called Set in
+ // NetworkStats.
optional int32 is_foreground = 2;
optional int64 rx_bytes = 3;
@@ -1648,7 +1658,7 @@
* StatsCompanionService
*/
message BluetoothBytesTransfer {
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
optional int64 rx_bytes = 2;
@@ -1708,7 +1718,7 @@
* Note that isolated process uid time should be attributed to host uids.
*/
message CpuTimePerUid {
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
optional uint64 user_time_millis = 2;
optional uint64 sys_time_millis = 3;
}
@@ -1719,7 +1729,7 @@
* For each uid, we order the time by descending frequencies.
*/
message CpuTimePerUidFreq {
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
optional uint32 freq_index = 2;
optional uint64 time_millis = 3;
}
@@ -1801,7 +1811,7 @@
*/
message ProcessMemoryState {
// The uid if available. -1 means not available.
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
// The process name.
optional string process_name = 2;
@@ -1853,7 +1863,7 @@
* The file contains a monotonically increasing count of time for a single boot.
*/
message CpuActiveTime {
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
optional uint64 time_millis = 2;
}
@@ -1867,7 +1877,7 @@
* The file contains a monotonically increasing count of time for a single boot.
*/
message CpuClusterTime {
- optional int32 uid = 1;
+ optional int32 uid = 1 [(is_uid) = true];
optional int32 cluster_index = 2;
optional uint64 time_millis = 3;
}
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 72a00cb..fb0be73 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -166,6 +166,11 @@
// 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/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index 0b0c5c4..ea23623 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -112,7 +112,8 @@
VLOG("Unknown pull atom id %d", tagId);
return;
}
- if (android::util::kAtomsWithUidField.find(tagId) == android::util::kAtomsWithUidField.end()) {
+ if (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) ==
+ android::util::AtomsInfo::kAtomsWithUidField.end()) {
VLOG("No uid to merge for atom %d", tagId);
return;
}
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 778eb8e..bd8b293 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -134,8 +134,8 @@
uint64_t wrapperToken =
mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
const bool truncateTimestamp =
- android::util::kNotTruncatingTimestampAtomWhiteList.find(event.GetTagId()) ==
- android::util::kNotTruncatingTimestampAtomWhiteList.end();
+ android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.find(event.GetTagId()) ==
+ android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.end();
if (truncateTimestamp) {
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS,
(long long)truncateTimestampNsToFiveMinutes(event.GetElapsedTimestampNs()));
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 55a281e..49034ac 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -195,8 +195,9 @@
protoOutput->end(atomsToken);
}
const bool truncateTimestamp =
- android::util::kNotTruncatingTimestampAtomWhiteList.find(mTagId) ==
- android::util::kNotTruncatingTimestampAtomWhiteList.end();
+ android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.find(
+ mTagId) ==
+ android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.end();
const int64_t wall_clock_ns = truncateTimestamp ?
truncateTimestampNsToFiveMinutes(getWallClockNs()) : getWallClockNs();
for (const auto& atom : bucket.mGaugeAtoms) {
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 901f500..c6112fd 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -170,9 +170,10 @@
// 1. must not have "stop". must have "dimension"
if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) {
// TODO: need to check the start atom matcher too.
- auto it = android::util::kStateAtomsFieldOptions.find(simplePredicate.dimensions().field());
+ auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(
+ simplePredicate.dimensions().field());
// 2. must be based on a state atom.
- if (it != android::util::kStateAtomsFieldOptions.end()) {
+ if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
// 3. dimension must be primary fields + state field IN ORDER
size_t expectedDimensionCount = it->second.primaryFields.size() + 1;
vector<Matcher> dimensions;
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index e97d8b2..3ba4b7a 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -331,25 +331,25 @@
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 @@
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 @@
// 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 b0181f7..9dc73f4 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 @@
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 @@
// 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 @@
// 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 @@
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 cd41f53..3d8aa47 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -160,38 +160,46 @@
}
}
-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 @@
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 4b75628..fb7b085 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -66,7 +66,7 @@
* 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 0000000..a7b4136
--- /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 ee7d770..c9492eb 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 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 @@
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 @@
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 @@
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 @@
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 @@
} // 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 e2121dd..e238f06 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -20,8 +20,20 @@
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;->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/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;->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;->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/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;->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/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;->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/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/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_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;->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;->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;->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;->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;->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;->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;->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;->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;->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;->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/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/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;->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 952b28b..fe9a5db 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;->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/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;->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/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;->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;->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;->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;->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;-><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()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;->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/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;->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;->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;->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;->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;->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;->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 @@
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/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 0b10a35..452225c 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,6 +16,8 @@
package android.accessibilityservice;
+import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+
import android.annotation.IntDef;
import android.content.ComponentName;
import android.content.Context;
@@ -50,8 +52,6 @@
import java.util.Collections;
import java.util.List;
-import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
-
/**
* This class describes an {@link AccessibilityService}. The system notifies an
* {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
@@ -410,6 +410,15 @@
public int flags;
/**
+ * Whether or not the service has crashed and is awaiting restart. Only valid from {@link
+ * android.view.accessibility.AccessibilityManager#getEnabledAccessibilityServiceList(int)},
+ * because that is populated from the internal list of running services.
+ *
+ * @hide
+ */
+ public boolean crashed;
+
+ /**
* The component name the accessibility service.
*/
private ComponentName mComponentName;
@@ -757,6 +766,7 @@
parcel.writeInt(feedbackType);
parcel.writeLong(notificationTimeout);
parcel.writeInt(flags);
+ parcel.writeInt(crashed ? 1 : 0);
parcel.writeParcelable(mComponentName, flagz);
parcel.writeParcelable(mResolveInfo, 0);
parcel.writeString(mSettingsActivityName);
@@ -773,6 +783,7 @@
feedbackType = parcel.readInt();
notificationTimeout = parcel.readLong();
flags = parcel.readInt();
+ crashed = parcel.readInt() != 0;
mComponentName = parcel.readParcelable(this.getClass().getClassLoader());
mResolveInfo = parcel.readParcelable(null);
mSettingsActivityName = parcel.readString();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 459a587..1d069bc 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -30,12 +30,15 @@
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 @@
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 @@
// message is handled.
transaction.recycle();
}
+ // TODO(lifecycler): Recycle locally scheduled transactions.
break;
}
Object obj = msg.obj;
@@ -4034,9 +4042,11 @@
r.setState(ON_PAUSE);
}
+ /** Called from {@link LocalActivityManager}. */
final void performStopActivity(IBinder token, boolean saveState, String reason) {
ActivityClientRecord r = mActivities.get(token);
- performStopActivityInner(r, null, false, saveState, reason);
+ performStopActivityInner(r, null /* stopInfo */, false /* keepShown */, saveState,
+ false /* finalStateRequest */, reason);
}
private static final class ProviderRefCount {
@@ -4068,9 +4078,16 @@
* it the result when it is done, but the window may still be visible.
* For the client, we want to call onStop()/onStart() to indicate when
* the activity's UI visibility changes.
+ * @param r Target activity client record.
+ * @param info Action that will report activity stop to server.
+ * @param keepShown Flag indicating whether the activity is still shown.
+ * @param saveState Flag indicating whether the activity state should be saved.
+ * @param finalStateRequest Flag indicating if this call is handling final lifecycle state
+ * request for a transaction.
+ * @param reason Reason for performing this operation.
*/
private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown,
- boolean saveState, String reason) {
+ boolean saveState, boolean finalStateRequest, String reason) {
if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
if (r != null) {
if (!keepShown && r.stopped) {
@@ -4080,11 +4097,13 @@
// if the activity isn't resumed.
return;
}
- RuntimeException e = new RuntimeException(
- "Performing stop of activity that is already stopped: "
- + r.intent.getComponent().toShortString());
- Slog.e(TAG, e.getMessage(), e);
- Slog.e(TAG, r.getStateString());
+ if (!finalStateRequest) {
+ final RuntimeException e = new RuntimeException(
+ "Performing stop of activity that is already stopped: "
+ + r.intent.getComponent().toShortString());
+ Slog.e(TAG, e.getMessage(), e);
+ Slog.e(TAG, r.getStateString());
+ }
}
// One must first be paused before stopped...
@@ -4177,12 +4196,13 @@
@Override
public void handleStopActivity(IBinder token, boolean show, int configChanges,
- PendingTransactionActions pendingActions, String reason) {
+ PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
final ActivityClientRecord r = mActivities.get(token);
r.activity.mConfigChangeFlags |= configChanges;
final StopInfo stopInfo = new StopInfo();
- performStopActivityInner(r, stopInfo, show, true, reason);
+ performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest,
+ reason);
if (localLOGV) Slog.v(
TAG, "Finishing stop of " + r + ": show=" + show
@@ -4234,7 +4254,8 @@
}
if (!show && !r.stopped) {
- performStopActivityInner(r, null, show, false, "handleWindowVisibility");
+ performStopActivityInner(r, null /* stopInfo */, show, false /* saveState */,
+ false /* finalStateRequest */, "handleWindowVisibility");
} else if (show && r.stopped) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
@@ -4710,14 +4731,25 @@
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 5d0143a..7032a2f 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -27,6 +27,7 @@
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 @@
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 21fb18a..b8c4ef7 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -20,6 +20,7 @@
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 @@
}
@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/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 206495d..961bca2 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -78,9 +78,19 @@
public abstract void handleResumeActivity(IBinder token, boolean finalStateRequest,
boolean isForward, String reason);
- /** Stop the activity. */
+ /**
+ * Stop the activity.
+ * @param token Target activity token.
+ * @param show Flag indicating whether activity is still shown.
+ * @param configChanges Activity configuration changes.
+ * @param pendingActions Pending actions to be used on this or later stages of activity
+ * transaction.
+ * @param finalStateRequest Flag indicating if this call is handling final lifecycle state
+ * request for a transaction.
+ * @param reason Reason for performing this operation.
+ */
public abstract void handleStopActivity(IBinder token, boolean show, int configChanges,
- PendingTransactionActions pendingActions, String reason);
+ PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
/** Report that activity was stopped to server. */
public abstract void reportStop(PendingTransactionActions pendingActions);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2ac3e23..c6568e1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -89,6 +89,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -2599,6 +2600,80 @@
};
/**
+ * @hide
+ */
+ public static boolean areActionsVisiblyDifferent(Notification first, Notification second) {
+ Notification.Action[] firstAs = first.actions;
+ Notification.Action[] secondAs = second.actions;
+ if (firstAs == null && secondAs != null || firstAs != null && secondAs == null) {
+ return true;
+ }
+ if (firstAs != null && secondAs != null) {
+ if (firstAs.length != secondAs.length) {
+ return true;
+ }
+ for (int i = 0; i < firstAs.length; i++) {
+ if (!Objects.equals(firstAs[i].title, secondAs[i].title)) {
+ return true;
+ }
+ RemoteInput[] firstRs = firstAs[i].getRemoteInputs();
+ RemoteInput[] secondRs = secondAs[i].getRemoteInputs();
+ if (firstRs == null) {
+ firstRs = new RemoteInput[0];
+ }
+ if (secondRs == null) {
+ secondRs = new RemoteInput[0];
+ }
+ if (firstRs.length != secondRs.length) {
+ return true;
+ }
+ for (int j = 0; j < firstRs.length; j++) {
+ if (!Objects.equals(firstRs[j].getLabel(), secondRs[j].getLabel())) {
+ return true;
+ }
+ CharSequence[] firstCs = firstRs[i].getChoices();
+ CharSequence[] secondCs = secondRs[i].getChoices();
+ if (firstCs == null) {
+ firstCs = new CharSequence[0];
+ }
+ if (secondCs == null) {
+ secondCs = new CharSequence[0];
+ }
+ if (firstCs.length != secondCs.length) {
+ return true;
+ }
+ for (int k = 0; k < firstCs.length; k++) {
+ if (!Objects.equals(firstCs[k], secondCs[k])) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean areStyledNotificationsVisiblyDifferent(Builder first, Builder second) {
+ if (first.getStyle() == null) {
+ return second.getStyle() != null;
+ }
+ if (second.getStyle() == null) {
+ return true;
+ }
+ return first.getStyle().areNotificationsVisiblyDifferent(second.getStyle());
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean areRemoteViewsChanged(Builder first, Builder second) {
+ return !first.usesStandardHeader() || !second.usesStandardHeader();
+ }
+
+ /**
* Parcelling creates multiple copies of objects in {@code extras}. Fix them.
* <p>
* For backwards compatibility {@code extras} holds some references to "real" member data such
@@ -4039,6 +4114,13 @@
}
/**
+ * Returns the style set by {@link #setStyle(Style)}.
+ */
+ public Style getStyle() {
+ return mStyle;
+ }
+
+ /**
* Specify the value of {@link #visibility}.
*
* @return The same Builder.
@@ -5495,6 +5577,24 @@
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();
+ }
}
/**
@@ -5867,6 +5967,21 @@
*/
public void validate(Context context) {
}
+
+ /**
+ * @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;
+ }
}
/**
@@ -5920,6 +6035,13 @@
}
/**
+ * @hide
+ */
+ public Bitmap getBigPicture() {
+ return mPicture;
+ }
+
+ /**
* Provide the bitmap to be used as the payload for the BigPicture notification.
*/
public BigPictureStyle bigPicture(Bitmap b) {
@@ -6059,6 +6181,18 @@
public boolean hasSummaryInHeader() {
return false;
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean areNotificationsVisiblyDifferent(Style other) {
+ if (other == null || getClass() != other.getClass()) {
+ return true;
+ }
+ BigPictureStyle otherS = (BigPictureStyle) other;
+ return !Objects.equals(getBigPicture(), otherS.getBigPicture());
+ }
}
/**
@@ -6122,6 +6256,13 @@
/**
* @hide
*/
+ public CharSequence getBigText() {
+ return mBigText;
+ }
+
+ /**
+ * @hide
+ */
public void addExtras(Bundle extras) {
super.addExtras(extras);
@@ -6191,6 +6332,18 @@
return contentView;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean areNotificationsVisiblyDifferent(Style other) {
+ if (other == null || getClass() != other.getClass()) {
+ return true;
+ }
+ BigTextStyle newS = (BigTextStyle) other;
+ return !Objects.equals(getBigText(), newS.getBigText());
+ }
+
static void applyBigTextContentView(Builder builder,
RemoteViews contentView, CharSequence bigTextText) {
contentView.setTextViewText(R.id.big_text, builder.processTextSpans(bigTextText));
@@ -6278,6 +6431,23 @@
}
/**
+ * @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() {
@@ -6533,6 +6703,58 @@
return remoteViews;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean areNotificationsVisiblyDifferent(Style other) {
+ if (other == null || getClass() != other.getClass()) {
+ return true;
+ }
+ MessagingStyle newS = (MessagingStyle) other;
+ List<MessagingStyle.Message> oldMs = getMessages();
+ List<MessagingStyle.Message> newMs = newS.getMessages();
+
+ if (oldMs == null) {
+ oldMs = new ArrayList<>();
+ }
+ if (newMs == null) {
+ newMs = new ArrayList<>();
+ }
+
+ int n = oldMs.size();
+ if (n != newMs.size()) {
+ return true;
+ }
+ for (int i = 0; i < n; i++) {
+ MessagingStyle.Message oldM = oldMs.get(i);
+ MessagingStyle.Message newM = newMs.get(i);
+ if (!Objects.equals(oldM.getText(), newM.getText())) {
+ return true;
+ }
+ if (!Objects.equals(oldM.getDataUri(), newM.getDataUri())) {
+ return true;
+ }
+ CharSequence oldSender = oldM.getSenderPerson() == null ? oldM.getSender()
+ : oldM.getSenderPerson().getName();
+ CharSequence newSender = newM.getSenderPerson() == null ? newM.getSender()
+ : newM.getSenderPerson().getName();
+ if (!Objects.equals(oldSender, newSender)) {
+ return true;
+ }
+
+ String oldKey = oldM.getSenderPerson() == null
+ ? null : oldM.getSenderPerson().getKey();
+ String newKey = newM.getSenderPerson() == null
+ ? null : newM.getSenderPerson().getKey();
+ if (!Objects.equals(oldKey, newKey)) {
+ return true;
+ }
+ // Other fields (like timestamp) intentionally excluded
+ }
+ return false;
+ }
+
private Message findLatestIncomingMessage() {
return findLatestIncomingMessage(mMessages);
}
@@ -6964,6 +7186,13 @@
/**
* @hide
*/
+ public ArrayList<CharSequence> getLines() {
+ return mTexts;
+ }
+
+ /**
+ * @hide
+ */
public void addExtras(Bundle extras) {
super.addExtras(extras);
@@ -7042,6 +7271,18 @@
return contentView;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean areNotificationsVisiblyDifferent(Style other) {
+ if (other == null || getClass() != other.getClass()) {
+ return true;
+ }
+ InboxStyle newS = (InboxStyle) other;
+ return !Objects.equals(getLines(), newS.getLines());
+ }
+
private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
int endMargin = 0;
if (first) {
@@ -7205,6 +7446,18 @@
}
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean areNotificationsVisiblyDifferent(Style other) {
+ if (other == null || getClass() != other.getClass()) {
+ return true;
+ }
+ // All fields to compare are on the Notification object
+ return false;
+ }
+
private RemoteViews generateMediaActionButton(Action action, int color) {
final boolean tombstone = (action.actionIntent == null);
RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
@@ -7414,6 +7667,18 @@
}
remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean areNotificationsVisiblyDifferent(Style other) {
+ if (other == null || getClass() != other.getClass()) {
+ return true;
+ }
+ // Comparison done for all custom RemoteViews, independent of style
+ return false;
+ }
}
/**
@@ -7504,6 +7769,18 @@
return makeBigContentViewWithCustomContent(customRemoteView);
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean areNotificationsVisiblyDifferent(Style other) {
+ if (other == null || getClass() != other.getClass()) {
+ return true;
+ }
+ // Comparison done for all custom RemoteViews, independent of style
+ return false;
+ }
+
private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
RemoteViews customContent) {
if (customContent != null) {
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index 0a61fab..8db38d3 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -39,7 +39,7 @@
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
client.handleStopActivity(token, mShowWindow, mConfigChanges, pendingActions,
- "STOP_ACTIVITY_ITEM");
+ true /* finalStateRequest */, "STOP_ACTIVITY_ITEM");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 0d995e8..553c3ae 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -204,7 +204,8 @@
break;
case ON_STOP:
mTransactionHandler.handleStopActivity(r.token, false /* show */,
- 0 /* configChanges */, mPendingActions, "LIFECYCLER_STOP_ACTIVITY");
+ 0 /* configChanges */, mPendingActions, false /* finalStateRequest */,
+ "LIFECYCLER_STOP_ACTIVITY");
break;
case ON_DESTROY:
mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 7e66fd7..01b13a2 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.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 @@
* {@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 @@
* 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 @@
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 d6f2352..61679cb 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -66,10 +66,27 @@
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 @@
/**
* 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 @@
/**
* 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 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 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 @@
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 @@
/**
* Add hints to the Slice being constructed
*/
- public Builder addHints(@SliceHint String... hints) {
- mHints.addAll(Arrays.asList(hints));
+ public Builder addHints(@SliceHint List<String> hints) {
+ mHints.addAll(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()]));
- }
-
- /**
- * Add the spec for this slice.
+ * @deprecated TO BE REMOVED
*/
public Builder setSpec(SliceSpec spec) {
mSpec = spec;
@@ -362,17 +397,10 @@
/**
* 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 @@
/**
* 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 @@
* @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,70 +460,48 @@
* @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));
+ public Slice.Builder addLong(long value, @Nullable @SliceSubtype String subType,
+ @SliceHint List<String> hints) {
+ mItems.add(new SliceItem(value, SliceItem.FORMAT_LONG, subType,
+ hints.toArray(new String[hints.size()])));
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,
- @SliceHint List<String> hints) {
- return addTimestamp(time, subType, hints.toArray(new String[hints.size()]));
- }
-
- /**
* 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 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 9eb2bb8..019ae49 100644
--- a/core/java/android/app/slice/SliceItem.java
+++ b/core/java/android/app/slice/SliceItem.java
@@ -67,7 +67,7 @@
FORMAT_IMAGE,
FORMAT_ACTION,
FORMAT_INT,
- FORMAT_TIMESTAMP,
+ FORMAT_LONG,
FORMAT_REMOTE_INPUT,
FORMAT_BUNDLE,
})
@@ -98,9 +98,14 @@
*/
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 @@
* @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/SliceMetrics.java b/core/java/android/app/slice/SliceMetrics.java
new file mode 100644
index 0000000..a7069bc
--- /dev/null
+++ b/core/java/android/app/slice/SliceMetrics.java
@@ -0,0 +1,63 @@
+/*
+ * 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.app.slice;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.Uri;
+
+import com.android.internal.logging.MetricsLogger;
+
+/**
+ * Metrics interface for slices.
+ *
+ * This is called by SliceView, so Slice develoers should
+ * not need to reference this class.
+ *
+ * @see androidx.slice.widget.SliceView
+ */
+public class SliceMetrics {
+
+ private static final String TAG = "SliceMetrics";
+ private MetricsLogger mMetricsLogger;
+
+ /**
+ * An object to be used throughout the life of a slice to register events.
+ */
+ public SliceMetrics(@NonNull Context context, @NonNull Uri uri) {
+ mMetricsLogger = new MetricsLogger();
+ }
+
+ /**
+ * To be called whenever the slice becomes visible to the user.
+ */
+ public void logVisible() {
+ }
+
+ /**
+ * To be called whenever the slice becomes invisible to the user.
+ */
+ public void logHidden() {
+ }
+
+ /**
+ * To be called whenever the use interacts with a slice.
+ *@param subSlice The URI of the sub-slice that is the subject of the interaction.
+ */
+ public void logTouch(@NonNull Uri subSlice) {
+ }
+}
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index aa2cf46..dd89293 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -41,6 +41,7 @@
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 @@
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 4c7e97b..b354e81 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -81,6 +81,7 @@
* An event type denoting that a package was interacted with in some way by the system.
* @hide
*/
+ @SystemApi
public static final int SYSTEM_INTERACTION = 6;
/**
@@ -124,6 +125,20 @@
@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 59f001c..1c384580 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -107,25 +107,35 @@
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 @@
/**
* 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/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index f67ec4a..20248b9 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -679,11 +679,13 @@
}
/**
- * Updates the info for the supplied AppWidget provider.
+ * Updates the info for the supplied AppWidget provider. Apps can use this to change the default
+ * behavior of the widget based on the state of the app (for e.g., if the user is logged in
+ * or not). Calling this API completely replaces the previous definition.
*
* <p>
* The manifest entry of the provider should contain an additional meta-data tag similar to
- * {@link #META_DATA_APPWIDGET_PROVIDER} which should point to any additional definitions for
+ * {@link #META_DATA_APPWIDGET_PROVIDER} which should point to any alternative definitions for
* the provider.
*
* <p>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bd7961f..3536eea 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4183,6 +4183,12 @@
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/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index d5b052e..390b83f 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1242,7 +1242,7 @@
* 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 @@
* <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 @@
* 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 @@
* 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 @@
* <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 @@
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 @@
* 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 @@
* <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 @@
* 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 @@
*
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @deprecated
+ * <p>Not used in HALv3 or newer</p>
+
* @hide
*/
@Deprecated
@@ -1883,6 +1938,8 @@
* <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 @@
* 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 @@
* {@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 @@
* <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 14c2865..7669c01 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -684,7 +684,7 @@
* <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 @@
* 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 @@
* <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 @@
* 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 d7c5564..b0cbec7 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2791,8 +2791,10 @@
* <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 e84e48f..6331942 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2783,7 +2783,7 @@
* 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 @@
* <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 @@
* 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 @@
* 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 @@
* <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 @@
* 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 @@
* > 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 @@
*
* @see CaptureRequest#COLOR_CORRECTION_GAINS
* @deprecated
+ * <p>Never fully implemented or specified; do not use</p>
+
* @hide
*/
@Deprecated
@@ -3801,6 +3854,8 @@
* 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 @@
* <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 e81fbee..a3b2d22 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -668,6 +668,45 @@
}
/**
+ * 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 d7f7c86..1f67a6b 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -490,6 +490,32 @@
}
/**
+ * 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 0571ae1..9fcb9d3 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -96,6 +96,14 @@
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 b635088..dde8a33 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.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 @@
* 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 @@
* 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 @@
/*****************************************************************************
* 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 @@
* 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 @@
/*****************************************************************************
* 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 @@
/**
* 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 @@
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 @@
}
};
+ /** @hide */
protected static RecognitionEvent fromParcel(Parcel in) {
int status = in.readInt();
int soundModelHandle = in.readInt();
@@ -619,12 +752,12 @@
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 @@
* 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 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 @@
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
* 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 @@
* - {@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 @@
* @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 @@
/**
* 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 3ce0283..3a3ddcc 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 @@
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 c69a4d4..f4b328e 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -38,6 +38,13 @@
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 fbf3056..4e1f834 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -656,10 +656,14 @@
* 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 @@
* <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/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index b02d48d..c3f23a1 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -225,7 +225,7 @@
* Indicates the Secure Element on which the transaction occurred.
* eSE1...eSEn for Embedded Secure Elements, SIM1...SIMn for UICC, etc.
*/
- public static final String EXTRA_SE_NAME = "android.nfc.extra.SE_NAME";
+ public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
public static final int STATE_OFF = 1;
public static final int STATE_TURNING_ON = 2;
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 1681f11..13e4e38 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -65,4 +65,7 @@
// 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 66fa629..c00100b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1280,6 +1280,19 @@
}
/**
+ * 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 79b6ea6..b9aad11 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -83,7 +83,6 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.MemoryIntArray;
-import android.util.StatsLog;
import android.view.textservice.TextServicesManager;
import com.android.internal.annotations.GuardedBy;
@@ -784,6 +783,21 @@
"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>
@@ -1942,11 +1956,7 @@
arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
IContentProvider cp = mProviderHolder.getProvider(cr);
- String prevValue = getStringForUser(cr, name, userHandle);
cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
- String newValue = getStringForUser(cr, name, userHandle);
- StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newValue, prevValue, tag,
- makeDefault, userHandle);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
return false;
@@ -9541,6 +9551,12 @@
public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS =
"ble_scan_low_latency_interval_ms";
+ /**
+ * The mode that BLE scanning clients will be moved to when in the background.
+ * @hide
+ */
+ public static final String BLE_SCAN_BACKGROUND_MODE = "ble_scan_background_mode";
+
/**
* Used to save the Wifi_ON state prior to tethering.
* This state will be checked to restore Wifi after
@@ -11591,6 +11607,24 @@
"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.
*
@@ -12326,6 +12360,16 @@
"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/KeyDerivationParams.java b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
index ef5e90c..428eaaa 100644
--- a/core/java/android/security/keystore/recovery/KeyDerivationParams.java
+++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
@@ -37,25 +37,26 @@
@SystemApi
public final class KeyDerivationParams implements Parcelable {
private final int mAlgorithm;
- private byte[] mSalt;
+ private final byte[] mSalt;
+ private final int mDifficulty;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"ALGORITHM_"}, value = {ALGORITHM_SHA256, ALGORITHM_ARGON2ID})
+ @IntDef(prefix = {"ALGORITHM_"}, value = {ALGORITHM_SHA256, ALGORITHM_SCRYPT})
public @interface KeyDerivationAlgorithm {
}
/**
- * Salted SHA256
+ * Salted SHA256.
*/
public static final int ALGORITHM_SHA256 = 1;
/**
- * Argon2ID
+ * SCRYPT.
+ *
* @hide
*/
- // TODO: add Argon2ID support.
- public static final int ALGORITHM_ARGON2ID = 2;
+ public static final int ALGORITHM_SCRYPT = 2;
/**
* Creates instance of the class to to derive key using salted SHA256 hash.
@@ -65,12 +66,30 @@
}
/**
+ * Creates instance of the class to to derive key using the password hashing algorithm SCRYPT.
+ *
+ * @hide
+ */
+ public static KeyDerivationParams createScryptParams(@NonNull byte[] salt, int difficulty) {
+ return new KeyDerivationParams(ALGORITHM_SCRYPT, salt, difficulty);
+ }
+
+ /**
* @hide
*/
// TODO: Make private once legacy API is removed
public KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
+ this(algorithm, salt, /*difficulty=*/ 0);
+ }
+
+ /**
+ * @hide
+ */
+ KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt,
+ int difficulty) {
mAlgorithm = algorithm;
mSalt = Preconditions.checkNotNull(salt);
+ mDifficulty = difficulty;
}
/**
@@ -87,6 +106,15 @@
return mSalt;
}
+ /**
+ * Gets hashing difficulty.
+ *
+ * @hide
+ */
+ public int getDifficulty() {
+ return mDifficulty;
+ }
+
public static final Parcelable.Creator<KeyDerivationParams> CREATOR =
new Parcelable.Creator<KeyDerivationParams>() {
public KeyDerivationParams createFromParcel(Parcel in) {
@@ -102,6 +130,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mAlgorithm);
out.writeByteArray(mSalt);
+ out.writeInt(mDifficulty);
}
/**
@@ -110,6 +139,7 @@
protected KeyDerivationParams(Parcel in) {
mAlgorithm = in.readInt();
mSalt = in.createByteArray();
+ mDifficulty = in.readInt();
}
@Override
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 7523afd..10c1c9e 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -547,10 +547,7 @@
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 @@
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 @@
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 @@
}
/**
+ * 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 137dd89..744bfa3 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 @@
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 @@
}
/**
+ * 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
similarity index 85%
rename from core/java/android/security/backup/TrustedRootCertificates.java
rename to core/java/android/security/keystore/recovery/TrustedRootCertificates.java
index ed922ed..4bdde8a 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 @@
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
similarity index 98%
rename from core/java/android/security/backup/X509CertificateParsingUtils.java
rename to core/java/android/security/keystore/recovery/X509CertificateParsingUtils.java
index 30495de..fa72c83 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 ea10ae7..3726e66 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1418,6 +1418,7 @@
private ArrayList<SnoozeCriterion> mSnoozeCriteria;
private boolean mShowBadge;
private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
+ private boolean mHidden;
public Ranking() {}
@@ -1557,6 +1558,16 @@
}
/**
+ * 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 @@
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 @@
mSnoozeCriteria = snoozeCriteria;
mShowBadge = showBadge;
mUserSentiment = userSentiment;
+ mHidden = hidden;
}
/**
@@ -1628,6 +1640,7 @@
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 @@
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 @@
? 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 @@
}
}
+ // 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 6d51db0..00c47ec 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -36,12 +36,13 @@
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 @@
mSnoozeCriteria = snoozeCriteria;
mShowBadge = showBadge;
mUserSentiment = userSentiment;
+ mHidden = hidden;
}
public NotificationRankingUpdate(Parcel in) {
@@ -70,6 +72,7 @@
mSnoozeCriteria = in.readBundle();
mShowBadge = in.readBundle();
mUserSentiment = in.readBundle();
+ mHidden = in.readBundle();
}
@Override
@@ -91,6 +94,7 @@
out.writeBundle(mSnoozeCriteria);
out.writeBundle(mShowBadge);
out.writeBundle(mUserSentiment);
+ out.writeBundle(mHidden);
}
public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -151,4 +155,8 @@
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 40e84b9..61277e2 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -545,7 +545,7 @@
*/
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 aafcf44..980f4704 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -675,7 +675,7 @@
/**
* 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 b740193..9458184 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -19,7 +19,6 @@
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 @@
}
};
+ /** @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 @@
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 @@
* </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 @@
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 @@
* Returns the count of paragraphs.
*/
public @IntRange(from = 0) int getParagraphCount() {
- return mParagraphBreakPoints.length;
+ return mParagraphInfo.length;
}
/**
@@ -392,7 +396,7 @@
*/
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 @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 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 56d0946..afb5df8 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 @@
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 366ec14..5dd1a52 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -26,7 +26,7 @@
/* 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 @@
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 @@
* @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 @@
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 afed221..acee3c5 100644
--- a/core/java/android/text/SpannedString.java
+++ b/core/java/android/text/SpannedString.java
@@ -26,12 +26,27 @@
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 299bde2..0899074 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -651,31 +651,29 @@
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 @@
}
}
- 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 c44f42b..46e3169 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -219,7 +219,7 @@
&& 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 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 @@
&& 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 @@
}
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 613e6d8..6486230 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -428,4 +428,14 @@
* 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 b7524fb..7ff4f21 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -566,7 +566,7 @@
*/
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 779eefb..886f5c8 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -445,9 +445,15 @@
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/WebView.java b/core/java/android/webkit/WebView.java
index 5178a97..fc94b1f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2451,6 +2451,14 @@
* 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 1bee692..93730df 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 @@
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 82ac241..d53090b 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 @@
}
/**
- *
+ * 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 @@
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 25e1589..bec70fd 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -78,10 +78,14 @@
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 fde7e96..d4ab426 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.util.ArrayList;
import java.util.Collection;
import java.util.List;
-
/**
* Utilities for the lock pattern and its settings.
*/
@@ -585,7 +593,7 @@
return quality;
}
- return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ return PASSWORD_QUALITY_UNSPECIFIED;
}
/**
@@ -604,13 +612,16 @@
* 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 @@
* @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);
-
- // 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);
- }
- }
-
- reportPatternWasChosen(userId);
- onAfterChangingPassword(userId);
- } catch (RemoteException re) {
- Log.e(TAG, "Couldn't save lock pattern " + re);
+ 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.");
}
+
+ 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);
}
private void updateCryptoUserInfo(int userId) {
@@ -796,25 +809,27 @@
*/
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);
-
- 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);
+ 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);
}
+
+ 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 @@
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 @@
* @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 @@
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 @@
}
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 @@
}
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 @@
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 @@
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 d6496cd..7166c75 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -42,7 +42,6 @@
}
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 @@
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 @@
return 0;
}
picture = recorder.finishRecordingAsPicture();
+ bytesUsed += picture->approximateBytesUsed();
}
@@ -74,7 +103,10 @@
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 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/graphics/pdf/PdfRenderer.cpp b/core/jni/android/graphics/pdf/PdfRenderer.cpp
index d20c7ef..32ac30f 100644
--- a/core/jni/android/graphics/pdf/PdfRenderer.cpp
+++ b/core/jni/android/graphics/pdf/PdfRenderer.cpp
@@ -92,20 +92,7 @@
renderFlags |= FPDF_PRINTING;
}
- // PDF's coordinate system origin is left-bottom while in graphics it
- // is the top-left. So, translate the PDF coordinates to ours.
- SkMatrix reflectOnX = SkMatrix::MakeScale(1, -1);
- SkMatrix moveUp = SkMatrix::MakeTrans(0, FPDF_GetPageHeight(page));
- SkMatrix coordinateChange = SkMatrix::Concat(moveUp, reflectOnX);
-
- // Apply the transformation
- SkMatrix matrix;
- if (transformPtr == 0) {
- matrix = coordinateChange;
- } else {
- matrix = SkMatrix::Concat(*reinterpret_cast<SkMatrix*>(transformPtr), coordinateChange);
- }
-
+ SkMatrix matrix = *reinterpret_cast<SkMatrix*>(transformPtr);
SkScalar transformValues[6];
if (!matrix.asAffine(transformValues)) {
jniThrowException(env, "java/lang/IllegalArgumentException",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a30b2ad..04cb08f 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -116,9 +116,13 @@
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 4657dc4..6a3aaab 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -118,17 +118,17 @@
// 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 @@
];
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 53e6532..4e781d6 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -468,6 +468,8 @@
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 @@
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/proto/android/service/graphicsstats.proto b/core/proto/android/service/graphicsstats.proto
index f422065..c2fedf5 100644
--- a/core/proto/android/service/graphicsstats.proto
+++ b/core/proto/android/service/graphicsstats.proto
@@ -56,7 +56,7 @@
// Number of "missed vsync" events.
optional int32 missed_vsync_count = 3;
- // Number of "high input latency" events.
+ // Number of frames in triple-buffering scenario (high input latency)
optional int32 high_input_latency_count = 4;
// Number of "slow UI thread" events.
@@ -67,6 +67,9 @@
// Number of "slow draw" events.
optional int32 slow_draw_count = 7;
+
+ // Number of frames that missed their deadline (aka, visibly janked)
+ optional int32 missed_deadline_count = 8;
}
message GraphicsStatsHistogramBucketProto {
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 5c40e5f..cccd2fe 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -93,7 +93,8 @@
message ServiceProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
- repeated string name = 1 [ (.android.privacy).dest = DEST_EXPLICIT ];
+ // Package or component name.
+ repeated string name = 1;
optional int32 user_id = 2;
optional bool is_primary = 3;
}
@@ -169,16 +170,16 @@
message ZenRuleProto {
option (android.msg_privacy).dest = DEST_EXPLICIT;
- // Required for automatic (unique).
+ // Required for automatic ZenRules (unique).
optional string id = 1;
- // Required for automatic.
+ // Required for automatic ZenRules.
optional string name = 2;
- // Required for automatic.
+ // Required for automatic ZenRules.
optional int64 creation_time_ms = 3 [
(android.privacy).dest = DEST_AUTOMATIC
];
optional bool enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
- // Package name, only used for manual rules.
+ // Package name, only used for manual ZenRules.
optional string enabler = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
// User manually disabled this instance.
optional bool is_snoozing = 6 [
@@ -188,7 +189,7 @@
(android.privacy).dest = DEST_AUTOMATIC
];
- // Required for automatic.
+ // Required for automatic ZenRules.
optional string condition_id = 8;
optional ConditionProto condition = 9;
optional android.content.ComponentNameProto component = 10;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9b11a33..1b4d571 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/shutdown_dialog.xml b/core/res/res/layout/shutdown_dialog.xml
index 398bfb1..2d214b3 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 4a72bf9..75b3bcf 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 ad4d7dd..ac01c4e 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/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 4628aa9..a99e139 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -22,6 +22,7 @@
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.ResumeActivityItem;
+import android.app.servertransaction.StopActivityItem;
import android.content.Intent;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
@@ -34,6 +35,8 @@
/**
* Test for verifying {@link android.app.ActivityThread} class.
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:android.app.activity.ActivityThreadTest
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
@@ -63,15 +66,23 @@
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
+ @Test
+ public void testSleepAndStop() throws Exception {
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+
+ appThread.scheduleSleeping(activity.getActivityToken(), true /* sleeping */);
+ appThread.scheduleTransaction(newStopTransaction(activity));
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
private static ClientTransaction newRelaunchResumeTransaction(Activity activity) {
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(null,
- null, 0, new MergedConfiguration(),
- false /* preserveWindow */);
+ null, 0, new MergedConfiguration(), false /* preserveWindow */);
final ResumeActivityItem resumeStateRequest =
ResumeActivityItem.obtain(true /* isForward */);
- final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
- final ClientTransaction transaction =
- ClientTransaction.obtain(appThread, activity.getActivityToken());
+
+ final ClientTransaction transaction = newTransaction(activity);
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(resumeStateRequest);
@@ -81,14 +92,28 @@
private static ClientTransaction newResumeTransaction(Activity activity) {
final ResumeActivityItem resumeStateRequest =
ResumeActivityItem.obtain(true /* isForward */);
- final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
- final ClientTransaction transaction =
- ClientTransaction.obtain(appThread, activity.getActivityToken());
+
+ final ClientTransaction transaction = newTransaction(activity);
transaction.setLifecycleStateRequest(resumeStateRequest);
return transaction;
}
+ private static ClientTransaction newStopTransaction(Activity activity) {
+ final StopActivityItem stopStateRequest =
+ StopActivityItem.obtain(false /* showWindow */, 0 /* configChanges */);
+
+ final ClientTransaction transaction = newTransaction(activity);
+ transaction.setLifecycleStateRequest(stopStateRequest);
+
+ return transaction;
+ }
+
+ private static ClientTransaction newTransaction(Activity activity) {
+ final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+ return ClientTransaction.obtain(appThread, activity.getActivityToken());
+ }
+
// Test activity
public static class TestActivity extends Activity {
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index cc36b96..2036e2f 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -129,6 +129,7 @@
Settings.Global.BLE_SCAN_BALANCED_INTERVAL_MS,
Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS,
Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
+ Settings.Global.BLE_SCAN_BACKGROUND_MODE,
Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX,
Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX,
Settings.Global.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX,
@@ -169,6 +170,7 @@
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,
@@ -266,6 +268,7 @@
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,
@@ -369,6 +372,7 @@
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
similarity index 90%
rename from core/tests/coretests/src/android/security/backup/TrustedRootCertificatesTest.java
rename to core/tests/coretests/src/android/security/keystore/recovery/TrustedRootCertificatesTest.java
index 0f9cb45..3b4ad38 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
similarity index 96%
rename from core/tests/coretests/src/android/security/backup/X509CertificateParsingUtilsTest.java
rename to core/tests/coretests/src/android/security/keystore/recovery/X509CertificateParsingUtilsTest.java
index ad85c25..7f0eb43 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 0000000..c205f96
--- /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 0000000..0680924
--- /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 be0372d..bacddf14 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -25,42 +25,50 @@
<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 @@
<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 @@
<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 @@
<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/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index b455419..c6a6113 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -325,6 +325,7 @@
<permission name="android.permission.SET_TIME"/>
<permission name="android.permission.SET_TIME_ZONE"/>
<permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES"/>
+ <permission name="android.permission.START_TASKS_FROM_RECENTS" />
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index c0f4920..a47ecf5 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -292,8 +292,7 @@
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/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index cf29e43..81a7980 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -38,16 +38,27 @@
namespace uirenderer {
struct Comparison {
+ JankType type;
+ std::function<int64_t(nsecs_t)> computeThreadshold;
FrameInfoIndex start;
FrameInfoIndex end;
};
-static const Comparison COMPARISONS[] = {
- {FrameInfoIndex::IntendedVsync, FrameInfoIndex::Vsync},
- {FrameInfoIndex::OldestInputEvent, FrameInfoIndex::Vsync},
- {FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart},
- {FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart},
- {FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},
+static const std::array<Comparison, 4> COMPARISONS{
+ Comparison{JankType::kMissedVsync, [](nsecs_t) { return 1; }, FrameInfoIndex::IntendedVsync,
+ FrameInfoIndex::Vsync},
+
+ Comparison{JankType::kSlowUI,
+ [](nsecs_t frameInterval) { return static_cast<int64_t>(.5 * frameInterval); },
+ FrameInfoIndex::Vsync, FrameInfoIndex::SyncStart},
+
+ Comparison{JankType::kSlowSync,
+ [](nsecs_t frameInterval) { return static_cast<int64_t>(.2 * frameInterval); },
+ FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart},
+
+ Comparison{JankType::kSlowRT,
+ [](nsecs_t frameInterval) { return static_cast<int64_t>(.75 * frameInterval); },
+ FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::FrameCompleted},
};
// If the event exceeds 10 seconds throw it away, this isn't a jank event
@@ -91,24 +102,10 @@
void JankTracker::setFrameInterval(nsecs_t frameInterval) {
mFrameInterval = frameInterval;
- mThresholds[kMissedVsync] = 1;
- /*
- * Due to interpolation and sample rate differences between the touch
- * panel and the display (example, 85hz touch panel driving a 60hz display)
- * we call high latency 1.5 * frameinterval
- *
- * NOTE: Be careful when tuning this! A theoretical 1,000hz touch panel
- * on a 60hz display will show kOldestInputEvent - kIntendedVsync of being 15ms
- * Thus this must always be larger than frameInterval, or it will fail
- */
- mThresholds[kHighInputLatency] = static_cast<int64_t>(1.5 * frameInterval);
- // Note that these do not add up to 1. This is intentional. It's to deal
- // with variance in values, and should be sort of an upper-bound on what
- // is reasonable to expect.
- mThresholds[kSlowUI] = static_cast<int64_t>(.5 * frameInterval);
- mThresholds[kSlowSync] = static_cast<int64_t>(.2 * frameInterval);
- mThresholds[kSlowRT] = static_cast<int64_t>(.75 * frameInterval);
+ for (auto& comparison : COMPARISONS) {
+ mThresholds[comparison.type] = comparison.computeThreadshold(frameInterval);
+ }
}
void JankTracker::finishFrame(const FrameInfo& frame) {
@@ -129,28 +126,48 @@
totalDuration -= forgiveAmount;
}
}
+
LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64, totalDuration);
mData->reportFrame(totalDuration);
(*mGlobalData)->reportFrame(totalDuration);
- // Keep the fast path as fast as possible.
- if (CC_LIKELY(totalDuration < mFrameInterval)) {
- return;
- }
-
// Only things like Surface.lockHardwareCanvas() are exempt from tracking
- if (frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS) {
+ if (CC_UNLIKELY(frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS)) {
return;
}
- mData->reportJank();
- (*mGlobalData)->reportJank();
+ if (totalDuration > mFrameInterval) {
+ mData->reportJank();
+ (*mGlobalData)->reportJank();
+ }
- for (int i = 0; i < NUM_BUCKETS; i++) {
- int64_t delta = frame.duration(COMPARISONS[i].start, COMPARISONS[i].end);
- if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
- mData->reportJankType((JankType)i);
- (*mGlobalData)->reportJankType((JankType)i);
+ bool isTripleBuffered = mSwapDeadline > frame[FrameInfoIndex::IntendedVsync];
+
+ mSwapDeadline = std::max(mSwapDeadline + mFrameInterval,
+ frame[FrameInfoIndex::IntendedVsync] + mFrameInterval);
+
+ // If we hit the deadline, cool!
+ if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline) {
+ if (isTripleBuffered) {
+ mData->reportJankType(JankType::kHighInputLatency);
+ (*mGlobalData)->reportJankType(JankType::kHighInputLatency);
+ }
+ return;
+ }
+
+ mData->reportJankType(JankType::kMissedDeadline);
+ (*mGlobalData)->reportJankType(JankType::kMissedDeadline);
+
+ // Janked, reset the swap deadline
+ nsecs_t jitterNanos = frame[FrameInfoIndex::FrameCompleted] - frame[FrameInfoIndex::Vsync];
+ nsecs_t lastFrameOffset = jitterNanos % mFrameInterval;
+ mSwapDeadline = frame[FrameInfoIndex::FrameCompleted] - lastFrameOffset + mFrameInterval;
+
+ for (auto& comparison : COMPARISONS) {
+ int64_t delta = frame.duration(comparison.start, comparison.end);
+ if (delta >= mThresholds[comparison.type] && delta < IGNORE_EXCEEDING) {
+ mData->reportJankType(comparison.type);
+ (*mGlobalData)->reportJankType(comparison.type);
}
}
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index dc6a7ff..110211e 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -75,6 +75,7 @@
std::array<int64_t, NUM_BUCKETS> mThresholds;
int64_t mFrameInterval;
+ nsecs_t mSwapDeadline;
// The amount of time we will erase from the total duration to account
// for SF vsync offsets with HWC2 blocking dequeueBuffers.
// (Vsync + mDequeueBlockTolerance) is the point at which we expect
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp
index b392ecd..f9cf549 100644
--- a/libs/hwui/ProfileData.cpp
+++ b/libs/hwui/ProfileData.cpp
@@ -23,8 +23,7 @@
static const char* JANK_TYPE_NAMES[] = {
"Missed Vsync", "High input latency", "Slow UI thread",
- "Slow bitmap uploads", "Slow issue draw commands",
-};
+ "Slow bitmap uploads", "Slow issue draw commands", "Frame deadline missed"};
// The bucketing algorithm controls so to speak
// If a frame is <= to this it goes in bucket 0
diff --git a/libs/hwui/ProfileData.h b/libs/hwui/ProfileData.h
index 1e688ab..564920b 100644
--- a/libs/hwui/ProfileData.h
+++ b/libs/hwui/ProfileData.h
@@ -33,6 +33,7 @@
kSlowUI,
kSlowSync,
kSlowRT,
+ kMissedDeadline,
// must be last
NUM_BUCKETS,
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 28d0bc4..c529f87 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 f4e2ba7..a92b62d 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -45,7 +45,9 @@
*/
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 @@
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/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index e0303a8..599226b 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -176,6 +176,8 @@
summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() +
data->jankTypeCount(kSlowSync));
summary->set_slow_draw_count(summary->slow_draw_count() + data->jankTypeCount(kSlowRT));
+ summary->set_missed_deadline_count(summary->missed_deadline_count()
+ + data->jankTypeCount(kMissedDeadline));
bool creatingHistogram = false;
if (proto->histogram_size() == 0) {
@@ -246,6 +248,7 @@
dprintf(fd, "\nNumber Slow UI thread: %d", summary.slow_ui_thread_count());
dprintf(fd, "\nNumber Slow bitmap uploads: %d", summary.slow_bitmap_upload_count());
dprintf(fd, "\nNumber Slow issue draw commands: %d", summary.slow_draw_count());
+ dprintf(fd, "\nNumber Frame deadline missed: %d", summary.missed_deadline_count());
dprintf(fd, "\nHISTOGRAM:");
for (const auto& it : proto->histogram()) {
dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count());
diff --git a/libs/hwui/tests/common/scenes/JankyScene.cpp b/libs/hwui/tests/common/scenes/JankyScene.cpp
new file mode 100644
index 0000000..f5e6b31
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/JankyScene.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#include "TestSceneBase.h"
+
+#include <unistd.h>
+
+class JankyScene;
+
+static TestScene::Registrar _JankyScene(TestScene::Info{
+ "janky",
+ "A scene that intentionally janks just enough to stay in "
+ "triple buffering.",
+ TestScene::simpleCreateScene<JankyScene>});
+
+class JankyScene : public TestScene {
+public:
+ sp<RenderNode> card;
+
+ void createContent(int width, int height, Canvas& canvas) override {
+ card = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(0xFF0000FF, SkBlendMode::kSrcOver);
+ });
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); // background
+ canvas.drawRenderNode(card.get());
+ }
+
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ if (curFrame & 1) {
+ usleep(15000);
+ }
+ // we animate left and top coordinates, which in turn animates width and
+ // height (bottom/right coordinates are fixed)
+ card->mutateStagingProperties().setLeftTop(curFrame, curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+};
\ No newline at end of file
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 6abba95..96a0817 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -339,7 +339,8 @@
* 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 b50aa47..3bfbcc2 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -105,10 +105,10 @@
* <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 @@
* 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 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 @@
*
* 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 @@
*
* 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 1aeed6d..0955dd6 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -427,20 +427,40 @@
* 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 @@
* @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 @@
* 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 @@
}
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 @@
* 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");
- }
+ public Bitmap getImageAtIndex(int imageIndex, @NonNull BitmapParams params) {
+ return getImageAtIndexInternal(imageIndex, params);
+ }
- String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT);
- if (imageIndex >= Integer.parseInt(imageCount)) {
- throw new IllegalArgumentException("Invalid image index: " + imageCount);
- }
-
- 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 @@
* 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 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 0000000..ef4bb01
--- /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/wifi/java/android/net/wifi/ScanSettings.aidl b/media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl
similarity index 61%
rename from wifi/java/android/net/wifi/ScanSettings.aidl
rename to media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl
index ebd2a39..230735a 100644
--- a/wifi/java/android/net/wifi/ScanSettings.aidl
+++ b/media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,13 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.media.soundtrigger;
-parcelable ScanSettings;
+/**
+ * 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 0000000..7381d97
--- /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 92ffae0..c9ec752 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 @@
}
/**
+ * 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 @@
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 3a67142..4f6763e 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -449,13 +449,14 @@
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/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index 350b648..8473c06 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -20,6 +20,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.UserHandle;
@@ -50,6 +51,19 @@
return getEnabledServicesFromSettings(context, UserHandle.myUserId());
}
+ public static boolean hasServiceCrashed(String packageName, String serviceName,
+ List<AccessibilityServiceInfo> enabledServiceInfos) {
+ for (int i = 0; i < enabledServiceInfos.size(); i++) {
+ AccessibilityServiceInfo accessibilityServiceInfo = enabledServiceInfos.get(i);
+ final ServiceInfo serviceInfo = enabledServiceInfos.get(i).getResolveInfo().serviceInfo;
+ if (TextUtils.equals(serviceInfo.packageName, packageName)
+ && TextUtils.equals(serviceInfo.name, serviceName)) {
+ return accessibilityServiceInfo.crashed;
+ }
+ }
+ return false;
+ }
+
/**
* @return the set of enabled accessibility services for {@param userId}. If there are no
* services, it returns the unmodifiable {@link Collections#emptySet()}.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index f6ec6a8..ec25d2d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -416,6 +416,29 @@
}
}
+ /**
+ * Set this device as active device
+ * @return true if at least one profile on this device is set to active, false otherwise
+ */
+ public boolean setActive() {
+ boolean result = false;
+ A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+ if (a2dpProfile != null && isConnectedProfile(a2dpProfile)) {
+ if (a2dpProfile.setActiveDevice(getDevice())) {
+ Log.i(TAG, "OnPreferenceClickListener: A2DP active device=" + this);
+ result = true;
+ }
+ }
+ HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+ if ((headsetProfile != null) && isConnectedProfile(headsetProfile)) {
+ if (headsetProfile.setActiveDevice(getDevice())) {
+ Log.i(TAG, "OnPreferenceClickListener: Headset active device=" + this);
+ result = true;
+ }
+ }
+ return result;
+ }
+
void refreshName() {
fetchName();
dispatchAttributesChanged();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 085e112..8f80527 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 @@
@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 @@
*
* <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 @@
@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 fd48eea..61efabc 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 @@
* ["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 @@
// 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 7fb4dc5..517db78 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.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 @@
@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 @@
mAccessPointsChangedLatch.countDown();
}
return null;
- }).when(mockWifiListenerExecutor).onAccessPointsChanged();
+ }).when(mockWifiListener).onAccessPointsChanged();
// Turn on Scoring UI features
mOriginalScoringUiSettingValue = Settings.Global.getInt(
@@ -271,7 +273,7 @@
private WifiTracker createMockedWifiTracker() {
final WifiTracker wifiTracker = new WifiTracker(
mContext,
- mockWifiListenerExecutor,
+ mockWifiListener,
mockWifiManager,
mockConnectivityManager,
mockNetworkScoreManager,
@@ -690,7 +692,7 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
@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 @@
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 @@
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/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 0775727..632b014 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -294,4 +294,24 @@
// Verify new alias is returned on getName
assertThat(cachedBluetoothDevice.getName()).isEqualTo(DEVICE_ALIAS_NEW);
}
+
+ @Test
+ public void testSetActive() {
+ when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+ when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile);
+ when(mA2dpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
+ when(mHfpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
+
+ assertThat(mCachedDevice.setActive()).isFalse();
+
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.setActive()).isTrue();
+
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.setActive()).isTrue();
+
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.setActive()).isFalse();
+ }
}
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 ea8ecba..91d9f91 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 @@
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.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.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
+import java.util.Set;
@RunWith(SettingsLibRobolectricTestRunner.class)
public class WifiUtilsTest {
@@ -56,6 +59,8 @@
private RssiCurve mockBadgeCurve;
@Mock
private WifiNetworkScoreCache mockWifiNetworkScoreCache;
+ @Mock
+ private AccessPoint mAccessPoint;
@Before
public void setUp() {
@@ -84,6 +89,15 @@
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 ecda53a..8b78366 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 f895a98..34aae49 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 @@
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 5102cbd..f215c12 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2938,7 +2938,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 159;
+ private static final int SETTINGS_VERSION = 161;
private final int mUserId;
@@ -3646,6 +3646,57 @@
currentVersion = 159;
}
+ if (currentVersion == 159) {
+ // Version 160: Hiding notifications from the lockscreen is only available as
+ // primary user option, profiles can only make them redacted. If a profile was
+ // configured to not show lockscreen notifications, ensure that at the very
+ // least these will be come hidden.
+ if (mUserManager.isManagedProfile(userId)) {
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ Setting showNotifications = secureSettings.getSettingLocked(
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+ // The default value is "1", check if user has turned it off.
+ if ("0".equals(showNotifications.getValue())) {
+ secureSettings.insertSettingLocked(
+ Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, "0",
+ null /* tag */, false /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ // The setting is no longer valid for managed profiles, it should be
+ // treated as if it was set to "1".
+ secureSettings.deleteSettingLocked(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+ }
+ 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/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 6d341c5..449946d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -42,6 +42,7 @@
import android.util.Base64;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
@@ -386,6 +387,9 @@
mSettings.put(name, newState);
}
+ StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newState.value, oldValue, tag,
+ makeDefault, getUserIdFromKey(mKey), StatsLog.SETTING_CHANGED__REASON__UPDATED);
+
addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
updateMemoryUsagePerPackageLocked(packageName, oldValue, value,
@@ -410,6 +414,10 @@
Setting oldState = mSettings.remove(name);
+ StatsLog.write(StatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "",
+ oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
+ StatsLog.SETTING_CHANGED__REASON__DELETED);
+
updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
null, oldState.defaultValue, null);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 589ae2a..b49f1ac 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -121,6 +121,7 @@
<uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
<uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
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 59000fc..0000000
--- 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 3345f61..6a7f18b 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/drawable/ic_lock_lockdown.xml b/packages/SystemUI/res/drawable/ic_lock_lockdown.xml
index b517fc8..65b9813 100644
--- a/packages/SystemUI/res/drawable/ic_lock_lockdown.xml
+++ b/packages/SystemUI/res/drawable/ic_lock_lockdown.xml
@@ -20,6 +20,7 @@
android:viewportHeight="24.0">
<path
- android:fillColor="#757575"
+ android:fillColor="#000000"
+ android:alpha="0.87"
android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/>
</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 0000000..bacb5c1
--- /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 6732e6c..fa696cc 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 734c877..26cf792 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 8c0b9ba..9d336e2 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"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="horizontal" />
+ <include layout="@layout/heads_up_status_bar_layout" />
- </LinearLayout>
+ <LinearLayout
+ android:layout_height="match_parent"
+ 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>
+ </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 ef442e5..75403b9 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 cb3c282..d65e42b 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 bf3fa29..3c8a695 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -365,9 +365,6 @@
<!-- Content description of the data connection type GPRS. [CHAR LIMIT=NONE] -->
<string name="data_connection_gprs">GPRS</string>
- <!-- Content description of the data connection type 1x. [CHAR LIMIT=NONE] -->
- <string name="data_connection_1x">1 X</string>
-
<!-- Content description of the data connection type HSPA and its variants. [CHAR LIMIT=NONE] -->
<string name="data_connection_hspa">HSPA</string>
@@ -1848,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 1470dfa..20d8834 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/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 940c9ef..5fa6c79 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -29,6 +29,8 @@
private IRecentsAnimationController mAnimationController;
+ public RecentsAnimationControllerCompat() { }
+
public RecentsAnimationControllerCompat(IRecentsAnimationController animationController) {
mAnimationController = animationController;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9d5fb52..d24675c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1726,6 +1726,7 @@
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 0000000..646f69e
--- /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 9a20c81..a0fa69e 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.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 @@
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 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 @@
}
}
}
+
+ @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 91edfda..391843c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -100,10 +100,10 @@
}
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 092f3d2..5bf62f6 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.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 @@
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 @@
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 @@
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 6ff8e3d..152b9fc 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 @@
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 7d14564..f72bff5d6 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.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 @@
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 @@
// 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;
- if (!messagePending) {
- mHandler.post(mApplyPendingScreenState);
+
+ // 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();
}
- return;
+
+ if (!messagePending) {
+ 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);
+ }
+ } else {
+ applyScreenState(screenState);
}
- applyScreenState(screenState);
}
private void applyPendingScreenState() {
@@ -87,6 +118,10 @@
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 75f1b50..778e630 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.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 @@
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 @@
// Keep current state.
break;
default:
- mHost.setAnimateWakeup(mCanAnimateWakeup);
+ mHost.setAnimateWakeup(mCanAnimateTransition && mDozeParameters.getAlwaysOn());
break;
}
}
@@ -170,4 +197,9 @@
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 5156272..9d110fb 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.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 @@
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 @@
final boolean animated;
if (isAmbientMode) {
- animated = mDozeParameters.getCanControlScreenOffAnimation() && !mKeyguardVisible;
+ animated = mDozeParameters.shouldControlScreenOff();
} else {
animated = !mDozeParameters.getDisplayNeedsBlanking();
}
@@ -88,8 +87,10 @@
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 951c0ea..59c7f23 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 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 bdc5e7d..3ba5fe6 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 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 @@
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 @@
if (mAccessibilityMoving) {
selectPosition(position, v);
} else {
- if (position < mEditIndex) {
+ if (position < mEditIndex && canRemoveTiles()) {
showAccessibilityDialog(position, v);
} else {
startAccessibleDrag(position);
@@ -297,6 +303,10 @@
}
}
}
+
+ private boolean canRemoveTiles() {
+ return mCurrentSpecs.size() > MIN_NUM_TILES;
+ }
private void selectPosition(int position, View v) {
// Remove the placeholder.
@@ -507,7 +517,7 @@
break;
}
}
- };
+ }
private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() {
@@ -551,6 +561,9 @@
@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 cb9453b..b7a5d31 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_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 @@
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 0876507..8c28af5 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.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 @@
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 @@
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 @@
}
@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 @@
mAppearAnimationTranslation = 0;
}
}
+ mIsAppearing = isAppearing;
float targetValue;
if (isAppearing) {
@@ -803,6 +828,9 @@
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 @@
/ (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 @@
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 e8f0925..4728a1d 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 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 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 @@
}
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 e5c5dcd..9bd8080 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 @@
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 @@
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 @@
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 @@
}
}
+ @Override
public boolean isPinned() {
return mIsPinned;
}
@@ -741,11 +765,11 @@
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 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 @@
* @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 @@
groupSummary.performDismiss(fromAccessibility);
}
}
- setDismissed(true, fromAccessibility);
+ setDismissed(fromAccessibility);
if (isClearable()) {
if (mOnDismissRunnable != null) {
mOnDismissRunnable.run();
@@ -1921,9 +1947,9 @@
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 @@
@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 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 int getMaxExpandHeight() {
- return mMaxExpandHeight;
+ return mPrivateLayout.getExpandHeight();
+ }
+
+
+ private int getHeadsUpHeight() {
+ return mPrivateLayout.getHeadsUpHeight();
}
public boolean areGutsExposed() {
@@ -2273,7 +2298,7 @@
} 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 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 8bc2201..67268c0 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 @@
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 @@
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 204adc8..df6a977 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 @@
/**
* 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 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 0000000..5e03fbf
--- /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 73c8795..f64b1bc 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 @@
&& (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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 22a186f..775faee 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 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 @@
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 @@
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/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index cc584e7e..ccabb79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -389,7 +389,7 @@
* "public" (secure & locked) mode?
*/
private boolean userAllowsNotificationsInPublic(int userHandle) {
- if (userHandle == UserHandle.USER_ALL) {
+ if (isCurrentProfile(userHandle)) {
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 4f09133..aecf5fb 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 @@
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 @@
}
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 @@
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 @@
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 0a7ee51..badc40d 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 @@
}
@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 6cfd42f..5bb85e2 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.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.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 @@
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 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 @@
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 @@
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 @@
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 dfcd5e6..251e04b 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 @@
protected final ViewInvertHelper mInvertHelper;
protected final ViewTransformationHelper mTransformationHelper;
+ private final int mTranslationForHeader;
protected int mColor;
private ImageView mIcon;
@@ -63,6 +64,7 @@
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 @@
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 @@
}
@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 f967118..f5110a2d 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 @@
: builder.makeAmbientNotification();
}
result.packageContext = packageContext;
+ result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
+ result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
+ true /* showingPublic */);
return result;
}
@@ -456,6 +459,8 @@
}
entry.cachedAmbientContentView = result.newAmbientView;
}
+ entry.headsUpStatusBarText = result.headsUpStatusBarText;
+ entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
if (endListener != null) {
endListener.onAsyncInflationFinished(row.getEntry());
}
@@ -665,6 +670,8 @@
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 d463eae..28beb21 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 @@
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 17eb4c1..873f088 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 @@
return null;
}
+ public int getHeaderTranslation() {
+ return 0;
+ }
+
@Override
public TransformState getCurrentState(int fadingView) {
return null;
@@ -198,4 +202,7 @@
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 fb3adf4..07b79a2 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.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 @@
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 DozeParameters(Context context) {
+ public static DozeParameters getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new DozeParameters(context);
+ }
+ return sInstance;
+ }
+
+ @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 @@
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 1011383..afd64f3 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 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 0000000..c576801
--- /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 d2cdc27..fa0a774 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 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 @@
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 @@
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 @@
});
}
+ 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 @@
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 2bfdefe..903b813 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 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 @@
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 @@
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 b493d7a..a39800d 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 @@
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 e1aed7a..9063dea 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 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 @@
*/
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 @@
mStatusBar = statusBar;
mNotificationColorUtil = NotificationColorUtil.getInstance(context);
mContext = context;
+ mEntryManager = Dependency.get(NotificationEntryManager.class);
initializeNotificationAreaViews(context);
}
@@ -129,8 +134,8 @@
}
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 @@
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 @@
/**
* 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 @@
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 @@
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 @@
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 b29ac90..5517434 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 @@
}
}.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 @@
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 @@
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 @@
mAddAnimationStartIndex = -1;
mCannedAnimationStartIndex = -1;
mDisallowNextAnimation = false;
+ mIsolatedIconForAnimation = null;
}
@Override
@@ -281,8 +318,10 @@
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 @@
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 @@
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 @@
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 @@
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 2711d7a..6852df6 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.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 @@
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 @@
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 @@
@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 @@
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 @@
}
} 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 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
if (mLaunchingNotification) {
return mHideIconsDuringNotificationLaunch;
}
+ if (mHeadsUpAppearanceController != null
+ && mHeadsUpAppearanceController.shouldBeVisible()) {
+ return false;
+ }
return !isFullWidth() || !mShowIconsWhenExpanded;
}
@@ -2600,11 +2630,16 @@
}
}
- 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 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 ae55ae8e..cfc0cc6 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.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 @@
* 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 @@
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 @@
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;
@@ -158,13 +153,13 @@
private final WakeLock mWakeLock;
private boolean mWakeLockHeld;
+ 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);
@@ -195,7 +190,6 @@
}
mState = ScrimState.UNINITIALIZED;
- updateHeadsUpScrim(false);
updateScrims();
}
@@ -268,12 +262,13 @@
// AOD wallpapers should fade away after a while
if (mWallpaperSupportsAmbientMode && mDozeParameters.getAlwaysOn()
- && (mState == ScrimState.AOD || mState == ScrimState.PULSING)) {
+ && mState == ScrimState.AOD) {
if (!mWallpaperVisibilityTimedOut) {
mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
}
- } else {
+ // Do not re-schedule timeout when pulsing, let's save some extra battery.
+ } else if (mState != ScrimState.PULSING) {
mTimeTicker.cancel();
mWallpaperVisibilityTimedOut = false;
}
@@ -283,10 +278,11 @@
// 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();
@@ -317,7 +313,7 @@
@VisibleForTesting
protected void onHideWallpaperTimeout() {
- if (mState != ScrimState.AOD && mState != ScrimState.PULSING) {
+ if (mState != ScrimState.AOD) {
return;
}
@@ -361,10 +357,6 @@
return;
}
- if (mPinnedHeadsUpCount != 0) {
- updateHeadsUpScrim(false);
- }
-
setOrAdaptCurrentAnimation(mScrimBehind);
setOrAdaptCurrentAnimation(mScrimInFront);
}
@@ -478,11 +470,13 @@
mLightBarController.setScrimColor(mScrimInFront.getColors());
}
- // We want to override the back scrim opacity for AOD and PULSING
+ // We want to override the back scrim opacity for the AOD state
// when it's time to fade the wallpaper away.
- boolean overrideBackScrimAlpha = (mState == ScrimState.PULSING || mState == ScrimState.AOD)
- && mWallpaperVisibilityTimedOut;
- if (overrideBackScrimAlpha) {
+ boolean aodWallpaperTimeout = mState == ScrimState.AOD && mWallpaperVisibilityTimedOut;
+ // We also want to hide FLAG_SHOW_WHEN_LOCKED activities under the scrim.
+ boolean occludedKeyguard = (mState == ScrimState.PULSING || mState == ScrimState.AOD)
+ && mKeyguardOccluded;
+ if (aodWallpaperTimeout || occludedKeyguard) {
mCurrentBehindAlpha = 1;
}
@@ -606,8 +600,6 @@
return mCurrentInFrontAlpha;
} else if (scrim == mScrimBehind) {
return mCurrentBehindAlpha;
- } else if (scrim == mHeadsUpScrim) {
- return calculateHeadsUpAlpha();
} else {
throw new IllegalArgumentException("Unknown scrim view");
}
@@ -618,8 +610,6 @@
return mCurrentInFrontTint;
} else if (scrim == mScrimBehind) {
return mCurrentBehindTint;
- } else if (scrim == mHeadsUpScrim) {
- return Color.TRANSPARENT;
} else {
throw new IllegalArgumentException("Unknown scrim view");
}
@@ -666,40 +656,6 @@
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;
@@ -806,33 +762,6 @@
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);
}
@@ -847,13 +776,6 @@
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.
}
@@ -926,6 +848,10 @@
mExpansionAffectsAlpha = expansionAffectsAlpha;
}
+ public void setKeyguardOccluded(boolean keyguardOccluded) {
+ mKeyguardOccluded = keyguardOccluded;
+ }
+
public interface Callback {
default void onStart() {
}
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 55e8714..5b734eb 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 @@
@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 @@
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 @@
ScrimView mScrimBehind;
DozeParameters mDozeParameters;
boolean mDisplayRequiresBlanking;
- boolean mCanControlScreenOff;
boolean mWallpaperSupportsAmbientMode;
KeyguardUpdateMonitor mKeyguardUpdateMonitor;
int mIndex;
@@ -192,7 +190,6 @@
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 c92f300..feb7dc3 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.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 @@
private NavigationBarFragment mNavigationBar;
private View mNavigationBarView;
protected ActivityLaunchAnimator mActivityLaunchAnimator;
+ private HeadsUpAppearanceController mHeadsUpAppearanceController;
@Override
public void start() {
@@ -807,6 +809,8 @@
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
+ mHeadsUpAppearanceController = new HeadsUpAppearanceController(
+ mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
setAreThereNotifications();
checkBarModes();
}).getFragmentManager()
@@ -899,14 +903,14 @@
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 @@
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 @@
mReinflateNotificationsOnUserSwitched = true;
}
// end old BaseStatusBar.onDensityOrFontScaleChanged().
- mScrimController.onDensityOrFontScaleChanged();
// TODO: Remove this.
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onDensityOrFontScaleChanged();
@@ -1087,6 +1090,7 @@
mKeyguardUserSwitcher.onDensityOrFontScaleChanged();
}
mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
+ mHeadsUpManager.onDensityOrFontScaleChanged();
reevaluateStyles();
}
@@ -1384,8 +1388,7 @@
updateQsExpansionEnabled();
// Let's also update the icons
- mNotificationIconAreaController.updateNotificationIcons(
- mEntryManager.getNotificationData());
+ mNotificationIconAreaController.updateNotificationIcons();
}
@Override
@@ -1867,7 +1870,7 @@
@Override
public boolean isDozing() {
- return mDozing;
+ return mDozing && mStackScroller.isFullyDark();
}
@Override
@@ -1991,7 +1994,7 @@
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();
}
@@ -2040,6 +2043,7 @@
public void setOccluded(boolean occluded) {
mIsOccluded = occluded;
+ mScrimController.setKeyguardOccluded(occluded);
updateHideIconsForBouncer(false /* animate */);
}
@@ -2812,7 +2816,7 @@
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);
@@ -3816,13 +3820,16 @@
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();
@@ -3832,6 +3839,9 @@
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);
@@ -4738,6 +4748,7 @@
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 b26b7c9..a9c467e 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 @@
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 boolean isGoingToSleepVisibleNotOccluded() {
+ return mGoingToSleepVisibleNotOccluded;
+ }
+
public void onStartedGoingToSleep() {
- // TODO: remove
+ mGoingToSleepVisibleNotOccluded = isShowing() && !isOccluded();
}
public void onFinishedGoingToSleep() {
+ mGoingToSleepVisibleNotOccluded = false;
mBouncer.onScreenTurnedOff();
}
@@ -371,6 +377,7 @@
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 defb46c..309a1a7 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 @@
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 @@
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 040d7ec..aeda55a 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 @@
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 0f637fb..7c1c566 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 @@
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 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 53377d9..c26568e 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.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 boolean animateShadowAlpha;
boolean hasDelays;
boolean hasGoToFullShadeEvent;
- boolean hasHeadsUpDisappearClickEvent;
+ long customDelay;
private ArraySet<Property> mAnimatedProperties = new ArraySet<>();
public AnimationFilter animateAlpha() {
@@ -129,8 +129,14 @@
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 @@
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 3bf7d89..a7925aa 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 @@
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 05c0099..59ce0ca 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 @@
* 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 @@
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 ac2a1e1..e5ab712 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 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
}
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 @@
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 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 @@
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 0000000..a36c966
--- /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 a85f4e2..a572450 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.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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
private void updateAlgorithmLayoutMinHeight() {
- mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() ? getLayoutMinHeight() : 0);
+ mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() || isHeadsUpTransition()
+ ? getLayoutMinHeight() : 0);
}
/**
@@ -820,6 +833,7 @@
if (mRegularTopPadding != topPadding) {
mRegularTopPadding = topPadding;
mDarkTopPadding = topPadding + mDarkSeparatorPadding;
+ mAmbientState.setDarkTopPadding(mDarkTopPadding);
updateAlgorithmHeightAndPadding();
updateContentHeight();
if (animate && mAnimationsEnabled && mIsExpanded) {
@@ -854,11 +868,12 @@
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 @@
// 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 @@
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 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 @@
* 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 @@
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 @@
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 @@
@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 @@
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 @@
&& !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 @@
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 int getLayoutMinHeight() {
+ if (isHeadsUpTransition()) {
+ return getTopHeadsUpPinnedHeight();
+ }
return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
}
@@ -2929,42 +2954,18 @@
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 @@
.animateY(mShelf));
ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
mAnimationEvents.add(ev);
- startBackgroundFadeIn();
+ startBackgroundFade();
}
mDarkNeedsAnimation = false;
}
@@ -3889,7 +3890,6 @@
requestChildrenUpdate();
applyCurrentBackgroundBounds();
updateWillNotDraw();
- updateContentHeight();
updateAntiBurnInTranslation();
notifyHeightChangeListener(mShelf);
}
@@ -3910,6 +3910,11 @@
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 @@
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 void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
mAmbientState.setHeadsUpManager(headsUpManager);
+ mHeadsUpManager.addListener(mRoundnessManager);
}
public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
@@ -4319,8 +4326,9 @@
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 @@
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 @@
.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 51737a8..7c8e0fc 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 @@
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 @@
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 @@
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 @@
}
}
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 @@
childViewState.inShelf = true;
childViewState.headsUpIsVisible = false;
}
- if (!ambientState.isShadeExpanded()) {
- childViewState.height = (int) (mStatusBarHeight - childViewState.yTranslation);
- }
}
protected int getMaxAllowedChildHeight(View child) {
@@ -592,6 +595,13 @@
} 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 236c348..d48ae76 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.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 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 @@
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 @@
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 @@
private void onAnimationFinished() {
mHostLayout.onChildAnimationFinished();
- for (View v : mChildrenToClearFromOverlay) {
- removeFromOverlay(v);
- }
- mChildrenToClearFromOverlay.clear();
}
/**
@@ -396,13 +398,14 @@
}
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 @@
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 @@
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 04a7bd7..4b3643f 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 @@
}
/**
+ * 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 8c4fd73..66836365 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 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 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 @@
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 @@
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 75ade9d..0d8d952 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.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.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 @@
}
@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);
- DozeParameters params = mock(DozeParameters.class);
- new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, params);
- Assert.assertFalse("animateScreenOff should be false", mHost.animateScreenOff);
+ mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
+ verify(mHost).setAnimateScreenOff(eq(false));
}
@Test
- public void transitionSetsAnimateWakeup() {
- mHost.animateWakeup = false;
+ 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));
+ }
+
+ @Test
+ public void transitionSetsAnimateWakeup_alwaysOn() {
+ when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+ when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
mDozeUi.transitionTo(UNINITIALIZED, DOZE);
- Assert.assertTrue("animateScreenOff should be true", mHost.animateWakeup);
+ 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_noAlwaysOn() {
+ mDozeUi.transitionTo(UNINITIALIZED, DOZE);
+ 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 2705bca..5c80bb5 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.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.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 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 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 e15e0b4..9eba9b8 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 @@
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 @@
}
@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 b1e1c02..2000bff 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 @@
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 @@
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 @@
@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 7e5db34..3703d6a 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 @@
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 e89ff97..550a35d 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.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 @@
}
}
+ @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 ca2f713..203ebe6 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 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 @@
}).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 0000000..c904ef3
--- /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 f088c0b..45845fc 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 @@
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 @@
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 @@
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 @@
}
@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 @@
}
@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());
@@ -475,6 +423,19 @@
Assert.assertTrue("Should be focusable on keyguard", mScrimInFront.isFocusable());
}
+ @Test
+ public void testHidesShowWhenLockedActivity() {
+ mScrimController.setWallpaperSupportsAmbientMode(true);
+ mScrimController.setKeyguardOccluded(true);
+ mScrimController.transitionTo(ScrimState.AOD);
+ mScrimController.finishAnimationsImmediately();
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+
+ mScrimController.transitionTo(ScrimState.PULSING);
+ mScrimController.finishAnimationsImmediately();
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+ }
+
/**
* Conserves old notification density after leaving state and coming back.
*
@@ -527,16 +488,23 @@
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);
@@ -549,7 +517,6 @@
// 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/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 14baaeb..f13fa4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.doAnswer;
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;
@@ -547,6 +548,16 @@
verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
}
+ @Test
+ public void testSetOccluded_propagatesToScrimController() {
+ mStatusBar.setOccluded(true);
+ verify(mScrimController).setKeyguardOccluded(eq(true));
+
+ reset(mScrimController);
+ mStatusBar.setOccluded(false);
+ verify(mScrimController).setKeyguardOccluded(eq(false));
+ }
+
static class TestableStatusBar extends StatusBar {
public TestableStatusBar(StatusBarKeyguardViewManager man,
UnlockMethodCache unlock, KeyguardIndicationController key,
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 0000000..1d2c01d
--- /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/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index ed068b9..5c5978a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -88,7 +88,7 @@
final int mId;
- final AccessibilityServiceInfo mAccessibilityServiceInfo;
+ protected final AccessibilityServiceInfo mAccessibilityServiceInfo;
// Lock must match the one used by AccessibilityManagerService
protected final Object mLock;
@@ -340,6 +340,10 @@
}
}
+ public int getCapabilities() {
+ return mAccessibilityServiceInfo.getCapabilities();
+ }
+
int getRelevantEventTypes() {
return (mUsesAccessibilityCache ? AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK : 0)
| mEventTypes;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ecd47e8..8941b49 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -621,7 +621,7 @@
for (int i = 0; i < serviceCount; ++i) {
final AccessibilityServiceConnection service = services.get(i);
if ((service.mFeedbackType & feedbackType) != 0) {
- result.add(service.mAccessibilityServiceInfo);
+ result.add(service.getServiceInfo());
}
}
return result;
@@ -1874,7 +1874,7 @@
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
AccessibilityServiceConnection service = userState.mBoundServices.get(i);
- if ((service.mAccessibilityServiceInfo.getCapabilities()
+ if ((service.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0) {
userState.mIsPerformGesturesEnabled = true;
return;
@@ -1888,7 +1888,7 @@
for (int i = 0; i < serviceCount; i++) {
AccessibilityServiceConnection service = userState.mBoundServices.get(i);
if (service.mRequestFilterKeyEvents
- && (service.mAccessibilityServiceInfo.getCapabilities()
+ && (service.getCapabilities()
& AccessibilityServiceInfo
.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) != 0) {
userState.mIsFilterKeyEventsEnabled = true;
@@ -2124,7 +2124,7 @@
// Starting in JB-MR2 we request an accessibility service to declare
// certain capabilities in its meta-data to allow it to enable the
// corresponding features.
- if ((service.mAccessibilityServiceInfo.getCapabilities()
+ if ((service.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION) != 0) {
return true;
}
@@ -3446,22 +3446,22 @@
}
public boolean canRetrieveWindowContentLocked(AbstractAccessibilityServiceConnection service) {
- return (service.mAccessibilityServiceInfo.getCapabilities()
+ return (service.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
}
public boolean canControlMagnification(AbstractAccessibilityServiceConnection service) {
- return (service.mAccessibilityServiceInfo.getCapabilities()
+ return (service.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION) != 0;
}
public boolean canPerformGestures(AccessibilityServiceConnection service) {
- return (service.mAccessibilityServiceInfo.getCapabilities()
+ return (service.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0;
}
public boolean canCaptureFingerprintGestures(AccessibilityServiceConnection service) {
- return (service.mAccessibilityServiceInfo.getCapabilities()
+ return (service.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES) != 0;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 89bf82d..eb18f06 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -165,7 +165,14 @@
}
}
- public void initializeService() {
+ @Override
+ public AccessibilityServiceInfo getServiceInfo() {
+ // Update crashed data
+ mAccessibilityServiceInfo.crashed = mWasConnectedAndDied;
+ return mAccessibilityServiceInfo;
+ }
+
+ private void initializeService() {
IAccessibilityServiceClient serviceInterface = null;
synchronized (mLock) {
UserState userState = mUserStateWeakReference.get();
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index 5b5d18f..9f44197 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -55,7 +55,7 @@
* magnification region. If a value is out of bounds, it will be adjusted to guarantee these
* constraints.
*/
-class MagnificationController implements Handler.Callback {
+public class MagnificationController implements Handler.Callback {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "MagnificationController";
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 755fc54..7f57615 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1438,11 +1438,25 @@
final AutofillValue filledValue = viewState.getAutofilledValue();
if (!value.equals(filledValue)) {
- if (sDebug) {
- Slog.d(TAG, "found a change on required " + id + ": " + filledValue
- + " => " + value);
+ boolean changed = true;
+ if (filledValue == null) {
+ // Dataset was not autofilled, make sure initial value didn't change.
+ final AutofillValue initialValue = getValueFromContextsLocked(id);
+ if (initialValue != null && initialValue.equals(value)) {
+ if (sDebug) {
+ Slog.d(TAG, "id " + id + " is part of dataset but initial value "
+ + "didn't change: " + value);
+ }
+ changed = false;
+ }
}
- atLeastOneChanged = true;
+ if (changed) {
+ if (sDebug) {
+ Slog.d(TAG, "found a change on required " + id + ": " + filledValue
+ + " => " + value);
+ }
+ atLeastOneChanged = true;
+ }
}
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d6f6c6c..4cac707 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 @@
// 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 @@
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 @@
return mConstants;
}
+ public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
+ return mAgentTimeoutParameters;
+ }
+
public Context getContext() {
return mContext;
}
@@ -856,6 +854,10 @@
// 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 @@
}
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 7b021c6..aabe7f6 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 @@
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 4755877..f08c655 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_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 @@
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 @@
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 @@
// 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 @@
}
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 0582aba..597da21 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.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.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 @@
private final long mQuota;
private final int mOpToken;
private final int mTransportFlags;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
class FullBackupRunner implements Runnable {
@@ -137,8 +136,8 @@
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 @@
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 40b6967..d441cf6 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 @@
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.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 @@
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 @@
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 2c2dd85..1ea3eb5 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.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 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 @@
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 @@
mUserInitiated = userInitiated;
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
mBackupRunnerOpToken = backupManagerService.generateRandomIntegerToken();
+ mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
if (backupManagerService.isBackupOperationInProgress()) {
if (DEBUG) {
@@ -698,9 +700,11 @@
@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 @@
// 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 @@
@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 @@
// 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 @@
}
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 136fada..5886862 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 @@
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 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 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 @@
// 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 11394e66..0313066 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_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.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 @@
private boolean mFinished;
private final boolean mUserInitiated;
private final boolean mNonIncremental;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private volatile boolean mCancelAll;
@@ -162,6 +163,7 @@
mPendingFullBackups = pendingFullBackups;
mUserInitiated = userInitiated;
mNonIncremental = nonIncremental;
+ mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
mStateDir = new File(backupManagerService.getBaseStateDir(), dirName);
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
@@ -711,8 +713,10 @@
// 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 e4f3a9d..6175629 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 @@
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 @@
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 c1a1c1d..f168afed 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.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.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 @@
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 @@
mAllowObbs = allowObbs;
mBuffer = new byte[32 * 1024];
mBytes = 0;
+ mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
}
public IBackupAgent getAgent() {
@@ -381,8 +382,8 @@
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 dacde0b..221637c 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.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 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 @@
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 @@
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 @@
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 @@
// 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 4b467e5..069e3b6 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.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.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 @@
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 @@
mFinished = false;
mDidLaunch = false;
mListener = listener;
+ mAgentTimeoutParameters = backupManagerService.getAgentTimeoutParameters();
if (targetPackage != null) {
// Single package restore
@@ -760,8 +760,9 @@
// 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 @@
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 @@
} 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 45a4dfb9..f1f251f 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.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 @@
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 @@
.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 @@
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 @@
* 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 @@
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 @@
*/
@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 @@
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 @@
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 @@
(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 305c1d0..fb8f749 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_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 @@
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 @@
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 @@
// 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 @@
}
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 19fffbb..e4695b6 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 @@
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 @@
r.setProcess(app);
- if (mKeyguardController.isKeyguardLocked()) {
+ if (getKeyguardController().isKeyguardLocked()) {
r.notifyUnknownVisibilityLaunched();
}
@@ -3377,7 +3377,7 @@
} 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 @@
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 @@
}
}
} finally {
- mKeyguardController.endActivityVisibilityUpdate();
+ getKeyguardController().endActivityVisibilityUpdate();
}
}
@@ -3799,7 +3799,7 @@
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 @@
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 bd53eac..a30a944 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 @@
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 @@
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 690d985..5bf5020 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 @@
{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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9ef84d2..c036549 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1649,16 +1649,7 @@
// Check if volume update should be send to Hearing Aid
if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
- synchronized (mHearingAidLock) {
- if (mHearingAid != null) {
- //hearing aid expect volume value in range -128dB to 0dB
- int gainDB = (int)AudioSystem.getStreamVolumeDB(streamType, newIndex/10,
- AudioSystem.DEVICE_OUT_HEARING_AID);
- if (gainDB < BT_HEARING_AID_GAIN_MIN)
- gainDB = BT_HEARING_AID_GAIN_MIN;
- mHearingAid.setVolume(gainDB);
- }
- }
+ setHearingAidVolume(newIndex);
}
// Check if volume update should be sent to Hdmi system audio.
@@ -1907,16 +1898,7 @@
}
if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
- synchronized (mHearingAidLock) {
- if (mHearingAid != null) {
- //hearing aid expect volume value in range -128dB to 0dB
- int gainDB = (int)AudioSystem.getStreamVolumeDB(streamType, index/10, AudioSystem.DEVICE_OUT_HEARING_AID);
- if (gainDB < BT_HEARING_AID_GAIN_MIN)
- gainDB = BT_HEARING_AID_GAIN_MIN;
- mHearingAid.setVolume(gainDB);
-
- }
- }
+ setHearingAidVolume(index);
}
if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
@@ -5624,8 +5606,24 @@
makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
}
+ private void setHearingAidVolume(int index) {
+ synchronized (mHearingAidLock) {
+ if (mHearingAid != null) {
+ //hearing aid expect volume value in range -128dB to 0dB
+ int gainDB = (int)AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC, index/10,
+ AudioSystem.DEVICE_OUT_HEARING_AID);
+ if (gainDB < BT_HEARING_AID_GAIN_MIN)
+ gainDB = BT_HEARING_AID_GAIN_MIN;
+ mHearingAid.setVolume(gainDB);
+ }
+ }
+ }
+
// must be called synchronized on mConnectedDevices
private void makeHearingAidDeviceAvailable(String address, String name, String eventSource) {
+ int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(AudioSystem.DEVICE_OUT_HEARING_AID);
+ setHearingAidVolume(index);
+
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
AudioSystem.DEVICE_STATE_AVAILABLE, address, name);
mConnectedDevices.put(
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index cb53521..5e1afeb 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 @@
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 c0d2599..4313d17 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 @@
/** @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 @@
}
@Override
+ public BrightnessConfiguration getDefaultConfig() { return null; }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println("SimpleMappingStrategy");
pw.println(" mSpline=" + mSpline);
@@ -533,6 +538,9 @@
}
@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 9861ea7..c4b2b5e 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 @@
}
@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 fa39ce4..ff8b88b 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 @@
}
}
+ 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 3eae157..4100a9a 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.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 @@
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 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 729ac0c..e02feec 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 @@
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 @@
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 @@
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 74ebf3e4..a87a113 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 @@
@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 @@
}
@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 20f3403..5b10add 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.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.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.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.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 @@
}
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 @@
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 @NonNull byte[] startRecoverySessionWithCertPath(
@NonNull String sessionId,
+ @NonNull String rootCertificateAlias,
@NonNull RecoveryCertPath verifierCertPath,
@NonNull byte[] vaultParams,
@NonNull byte[] vaultChallenge,
@@ -495,11 +504,10 @@
}
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 @@
}
/**
+ * 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 @@
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 @@
// 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 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 @@
}
}
+ 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 7195d62..0000000
--- 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 3d97623..84ddbf7 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.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 java.security.KeyStore.SecretKeyEntry;
import java.security.KeyStoreException;
+import java.util.Locale;
+
import javax.crypto.spec.SecretKeySpec;
/**
@@ -40,11 +43,13 @@
* 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 @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 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/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index 8983ec3..bda2ed3 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -175,7 +175,7 @@
/**
* The algorithm used to derive cryptographic material from the key and salt. One of
* {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_SHA256} or
- * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_ARGON2ID}.
+ * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_SCRYPT}.
*/
static final String COLUMN_NAME_KEY_DERIVATION_ALGORITHM = "key_derivation_algorithm";
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1db373d..27eeb93 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 @@
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 @@
|| (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 @@
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 @@
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);
@@ -1294,7 +1308,8 @@
NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
NotificationUsageStats usageStats, AtomicFile policyFile,
- ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am) {
+ ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
+ UsageStatsManagerInternal appUsageStats) {
Resources resources = getContext().getResources();
mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -1307,7 +1322,7 @@
mPackageManagerClient = packageManagerClient;
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
- mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+ mAppUsageStats = appUsageStats;
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mCompanionManager = companionManager;
mActivityManager = activityManager;
@@ -1449,7 +1464,8 @@
null, snoozeHelper, new NotificationUsageStats(getContext()),
new AtomicFile(new File(systemDir, "notification_policy.xml"), "notification-policy"),
(ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
- getGroupHelper(), ActivityManager.getService());
+ getGroupHelper(), ActivityManager.getService(),
+ LocalServices.getService(UsageStatsManagerInternal.class));
// register for various Intents
IntentFilter filter = new IntentFilter();
@@ -1477,6 +1493,7 @@
IntentFilter suspendedPkgFilter = new IntentFilter();
suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
+ suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
suspendedPkgFilter, null, null);
@@ -2484,6 +2501,7 @@
try {
synchronized (mNotificationLock) {
final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+
if (keys != null) {
final int N = keys.length;
for (int i = 0; i < N; i++) {
@@ -4269,6 +4287,14 @@
}
}
+ @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;
@@ -4293,6 +4319,8 @@
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();
@@ -4300,6 +4328,7 @@
if (index < 0) {
mNotificationList.add(r);
mUsageStats.registerPostedByApp(r);
+ r.setInterruptive(true);
} else {
old = mNotificationList.get(index);
mNotificationList.set(index, r);
@@ -4310,6 +4339,7 @@
// revoke uri permissions for changed uris
revokeUriPermissions(r, old);
r.isUpdate = true;
+ r.setInterruptive(isVisuallyInterruptive(old, r));
}
mNotificationsByKey.put(n.getKey(), r);
@@ -4343,7 +4373,7 @@
} 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
@@ -4359,7 +4389,9 @@
+ n.getPackageName());
}
- buzzBeepBlinkLocked(r);
+ if (!r.isHidden()) {
+ buzzBeepBlinkLocked(r);
+ }
maybeRecordInterruptionLocked(r);
} finally {
int N = mEnqueuedNotifications.size();
@@ -4376,6 +4408,52 @@
}
/**
+ * If the notification differs enough visually, consider it a new interruptive notification.
+ */
+ @GuardedBy("mNotificationLock")
+ @VisibleForTesting
+ protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) {
+ Notification oldN = old.sbn.getNotification();
+ Notification newN = r.sbn.getNotification();
+ if (oldN.extras == null || newN.extras == null) {
+ return false;
+ }
+ if (!Objects.equals(oldN.extras.get(Notification.EXTRA_TITLE),
+ newN.extras.get(Notification.EXTRA_TITLE))) {
+ return true;
+ }
+ if (!Objects.equals(oldN.extras.get(Notification.EXTRA_TEXT),
+ newN.extras.get(Notification.EXTRA_TEXT))) {
+ return true;
+ }
+ if (oldN.extras.containsKey(Notification.EXTRA_PROGRESS) && newN.hasCompletedProgress()) {
+ return true;
+ }
+ // Actions
+ if (Notification.areActionsVisiblyDifferent(oldN, newN)) {
+ return true;
+ }
+
+ try {
+ Notification.Builder oldB = Notification.Builder.recoverBuilder(getContext(), oldN);
+ Notification.Builder newB = Notification.Builder.recoverBuilder(getContext(), newN);
+
+ // Style based comparisons
+ if (Notification.areStyledNotificationsVisiblyDifferent(oldB, newB)) {
+ return true;
+ }
+
+ // Remote views
+ if (Notification.areRemoteViewsChanged(oldB, newB)) {
+ return true;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "error recovering builder", e);
+ }
+ return false;
+ }
+
+ /**
* Keeps the last 5 packages that have notified, by user.
*/
@GuardedBy("mNotificationLock")
@@ -4972,7 +5050,7 @@
private void handleSendRankingUpdate() {
synchronized (mNotificationLock) {
- mListeners.notifyRankingUpdateLocked();
+ mListeners.notifyRankingUpdateLocked(null);
}
}
@@ -5157,7 +5235,7 @@
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() {
@@ -5266,6 +5344,7 @@
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
@@ -5657,6 +5736,42 @@
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();
@@ -5776,6 +5891,7 @@
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)) {
@@ -5802,6 +5918,7 @@
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]);
@@ -5812,7 +5929,7 @@
}
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) {
@@ -6101,6 +6218,16 @@
*/
@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);
@@ -6114,6 +6241,21 @@
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.
@@ -6168,8 +6310,9 @@
* 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
@@ -6178,6 +6321,21 @@
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;
@@ -6192,21 +6350,44 @@
}
/**
- * 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);
+ }
+ });
+ }
}
}
@@ -6225,6 +6406,52 @@
}
}
+ /**
+ * 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()) {
@@ -6453,6 +6680,22 @@
}
}
+ @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.
@@ -6481,7 +6724,9 @@
+ "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) {
@@ -6550,7 +6795,16 @@
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 f1908bf..c887085 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 @@
// 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 @@
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 @@
+ 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 @@
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/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index bf85f30..83fe1c9 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -55,6 +55,7 @@
import android.security.Credentials;
import android.service.textclassifier.TextClassifierService;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -829,13 +830,11 @@
}
// TextClassifier Service
- ComponentName textClassifierComponent =
- TextClassifierService.getServiceComponentName(mContext);
- if (textClassifierComponent != null) {
- Intent textClassifierServiceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
- .setComponent(textClassifierComponent);
+ String textClassifierPackageName =
+ mContext.getPackageManager().getSystemTextClassifierPackageName();
+ if (!TextUtils.isEmpty(textClassifierPackageName)) {
PackageParser.Package textClassifierPackage =
- getDefaultSystemHandlerServicePackage(textClassifierServiceIntent, userId);
+ getSystemPackage(textClassifierPackageName);
if (textClassifierPackage != null
&& doesPackageSupportRuntimePermissions(textClassifierPackage)) {
grantRuntimePermissions(textClassifierPackage, PHONE_PERMISSIONS, true, userId);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6cd60e6a..61b1eb4 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 @@
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 055e6ea..adbf7ed 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.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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 54487e3..18e5ce4 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 @@
// 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 @@
*
* @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 @@
}
// 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 a7dfd35..0b7d9d0 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.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 @@
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 @@
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 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,38 +368,45 @@
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();
- 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();
- }
- } finally {
- // I know, the double finally seems ugly, but seems safest for the identity.
- Binder.restoreCallingIdentity(ident);
+ 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 {
+ holder = activityManager.getContentProviderExternal(
+ providerName, getUserIdFromUri(uri, user), token);
+ if (holder != null && holder.info != null) {
+ return holder.info.packageName;
+ } else {
+ return null;
+ }
+ } finally {
+ 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);
+ }
+ }
+
private void enforceCrossUser(String pkg, Uri uri) {
int user = Binder.getCallingUserHandle().getIdentifier();
if (getUserIdFromUri(uri, user) != user) {
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 6053512..6df7092 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.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.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 @@
Slog.e(LOG_TAG, "Could not start the TextClassificationManagerService.", t);
}
}
+
+ @Override
+ public void onStartUser(int userId) {
+ processAnyPendingWork(userId);
+ }
+
+ @Override
+ public void onUnlockUser(int userId) {
+ // Rebind if we failed earlier due to locked encrypted user
+ processAnyPendingWork(userId);
+ }
+
+ private void processAnyPendingWork(int userId) {
+ synchronized (mManagerService.mLock) {
+ mManagerService.getUserStateLocked(userId).bindIfHasPendingRequestsLocked();
+ }
+ }
+
+ @Override
+ public void onStopUser(int userId) {
+ synchronized (mManagerService.mLock) {
+ UserState userState = mManagerService.peekUserStateLocked(userId);
+ if (userState != null) {
+ userState.mConnection.cleanupService();
+ mManagerService.mUserStates.remove(userId);
+ }
+ }
+ }
+
}
private final Context mContext;
- private final Intent mServiceIntent;
- private final ServiceConnection mConnection;
private final Object mLock;
@GuardedBy("mLock")
- private final Queue<PendingRequest> mPendingRequests;
-
- @GuardedBy("mLock")
- private ITextClassifierService mService;
- @GuardedBy("mLock")
- private boolean mBinding;
+ final SparseArray<UserState> mUserStates = new SparseArray<>();
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();
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- cleanupService();
- }
-
- @Override
- public void onBindingDied(ComponentName name) {
- cleanupService();
- }
-
- @Override
- public void onNullBinding(ComponentName name) {
- cleanupService();
- }
-
- private void cleanupService() {
- synchronized (mLock) {
- mService = null;
- setBindingLocked(false);
- handlePendingRequestsLocked();
- }
- }
- };
- mPendingRequests = new LinkedList<>();
mLock = new Object();
}
@@ -133,30 +124,20 @@
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 @@
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 @@
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 @@
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 */);
+ userState.mPendingRequests.add(new PendingRequest(
+ () -> onSelectionEvent(event),
+ null /* onServiceFailure */, null /* binder */, this, userState));
}
}
}
- /**
- * @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;
- }
+ private UserState getCallingUserStateLocked() {
+ return getUserStateLocked(UserHandle.getCallingUserId());
+ }
- // 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);
- }
- return willBind;
+ 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 boolean isBoundLocked() {
- return mService != null;
+ UserState peekUserStateLocked(int userId) {
+ return mUserStates.get(userId);
}
- @GuardedBy("mLock")
- private boolean isBindingLocked() {
- return mBinding;
- }
+ private static final class PendingRequest implements IBinder.DeathRecipient {
- @GuardedBy("mLock")
- private void setBindingLocked(boolean binding) {
- mBinding = binding;
- }
-
- @GuardedBy("mLock")
- private void enqueueRequestLocked(
- Callable<Void> request, Callable<Void> onServiceFailure, IBinder binder) {
- mPendingRequests.add(new PendingRequest(request, onServiceFailure, binder));
- }
-
- @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 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 @@
}
}
- @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 @@
@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 @@
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 c31cdec..641a1ba 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 @@
// 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 @@
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 @@
(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 0000000..ae343da
--- /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 d2ddf55..f8a4540 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 @@
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 @@
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;
}
@@ -1784,6 +1795,21 @@
}
@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 +1851,9 @@
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 c35c05d..e2e1690 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 @@
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 @@
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/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index ad2fabb..235f63e 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -29,6 +29,7 @@
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.os.IBinder;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -268,15 +269,23 @@
.build();
// capture a screenshot into the surface we just created
- Surface sur = new Surface();
- sur.copyFrom(mSurfaceControl);
// TODO(multidisplay): we should use the proper display
- SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
- SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
- t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
- t.setAlpha(mSurfaceControl, 0);
- t.show(mSurfaceControl);
- sur.destroy();
+ final int displayId = SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN;
+ final IBinder displayHandle = SurfaceControl.getBuiltInDisplay(displayId);
+ // This null check below is to guard a race condition where WMS didn't have a chance to
+ // respond to display disconnection before handling rotation , that surfaceflinger may
+ // return a null handle here because it doesn't think that display is valid anymore.
+ if (displayHandle != null) {
+ Surface sur = new Surface();
+ sur.copyFrom(mSurfaceControl);
+ SurfaceControl.screenshot(displayHandle, sur);
+ t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
+ t.setAlpha(mSurfaceControl, 0);
+ t.show(mSurfaceControl);
+ sur.destroy();
+ } else {
+ Slog.w(TAG, "Built-in display " + displayId + " is null.");
+ }
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate freeze surface", e);
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index c06caaf..f10ff8c 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 @@
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 @@
* @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 b5d00a7..460edec 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 @@
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 @@
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 5ae4dc5..27c0b3b 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 @@
}
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 9f8de58b..5f0769d 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 @@
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 @@
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 @@
}
@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 c100511..eebf2fd 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 @@
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 @@
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 410eddf..0c2b075 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 @@
// 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 @@
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 @@
// 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 70abf80..d165a45 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -196,6 +196,8 @@
"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 @@
}
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 f603a09..d0398ad 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 @@
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 03792b1..869c50b 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.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 @@
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 @@
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 c210fde..5a886e3 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.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 @@
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 @@
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/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 0462b14..e5c6c6e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -16,6 +16,9 @@
package com.android.server.accessibility;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -57,6 +60,7 @@
static final int SERVICE_ID = 42;
AccessibilityServiceConnection mConnection;
+
@Mock AccessibilityManagerService.UserState mMockUserState;
@Mock Context mMockContext;
@Mock AccessibilityServiceInfo mMockServiceInfo;
@@ -66,7 +70,9 @@
@Mock WindowManagerInternal mMockWindowManagerInternal;
@Mock GlobalActionPerformer mMockGlobalActionPerformer;
@Mock KeyEventDispatcher mMockKeyEventDispatcher;
+ @Mock MagnificationController mMockMagnificationController;
+ MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
@BeforeClass
public static void oneTimeInitialization() {
@@ -79,12 +85,15 @@
public void setup() {
MockitoAnnotations.initMocks(this);
when(mMockSystemSupport.getKeyEventDispatcher()).thenReturn(mMockKeyEventDispatcher);
+ when(mMockSystemSupport.getMagnificationController())
+ .thenReturn(mMockMagnificationController);
+
when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
mMockResolveInfo.serviceInfo = mock(ServiceInfo.class);
mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
- COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, new Handler(), new Object(),
+ COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal,
mMockGlobalActionPerformer);
}
@@ -106,12 +115,30 @@
@Test
public void bindConnectUnbind_linksAndUnlinksToServiceDeath() throws RemoteException {
IBinder mockBinder = mock(IBinder.class);
- when(mMockUserState.getBindingServicesLocked())
- .thenReturn(new HashSet<>(Arrays.asList(COMPONENT_NAME)));
+ setServiceBinding(COMPONENT_NAME);
mConnection.bindLocked();
mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
verify(mockBinder).linkToDeath(eq(mConnection), anyInt());
mConnection.unbindLocked();
verify(mockBinder).unlinkToDeath(eq(mConnection), anyInt());
}
+
+ @Test
+ public void connectedServiceCrashedAndRestarted_crashReportedInServiceInfo() {
+ IBinder mockBinder = mock(IBinder.class);
+ setServiceBinding(COMPONENT_NAME);
+ mConnection.bindLocked();
+ mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+ assertFalse(mConnection.getServiceInfo().crashed);
+ mConnection.binderDied();
+ assertTrue(mConnection.getServiceInfo().crashed);
+ mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+ mHandler.sendAllMessages();
+ assertFalse(mConnection.getServiceInfo().crashed);
+ }
+
+ private void setServiceBinding(ComponentName componentName) {
+ when(mMockUserState.getBindingServicesLocked())
+ .thenReturn(new HashSet<>(Arrays.asList(componentName)));
+ }
}
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 2378e6d..8721d9c 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.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 @@
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 5906db3..8ff3e45 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 @@
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 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.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 @@
}
}
- 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 @@
// 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 @@
// 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 @@
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 @@
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 c130592..6fb1b2e 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 @@
*/
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 613d0af..10a21fd 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 @@
}
@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 e8170ee..d2fb1ca 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 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 0ceb558..260bb0a 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.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 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.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 @@
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 @@
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 @@
mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
mMockContext,
- mKeyStore,
+ KeyStore.getInstance(),
mRecoverableKeyStoreDb,
mRecoverySessionStorage,
Executors.newSingleThreadExecutor(),
@@ -219,6 +223,7 @@
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 @@
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 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 @@
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 @@
try {
mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
TEST_SESSION_ID,
+ TEST_ROOT_CERT_ALIAS,
RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
vaultParams,
TEST_VAULT_CHALLENGE,
@@ -631,6 +642,7 @@
try {
mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
TEST_SESSION_ID,
+ TEST_ROOT_CERT_ALIAS,
RecoveryCertPath.createRecoveryCertPath(emptyCertPath),
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
@@ -655,6 +667,7 @@
try {
mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
TEST_SESSION_ID,
+ TEST_ROOT_CERT_ALIAS,
RecoveryCertPath.createRecoveryCertPath(shortCertPath),
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
@@ -671,9 +684,9 @@
}
@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 @@
}
@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 @@
TEST_SECRET)));
try {
- mRecoverableKeyStoreManager.recoverKeys(
+ mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
TEST_SESSION_ID,
/*encryptedRecoveryKey=*/ randomBytes(60),
/*applicationKeys=*/ ImmutableList.of());
@@ -710,7 +723,7 @@
}
@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 @@
encryptedApplicationKey(randomRecoveryKey(), randomBytes(32)));
try {
- mRecoverableKeyStoreManager.recoverKeys(
+ mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
TEST_SESSION_ID,
/*encryptedRecoveryKey=*/ encryptedClaimResponse,
/*applicationKeys=*/ ImmutableList.of(badApplicationKey));
@@ -742,7 +755,8 @@
}
@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 @@
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 @@
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 @@
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 edf1f74..552c915 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 @@
}
@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 0000000..8b78f10
--- /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 4f49a4a..b645700 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 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 @@
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 a120eba..6506872 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.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 @@
private SurfaceSession mSession = new SurfaceSession();
private MyAnimatable mAnimatable;
private MyAnimatable mAnimatable2;
+ private DeferFinishAnimatable mDeferFinishAnimatable;
@Before
public void setUp() throws Exception {
@@ -71,6 +73,7 @@
MockitoAnnotations.initMocks(this);
mAnimatable = new MyAnimatable();
mAnimatable2 = new MyAnimatable();
+ mDeferFinishAnimatable = new DeferFinishAnimatable();
}
@Test
@@ -166,6 +169,29 @@
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 @@
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 f4313b8..181fceb 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 @@
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 @@
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 @@
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 @@
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 4fe54b9..b82becd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -68,6 +68,7 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.content.ComponentName;
import android.content.Context;
@@ -264,7 +265,7 @@
mPackageManager, mPackageManagerClient, mockLightsManager,
mListeners, mAssistants, mConditionProviders,
mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
- mGroupHelper, mAm);
+ mGroupHelper, mAm, mock(UsageStatsManagerInternal.class));
} catch (SecurityException e) {
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
throw e;
@@ -2662,4 +2663,120 @@
assertEquals(expected, actual);
}
+
+ @Test
+ public void testVisualDifference_diffTitle() {
+ Notification.Builder nb1 = new Notification.Builder(mContext, "")
+ .setContentTitle("foo");
+ StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb1.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r1 =
+ new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setContentTitle("bar");
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertTrue(mService.isVisuallyInterruptive(r1, r2));
+ }
+
+ @Test
+ public void testVisualDifference_diffText() {
+ Notification.Builder nb1 = new Notification.Builder(mContext, "")
+ .setContentText("foo");
+ StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb1.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r1 =
+ new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setContentText("bar");
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertTrue(mService.isVisuallyInterruptive(r1, r2));
+ }
+
+ @Test
+ public void testVisualDifference_diffProgress() {
+ Notification.Builder nb1 = new Notification.Builder(mContext, "")
+ .setProgress(100, 90, false);
+ StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb1.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r1 =
+ new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setProgress(100, 100, false);
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertTrue(mService.isVisuallyInterruptive(r1, r2));
+ }
+
+ @Test
+ public void testVisualDifference_diffProgressNotDone() {
+ Notification.Builder nb1 = new Notification.Builder(mContext, "")
+ .setProgress(100, 90, false);
+ StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb1.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r1 =
+ new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setProgress(100, 91, false);
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ 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/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
index 4bfb236..c4a688b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
@@ -20,18 +20,27 @@
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
+import android.app.Notification.Person;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
import android.os.Build;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.widget.RemoteViews;
import com.android.server.UiServiceTestCase;
@@ -112,5 +121,272 @@
assertEquals(Color.RED, new Notification.CarExtender(before).getColor());
assertEquals("dismiss", new Notification.WearableExtender(before).getDismissalId());
}
+
+ @Test
+ public void testStyleChangeVisiblyDifferent_noStyles() {
+ Notification.Builder n1 = new Notification.Builder(mContext, "test");
+ Notification.Builder n2 = new Notification.Builder(mContext, "test");
+
+ assertFalse(Notification.areStyledNotificationsVisiblyDifferent(n1, n2));
+ }
+
+ @Test
+ public void testStyleChangeVisiblyDifferent_noStyleToStyle() {
+ Notification.Builder n1 = new Notification.Builder(mContext, "test");
+ Notification.Builder n2 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.BigTextStyle());
+
+ assertTrue(Notification.areStyledNotificationsVisiblyDifferent(n1, n2));
+ }
+
+ @Test
+ public void testStyleChangeVisiblyDifferent_styleToNoStyle() {
+ Notification.Builder n2 = new Notification.Builder(mContext, "test");
+ Notification.Builder n1 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.BigTextStyle());
+
+ assertTrue(Notification.areStyledNotificationsVisiblyDifferent(n1, n2));
+ }
+
+ @Test
+ public void testStyleChangeVisiblyDifferent_changeStyle() {
+ Notification.Builder n1 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.InboxStyle());
+ Notification.Builder n2 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.BigTextStyle());
+
+ assertTrue(Notification.areStyledNotificationsVisiblyDifferent(n1, n2));
+ }
+
+ @Test
+ public void testInboxTextChange() {
+ Notification.Builder nInbox1 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.InboxStyle().addLine("a").addLine("b"));
+ Notification.Builder nInbox2 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.InboxStyle().addLine("b").addLine("c"));
+
+ assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nInbox1, nInbox2));
+ }
+
+ @Test
+ public void testBigTextTextChange() {
+ Notification.Builder nBigText1 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.BigTextStyle().bigText("something"));
+ Notification.Builder nBigText2 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.BigTextStyle().bigText("else"));
+
+ assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nBigText1, nBigText2));
+ }
+
+ @Test
+ public void testBigPictureChange() {
+ Notification.Builder nBigPic1 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.BigPictureStyle().bigPicture(mock(Bitmap.class)));
+ Notification.Builder nBigPic2 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.BigPictureStyle().bigPicture(mock(Bitmap.class)));
+
+ assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nBigPic1, nBigPic2));
+ }
+
+ @Test
+ public void testMessagingChange_text() {
+ Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(new Notification.MessagingStyle.Message(
+ "a", 100, mock(Notification.Person.class))));
+ Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(new Notification.MessagingStyle.Message(
+ "a", 100, mock(Notification.Person.class)))
+ .addMessage(new Notification.MessagingStyle.Message(
+ "b", 100, mock(Notification.Person.class)))
+ );
+
+ assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+ }
+
+ @Test
+ public void testMessagingChange_data() {
+ Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(new Notification.MessagingStyle.Message(
+ "a", 100, mock(Person.class))
+ .setData("text", mock(Uri.class))));
+ Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(new Notification.MessagingStyle.Message(
+ "a", 100, mock(Person.class))));
+
+ assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+ }
+
+ @Test
+ public void testMessagingChange_sender() {
+ Person a = mock(Person.class);
+ when(a.getName()).thenReturn("A");
+ Person b = mock(Person.class);
+ when(b.getName()).thenReturn("b");
+ Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(new Notification.MessagingStyle.Message("a", 100, b)));
+ Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(new Notification.MessagingStyle.Message("a", 100, a)));
+
+ assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+ }
+
+ @Test
+ public void testMessagingChange_key() {
+ Person a = mock(Person.class);
+ when(a.getKey()).thenReturn("A");
+ Person b = mock(Person.class);
+ when(b.getKey()).thenReturn("b");
+ Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(new Notification.MessagingStyle.Message("a", 100, a)));
+ Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(new Notification.MessagingStyle.Message("a", 100, b)));
+
+ assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+ }
+
+ @Test
+ public void testMessagingChange_ignoreTimeChange() {
+ Notification.Builder nM1 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(new Notification.MessagingStyle.Message(
+ "a", 100, mock(Notification.Person.class))));
+ Notification.Builder nM2 = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(new Notification.MessagingStyle.Message(
+ "a", 1000, mock(Notification.Person.class)))
+ );
+
+ assertFalse(Notification.areStyledNotificationsVisiblyDifferent(nM1, nM2));
+ }
+
+ @Test
+ public void testRemoteViews_nullChange() {
+ Notification.Builder n1 = new Notification.Builder(mContext, "test")
+ .setContent(mock(RemoteViews.class));
+ Notification.Builder n2 = new Notification.Builder(mContext, "test");
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test");
+ n2 = new Notification.Builder(mContext, "test")
+ .setContent(mock(RemoteViews.class));
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test")
+ .setCustomBigContentView(mock(RemoteViews.class));
+ n2 = new Notification.Builder(mContext, "test");
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test");
+ n2 = new Notification.Builder(mContext, "test")
+ .setCustomBigContentView(mock(RemoteViews.class));
+ assertTrue(Notification.areRemoteViewsChanged(n1, n2));
+
+ n1 = new Notification.Builder(mContext, "test");
+ n2 = new Notification.Builder(mContext, "test");
+ assertFalse(Notification.areRemoteViewsChanged(n1, n2));
+ }
+
+ @Test
+ public void testActionsDifferent_null() {
+ Notification n1 = new Notification.Builder(mContext, "test")
+ .build();
+ Notification n2 = new Notification.Builder(mContext, "test")
+ .build();
+
+ assertFalse(Notification.areActionsVisiblyDifferent(n1, n2));
+ }
+
+ @Test
+ public void testActionsDifferentSame() {
+ PendingIntent intent = mock(PendingIntent.class);
+ Icon icon = mock(Icon.class);
+
+ Notification n1 = new Notification.Builder(mContext, "test")
+ .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+ .build();
+ Notification n2 = new Notification.Builder(mContext, "test")
+ .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+ .build();
+
+ assertFalse(Notification.areActionsVisiblyDifferent(n1, n2));
+ }
+
+ @Test
+ public void testActionsDifferentText() {
+ PendingIntent intent = mock(PendingIntent.class);
+ Icon icon = mock(Icon.class);
+
+ Notification n1 = new Notification.Builder(mContext, "test")
+ .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+ .build();
+ Notification n2 = new Notification.Builder(mContext, "test")
+ .addAction(new Notification.Action.Builder(icon, "TEXT 2", intent).build())
+ .build();
+
+ assertTrue(Notification.areActionsVisiblyDifferent(n1, n2));
+ }
+
+ @Test
+ public void testActionsDifferentNumber() {
+ PendingIntent intent = mock(PendingIntent.class);
+ Icon icon = mock(Icon.class);
+
+ Notification n1 = new Notification.Builder(mContext, "test")
+ .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+ .build();
+ Notification n2 = new Notification.Builder(mContext, "test")
+ .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent).build())
+ .addAction(new Notification.Action.Builder(icon, "TEXT 2", intent).build())
+ .build();
+
+ assertTrue(Notification.areActionsVisiblyDifferent(n1, n2));
+ }
+
+ @Test
+ public void testActionsDifferentIntent() {
+ PendingIntent intent1 = mock(PendingIntent.class);
+ PendingIntent intent2 = mock(PendingIntent.class);
+ Icon icon = mock(Icon.class);
+
+ Notification n1 = new Notification.Builder(mContext, "test")
+ .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent1).build())
+ .build();
+ Notification n2 = new Notification.Builder(mContext, "test")
+ .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent2).build())
+ .build();
+
+ assertFalse(Notification.areActionsVisiblyDifferent(n1, n2));
+ }
+
+ @Test
+ public void testActionsDifferentRemoteInputs() {
+ PendingIntent intent = mock(PendingIntent.class);
+ Icon icon = mock(Icon.class);
+
+ Notification n1 = new Notification.Builder(mContext, "test")
+ .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent)
+ .addRemoteInput(new RemoteInput.Builder("a")
+ .setChoices(new CharSequence[] {"i", "m"})
+ .build())
+ .build())
+ .build();
+ Notification n2 = new Notification.Builder(mContext, "test")
+ .addAction(new Notification.Action.Builder(icon, "TEXT 1", intent)
+ .addRemoteInput(new RemoteInput.Builder("a")
+ .setChoices(new CharSequence[] {"t", "m"})
+ .build())
+ .build())
+ .build();
+
+ assertTrue(Notification.areActionsVisiblyDifferent(n1, n2));
+ }
}
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 4f446a9..1073a80 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 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 @@
@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 @@
@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 e836677..1af5f46 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_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.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.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 @@
|| 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 @@
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 d974282..c2e38f2 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 @@
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 cd3fdee..1160943 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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
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 @@
return ret;
}
synchronized (mCallbacksLock) {
- mIntentCallbacks.put(soundModelId.getUuid(), callback);
+ mCallbacks.put(soundModelId.getUuid(), callback);
}
}
return STATUS_OK;
@@ -320,9 +374,9 @@
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 @@
return ret;
}
synchronized (mCallbacksLock) {
- mIntentCallbacks.remove(soundModelId.getUuid());
+ mCallbacks.remove(soundModelId.getUuid());
}
}
return STATUS_OK;
@@ -394,8 +448,7 @@
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 @@
}
}
- 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 @@
private void removeCallback(boolean releaseWakeLock) {
mCallbackIntent = null;
synchronized (mCallbacksLock) {
- mIntentCallbacks.remove(mUuid);
+ mCallbacks.remove(mUuid);
if (releaseWakeLock) {
mWakelock.release();
}
@@ -536,6 +589,478 @@
}
}
+ /**
+ * 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/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 105ddb0..713ac00 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.text.TextUtils;
@@ -181,6 +182,7 @@
* @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.
*/
+ @Nullable
public CharSequence getOperatorAlphaLong() {
return mAlphaLong;
}
@@ -189,6 +191,7 @@
* @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.
*/
+ @Nullable
public CharSequence getOperatorAlphaShort() {
return mAlphaShort;
}
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 52944a8a..aae7929 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.text.TextUtils;
@@ -191,6 +192,7 @@
* @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.
*/
+ @Nullable
public CharSequence getOperatorAlphaLong() {
return mAlphaLong;
}
@@ -199,6 +201,7 @@
* @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.
*/
+ @Nullable
public CharSequence getOperatorAlphaShort() {
return mAlphaShort;
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 37fb075..9b3ef56 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.text.TextUtils;
@@ -201,6 +202,7 @@
* @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.
*/
+ @Nullable
public CharSequence getOperatorAlphaLong() {
return mAlphaLong;
}
@@ -209,6 +211,7 @@
* @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.
*/
+ @Nullable
public CharSequence getOperatorAlphaShort() {
return mAlphaShort;
}
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 992545d..7475c74 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 @@
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 @@
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
mCpid = Integer.MAX_VALUE;
+ mAlphaLong = null;
+ mAlphaShort = null;
}
/**
@@ -55,7 +62,7 @@
* @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 @@
* @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 @@
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 @@
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 @@
&& 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 @@
.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 @@
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 @@
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/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index affa0c1..52fa54f 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.text.TextUtils;
@@ -182,6 +183,7 @@
* @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.
*/
+ @Nullable
public CharSequence getOperatorAlphaLong() {
return mAlphaLong;
}
@@ -190,6 +192,7 @@
* @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.
*/
+ @Nullable
public CharSequence getOperatorAlphaShort() {
return mAlphaShort;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7dff667..0b15191 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1455,7 +1455,6 @@
* {@hide}
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public int getCurrentPhoneType() {
return getCurrentPhoneType(getSubId());
}
@@ -1471,17 +1470,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public int getCurrentPhoneType(int subId) {
- return getCurrentPhoneType(subId, false);
- }
-
- /**
- * getCurrentPhoneType() with optional check if device is voice capable.
- *
- * @hide
- */
- public int getCurrentPhoneType(int subId, boolean checkIsVoiceCapable) {
int phoneId;
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
// if we don't have any sims, we don't have subscriptions, but we
@@ -1490,7 +1479,8 @@
} else {
phoneId = SubscriptionManager.getPhoneId(subId);
}
- return getCurrentPhoneTypeForSlot(phoneId, checkIsVoiceCapable);
+
+ return getCurrentPhoneTypeForSlot(phoneId);
}
/**
@@ -1498,15 +1488,11 @@
*
* @hide
*/
- public int getCurrentPhoneTypeForSlot(int slotIndex, boolean checkIsVoiceCapable) {
+ public int getCurrentPhoneTypeForSlot(int slotIndex) {
try{
ITelephony telephony = getITelephony();
if (telephony != null) {
- if (checkIsVoiceCapable) {
- return telephony.getVoiceCapableActivePhoneTypeForSlot(slotIndex);
- } else {
- return telephony.getActivePhoneTypeForSlot(slotIndex);
- }
+ return telephony.getActivePhoneTypeForSlot(slotIndex);
} else {
// This can happen when the ITelephony interface is not up yet.
return getPhoneTypeFromProperty(slotIndex);
@@ -1532,7 +1518,10 @@
* @see #PHONE_TYPE_SIP
*/
public int getPhoneType() {
- return getCurrentPhoneType(getSubId(), true);
+ if (!isVoiceCapable()) {
+ return PHONE_TYPE_NONE;
+ }
+ return getCurrentPhoneType();
}
private int getPhoneTypeFromProperty() {
@@ -5469,7 +5458,10 @@
* 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.
*/
@@ -5495,7 +5487,7 @@
@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);
}
/**
@@ -5842,14 +5834,12 @@
/** @hide */
@SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public List<String> getCarrierPackageNamesForIntent(Intent intent) {
return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId());
}
/** @hide */
@SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public List<String> getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) {
try {
ITelephony telephony = getITelephony();
@@ -5959,11 +5949,7 @@
}
}
- /**
- * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
- * @hide
- */
- @Deprecated
+ /** @hide */
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -5980,11 +5966,7 @@
return false;
}
- /**
- * @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead
- * @hide
- */
- @Deprecated
+ /** @hide */
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -6001,11 +5983,7 @@
return false;
}
- /**
- * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
- * @hide
- */
- @Deprecated
+ /** @hide */
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -6022,11 +6000,7 @@
return true;
}
- /**
- * @deprecated Use {@link android.telephony.TelephonyManager#getServiceState} instead
- * @hide
- */
- @Deprecated
+ /** @hide */
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -6317,7 +6291,6 @@
/** @hide */
@SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isDataConnectivityPossible() {
try {
ITelephony telephony = getITelephony();
@@ -6332,7 +6305,6 @@
/** @hide */
@SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean needsOtaServiceProvisioning() {
try {
ITelephony telephony = getITelephony();
@@ -6435,7 +6407,10 @@
/** @hide */
@SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE
+ })
public boolean isVideoCallingEnabled() {
try {
ITelephony telephony = getITelephony();
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 99e2db8..96ff332 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -137,8 +137,10 @@
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 @@
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 73a05af..145ed7e 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -17,10 +17,10 @@
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.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 @@
*/
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 @@
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 @@
*
* @return proxy address.
*/
- public InetAddress getProxy() {
- return mProxy;
+ public InetAddress getProxyAddress() {
+ return mProxyAddress;
}
/**
@@ -220,15 +367,15 @@
*
* @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 @@
*
* @return MMS proxy address.
*/
- public InetAddress getMmsProxy() {
- return mMmsProxy;
+ public InetAddress getMmsProxyAddress() {
+ return mMmsProxyAddress;
}
/**
@@ -246,8 +393,8 @@
*
* @return MMS proxy port
*/
- public int getMmsPort() {
- return mMmsPort;
+ public int getMmsProxyPort() {
+ return mMmsProxyPort;
}
/**
@@ -268,21 +415,9 @@
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 @@
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 @@
}
/**
- * 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 @@
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 @@
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 @@
/** @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 @@
/** @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 @@
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 @@
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 @@
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 @@
/** @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 @@
.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 @@
* @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 @@
&& 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 @@
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 @@
&& !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 @@
|| 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);
+ return first == NO_PORT_SPECIFIED || second == NO_PORT_SPECIFIED
+ || 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;
- }
- }
-
- 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 @@
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 @@
/**
* @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(",");
- }
- 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;
+ int result = 0;
+ for (String str : types.split(",")) {
+ Integer type = APN_TYPE_STRING_MAP.get(str);
+ if (type != null) {
+ result |= type;
+ }
+ }
+ return result;
}
}
- private static String URLToString(URL url) {
- return url == null ? "" : url.toString();
+ private static Uri UriFromString(String uri) {
+ return TextUtils.isEmpty(uri) ? null : Uri.parse(uri);
+ }
+
+ private static String UriToString(Uri uri) {
+ return uri == null ? "" : uri.toString();
}
private static InetAddress inetAddressFromString(String inetAddress) {
@@ -887,7 +952,7 @@
}
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 @@
}
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 @@
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 @@
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 @@
}
};
- /**
- * 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 @@
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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
/**
* 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 @@
}
/**
- * Sets the list of APN types of the APN.
+ * Sets 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.
*
- * @param types the list of APN types to set for the APN
+ * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+ *
+ * @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 @@
/**
* 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 @@
/**
* 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 1637c55..dff1c6f 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 @@
*/
@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 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public int getOtaStatus() {
if (!isEnabled()) {
return EUICC_OTA_STATUS_UNAVAILABLE;
@@ -325,6 +328,7 @@
* @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 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
if (!isEnabled()) {
PendingIntent callbackIntent =
@@ -422,6 +427,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void getDownloadableSubscriptionMetadata(
DownloadableSubscription subscription, PendingIntent callbackIntent) {
if (!isEnabled()) {
@@ -452,6 +458,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
if (!isEnabled()) {
sendUnavailableError(callbackIntent);
@@ -496,6 +503,7 @@
* @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 @@
* 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 @@
* @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 @@
* 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/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 23b4dc0..fbb69ad 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -375,8 +375,6 @@
/**
* Report whether data connectivity is possible.
- *
- * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
*/
boolean isDataConnectivityPossible(int subId);
@@ -415,25 +413,11 @@
* Returns the current active phone type as integer for particular slot.
* Returns TelephonyManager.PHONE_TYPE_CDMA if RILConstants.CDMA_PHONE
* and TelephonyManager.PHONE_TYPE_GSM if RILConstants.GSM_PHONE
- *
- * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
- *
* @param slotIndex - slot to query.
*/
int getActivePhoneTypeForSlot(int slotIndex);
/**
- * Returns the current active phone type as integer for particular slot.
- * Returns TelephonyManager.PHONE_TYPE_CDMA if RILConstants.CDMA_PHONE
- * and TelephonyManager.PHONE_TYPE_GSM if RILConstants.GSM_PHONE
- *
- * If the device is not voice-capable, return PHONE_TYPE_NONE
- *
- * @param slotIndex - slot to query.
- */
- int getVoiceCapableActivePhoneTypeForSlot(int slotIndex);
-
- /**
* Returns the CDMA ERI icon index to display
* @param callingPackage package making the call.
*/
@@ -480,8 +464,6 @@
* Returns true if OTA service provisioning needs to run.
* Only relevant on some technologies, others will always
* return false.
- *
- * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
*/
boolean needsOtaServiceProvisioning();
@@ -994,8 +976,6 @@
* Returns list of the package names of the carrier apps that should handle the input intent
* and have carrier privileges for the given phoneId.
*
- * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
- *
* @param intent Intent that will be sent.
* @param phoneId The phoneId on which the carrier app has carrier privileges.
* @return list of carrier app package names that can handle the intent on phoneId.
@@ -1120,8 +1100,6 @@
/**
* Whether video calling has been enabled by the user.
*
- * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
- *
* @param callingPackage The package making the call.
* @return {@code true} if the user has enabled video calling, {@code false} otherwise.
*/
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 14c5f4b..964a313 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -470,6 +470,9 @@
int bearerDataLength;
SmsEnvelope env = new SmsEnvelope();
CdmaSmsAddress addr = new CdmaSmsAddress();
+ // We currently do not parse subaddress in PDU, but it is required when determining
+ // fingerprint (see getIncomingSmsFingerprint()).
+ CdmaSmsSubaddress subaddr = new CdmaSmsSubaddress();
try {
env.messageType = dis.readInt();
@@ -520,6 +523,7 @@
// link the filled objects to this SMS
mOriginatingAddress = addr;
env.origAddress = addr;
+ env.origSubaddress = subaddr;
mEnvelope = env;
mPdu = pdu;
@@ -1009,8 +1013,11 @@
output.write(mEnvelope.teleService);
output.write(mEnvelope.origAddress.origBytes, 0, mEnvelope.origAddress.origBytes.length);
output.write(mEnvelope.bearerData, 0, mEnvelope.bearerData.length);
- output.write(mEnvelope.origSubaddress.origBytes, 0,
- mEnvelope.origSubaddress.origBytes.length);
+ // subaddress is not set when parsing some MT SMS.
+ if (mEnvelope.origSubaddress != null && mEnvelope.origSubaddress.origBytes != null) {
+ output.write(mEnvelope.origSubaddress.origBytes, 0,
+ mEnvelope.origSubaddress.origBytes.length);
+ }
return output.toByteArray();
}
diff --git a/test-mock/api/android-test-mock-current.txt b/test-mock/api/android-test-mock-current.txt
index 07acfef..f3b253c 100644
--- a/test-mock/api/android-test-mock-current.txt
+++ b/test-mock/api/android-test-mock-current.txt
@@ -278,6 +278,7 @@
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 1af7c3a..8ebb77d 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -18,6 +18,7 @@
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 @@
}
@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 cc3366f..0ca20de 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -50,13 +50,18 @@
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 @@
*/
@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 @@
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 @@
randomSpi.close();
- verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId);
+ verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID);
}
/*
@@ -165,11 +168,10 @@
@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 @@
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 @@
encapSocket.close();
- verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId);
+ verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID);
}
@Test
@@ -219,4 +220,45 @@
}
// 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 3e1ff6d..a5c55e8 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.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 @@
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 @@
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 @@
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 @@
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()));
+ }
}
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index ab106d7..ebdcdfd 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -46,7 +46,8 @@
message(that.message),
fields(that.fields),
primaryFields(that.primaryFields),
- exclusiveField(that.exclusiveField) {}
+ exclusiveField(that.exclusiveField),
+ uidField(that.uidField) {}
AtomDecl::AtomDecl(int c, const string& n, const string& m)
:code(c),
@@ -262,6 +263,18 @@
errorCount++;
}
}
+
+ if (field->options().GetExtension(os::statsd::is_uid) == true) {
+ if (javaType != JAVA_TYPE_INT) {
+ errorCount++;
+ }
+
+ if (atomDecl->uidField == 0) {
+ atomDecl->uidField = it->first;
+ } else {
+ errorCount++;
+ }
+ }
}
return errorCount;
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index edba3e2..5d2c302 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -84,6 +84,8 @@
vector<int> primaryFields;
int exclusiveField = 0;
+ int uidField = 0;
+
AtomDecl();
AtomDecl(const AtomDecl& that);
AtomDecl(int code, const string& name, const string& message);
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index d58c223..300c701 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -113,6 +113,98 @@
fprintf(out, "// the single event tag id for all stats logs\n");
fprintf(out, "const static int kStatsEventTag = 1937006964;\n");
+ std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed",
+ "audio_state_changed",
+ "call_state_changed",
+ "phone_signal_strength_changed",
+ "mobile_bytes_transfer_by_fg_bg",
+ "mobile_bytes_transfer"};
+ fprintf(out,
+ "const std::set<int> "
+ "AtomsInfo::kNotTruncatingTimestampAtomWhiteList = {\n");
+ for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+ atom != atoms.decls.end(); atom++) {
+ if (kTruncatingAtomNames.find(atom->name) ==
+ kTruncatingAtomNames.end()) {
+ string constant = make_constant_name(atom->name);
+ fprintf(out, " %s,\n", constant.c_str());
+ }
+ }
+ fprintf(out, "};\n");
+ fprintf(out, "\n");
+
+ fprintf(out,
+ "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n");
+ for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+ atom != atoms.decls.end(); atom++) {
+ for (vector<AtomField>::const_iterator field = atom->fields.begin();
+ field != atom->fields.end(); field++) {
+ if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ string constant = make_constant_name(atom->name);
+ fprintf(out, " %s,\n", constant.c_str());
+ break;
+ }
+ }
+ }
+ fprintf(out, "};\n");
+ fprintf(out, "\n");
+
+ fprintf(out,
+ "static std::map<int, int> "
+ "getAtomUidField() {\n");
+ fprintf(out, " std::map<int, int> uidField;\n");
+ for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+ atom != atoms.decls.end(); atom++) {
+ if (atom->uidField == 0) {
+ continue;
+ }
+ fprintf(out,
+ "\n // Adding uid field for atom "
+ "(%d)%s\n",
+ atom->code, atom->name.c_str());
+ fprintf(out, " uidField[static_cast<int>(%s)] = %d;\n",
+ make_constant_name(atom->name).c_str(), atom->uidField);
+ }
+
+ fprintf(out, " return uidField;\n");
+ fprintf(out, "};\n");
+
+ fprintf(out,
+ "const std::map<int, int> AtomsInfo::kAtomsWithUidField = "
+ "getAtomUidField();\n");
+
+ fprintf(out,
+ "static std::map<int, StateAtomFieldOptions> "
+ "getStateAtomFieldOptions() {\n");
+ fprintf(out, " std::map<int, StateAtomFieldOptions> options;\n");
+ fprintf(out, " StateAtomFieldOptions opt;\n");
+ for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+ atom != atoms.decls.end(); atom++) {
+ if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) {
+ continue;
+ }
+ fprintf(out,
+ "\n // Adding primary and exclusive fields for atom "
+ "(%d)%s\n",
+ atom->code, atom->name.c_str());
+ fprintf(out, " opt.primaryFields.clear();\n");
+ for (const auto& field : atom->primaryFields) {
+ fprintf(out, " opt.primaryFields.push_back(%d);\n", field);
+ }
+
+ fprintf(out, " opt.exclusiveField = %d;\n", atom->exclusiveField);
+ fprintf(out, " options[static_cast<int>(%s)] = opt;\n",
+ make_constant_name(atom->name).c_str());
+ }
+
+ fprintf(out, " return options;\n");
+ fprintf(out, " }\n");
+
+ fprintf(out,
+ "const std::map<int, StateAtomFieldOptions> "
+ "AtomsInfo::kStateAtomsFieldOptions = "
+ "getStateAtomFieldOptions();\n");
+
// Print write methods
fprintf(out, "\n");
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
@@ -366,89 +458,26 @@
fprintf(out, "};\n");
fprintf(out, "\n");
- std::set<string> kTruncatingAtomNames =
- { "mobile_radio_power_state_changed", "audio_state_changed", "call_state_changed",
- "phone_signal_strength_changed", "mobile_bytes_transfer_by_fg_bg",
- "mobile_bytes_transfer"};
- fprintf(out, "const static std::set<int> kNotTruncatingTimestampAtomWhiteList = {\n");
- for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
- atom != atoms.decls.end(); atom++) {
- if (kTruncatingAtomNames.find(atom->name) == kTruncatingAtomNames.end()) {
- string constant = make_constant_name(atom->name);
- fprintf(out, " %s,\n", constant.c_str());
- }
- }
- fprintf(out, "};\n");
- fprintf(out, "\n");
-
- fprintf(out, "const static std::set<int> kAtomsWithUidField = {\n");
- for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
- atom != atoms.decls.end(); atom++) {
- for (vector<AtomField>::const_iterator field = atom->fields.begin();
- field != atom->fields.end(); field++) {
- if (field->name == "uid") {
- string constant = make_constant_name(atom->name);
- fprintf(out, " %s,\n", constant.c_str());
- break;
- }
- }
- }
- fprintf(out, "};\n");
- fprintf(out, "\n");
-
- fprintf(out, "const static std::set<int> kAtomsWithAttributionChain = {\n");
- for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
- atom != atoms.decls.end(); atom++) {
- for (vector<AtomField>::const_iterator field = atom->fields.begin();
- field != atom->fields.end(); field++) {
- if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- string constant = make_constant_name(atom->name);
- fprintf(out, " %s,\n", constant.c_str());
- break;
- }
- }
- }
- fprintf(out, "};\n");
- fprintf(out, "\n");
-
- fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", maxPushedAtomId);
-
fprintf(out, "struct StateAtomFieldOptions {\n");
fprintf(out, " std::vector<int> primaryFields;\n");
fprintf(out, " int exclusiveField;\n");
+ fprintf(out, "};\n");
fprintf(out, "\n");
+
+ fprintf(out, "struct AtomsInfo {\n");
fprintf(out,
- " static std::map<int, StateAtomFieldOptions> "
- "getStateAtomFieldOptions() {\n");
- fprintf(out, " std::map<int, StateAtomFieldOptions> options;\n");
- fprintf(out, " StateAtomFieldOptions opt;\n");
- for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
- atom != atoms.decls.end(); atom++) {
- if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) {
- continue;
- }
- fprintf(out,
- "\n // Adding primary and exclusive fields for atom "
- "(%d)%s\n",
- atom->code, atom->name.c_str());
- fprintf(out, " opt.primaryFields.clear();\n");
- for (const auto& field : atom->primaryFields) {
- fprintf(out, " opt.primaryFields.push_back(%d);\n", field);
- }
-
- fprintf(out, " opt.exclusiveField = %d;\n", atom->exclusiveField);
- fprintf(out, " options[static_cast<int>(%s)] = opt;\n",
- make_constant_name(atom->name).c_str());
- }
-
- fprintf(out, " return options;\n");
- fprintf(out, " }\n");
+ " const static std::set<int> "
+ "kNotTruncatingTimestampAtomWhiteList;\n");
+ fprintf(out, " const static std::map<int, int> kAtomsWithUidField;\n");
+ fprintf(out,
+ " const static std::set<int> kAtomsWithAttributionChain;\n");
+ fprintf(out,
+ " const static std::map<int, StateAtomFieldOptions> "
+ "kStateAtomsFieldOptions;\n");
fprintf(out, "};\n");
- fprintf(out,
- "const static std::map<int, StateAtomFieldOptions> "
- "kStateAtomsFieldOptions = "
- "StateAtomFieldOptions::getStateAtomFieldOptions();\n");
+ fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
+ maxPushedAtomId);
// Print write methods
fprintf(out, "//\n");
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 309bc80..2f7b50d 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -28,7 +28,6 @@
import android.net.wifi.ISoftApCallback;
import android.net.wifi.PasspointManagementObjectDefinition;
import android.net.wifi.ScanResult;
-import android.net.wifi.ScanSettings;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
@@ -86,7 +85,7 @@
boolean disableNetwork(int netId, String packageName);
- void startScan(in ScanSettings requested, in WorkSource ws, String packageName);
+ void startScan(String packageName);
List<ScanResult> getScanResults(String callingPackage);
diff --git a/wifi/java/android/net/wifi/ScanSettings.java b/wifi/java/android/net/wifi/ScanSettings.java
deleted file mode 100644
index 094ce34..0000000
--- a/wifi/java/android/net/wifi/ScanSettings.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * Bundle of customized scan settings
- *
- * @see WifiManager#startCustomizedScan
- *
- * @hide
- */
-public class ScanSettings implements Parcelable {
-
- /** channel set to scan. this can be null or empty, indicating a full scan */
- public Collection<WifiChannel> channelSet;
-
- /** public constructor */
- public ScanSettings() { }
-
- /** copy constructor */
- public ScanSettings(ScanSettings source) {
- if (source.channelSet != null)
- channelSet = new ArrayList<WifiChannel>(source.channelSet);
- }
-
- /** check for validity */
- public boolean isValid() {
- for (WifiChannel channel : channelSet)
- if (!channel.isValid()) return false;
- return true;
- }
-
- /** implement Parcelable interface */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /** implement Parcelable interface */
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(channelSet == null ? 0 : channelSet.size());
- if (channelSet != null)
- for (WifiChannel channel : channelSet) channel.writeToParcel(out, flags);
- }
-
- /** implement Parcelable interface */
- public static final Parcelable.Creator<ScanSettings> CREATOR =
- new Parcelable.Creator<ScanSettings>() {
- @Override
- public ScanSettings createFromParcel(Parcel in) {
- ScanSettings settings = new ScanSettings();
- int size = in.readInt();
- if (size > 0) {
- settings.channelSet = new ArrayList<WifiChannel>(size);
- while (size-- > 0)
- settings.channelSet.add(WifiChannel.CREATOR.createFromParcel(in));
- }
- return settings;
- }
-
- @Override
- public ScanSettings[] newArray(int size) {
- return new ScanSettings[size];
- }
- };
-}
diff --git a/wifi/java/android/net/wifi/WifiChannel.java b/wifi/java/android/net/wifi/WifiChannel.java
deleted file mode 100644
index 640481e..0000000
--- a/wifi/java/android/net/wifi/WifiChannel.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Wifi Channel
- *
- * @see ScanSettings
- *
- * @hide
- */
-public class WifiChannel implements Parcelable {
-
- private static final int MIN_FREQ_MHZ = 2412;
- private static final int MAX_FREQ_MHZ = 5825;
-
- private static final int MIN_CHANNEL_NUM = 1;
- private static final int MAX_CHANNEL_NUM = 196;
-
- /** frequency */
- public int freqMHz;
-
- /** channel number */
- public int channelNum;
-
- /** is it a DFS channel? */
- public boolean isDFS;
-
- /** public constructor */
- public WifiChannel() { }
-
- /** check for validity */
- public boolean isValid() {
- if (freqMHz < MIN_FREQ_MHZ || freqMHz > MAX_FREQ_MHZ) return false;
- if (channelNum < MIN_CHANNEL_NUM || channelNum > MAX_CHANNEL_NUM) return false;
- return true;
- }
-
- /** implement Parcelable interface */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /** implement Parcelable interface */
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(freqMHz);
- out.writeInt(channelNum);
- out.writeInt(isDFS ? 1 : 0);
- }
-
- /** implement Parcelable interface */
- public static final Parcelable.Creator<WifiChannel> CREATOR =
- new Parcelable.Creator<WifiChannel>() {
- @Override
- public WifiChannel createFromParcel(Parcel in) {
- WifiChannel channel = new WifiChannel();
- channel.freqMHz = in.readInt();
- channel.channelNum = in.readInt();
- channel.isDFS = in.readInt() != 0;
- return channel;
- }
-
- @Override
- public WifiChannel[] newArray(int size) {
- return new WifiChannel[size];
- }
- };
-}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index d2cdf8d..c8df087 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1634,7 +1634,7 @@
public boolean startScan(WorkSource workSource) {
try {
String packageName = mContext.getOpPackageName();
- mService.startScan(null, workSource, packageName);
+ mService.startScan(packageName);
return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();