diff options
596 files changed, 17834 insertions, 8541 deletions
diff --git a/Android.bp b/Android.bp index 129d6769cb98..d5e04f9f4e2d 100644 --- a/Android.bp +++ b/Android.bp @@ -676,6 +676,7 @@ java_library { "android.hardware.vibrator-V1.1-java-constants", "android.hardware.wifi-V1.0-java-constants", "android.hardware.radio-V1.0-java", + "android.hardware.usb.gadget-V1.0-java", ], // Loaded with System.loadLibrary by android.view.textclassifier diff --git a/Android.mk b/Android.mk index 35b5f92be310..a78a01a1600f 100644 --- a/Android.mk +++ b/Android.mk @@ -821,39 +821,41 @@ LOCAL_PROTOC_FLAGS := \ LOCAL_SRC_FILES := \ $(call all-proto-files-under, core/proto) \ $(call all-proto-files-under, libs/incident/proto/android/os) +# Protos have lots of MissingOverride and similar. +LOCAL_ERROR_PRONE_FLAGS := -XepDisableAllChecks include $(BUILD_STATIC_JAVA_LIBRARY) # ==== hiddenapi lists ======================================= -# Copy blacklist and dark greylist over into the build folder. +# Copy blacklist and light 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))) -$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\ - $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))) - -# Generate light greylist as private API minus (blacklist plus dark greylist). - -$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) -$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) -$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) -$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \ - $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \ - $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) - if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(DARK_GREYLIST))`" ]; then \ - echo "There should be no overlap between $(BLACKLIST) and $(DARK_GREYLIST)" 1>&2; \ +$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-light-greylist.txt,\ + $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_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; \ 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 $(DARK_GREYLIST))`" ]; then \ - echo "$(DARK_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \ + elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST))`" ]; then \ + echo "$(LIGHT_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \ exit 3; \ fi - comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(DARK_GREYLIST)) > $@ + comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(LIGHT_GREYLIST)) > $@ # Include subdirectory makefiles # ============================================================ diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java index bab2a859698c..231aaf2ca074 100644 --- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java @@ -232,7 +232,7 @@ public class StaticLayoutPerfTest { while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = new MeasuredText.Builder( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .build(); diff --git a/api/current.txt b/api/current.txt index 44f2f0836049..10fdea4da895 100644 --- a/api/current.txt +++ b/api/current.txt @@ -969,7 +969,9 @@ package android { field public static final int orderingFromXml = 16843239; // 0x10101e7 field public static final int orientation = 16842948; // 0x10100c4 field public static final int outAnimation = 16843128; // 0x1010178 + field public static final int outlineAmbientShadowColor = 16844162; // 0x1010582 field public static final int outlineProvider = 16843960; // 0x10104b8 + field public static final int outlineSpotShadowColor = 16844161; // 0x1010581 field public static final int overScrollFooter = 16843459; // 0x10102c3 field public static final int overScrollHeader = 16843458; // 0x10102c2 field public static final int overScrollMode = 16843457; // 0x10102c1 @@ -6085,16 +6087,16 @@ package android.app { method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String); method public android.view.accessibility.AccessibilityNodeInfo findFocus(int); method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow(); - method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo(); + method public android.accessibilityservice.AccessibilityServiceInfo getServiceInfo(); method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats(); method public android.view.WindowContentFrameStats getWindowContentFrameStats(int); method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows(); method public boolean injectInputEvent(android.view.InputEvent, boolean); - method public final boolean performGlobalAction(int); + method public boolean performGlobalAction(int); method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener); method public boolean setRotation(int); method public void setRunAsMonkey(boolean); - method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); + method public void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); method public android.graphics.Bitmap takeScreenshot(); method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException; field public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 1; // 0x1 @@ -7250,6 +7252,7 @@ package android.app.slice { method public android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>); method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri); method public java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri); + method public android.net.Uri mapIntentToUri(android.content.Intent); method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>); method public deprecated void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>); method public deprecated void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, java.util.concurrent.Executor); @@ -10050,6 +10053,7 @@ package android.content { field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000 field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000 field public static final int FLAG_ACTIVITY_LAUNCH_ADJACENT = 4096; // 0x1000 + field public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 2048; // 0x800 field public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728; // 0x8000000 field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 524288; // 0x80000 field public static final int FLAG_ACTIVITY_NEW_TASK = 268435456; // 0x10000000 @@ -11612,15 +11616,15 @@ package android.content.res { public final class AssetManager implements java.lang.AutoCloseable { method public void close(); - method public java.lang.String[] getLocales(); - method public java.lang.String[] list(java.lang.String) throws java.io.IOException; - method public java.io.InputStream open(java.lang.String) throws java.io.IOException; - method public java.io.InputStream open(java.lang.String, int) throws java.io.IOException; - method public android.content.res.AssetFileDescriptor openFd(java.lang.String) throws java.io.IOException; - method public android.content.res.AssetFileDescriptor openNonAssetFd(java.lang.String) throws java.io.IOException; - method public android.content.res.AssetFileDescriptor openNonAssetFd(int, java.lang.String) throws java.io.IOException; - method public android.content.res.XmlResourceParser openXmlResourceParser(java.lang.String) throws java.io.IOException; - method public android.content.res.XmlResourceParser openXmlResourceParser(int, java.lang.String) throws java.io.IOException; + method public final java.lang.String[] getLocales(); + method public final java.lang.String[] list(java.lang.String) throws java.io.IOException; + method public final java.io.InputStream open(java.lang.String) throws java.io.IOException; + method public final java.io.InputStream open(java.lang.String, int) throws java.io.IOException; + method public final android.content.res.AssetFileDescriptor openFd(java.lang.String) throws java.io.IOException; + method public final android.content.res.AssetFileDescriptor openNonAssetFd(java.lang.String) throws java.io.IOException; + method public final android.content.res.AssetFileDescriptor openNonAssetFd(int, java.lang.String) throws java.io.IOException; + method public final android.content.res.XmlResourceParser openXmlResourceParser(java.lang.String) throws java.io.IOException; + method public final android.content.res.XmlResourceParser openXmlResourceParser(int, java.lang.String) throws java.io.IOException; field public static final int ACCESS_BUFFER = 3; // 0x3 field public static final int ACCESS_RANDOM = 1; // 0x1 field public static final int ACCESS_STREAMING = 2; // 0x2 @@ -11628,15 +11632,9 @@ package android.content.res { } public final class AssetManager.AssetInputStream extends java.io.InputStream { - method public final int available() throws java.io.IOException; - method public final void close() throws java.io.IOException; - method public final void mark(int); - method public final boolean markSupported(); - method public final int read() throws java.io.IOException; - method public final int read(byte[]) throws java.io.IOException; - method public final int read(byte[], int, int) throws java.io.IOException; - method public final void reset() throws java.io.IOException; - method public final long skip(long) throws java.io.IOException; + method public void mark(int); + method public int read() throws java.io.IOException; + method public void reset() throws java.io.IOException; } public class ColorStateList implements android.os.Parcelable { @@ -12398,7 +12396,7 @@ package android.database.sqlite { method public java.util.List<android.util.Pair<java.lang.String, java.lang.String>> getAttachedDbs(); method public long getMaximumSize(); method public long getPageSize(); - method public final java.lang.String getPath(); + method public java.lang.String getPath(); method public deprecated java.util.Map<java.lang.String, java.lang.String> getSyncedTables(); method public int getVersion(); method public boolean inTransaction(); @@ -13050,34 +13048,36 @@ package android.graphics { method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config); method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config); method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, android.graphics.Bitmap.Config); + method public static android.graphics.Bitmap createBitmap(android.graphics.Picture); + method public static android.graphics.Bitmap createBitmap(android.graphics.Picture, int, int, android.graphics.Bitmap.Config); method public static android.graphics.Bitmap createScaledBitmap(android.graphics.Bitmap, int, int, boolean); method public int describeContents(); method public void eraseColor(int); method public android.graphics.Bitmap extractAlpha(); method public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]); - method public final int getAllocationByteCount(); - method public final int getByteCount(); - method public final android.graphics.ColorSpace getColorSpace(); - method public final android.graphics.Bitmap.Config getConfig(); + method public int getAllocationByteCount(); + method public int getByteCount(); + method public android.graphics.ColorSpace getColorSpace(); + method public android.graphics.Bitmap.Config getConfig(); method public int getDensity(); method public int getGenerationId(); - method public final int getHeight(); + method public int getHeight(); method public byte[] getNinePatchChunk(); method public int getPixel(int, int); method public void getPixels(int[], int, int, int, int, int, int); - method public final int getRowBytes(); + method public int getRowBytes(); method public int getScaledHeight(android.graphics.Canvas); method public int getScaledHeight(android.util.DisplayMetrics); method public int getScaledHeight(int); method public int getScaledWidth(android.graphics.Canvas); method public int getScaledWidth(android.util.DisplayMetrics); method public int getScaledWidth(int); - method public final int getWidth(); - method public final boolean hasAlpha(); - method public final boolean hasMipMap(); - method public final boolean isMutable(); - method public final boolean isPremultiplied(); - method public final boolean isRecycled(); + method public int getWidth(); + method public boolean hasAlpha(); + method public boolean hasMipMap(); + method public boolean isMutable(); + method public boolean isPremultiplied(); + method public boolean isRecycled(); method public void prepareToDraw(); method public void reconfigure(int, int, android.graphics.Bitmap.Config); method public void recycle(); @@ -13085,11 +13085,11 @@ package android.graphics { method public void setConfig(android.graphics.Bitmap.Config); method public void setDensity(int); method public void setHasAlpha(boolean); - method public final void setHasMipMap(boolean); + method public void setHasMipMap(boolean); method public void setHeight(int); method public void setPixel(int, int, int); method public void setPixels(int[], int, int, int, int, int, int); - method public final void setPremultiplied(boolean); + method public void setPremultiplied(boolean); method public void setWidth(int); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.graphics.Bitmap> CREATOR; @@ -13161,7 +13161,7 @@ package android.graphics { method public android.graphics.Bitmap decodeRegion(android.graphics.Rect, android.graphics.BitmapFactory.Options); method public int getHeight(); method public int getWidth(); - method public final boolean isRecycled(); + method public boolean isRecycled(); method public static android.graphics.BitmapRegionDecoder newInstance(byte[], int, int, boolean) throws java.io.IOException; method public static android.graphics.BitmapRegionDecoder newInstance(java.io.FileDescriptor, boolean) throws java.io.IOException; method public static android.graphics.BitmapRegionDecoder newInstance(java.io.InputStream, boolean) throws java.io.IOException; @@ -14107,6 +14107,7 @@ package android.graphics { method public void endRecording(); method public int getHeight(); method public int getWidth(); + method public boolean requiresHardwareAcceleration(); method public deprecated void writeToStream(java.io.OutputStream); } @@ -14221,22 +14222,22 @@ package android.graphics { ctor public Rect(); ctor public Rect(int, int, int, int); ctor public Rect(android.graphics.Rect); - method public final int centerX(); - method public final int centerY(); + method public int centerX(); + method public int centerY(); method public boolean contains(int, int); method public boolean contains(int, int, int, int); method public boolean contains(android.graphics.Rect); method public int describeContents(); - method public final float exactCenterX(); - method public final float exactCenterY(); + method public float exactCenterX(); + method public float exactCenterY(); method public java.lang.String flattenToString(); - method public final int height(); + method public int height(); method public void inset(int, int); method public boolean intersect(int, int, int, int); method public boolean intersect(android.graphics.Rect); method public boolean intersects(int, int, int, int); method public static boolean intersects(android.graphics.Rect, android.graphics.Rect); - method public final boolean isEmpty(); + method public boolean isEmpty(); method public void offset(int, int); method public void offsetTo(int, int); method public void readFromParcel(android.os.Parcel); @@ -14250,7 +14251,7 @@ package android.graphics { method public void union(int, int, int, int); method public void union(android.graphics.Rect); method public void union(int, int); - method public final int width(); + method public int width(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.graphics.Rect> CREATOR; field public int bottom; @@ -15819,9 +15820,7 @@ package android.hardware.camera2 { } public static final class CameraCharacteristics.Key<T> { - method public final boolean equals(java.lang.Object); method public java.lang.String getName(); - method public final int hashCode(); } public abstract class CameraConstrainedHighSpeedCaptureSession extends android.hardware.camera2.CameraCaptureSession { @@ -16183,9 +16182,7 @@ package android.hardware.camera2 { } public static final class CaptureRequest.Key<T> { - method public final boolean equals(java.lang.Object); method public java.lang.String getName(); - method public final int hashCode(); } public class CaptureResult extends android.hardware.camera2.CameraMetadata { @@ -16278,9 +16275,7 @@ package android.hardware.camera2 { } public static final class CaptureResult.Key<T> { - method public final boolean equals(java.lang.Object); method public java.lang.String getName(); - method public final int hashCode(); } public final class DngCreator implements java.lang.AutoCloseable { @@ -16392,7 +16387,7 @@ package android.hardware.camera2.params { method public float getComponent(int); method public float getGreenEven(); method public float getGreenOdd(); - method public final float getRed(); + method public float getRed(); field public static final int BLUE = 3; // 0x3 field public static final int COUNT = 4; // 0x4 field public static final int GREEN_EVEN = 1; // 0x1 @@ -16420,16 +16415,16 @@ package android.hardware.camera2.params { method public android.util.Range<java.lang.Integer>[] getHighSpeedVideoFpsRangesFor(android.util.Size); method public android.util.Size[] getHighSpeedVideoSizes(); method public android.util.Size[] getHighSpeedVideoSizesFor(android.util.Range<java.lang.Integer>); - method public final int[] getInputFormats(); + method public int[] getInputFormats(); method public android.util.Size[] getInputSizes(int); - method public final int[] getOutputFormats(); + method public int[] getOutputFormats(); method public long getOutputMinFrameDuration(int, android.util.Size); method public <T> long getOutputMinFrameDuration(java.lang.Class<T>, android.util.Size); method public <T> android.util.Size[] getOutputSizes(java.lang.Class<T>); method public android.util.Size[] getOutputSizes(int); method public long getOutputStallDuration(int, android.util.Size); method public <T> long getOutputStallDuration(java.lang.Class<T>, android.util.Size); - method public final int[] getValidOutputFormatsForInput(int); + method public int[] getValidOutputFormatsForInput(int); method public boolean isOutputSupportedFor(int); method public static <T> boolean isOutputSupportedFor(java.lang.Class<T>); method public boolean isOutputSupportedFor(android.view.Surface); @@ -16494,8 +16489,37 @@ package android.hardware.display { package android.hardware.fingerprint { public class FingerprintDialog { - method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback); - method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback); + method public void authenticate(android.hardware.fingerprint.FingerprintDialog.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintDialog.AuthenticationCallback); + method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintDialog.AuthenticationCallback); + field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0 + field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3 + field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2 + field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1 + field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5 + field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4 + field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5 + field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc + field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1 + field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7 + field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9 + field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb + field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4 + field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3 + field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2 + field public static final int FINGERPRINT_ERROR_USER_CANCELED = 10; // 0xa + field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8 + } + + public static abstract class FingerprintDialog.AuthenticationCallback { + ctor public FingerprintDialog.AuthenticationCallback(); + method public void onAuthenticationError(int, java.lang.CharSequence); + method public void onAuthenticationFailed(); + method public void onAuthenticationHelp(int, java.lang.CharSequence); + method public void onAuthenticationSucceeded(android.hardware.fingerprint.FingerprintDialog.AuthenticationResult); + } + + public static class FingerprintDialog.AuthenticationResult { + method public android.hardware.fingerprint.FingerprintDialog.CryptoObject getCryptoObject(); } public static class FingerprintDialog.Builder { @@ -16507,10 +16531,19 @@ package android.hardware.fingerprint { method public android.hardware.fingerprint.FingerprintDialog.Builder setTitle(java.lang.CharSequence); } - public class FingerprintManager { - method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler); - method public boolean hasEnrolledFingerprints(); - method public boolean isHardwareDetected(); + public static final class FingerprintDialog.CryptoObject { + ctor public FingerprintDialog.CryptoObject(java.security.Signature); + ctor public FingerprintDialog.CryptoObject(javax.crypto.Cipher); + ctor public FingerprintDialog.CryptoObject(javax.crypto.Mac); + method public javax.crypto.Cipher getCipher(); + method public javax.crypto.Mac getMac(); + method public java.security.Signature getSignature(); + } + + public deprecated class FingerprintManager { + method public deprecated void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler); + method public deprecated boolean hasEnrolledFingerprints(); + method public deprecated boolean isHardwareDetected(); field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0 field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3 field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2 @@ -16518,9 +16551,11 @@ package android.hardware.fingerprint { field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5 field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4 field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5 + field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1 field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7 field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9 + field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4 field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3 field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2 @@ -16737,12 +16772,12 @@ package android.icu.lang { public final class UCharacter implements android.icu.lang.UCharacterEnums.ECharacterCategory android.icu.lang.UCharacterEnums.ECharacterDirection { method public static int charCount(int); - method public static final int codePointAt(java.lang.CharSequence, int); - method public static final int codePointAt(char[], int); - method public static final int codePointAt(char[], int, int); - method public static final int codePointBefore(java.lang.CharSequence, int); - method public static final int codePointBefore(char[], int); - method public static final int codePointBefore(char[], int, int); + method public static int codePointAt(java.lang.CharSequence, int); + method public static int codePointAt(char[], int); + method public static int codePointAt(char[], int, int); + method public static int codePointBefore(java.lang.CharSequence, int); + method public static int codePointBefore(char[], int); + method public static int codePointBefore(char[], int, int); method public static int codePointCount(java.lang.CharSequence, int, int); method public static int codePointCount(char[], int, int); method public static int digit(int, int); @@ -16750,7 +16785,7 @@ package android.icu.lang { method public static int foldCase(int, boolean); method public static java.lang.String foldCase(java.lang.String, boolean); method public static int foldCase(int, int); - method public static final java.lang.String foldCase(java.lang.String, int); + method public static java.lang.String foldCase(java.lang.String, int); method public static char forDigit(int, int); method public static android.icu.util.VersionInfo getAge(int); method public static int getBidiPairedBracket(int); @@ -16802,8 +16837,8 @@ package android.icu.lang { method public static boolean isPrintable(int); method public static boolean isSpaceChar(int); method public static boolean isSupplementary(int); - method public static final boolean isSupplementaryCodePoint(int); - method public static final boolean isSurrogatePair(char, char); + method public static boolean isSupplementaryCodePoint(int); + method public static boolean isSurrogatePair(char, char); method public static boolean isTitleCase(int); method public static boolean isUAlphabetic(int); method public static boolean isULowercase(int); @@ -16812,13 +16847,13 @@ package android.icu.lang { method public static boolean isUnicodeIdentifierPart(int); method public static boolean isUnicodeIdentifierStart(int); method public static boolean isUpperCase(int); - method public static final boolean isValidCodePoint(int); + method public static boolean isValidCodePoint(int); method public static boolean isWhitespace(int); method public static int offsetByCodePoints(java.lang.CharSequence, int, int); method public static int offsetByCodePoints(char[], int, int, int, int); - method public static final int toChars(int, char[], int); - method public static final char[] toChars(int); - method public static final int toCodePoint(char, char); + method public static int toChars(int, char[], int); + method public static char[] toChars(int); + method public static int toCodePoint(char, char); method public static int toLowerCase(int); method public static java.lang.String toLowerCase(java.lang.String); method public static java.lang.String toLowerCase(java.util.Locale, java.lang.String); @@ -17108,7 +17143,7 @@ package android.icu.lang { } public static final class UCharacter.UnicodeBlock extends java.lang.Character.Subset { - method public static final android.icu.lang.UCharacter.UnicodeBlock forName(java.lang.String); + method public static android.icu.lang.UCharacter.UnicodeBlock forName(java.lang.String); method public int getID(); method public static android.icu.lang.UCharacter.UnicodeBlock getInstance(int); method public static android.icu.lang.UCharacter.UnicodeBlock of(int); @@ -17915,20 +17950,20 @@ package android.icu.lang { } public final class UScript { - method public static final boolean breaksBetweenLetters(int); - method public static final int[] getCode(java.util.Locale); - method public static final int[] getCode(android.icu.util.ULocale); - method public static final int[] getCode(java.lang.String); - method public static final int getCodeFromName(java.lang.String); - method public static final java.lang.String getName(int); - method public static final java.lang.String getSampleString(int); - method public static final int getScript(int); - method public static final int getScriptExtensions(int, java.util.BitSet); - method public static final java.lang.String getShortName(int); - method public static final android.icu.lang.UScript.ScriptUsage getUsage(int); - method public static final boolean hasScript(int, int); - method public static final boolean isCased(int); - method public static final boolean isRightToLeft(int); + method public static boolean breaksBetweenLetters(int); + method public static int[] getCode(java.util.Locale); + method public static int[] getCode(android.icu.util.ULocale); + method public static int[] getCode(java.lang.String); + method public static int getCodeFromName(java.lang.String); + method public static java.lang.String getName(int); + method public static java.lang.String getSampleString(int); + method public static int getScript(int); + method public static int getScriptExtensions(int, java.util.BitSet); + method public static java.lang.String getShortName(int); + method public static android.icu.lang.UScript.ScriptUsage getUsage(int); + method public static boolean hasScript(int, int); + method public static boolean isCased(int); + method public static boolean isRightToLeft(int); field public static final int ADLAM = 167; // 0xa7 field public static final int AFAKA = 147; // 0x93 field public static final int AHOM = 161; // 0xa1 @@ -18343,14 +18378,14 @@ package android.icu.text { method public deprecated int hashCode(); method public int next(); method public int previous(); - method public static final int primaryOrder(int); + method public static int primaryOrder(int); method public void reset(); - method public static final int secondaryOrder(int); + method public static int secondaryOrder(int); method public void setOffset(int); method public void setText(java.lang.String); method public void setText(android.icu.text.UCharacterIterator); method public void setText(java.text.CharacterIterator); - method public static final int tertiaryOrder(int); + method public static int tertiaryOrder(int); field public static final int IGNORABLE = 0; // 0x0 field public static final int NULLORDER = -1; // 0xffffffff } @@ -19577,7 +19612,7 @@ package android.icu.text { method public boolean isUpperCaseFirst(); method public void setAlternateHandlingDefault(); method public void setAlternateHandlingShifted(boolean); - method public final void setCaseFirstDefault(); + method public void setCaseFirstDefault(); method public void setCaseLevel(boolean); method public void setCaseLevelDefault(); method public void setDecompositionDefault(); @@ -20550,11 +20585,11 @@ package android.icu.util { public final class LocaleData { method public static android.icu.util.VersionInfo getCLDRVersion(); method public java.lang.String getDelimiter(int); - method public static final android.icu.util.LocaleData getInstance(android.icu.util.ULocale); - method public static final android.icu.util.LocaleData getInstance(); - method public static final android.icu.util.LocaleData.MeasurementSystem getMeasurementSystem(android.icu.util.ULocale); + method public static android.icu.util.LocaleData getInstance(android.icu.util.ULocale); + method public static android.icu.util.LocaleData getInstance(); + method public static android.icu.util.LocaleData.MeasurementSystem getMeasurementSystem(android.icu.util.ULocale); method public boolean getNoSubstitute(); - method public static final android.icu.util.LocaleData.PaperSize getPaperSize(android.icu.util.ULocale); + method public static android.icu.util.LocaleData.PaperSize getPaperSize(android.icu.util.ULocale); method public void setNoSubstitute(boolean); field public static final int ALT_QUOTATION_END = 3; // 0x3 field public static final int ALT_QUOTATION_START = 2; // 0x2 @@ -21954,6 +21989,7 @@ package android.media { field public static final int ENCODING_DTS = 7; // 0x7 field public static final int ENCODING_DTS_HD = 8; // 0x8 field public static final int ENCODING_E_AC3 = 6; // 0x6 + field public static final int ENCODING_E_AC3_JOC = 18; // 0x12 field public static final int ENCODING_IEC61937 = 13; // 0xd field public static final int ENCODING_INVALID = 0; // 0x0 field public static final int ENCODING_MP3 = 9; // 0x9 @@ -21984,6 +22020,7 @@ package android.media { method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations(); method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations(); method public android.media.AudioDeviceInfo[] getDevices(int); + method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException; method public int getMode(); method public java.lang.String getParameters(java.lang.String); method public java.lang.String getProperty(java.lang.String); @@ -22157,11 +22194,26 @@ package android.media { field public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR; } + public final class AudioPresentation { + method public java.util.Map<java.util.Locale, java.lang.String> getLabels(); + method public java.util.Locale getLocale(); + method public int getMasteringIndication(); + method public boolean hasAudioDescription(); + method public boolean hasDialogueEnhancement(); + method public boolean hasSpokenSubtitles(); + field public static final int MASTERED_FOR_3D = 3; // 0x3 + field public static final int MASTERED_FOR_HEADPHONE = 4; // 0x4 + field public static final int MASTERED_FOR_STEREO = 1; // 0x1 + field public static final int MASTERED_FOR_SURROUND = 2; // 0x2 + field public static final int MASTERING_NOT_INDICATED = 0; // 0x0 + } + public class AudioRecord implements android.media.AudioRouting { ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException; method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler); method protected void finalize(); + method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException; method public int getAudioFormat(); method public int getAudioSessionId(); method public int getAudioSource(); @@ -22322,6 +22374,7 @@ package android.media { method public int setPlaybackRate(int); method public int setPositionNotificationPeriod(int); method public boolean setPreferredDevice(android.media.AudioDeviceInfo); + method public int setPresentation(android.media.AudioPresentation); method protected deprecated void setState(int); method public deprecated int setStereoVolume(float, float); method public void setStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback); @@ -22826,40 +22879,40 @@ package android.media { method public static android.media.MediaCodec createByCodecName(java.lang.String) throws java.io.IOException; method public static android.media.MediaCodec createDecoderByType(java.lang.String) throws java.io.IOException; method public static android.media.MediaCodec createEncoderByType(java.lang.String) throws java.io.IOException; - method public final android.view.Surface createInputSurface(); + method public android.view.Surface createInputSurface(); method public static android.view.Surface createPersistentInputSurface(); - method public final int dequeueInputBuffer(long); - method public final int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long); + method public int dequeueInputBuffer(long); + method public int dequeueOutputBuffer(android.media.MediaCodec.BufferInfo, long); method protected void finalize(); - method public final void flush(); + method public void flush(); method public android.media.MediaCodecInfo getCodecInfo(); method public java.nio.ByteBuffer getInputBuffer(int); method public deprecated java.nio.ByteBuffer[] getInputBuffers(); - method public final android.media.MediaFormat getInputFormat(); + method public android.media.MediaFormat getInputFormat(); method public android.media.Image getInputImage(int); method public android.os.PersistableBundle getMetrics(); - method public final java.lang.String getName(); + method public java.lang.String getName(); method public java.nio.ByteBuffer getOutputBuffer(int); method public deprecated java.nio.ByteBuffer[] getOutputBuffers(); - method public final android.media.MediaFormat getOutputFormat(); - method public final android.media.MediaFormat getOutputFormat(int); + method public android.media.MediaFormat getOutputFormat(); + method public android.media.MediaFormat getOutputFormat(int); method public android.media.Image getOutputImage(int); - method public final void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException; - method public final void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException; - method public final void release(); - method public final void releaseOutputBuffer(int, boolean); - method public final void releaseOutputBuffer(int, long); - method public final void reset(); + method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException; + method public void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException; + method public void release(); + method public void releaseOutputBuffer(int, boolean); + method public void releaseOutputBuffer(int, long); + method public void reset(); method public void setCallback(android.media.MediaCodec.Callback, android.os.Handler); method public void setCallback(android.media.MediaCodec.Callback); method public void setInputSurface(android.view.Surface); method public void setOnFrameRenderedListener(android.media.MediaCodec.OnFrameRenderedListener, android.os.Handler); method public void setOutputSurface(android.view.Surface); - method public final void setParameters(android.os.Bundle); - method public final void setVideoScalingMode(int); - method public final void signalEndOfInputStream(); - method public final void start(); - method public final void stop(); + method public void setParameters(android.os.Bundle); + method public void setVideoScalingMode(int); + method public void signalEndOfInputStream(); + method public void start(); + method public void stop(); field public static final int BUFFER_FLAG_CODEC_CONFIG = 2; // 0x2 field public static final int BUFFER_FLAG_END_OF_STREAM = 4; // 0x4 field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1 @@ -22953,10 +23006,10 @@ package android.media { } public final class MediaCodecInfo { - method public final android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String); - method public final java.lang.String getName(); - method public final java.lang.String[] getSupportedTypes(); - method public final boolean isEncoder(); + method public android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(java.lang.String); + method public java.lang.String getName(); + method public java.lang.String[] getSupportedTypes(); + method public boolean isEncoder(); } public static final class MediaCodecInfo.AudioCapabilities { @@ -22976,9 +23029,9 @@ package android.media { method public int getMaxSupportedInstances(); method public java.lang.String getMimeType(); method public android.media.MediaCodecInfo.VideoCapabilities getVideoCapabilities(); - method public final boolean isFeatureRequired(java.lang.String); - method public final boolean isFeatureSupported(java.lang.String); - method public final boolean isFormatSupported(android.media.MediaFormat); + method public boolean isFeatureRequired(java.lang.String); + method public boolean isFeatureSupported(java.lang.String); + method public boolean isFormatSupported(android.media.MediaFormat); field public static final deprecated int COLOR_Format12bitRGB444 = 3; // 0x3 field public static final deprecated int COLOR_Format16bitARGB1555 = 5; // 0x5 field public static final deprecated int COLOR_Format16bitARGB4444 = 4; // 0x4 @@ -23236,11 +23289,11 @@ package android.media { public final class MediaCodecList { ctor public MediaCodecList(int); - method public final java.lang.String findDecoderForFormat(android.media.MediaFormat); - method public final java.lang.String findEncoderForFormat(android.media.MediaFormat); - method public static final deprecated int getCodecCount(); - method public static final deprecated android.media.MediaCodecInfo getCodecInfoAt(int); - method public final android.media.MediaCodecInfo[] getCodecInfos(); + method public java.lang.String findDecoderForFormat(android.media.MediaFormat); + method public java.lang.String findEncoderForFormat(android.media.MediaFormat); + method public static deprecated int getCodecCount(); + method public static deprecated android.media.MediaCodecInfo getCodecInfoAt(int); + method public android.media.MediaCodecInfo[] getCodecInfos(); field public static final int ALL_CODECS = 1; // 0x1 field public static final int REGULAR_CODECS = 0; // 0x0 } @@ -23248,10 +23301,10 @@ package android.media { public final class MediaCrypto { ctor public MediaCrypto(java.util.UUID, byte[]) throws android.media.MediaCryptoException; method protected void finalize(); - method public static final boolean isCryptoSchemeSupported(java.util.UUID); - method public final void release(); - method public final boolean requiresSecureDecoderComponent(java.lang.String); - method public final void setMediaDrmSession(byte[]) throws android.media.MediaCryptoException; + method public static boolean isCryptoSchemeSupported(java.util.UUID); + method public void release(); + method public boolean requiresSecureDecoderComponent(java.lang.String); + method public void setMediaDrmSession(byte[]) throws android.media.MediaCryptoException; } public final class MediaCryptoException extends java.lang.Exception { @@ -23267,10 +23320,10 @@ package android.media { public final class MediaDescrambler implements java.lang.AutoCloseable { ctor public MediaDescrambler(int) throws android.media.MediaCasException.UnsupportedCasException; method public void close(); - method public final int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo); + method public int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo); method protected void finalize(); - method public final boolean requiresSecureDecoderComponent(java.lang.String); - method public final void setMediaCasSession(android.media.MediaCas.Session); + method public boolean requiresSecureDecoderComponent(java.lang.String); + method public void setMediaCasSession(android.media.MediaCas.Session); } public class MediaDescription implements android.os.Parcelable { @@ -23326,8 +23379,8 @@ package android.media { method public java.util.List<byte[]> getSecureStopIds(); method public java.util.List<byte[]> getSecureStops(); method public int getSecurityLevel(byte[]); - method public static final boolean isCryptoSchemeSupported(java.util.UUID); - method public static final boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String); + method public static boolean isCryptoSchemeSupported(java.util.UUID); + method public static boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String); method public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException; method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException; method public void provideProvisionResponse(byte[]) throws android.media.DeniedByServerException; @@ -23478,6 +23531,7 @@ package android.media { ctor public MediaExtractor(); method public boolean advance(); method protected void finalize(); + method public java.util.List<android.media.AudioPresentation> getAudioPresentations(int); method public long getCachedDuration(); method public android.media.MediaExtractor.CasInfo getCasInfo(int); method public android.media.DrmInitData getDrmInitData(); @@ -23488,21 +23542,21 @@ package android.media { method public long getSampleSize(); method public long getSampleTime(); method public int getSampleTrackIndex(); - method public final int getTrackCount(); + method public int getTrackCount(); method public android.media.MediaFormat getTrackFormat(int); method public boolean hasCacheReachedEndOfStream(); method public int readSampleData(java.nio.ByteBuffer, int); - method public final void release(); + method public void release(); method public void seekTo(long, int); method public void selectTrack(int); - method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException; - method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException; - method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException; - method public final void setDataSource(java.lang.String) throws java.io.IOException; - method public final void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException; - method public final void setDataSource(java.io.FileDescriptor) throws java.io.IOException; - method public final void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException; - method public final void setMediaCas(android.media.MediaCas); + method public void setDataSource(android.media.MediaDataSource) throws java.io.IOException; + method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException; + method public void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException; + method public void setDataSource(java.lang.String) throws java.io.IOException; + method public void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException; + method public void setDataSource(java.io.FileDescriptor) throws java.io.IOException; + method public void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException; + method public void setMediaCas(android.media.MediaCas); method public void unselectTrack(int); field public static final int SAMPLE_FLAG_ENCRYPTED = 2; // 0x2 field public static final int SAMPLE_FLAG_PARTIAL_FRAME = 4; // 0x4 @@ -23525,22 +23579,22 @@ package android.media { public final class MediaFormat { ctor public MediaFormat(); - method public final boolean containsKey(java.lang.String); - method public static final android.media.MediaFormat createAudioFormat(java.lang.String, int, int); - method public static final android.media.MediaFormat createSubtitleFormat(java.lang.String, java.lang.String); - method public static final android.media.MediaFormat createVideoFormat(java.lang.String, int, int); - method public final java.nio.ByteBuffer getByteBuffer(java.lang.String); + method public boolean containsKey(java.lang.String); + method public static android.media.MediaFormat createAudioFormat(java.lang.String, int, int); + method public static android.media.MediaFormat createSubtitleFormat(java.lang.String, java.lang.String); + method public static android.media.MediaFormat createVideoFormat(java.lang.String, int, int); + method public java.nio.ByteBuffer getByteBuffer(java.lang.String); method public boolean getFeatureEnabled(java.lang.String); - method public final float getFloat(java.lang.String); - method public final int getInteger(java.lang.String); - method public final long getLong(java.lang.String); - method public final java.lang.String getString(java.lang.String); - method public final void setByteBuffer(java.lang.String, java.nio.ByteBuffer); + method public float getFloat(java.lang.String); + method public int getInteger(java.lang.String); + method public long getLong(java.lang.String); + method public java.lang.String getString(java.lang.String); + method public void setByteBuffer(java.lang.String, java.nio.ByteBuffer); method public void setFeatureEnabled(java.lang.String, boolean); - method public final void setFloat(java.lang.String, float); - method public final void setInteger(java.lang.String, int); - method public final void setLong(java.lang.String, long); - method public final void setString(java.lang.String, java.lang.String); + method public void setFloat(java.lang.String, float); + method public void setInteger(java.lang.String, int); + method public void setLong(java.lang.String, long); + method public void setString(java.lang.String, java.lang.String); field public static final int COLOR_RANGE_FULL = 1; // 0x1 field public static final int COLOR_RANGE_LIMITED = 2; // 0x2 field public static final int COLOR_STANDARD_BT2020 = 6; // 0x6 @@ -24166,6 +24220,7 @@ package android.media { ctor public MediaRecorder(); method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); method protected void finalize(); + method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException; method public static final int getAudioSourceMax(); method public int getMaxAmplitude() throws java.lang.IllegalStateException; method public android.os.PersistableBundle getMetrics(); @@ -24439,14 +24494,14 @@ package android.media { public final class MediaSync { ctor public MediaSync(); - method public final android.view.Surface createInputSurface(); + method public android.view.Surface createInputSurface(); method protected void finalize(); method public void flush(); method public android.media.PlaybackParams getPlaybackParams(); method public android.media.SyncParams getSyncParams(); method public android.media.MediaTimestamp getTimestamp(); method public void queueAudio(java.nio.ByteBuffer, int, long); - method public final void release(); + method public void release(); method public void setAudioTrack(android.media.AudioTrack); method public void setCallback(android.media.MediaSync.Callback, android.os.Handler); method public void setOnErrorListener(android.media.MediaSync.OnErrorListener, android.os.Handler); @@ -24481,6 +24536,41 @@ package android.media { method public float getMediaClockRate(); } + public final class MicrophoneInfo { + method public java.util.List<android.util.Pair<java.lang.Integer, java.lang.Integer>> getChannelMapping(); + method public java.lang.String getDescription(); + method public int getDirectionality(); + method public java.util.List<android.util.Pair<java.lang.Float, java.lang.Float>> getFrequencyResponse(); + method public int getGroup(); + method public int getId(); + method public int getIndexInTheGroup(); + method public int getLocation(); + method public float getMaxSpl(); + method public float getMinSpl(); + method public android.media.MicrophoneInfo.Coordinate3F getOrientation(); + method public android.media.MicrophoneInfo.Coordinate3F getPosition(); + method public float getSensitivity(); + method public int getType(); + field public static final int CHANNEL_MAPPING_DIRECT = 1; // 0x1 + field public static final int CHANNEL_MAPPING_PROCESSED = 2; // 0x2 + field public static final int DIRECTIONALITY_BI_DIRECTIONAL = 2; // 0x2 + field public static final int DIRECTIONALITY_CARDIOID = 3; // 0x3 + field public static final int DIRECTIONALITY_HYPER_CARDIOID = 4; // 0x4 + field public static final int DIRECTIONALITY_OMNI = 1; // 0x1 + field public static final int DIRECTIONALITY_SUPER_CARDIOID = 5; // 0x5 + field public static final int DIRECTIONALITY_UNKNOWN = 0; // 0x0 + field public static final int LOCATION_MAINBODY = 1; // 0x1 + field public static final int LOCATION_MAINBODY_MOVABLE = 2; // 0x2 + field public static final int LOCATION_PERIPHERAL = 3; // 0x3 + field public static final int LOCATION_UNKNOWN = 0; // 0x0 + } + + public class MicrophoneInfo.Coordinate3F { + field public final float x; + field public final float y; + field public final float z; + } + public final class NotProvisionedException extends android.media.MediaDrmException { ctor public NotProvisionedException(java.lang.String); } @@ -25401,7 +25491,7 @@ package android.media.midi { public final class MidiInputPort extends android.media.midi.MidiReceiver implements java.io.Closeable { method public void close() throws java.io.IOException; - method public final int getPortNumber(); + method public int getPortNumber(); method public void onSend(byte[], int, int, long) throws java.io.IOException; } @@ -25426,7 +25516,7 @@ package android.media.midi { public final class MidiOutputPort extends android.media.midi.MidiSender implements java.io.Closeable { method public void close() throws java.io.IOException; - method public final int getPortNumber(); + method public int getPortNumber(); method public void onConnect(android.media.midi.MidiReceiver); method public void onDisconnect(android.media.midi.MidiReceiver); } @@ -25701,7 +25791,7 @@ package android.media.session { package android.media.tv { public final class TvContentRating { - method public final boolean contains(android.media.tv.TvContentRating); + method public boolean contains(android.media.tv.TvContentRating); method public static android.media.tv.TvContentRating createRating(java.lang.String, java.lang.String, java.lang.String, java.lang.String...); method public java.lang.String flattenToString(); method public java.lang.String getDomain(); @@ -25751,7 +25841,7 @@ package android.media.tv { } public static final class TvContract.Channels implements android.media.tv.TvContract.BaseTvColumns { - method public static final java.lang.String getVideoResolution(java.lang.String); + method public static java.lang.String getVideoResolution(java.lang.String); field public static final java.lang.String COLUMN_APP_LINK_COLOR = "app_link_color"; field public static final java.lang.String COLUMN_APP_LINK_ICON_URI = "app_link_icon_uri"; field public static final java.lang.String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; @@ -26276,18 +26366,18 @@ package android.media.tv { public final class TvTrackInfo implements android.os.Parcelable { method public int describeContents(); - method public final int getAudioChannelCount(); - method public final int getAudioSampleRate(); - method public final java.lang.CharSequence getDescription(); - method public final android.os.Bundle getExtra(); - method public final java.lang.String getId(); - method public final java.lang.String getLanguage(); - method public final int getType(); - method public final byte getVideoActiveFormatDescription(); - method public final float getVideoFrameRate(); - method public final int getVideoHeight(); - method public final float getVideoPixelAspectRatio(); - method public final int getVideoWidth(); + method public int getAudioChannelCount(); + method public int getAudioSampleRate(); + method public java.lang.CharSequence getDescription(); + method public android.os.Bundle getExtra(); + method public java.lang.String getId(); + method public java.lang.String getLanguage(); + method public int getType(); + method public byte getVideoActiveFormatDescription(); + method public float getVideoFrameRate(); + method public int getVideoHeight(); + method public float getVideoPixelAspectRatio(); + method public int getVideoWidth(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.media.tv.TvTrackInfo> CREATOR; field public static final int TYPE_AUDIO = 0; // 0x0 @@ -26298,16 +26388,16 @@ package android.media.tv { public static final class TvTrackInfo.Builder { ctor public TvTrackInfo.Builder(int, java.lang.String); method public android.media.tv.TvTrackInfo build(); - method public final android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int); - method public final android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int); - method public final android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence); - method public final android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle); - method public final android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String); - method public final android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte); - method public final android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float); - method public final android.media.tv.TvTrackInfo.Builder setVideoHeight(int); - method public final android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float); - method public final android.media.tv.TvTrackInfo.Builder setVideoWidth(int); + method public android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int); + method public android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int); + method public android.media.tv.TvTrackInfo.Builder setDescription(java.lang.CharSequence); + method public android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle); + method public android.media.tv.TvTrackInfo.Builder setLanguage(java.lang.String); + method public android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte); + method public android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float); + method public android.media.tv.TvTrackInfo.Builder setVideoHeight(int); + method public android.media.tv.TvTrackInfo.Builder setVideoPixelAspectRatio(float); + method public android.media.tv.TvTrackInfo.Builder setVideoWidth(int); } public class TvView extends android.view.ViewGroup { @@ -26538,34 +26628,34 @@ package android.mtp { } public final class MtpObjectInfo { - method public final int getAssociationDesc(); - method public final int getAssociationType(); - method public final int getCompressedSize(); - method public final long getCompressedSizeLong(); - method public final long getDateCreated(); - method public final long getDateModified(); - method public final int getFormat(); - method public final int getImagePixDepth(); - method public final long getImagePixDepthLong(); - method public final int getImagePixHeight(); - method public final long getImagePixHeightLong(); - method public final int getImagePixWidth(); - method public final long getImagePixWidthLong(); - method public final java.lang.String getKeywords(); - method public final java.lang.String getName(); - method public final int getObjectHandle(); - method public final int getParent(); - method public final int getProtectionStatus(); - method public final int getSequenceNumber(); - method public final long getSequenceNumberLong(); - method public final int getStorageId(); - method public final int getThumbCompressedSize(); - method public final long getThumbCompressedSizeLong(); - method public final int getThumbFormat(); - method public final int getThumbPixHeight(); - method public final long getThumbPixHeightLong(); - method public final int getThumbPixWidth(); - method public final long getThumbPixWidthLong(); + method public int getAssociationDesc(); + method public int getAssociationType(); + method public int getCompressedSize(); + method public long getCompressedSizeLong(); + method public long getDateCreated(); + method public long getDateModified(); + method public int getFormat(); + method public int getImagePixDepth(); + method public long getImagePixDepthLong(); + method public int getImagePixHeight(); + method public long getImagePixHeightLong(); + method public int getImagePixWidth(); + method public long getImagePixWidthLong(); + method public java.lang.String getKeywords(); + method public java.lang.String getName(); + method public int getObjectHandle(); + method public int getParent(); + method public int getProtectionStatus(); + method public int getSequenceNumber(); + method public long getSequenceNumberLong(); + method public int getStorageId(); + method public int getThumbCompressedSize(); + method public long getThumbCompressedSizeLong(); + method public int getThumbFormat(); + method public int getThumbPixHeight(); + method public long getThumbPixHeightLong(); + method public int getThumbPixWidth(); + method public long getThumbPixWidthLong(); } public static class MtpObjectInfo.Builder { @@ -26595,11 +26685,11 @@ package android.mtp { } public final class MtpStorageInfo { - method public final java.lang.String getDescription(); - method public final long getFreeSpace(); - method public final long getMaxCapacity(); - method public final int getStorageId(); - method public final java.lang.String getVolumeIdentifier(); + method public java.lang.String getDescription(); + method public long getFreeSpace(); + method public long getMaxCapacity(); + method public int getStorageId(); + method public java.lang.String getVolumeIdentifier(); } } @@ -27032,10 +27122,10 @@ package android.net { public final class Proxy { ctor public Proxy(); - method public static final deprecated java.lang.String getDefaultHost(); - method public static final deprecated int getDefaultPort(); - method public static final deprecated java.lang.String getHost(android.content.Context); - method public static final deprecated int getPort(android.content.Context); + method public static deprecated java.lang.String getDefaultHost(); + method public static deprecated int getDefaultPort(); + method public static deprecated java.lang.String getHost(android.content.Context); + method public static deprecated int getPort(android.content.Context); field public static final deprecated java.lang.String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO"; field public static final java.lang.String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE"; } @@ -32062,9 +32152,9 @@ package android.os { method public static void dumpHprofData(java.lang.String) throws java.io.IOException; method public static boolean dumpService(java.lang.String, java.io.FileDescriptor, java.lang.String[]); method public static void enableEmulatorTraceOutput(); - method public static final int getBinderDeathObjectCount(); - method public static final int getBinderLocalObjectCount(); - method public static final int getBinderProxyObjectCount(); + method public static int getBinderDeathObjectCount(); + method public static int getBinderLocalObjectCount(); + method public static int getBinderProxyObjectCount(); method public static int getBinderReceivedTransactions(); method public static int getBinderSentTransactions(); method public static deprecated int getGlobalAllocCount(); @@ -32473,114 +32563,114 @@ package android.os { } public final class Parcel { - method public final void appendFrom(android.os.Parcel, int, int); - method public final android.os.IBinder[] createBinderArray(); - method public final java.util.ArrayList<android.os.IBinder> createBinderArrayList(); - method public final boolean[] createBooleanArray(); - method public final byte[] createByteArray(); - method public final char[] createCharArray(); - method public final double[] createDoubleArray(); - method public final float[] createFloatArray(); - method public final int[] createIntArray(); - method public final long[] createLongArray(); - method public final java.lang.String[] createStringArray(); - method public final java.util.ArrayList<java.lang.String> createStringArrayList(); - method public final <T> T[] createTypedArray(android.os.Parcelable.Creator<T>); - method public final <T> java.util.ArrayList<T> createTypedArrayList(android.os.Parcelable.Creator<T>); - method public final int dataAvail(); - method public final int dataCapacity(); - method public final int dataPosition(); - method public final int dataSize(); - method public final void enforceInterface(java.lang.String); - method public final boolean hasFileDescriptors(); - method public final byte[] marshall(); + method public void appendFrom(android.os.Parcel, int, int); + method public android.os.IBinder[] createBinderArray(); + method public java.util.ArrayList<android.os.IBinder> createBinderArrayList(); + method public boolean[] createBooleanArray(); + method public byte[] createByteArray(); + method public char[] createCharArray(); + method public double[] createDoubleArray(); + method public float[] createFloatArray(); + method public int[] createIntArray(); + method public long[] createLongArray(); + method public java.lang.String[] createStringArray(); + method public java.util.ArrayList<java.lang.String> createStringArrayList(); + method public <T> T[] createTypedArray(android.os.Parcelable.Creator<T>); + method public <T> java.util.ArrayList<T> createTypedArrayList(android.os.Parcelable.Creator<T>); + method public int dataAvail(); + method public int dataCapacity(); + method public int dataPosition(); + method public int dataSize(); + method public void enforceInterface(java.lang.String); + method public boolean hasFileDescriptors(); + method public byte[] marshall(); method public static android.os.Parcel obtain(); - method public final java.lang.Object[] readArray(java.lang.ClassLoader); - method public final java.util.ArrayList readArrayList(java.lang.ClassLoader); - method public final void readBinderArray(android.os.IBinder[]); - method public final void readBinderList(java.util.List<android.os.IBinder>); - method public final void readBooleanArray(boolean[]); - method public final android.os.Bundle readBundle(); - method public final android.os.Bundle readBundle(java.lang.ClassLoader); - method public final byte readByte(); - method public final void readByteArray(byte[]); - method public final void readCharArray(char[]); - method public final double readDouble(); - method public final void readDoubleArray(double[]); - method public final void readException(); - method public final void readException(int, java.lang.String); - method public final android.os.ParcelFileDescriptor readFileDescriptor(); - method public final float readFloat(); - method public final void readFloatArray(float[]); - method public final java.util.HashMap readHashMap(java.lang.ClassLoader); - method public final int readInt(); - method public final void readIntArray(int[]); - method public final void readList(java.util.List, java.lang.ClassLoader); - method public final long readLong(); - method public final void readLongArray(long[]); - method public final void readMap(java.util.Map, java.lang.ClassLoader); - method public final <T extends android.os.Parcelable> T readParcelable(java.lang.ClassLoader); - method public final android.os.Parcelable[] readParcelableArray(java.lang.ClassLoader); - method public final android.os.PersistableBundle readPersistableBundle(); - method public final android.os.PersistableBundle readPersistableBundle(java.lang.ClassLoader); - method public final java.io.Serializable readSerializable(); - method public final android.util.Size readSize(); - method public final android.util.SizeF readSizeF(); - method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader); - method public final android.util.SparseBooleanArray readSparseBooleanArray(); - method public final java.lang.String readString(); - method public final void readStringArray(java.lang.String[]); - method public final void readStringList(java.util.List<java.lang.String>); - method public final android.os.IBinder readStrongBinder(); - method public final <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>); - method public final <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>); - method public final <T> T readTypedObject(android.os.Parcelable.Creator<T>); - method public final java.lang.Object readValue(java.lang.ClassLoader); - method public final void recycle(); - method public final void setDataCapacity(int); - method public final void setDataPosition(int); - method public final void setDataSize(int); - method public final void unmarshall(byte[], int, int); - method public final void writeArray(java.lang.Object[]); - method public final void writeBinderArray(android.os.IBinder[]); - method public final void writeBinderList(java.util.List<android.os.IBinder>); - method public final void writeBooleanArray(boolean[]); - method public final void writeBundle(android.os.Bundle); - method public final void writeByte(byte); - method public final void writeByteArray(byte[]); - method public final void writeByteArray(byte[], int, int); - method public final void writeCharArray(char[]); - method public final void writeDouble(double); - method public final void writeDoubleArray(double[]); - method public final void writeException(java.lang.Exception); - method public final void writeFileDescriptor(java.io.FileDescriptor); - method public final void writeFloat(float); - method public final void writeFloatArray(float[]); - method public final void writeInt(int); - method public final void writeIntArray(int[]); - method public final void writeInterfaceToken(java.lang.String); - method public final void writeList(java.util.List); - method public final void writeLong(long); - method public final void writeLongArray(long[]); - method public final void writeMap(java.util.Map); - method public final void writeNoException(); - method public final void writeParcelable(android.os.Parcelable, int); - method public final <T extends android.os.Parcelable> void writeParcelableArray(T[], int); - method public final void writePersistableBundle(android.os.PersistableBundle); - method public final void writeSerializable(java.io.Serializable); - method public final void writeSize(android.util.Size); - method public final void writeSizeF(android.util.SizeF); - method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>); - method public final void writeSparseBooleanArray(android.util.SparseBooleanArray); - method public final void writeString(java.lang.String); - method public final void writeStringArray(java.lang.String[]); - method public final void writeStringList(java.util.List<java.lang.String>); - method public final void writeStrongBinder(android.os.IBinder); - method public final void writeStrongInterface(android.os.IInterface); - method public final <T extends android.os.Parcelable> void writeTypedArray(T[], int); - method public final <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>); - method public final <T extends android.os.Parcelable> void writeTypedObject(T, int); - method public final void writeValue(java.lang.Object); + method public java.lang.Object[] readArray(java.lang.ClassLoader); + method public java.util.ArrayList readArrayList(java.lang.ClassLoader); + method public void readBinderArray(android.os.IBinder[]); + method public void readBinderList(java.util.List<android.os.IBinder>); + method public void readBooleanArray(boolean[]); + method public android.os.Bundle readBundle(); + method public android.os.Bundle readBundle(java.lang.ClassLoader); + method public byte readByte(); + method public void readByteArray(byte[]); + method public void readCharArray(char[]); + method public double readDouble(); + method public void readDoubleArray(double[]); + method public void readException(); + method public void readException(int, java.lang.String); + method public android.os.ParcelFileDescriptor readFileDescriptor(); + method public float readFloat(); + method public void readFloatArray(float[]); + method public java.util.HashMap readHashMap(java.lang.ClassLoader); + method public int readInt(); + method public void readIntArray(int[]); + method public void readList(java.util.List, java.lang.ClassLoader); + method public long readLong(); + method public void readLongArray(long[]); + method public void readMap(java.util.Map, java.lang.ClassLoader); + method public <T extends android.os.Parcelable> T readParcelable(java.lang.ClassLoader); + method public android.os.Parcelable[] readParcelableArray(java.lang.ClassLoader); + method public android.os.PersistableBundle readPersistableBundle(); + method public android.os.PersistableBundle readPersistableBundle(java.lang.ClassLoader); + method public java.io.Serializable readSerializable(); + method public android.util.Size readSize(); + method public android.util.SizeF readSizeF(); + method public android.util.SparseArray readSparseArray(java.lang.ClassLoader); + method public android.util.SparseBooleanArray readSparseBooleanArray(); + method public java.lang.String readString(); + method public void readStringArray(java.lang.String[]); + method public void readStringList(java.util.List<java.lang.String>); + method public android.os.IBinder readStrongBinder(); + method public <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>); + method public <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>); + method public <T> T readTypedObject(android.os.Parcelable.Creator<T>); + method public java.lang.Object readValue(java.lang.ClassLoader); + method public void recycle(); + method public void setDataCapacity(int); + method public void setDataPosition(int); + method public void setDataSize(int); + method public void unmarshall(byte[], int, int); + method public void writeArray(java.lang.Object[]); + method public void writeBinderArray(android.os.IBinder[]); + method public void writeBinderList(java.util.List<android.os.IBinder>); + method public void writeBooleanArray(boolean[]); + method public void writeBundle(android.os.Bundle); + method public void writeByte(byte); + method public void writeByteArray(byte[]); + method public void writeByteArray(byte[], int, int); + method public void writeCharArray(char[]); + method public void writeDouble(double); + method public void writeDoubleArray(double[]); + method public void writeException(java.lang.Exception); + method public void writeFileDescriptor(java.io.FileDescriptor); + method public void writeFloat(float); + method public void writeFloatArray(float[]); + method public void writeInt(int); + method public void writeIntArray(int[]); + method public void writeInterfaceToken(java.lang.String); + method public void writeList(java.util.List); + method public void writeLong(long); + method public void writeLongArray(long[]); + method public void writeMap(java.util.Map); + method public void writeNoException(); + method public void writeParcelable(android.os.Parcelable, int); + method public <T extends android.os.Parcelable> void writeParcelableArray(T[], int); + method public void writePersistableBundle(android.os.PersistableBundle); + method public void writeSerializable(java.io.Serializable); + method public void writeSize(android.util.Size); + method public void writeSizeF(android.util.SizeF); + method public void writeSparseArray(android.util.SparseArray<java.lang.Object>); + method public void writeSparseBooleanArray(android.util.SparseBooleanArray); + method public void writeString(java.lang.String); + method public void writeStringArray(java.lang.String[]); + method public void writeStringList(java.util.List<java.lang.String>); + method public void writeStrongBinder(android.os.IBinder); + method public void writeStrongInterface(android.os.IInterface); + method public <T extends android.os.Parcelable> void writeTypedArray(T[], int); + method public <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>); + method public <T extends android.os.Parcelable> void writeTypedObject(T, int); + method public void writeValue(java.lang.Object); field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR; } @@ -33025,7 +33115,7 @@ package android.os { field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials"; field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time"; field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale"; - field public static final java.lang.String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode"; + field public static final java.lang.String DISALLOW_CONFIG_LOCATION = "no_config_location"; field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks"; field public static final java.lang.String DISALLOW_CONFIG_SCREEN_TIMEOUT = "no_config_screen_timeout"; field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering"; @@ -34234,7 +34324,7 @@ package android.provider { } public static final class CalendarContract.Attendees implements android.provider.BaseColumns android.provider.CalendarContract.AttendeesColumns android.provider.CalendarContract.EventsColumns { - method public static final android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]); + method public static android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]); field public static final android.net.Uri CONTENT_URI; } @@ -34363,7 +34453,7 @@ package android.provider { } public static final class CalendarContract.EventDays implements android.provider.CalendarContract.EventDaysColumns { - method public static final android.database.Cursor query(android.content.ContentResolver, int, int, java.lang.String[]); + method public static android.database.Cursor query(android.content.ContentResolver, int, int, java.lang.String[]); field public static final android.net.Uri CONTENT_URI; } @@ -34456,8 +34546,8 @@ package android.provider { } public static final class CalendarContract.Instances implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.EventsColumns { - method public static final android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long); - method public static final android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long, java.lang.String); + method public static android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long); + method public static android.database.Cursor query(android.content.ContentResolver, java.lang.String[], long, long, java.lang.String); field public static final java.lang.String BEGIN = "begin"; field public static final android.net.Uri CONTENT_BY_DAY_URI; field public static final android.net.Uri CONTENT_SEARCH_BY_DAY_URI; @@ -34472,7 +34562,7 @@ package android.provider { } public static final class CalendarContract.Reminders implements android.provider.BaseColumns android.provider.CalendarContract.EventsColumns android.provider.CalendarContract.RemindersColumns { - method public static final android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]); + method public static android.database.Cursor query(android.content.ContentResolver, long, java.lang.String[]); field public static final android.net.Uri CONTENT_URI; } @@ -34581,7 +34671,7 @@ package android.provider { method public static deprecated java.lang.Object decodeImProtocol(java.lang.String); method public static deprecated java.lang.String encodeCustomImProtocol(java.lang.String); method public static deprecated java.lang.String encodePredefinedImProtocol(int); - method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, int, java.lang.CharSequence); + method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, int, java.lang.CharSequence); field public static final deprecated java.lang.String CONTENT_EMAIL_ITEM_TYPE = "vnd.android.cursor.item/email"; field public static final deprecated java.lang.String CONTENT_EMAIL_TYPE = "vnd.android.cursor.dir/email"; field public static final deprecated android.net.Uri CONTENT_EMAIL_URI; @@ -34731,7 +34821,7 @@ package android.provider { } public static final deprecated class Contacts.Organizations implements android.provider.BaseColumns android.provider.Contacts.OrganizationColumns { - method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence); + method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence); field public static final deprecated java.lang.String CONTENT_DIRECTORY = "organizations"; field public static final deprecated android.net.Uri CONTENT_URI; field public static final deprecated java.lang.String DEFAULT_SORT_ORDER = "company, title, isprimary ASC"; @@ -34788,8 +34878,8 @@ package android.provider { } public static final deprecated class Contacts.Phones implements android.provider.BaseColumns android.provider.Contacts.PeopleColumns android.provider.Contacts.PhonesColumns { - method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence, java.lang.CharSequence[]); - method public static final deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence); + method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence, java.lang.CharSequence[]); + method public static deprecated java.lang.CharSequence getDisplayLabel(android.content.Context, int, java.lang.CharSequence); field public static final deprecated android.net.Uri CONTENT_FILTER_URL; field public static final deprecated java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone"; field public static final deprecated java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/phone"; @@ -34929,8 +35019,8 @@ package android.provider { } public static final class ContactsContract.CommonDataKinds.Email implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins { - method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); - method public static final int getTypeLabelResource(int); + method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); + method public static int getTypeLabelResource(int); field public static final java.lang.String ADDRESS = "data1"; field public static final android.net.Uri CONTENT_FILTER_URI; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/email_v2"; @@ -34950,7 +35040,7 @@ package android.provider { } public static final class ContactsContract.CommonDataKinds.Event implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins { - method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); + method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); method public static int getTypeResource(java.lang.Integer); field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_event"; field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX"; @@ -34981,10 +35071,10 @@ package android.provider { } public static final class ContactsContract.CommonDataKinds.Im implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins { - method public static final java.lang.CharSequence getProtocolLabel(android.content.res.Resources, int, java.lang.CharSequence); - method public static final int getProtocolLabelResource(int); - method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); - method public static final int getTypeLabelResource(int); + method public static java.lang.CharSequence getProtocolLabel(android.content.res.Resources, int, java.lang.CharSequence); + method public static int getProtocolLabelResource(int); + method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); + method public static int getTypeLabelResource(int); field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im"; field public static final java.lang.String CUSTOM_PROTOCOL = "data6"; field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX"; @@ -35029,8 +35119,8 @@ package android.provider { } public static final class ContactsContract.CommonDataKinds.Organization implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins { - method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); - method public static final int getTypeLabelResource(int); + method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); + method public static int getTypeLabelResource(int); field public static final java.lang.String COMPANY = "data1"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization"; field public static final java.lang.String DEPARTMENT = "data5"; @@ -35048,8 +35138,8 @@ package android.provider { } public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins { - method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); - method public static final int getTypeLabelResource(int); + method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); + method public static int getTypeLabelResource(int); field public static final android.net.Uri CONTENT_FILTER_URI; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone_v2"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/phone_v2"; @@ -35094,8 +35184,8 @@ package android.provider { } public static final class ContactsContract.CommonDataKinds.Relation implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins { - method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); - method public static final int getTypeLabelResource(int); + method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); + method public static int getTypeLabelResource(int); field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/relation"; field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX"; field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS"; @@ -35118,8 +35208,8 @@ package android.provider { } public static final class ContactsContract.CommonDataKinds.SipAddress implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins { - method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); - method public static final int getTypeLabelResource(int); + method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); + method public static int getTypeLabelResource(int); field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address"; field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX"; field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS"; @@ -35149,8 +35239,8 @@ package android.provider { } public static final class ContactsContract.CommonDataKinds.StructuredPostal implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins { - method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); - method public static final int getTypeLabelResource(int); + method public static java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence); + method public static int getTypeLabelResource(int); field public static final java.lang.String CITY = "data7"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/postal-address_v2"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/postal-address_v2"; @@ -35977,7 +36067,7 @@ package android.provider { public static final class MediaStore.Audio.Artists.Albums implements android.provider.MediaStore.Audio.AlbumColumns { ctor public MediaStore.Audio.Artists.Albums(); - method public static final android.net.Uri getContentUri(java.lang.String, long); + method public static android.net.Uri getContentUri(java.lang.String, long); } public static abstract interface MediaStore.Audio.AudioColumns implements android.provider.MediaStore.MediaColumns { @@ -36013,7 +36103,7 @@ package android.provider { public static final class MediaStore.Audio.Genres.Members implements android.provider.MediaStore.Audio.AudioColumns { ctor public MediaStore.Audio.Genres.Members(); - method public static final android.net.Uri getContentUri(java.lang.String, long); + method public static android.net.Uri getContentUri(java.lang.String, long); field public static final java.lang.String AUDIO_ID = "audio_id"; field public static final java.lang.String CONTENT_DIRECTORY = "members"; field public static final java.lang.String DEFAULT_SORT_ORDER = "title_key"; @@ -36049,8 +36139,8 @@ package android.provider { public static final class MediaStore.Audio.Playlists.Members implements android.provider.MediaStore.Audio.AudioColumns { ctor public MediaStore.Audio.Playlists.Members(); - method public static final android.net.Uri getContentUri(java.lang.String, long); - method public static final boolean moveItem(android.content.ContentResolver, long, int, int); + method public static android.net.Uri getContentUri(java.lang.String, long); + method public static boolean moveItem(android.content.ContentResolver, long, int, int); field public static final java.lang.String AUDIO_ID = "audio_id"; field public static final java.lang.String CONTENT_DIRECTORY = "members"; field public static final java.lang.String DEFAULT_SORT_ORDER = "play_order"; @@ -36073,7 +36163,7 @@ package android.provider { public static final class MediaStore.Files { ctor public MediaStore.Files(); method public static android.net.Uri getContentUri(java.lang.String); - method public static final android.net.Uri getContentUri(java.lang.String, long); + method public static android.net.Uri getContentUri(java.lang.String, long); } public static abstract interface MediaStore.Files.FileColumns implements android.provider.MediaStore.MediaColumns { @@ -36107,13 +36197,13 @@ package android.provider { public static final class MediaStore.Images.Media implements android.provider.MediaStore.Images.ImageColumns { ctor public MediaStore.Images.Media(); - method public static final android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException; + method public static android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException; method public static android.net.Uri getContentUri(java.lang.String); - method public static final java.lang.String insertImage(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String) throws java.io.FileNotFoundException; - method public static final java.lang.String insertImage(android.content.ContentResolver, android.graphics.Bitmap, java.lang.String, java.lang.String); - method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]); - method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String); - method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); + method public static java.lang.String insertImage(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String) throws java.io.FileNotFoundException; + method public static java.lang.String insertImage(android.content.ContentResolver, android.graphics.Bitmap, java.lang.String, java.lang.String); + method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]); + method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String); + method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/image"; field public static final java.lang.String DEFAULT_SORT_ORDER = "bucket_display_name"; field public static final android.net.Uri EXTERNAL_CONTENT_URI; @@ -36158,7 +36248,7 @@ package android.provider { public static final class MediaStore.Video { ctor public MediaStore.Video(); - method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]); + method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]); field public static final java.lang.String DEFAULT_SORT_ORDER = "_display_name"; } @@ -36390,12 +36480,12 @@ package android.provider { method public static long getLong(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException; method public static java.lang.String getString(android.content.ContentResolver, java.lang.String); method public static android.net.Uri getUriFor(java.lang.String); - method public static final deprecated boolean isLocationProviderEnabled(android.content.ContentResolver, java.lang.String); + method public static deprecated boolean isLocationProviderEnabled(android.content.ContentResolver, java.lang.String); method public static boolean putFloat(android.content.ContentResolver, java.lang.String, float); method public static boolean putInt(android.content.ContentResolver, java.lang.String, int); method public static boolean putLong(android.content.ContentResolver, java.lang.String, long); method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String); - method public static final deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean); + method public static deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean); field public static final java.lang.String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled"; field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled"; field public static final deprecated java.lang.String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password"; @@ -38198,6 +38288,37 @@ package android.security { method public java.security.KeyPair getKeyPair(); } + public class ConfirmationAlreadyPresentingException extends java.lang.Exception { + ctor public ConfirmationAlreadyPresentingException(); + ctor public ConfirmationAlreadyPresentingException(java.lang.String); + } + + public abstract class ConfirmationCallback { + ctor public ConfirmationCallback(); + method public void onConfirmedByUser(byte[]); + method public void onDismissedByApplication(); + method public void onDismissedByUser(); + method public void onError(java.lang.Exception); + } + + public class ConfirmationDialog { + method public void cancelPrompt(); + method public static boolean isSupported(); + method public void presentPrompt(java.util.concurrent.Executor, android.security.ConfirmationCallback) throws android.security.ConfirmationAlreadyPresentingException, android.security.ConfirmationNotAvailableException; + } + + public static class ConfirmationDialog.Builder { + ctor public ConfirmationDialog.Builder(); + method public android.security.ConfirmationDialog build(android.content.Context); + method public android.security.ConfirmationDialog.Builder setExtraData(byte[]); + method public android.security.ConfirmationDialog.Builder setPromptText(java.lang.CharSequence); + } + + public class ConfirmationNotAvailableException extends java.lang.Exception { + ctor public ConfirmationNotAvailableException(); + ctor public ConfirmationNotAvailableException(java.lang.String); + } + public final class KeyChain { ctor public KeyChain(); method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String); @@ -38307,6 +38428,7 @@ package android.security.keystore { method public boolean isTrustedUserPresenceRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); + method public boolean isUserConfirmationRequired(); } public static final class KeyGenParameterSpec.Builder { @@ -38334,6 +38456,7 @@ package android.security.keystore { method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int); + method public android.security.keystore.KeyGenParameterSpec.Builder setUserConfirmationRequired(boolean); } public class KeyInfo implements java.security.spec.KeySpec { @@ -38355,6 +38478,7 @@ package android.security.keystore { method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware(); method public boolean isUserAuthenticationValidWhileOnBody(); + method public boolean isUserConfirmationRequired(); } public class KeyNotYetValidException extends java.security.InvalidKeyException { @@ -38422,6 +38546,7 @@ package android.security.keystore { method public boolean isRandomizedEncryptionRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); + method public boolean isUserConfirmationRequired(); } public static final class KeyProtection.Builder { @@ -38440,6 +38565,7 @@ package android.security.keystore { method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int); + method public android.security.keystore.KeyProtection.Builder setUserConfirmationRequired(boolean); } public class StrongBoxUnavailableException extends java.security.ProviderException { @@ -40391,12 +40517,12 @@ package android.telecom { method public void playDtmfTone(char); method public void postDialContinue(boolean); method public void pullExternalCall(); - method public final void putExtras(android.os.Bundle); + method public void putExtras(android.os.Bundle); method public void registerCallback(android.telecom.Call.Callback); method public void registerCallback(android.telecom.Call.Callback, android.os.Handler); method public void reject(boolean, java.lang.String); - method public final void removeExtras(java.util.List<java.lang.String>); - method public final void removeExtras(java.lang.String...); + method public void removeExtras(java.util.List<java.lang.String>); + method public void removeExtras(java.lang.String...); method public void respondToRttRequest(int, boolean); method public void sendCallEvent(java.lang.String, android.os.Bundle); method public void sendRttRequest(); @@ -40965,23 +41091,23 @@ package android.telecom { public final class RemoteConference { method public void disconnect(); method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections(); - method public final int getConnectionCapabilities(); - method public final int getConnectionProperties(); - method public final java.util.List<android.telecom.RemoteConnection> getConnections(); + method public int getConnectionCapabilities(); + method public int getConnectionProperties(); + method public java.util.List<android.telecom.RemoteConnection> getConnections(); method public android.telecom.DisconnectCause getDisconnectCause(); - method public final android.os.Bundle getExtras(); - method public final int getState(); + method public android.os.Bundle getExtras(); + method public int getState(); method public void hold(); method public void merge(); method public void playDtmfTone(char); - method public final void registerCallback(android.telecom.RemoteConference.Callback); - method public final void registerCallback(android.telecom.RemoteConference.Callback, android.os.Handler); + method public void registerCallback(android.telecom.RemoteConference.Callback); + method public void registerCallback(android.telecom.RemoteConference.Callback, android.os.Handler); method public void separate(android.telecom.RemoteConnection); method public void setCallAudioState(android.telecom.CallAudioState); method public void stopDtmfTone(); method public void swap(); method public void unhold(); - method public final void unregisterCallback(android.telecom.RemoteConference.Callback); + method public void unregisterCallback(android.telecom.RemoteConference.Callback); } public static abstract class RemoteConference.Callback { @@ -41010,10 +41136,10 @@ package android.telecom { method public int getConnectionCapabilities(); method public int getConnectionProperties(); method public android.telecom.DisconnectCause getDisconnectCause(); - method public final android.os.Bundle getExtras(); + method public android.os.Bundle getExtras(); method public int getState(); method public android.telecom.StatusHints getStatusHints(); - method public final android.telecom.RemoteConnection.VideoProvider getVideoProvider(); + method public android.telecom.RemoteConnection.VideoProvider getVideoProvider(); method public int getVideoState(); method public void hold(); method public boolean isRingbackRequested(); @@ -42466,11 +42592,11 @@ package android.telephony.gsm { } public final deprecated class SmsManager { - method public final deprecated java.util.ArrayList<java.lang.String> divideMessage(java.lang.String); - method public static final deprecated android.telephony.gsm.SmsManager getDefault(); - method public final deprecated void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent); - method public final deprecated void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>); - method public final deprecated void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent); + method public deprecated java.util.ArrayList<java.lang.String> divideMessage(java.lang.String); + method public static deprecated android.telephony.gsm.SmsManager getDefault(); + method public deprecated void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent); + method public deprecated void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>); + method public deprecated void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent); field public static final deprecated int RESULT_ERROR_GENERIC_FAILURE = 1; // 0x1 field public static final deprecated int RESULT_ERROR_NO_SERVICE = 4; // 0x4 field public static final deprecated int RESULT_ERROR_NULL_PDU = 3; // 0x3 @@ -46529,76 +46655,76 @@ package android.view { public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable { method public static java.lang.String actionToString(int); - method public final void addBatch(long, float, float, float, float, int); - method public final void addBatch(long, android.view.MotionEvent.PointerCoords[], int); + method public void addBatch(long, float, float, float, float, int); + method public void addBatch(long, android.view.MotionEvent.PointerCoords[], int); method public static int axisFromString(java.lang.String); method public static java.lang.String axisToString(int); - method public final int findPointerIndex(int); - method public final int getAction(); - method public final int getActionButton(); - method public final int getActionIndex(); - method public final int getActionMasked(); - method public final float getAxisValue(int); - method public final float getAxisValue(int, int); - method public final int getButtonState(); - method public final int getDeviceId(); - method public final long getDownTime(); - method public final int getEdgeFlags(); - method public final long getEventTime(); - method public final int getFlags(); - method public final float getHistoricalAxisValue(int, int); - method public final float getHistoricalAxisValue(int, int, int); - method public final long getHistoricalEventTime(int); - method public final float getHistoricalOrientation(int); - method public final float getHistoricalOrientation(int, int); - method public final void getHistoricalPointerCoords(int, int, android.view.MotionEvent.PointerCoords); - method public final float getHistoricalPressure(int); - method public final float getHistoricalPressure(int, int); - method public final float getHistoricalSize(int); - method public final float getHistoricalSize(int, int); - method public final float getHistoricalToolMajor(int); - method public final float getHistoricalToolMajor(int, int); - method public final float getHistoricalToolMinor(int); - method public final float getHistoricalToolMinor(int, int); - method public final float getHistoricalTouchMajor(int); - method public final float getHistoricalTouchMajor(int, int); - method public final float getHistoricalTouchMinor(int); - method public final float getHistoricalTouchMinor(int, int); - method public final float getHistoricalX(int); - method public final float getHistoricalX(int, int); - method public final float getHistoricalY(int); - method public final float getHistoricalY(int, int); - method public final int getHistorySize(); - method public final int getMetaState(); - method public final float getOrientation(); - method public final float getOrientation(int); - method public final void getPointerCoords(int, android.view.MotionEvent.PointerCoords); - method public final int getPointerCount(); - method public final int getPointerId(int); - method public final void getPointerProperties(int, android.view.MotionEvent.PointerProperties); - method public final float getPressure(); - method public final float getPressure(int); - method public final float getRawX(); - method public final float getRawY(); - method public final float getSize(); - method public final float getSize(int); - method public final int getSource(); - method public final float getToolMajor(); - method public final float getToolMajor(int); - method public final float getToolMinor(); - method public final float getToolMinor(int); - method public final int getToolType(int); - method public final float getTouchMajor(); - method public final float getTouchMajor(int); - method public final float getTouchMinor(); - method public final float getTouchMinor(int); - method public final float getX(); - method public final float getX(int); - method public final float getXPrecision(); - method public final float getY(); - method public final float getY(int); - method public final float getYPrecision(); - method public final boolean isButtonPressed(int); + method public int findPointerIndex(int); + method public int getAction(); + method public int getActionButton(); + method public int getActionIndex(); + method public int getActionMasked(); + method public float getAxisValue(int); + method public float getAxisValue(int, int); + method public int getButtonState(); + method public int getDeviceId(); + method public long getDownTime(); + method public int getEdgeFlags(); + method public long getEventTime(); + method public int getFlags(); + method public float getHistoricalAxisValue(int, int); + method public float getHistoricalAxisValue(int, int, int); + method public long getHistoricalEventTime(int); + method public float getHistoricalOrientation(int); + method public float getHistoricalOrientation(int, int); + method public void getHistoricalPointerCoords(int, int, android.view.MotionEvent.PointerCoords); + method public float getHistoricalPressure(int); + method public float getHistoricalPressure(int, int); + method public float getHistoricalSize(int); + method public float getHistoricalSize(int, int); + method public float getHistoricalToolMajor(int); + method public float getHistoricalToolMajor(int, int); + method public float getHistoricalToolMinor(int); + method public float getHistoricalToolMinor(int, int); + method public float getHistoricalTouchMajor(int); + method public float getHistoricalTouchMajor(int, int); + method public float getHistoricalTouchMinor(int); + method public float getHistoricalTouchMinor(int, int); + method public float getHistoricalX(int); + method public float getHistoricalX(int, int); + method public float getHistoricalY(int); + method public float getHistoricalY(int, int); + method public int getHistorySize(); + method public int getMetaState(); + method public float getOrientation(); + method public float getOrientation(int); + method public void getPointerCoords(int, android.view.MotionEvent.PointerCoords); + method public int getPointerCount(); + method public int getPointerId(int); + method public void getPointerProperties(int, android.view.MotionEvent.PointerProperties); + method public float getPressure(); + method public float getPressure(int); + method public float getRawX(); + method public float getRawY(); + method public float getSize(); + method public float getSize(int); + method public int getSource(); + method public float getToolMajor(); + method public float getToolMajor(int); + method public float getToolMinor(); + method public float getToolMinor(int); + method public int getToolType(int); + method public float getTouchMajor(); + method public float getTouchMajor(int); + method public float getTouchMinor(); + method public float getTouchMinor(int); + method public float getX(); + method public float getX(int); + method public float getXPrecision(); + method public float getY(); + method public float getY(int); + method public float getYPrecision(); + method public boolean isButtonPressed(int); method public static android.view.MotionEvent obtain(long, long, int, int, android.view.MotionEvent.PointerProperties[], android.view.MotionEvent.PointerCoords[], int, int, float, float, int, int, int, int); method public static deprecated android.view.MotionEvent obtain(long, long, int, int, int[], android.view.MotionEvent.PointerCoords[], int, float, float, int, int, int, int); method public static android.view.MotionEvent obtain(long, long, int, float, float, float, float, int, float, float, int, int); @@ -46606,13 +46732,13 @@ package android.view { method public static android.view.MotionEvent obtain(long, long, int, float, float, int); method public static android.view.MotionEvent obtain(android.view.MotionEvent); method public static android.view.MotionEvent obtainNoHistory(android.view.MotionEvent); - method public final void offsetLocation(float, float); - method public final void recycle(); - method public final void setAction(int); - method public final void setEdgeFlags(int); - method public final void setLocation(float, float); - method public final void setSource(int); - method public final void transform(android.graphics.Matrix); + method public void offsetLocation(float, float); + method public void recycle(); + method public void setAction(int); + method public void setEdgeFlags(int); + method public void setLocation(float, float); + method public void setSource(int); + method public void transform(android.graphics.Matrix); method public void writeToParcel(android.os.Parcel, int); field public static final int ACTION_BUTTON_PRESS = 11; // 0xb field public static final int ACTION_BUTTON_RELEASE = 12; // 0xc @@ -47173,7 +47299,9 @@ package android.view { method public int getNextFocusRightId(); method public int getNextFocusUpId(); method public android.view.View.OnFocusChangeListener getOnFocusChangeListener(); + method public int getOutlineAmbientShadowColor(); method public android.view.ViewOutlineProvider getOutlineProvider(); + method public int getOutlineSpotShadowColor(); method public int getOverScrollMode(); method public android.view.ViewOverlay getOverlay(); method public int getPaddingBottom(); @@ -47495,7 +47623,9 @@ package android.view { method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener); method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener); method public void setOnTouchListener(android.view.View.OnTouchListener); + method public void setOutlineAmbientShadowColor(int); method public void setOutlineProvider(android.view.ViewOutlineProvider); + method public void setOutlineSpotShadowColor(int); method public void setOverScrollMode(int); method public void setPadding(int, int, int, int); method public void setPaddingRelative(int, int, int, int); @@ -48330,9 +48460,9 @@ package android.view { method public void addOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener); method public void addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener); method public void addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener); - method public final void dispatchOnDraw(); - method public final void dispatchOnGlobalLayout(); - method public final boolean dispatchOnPreDraw(); + method public void dispatchOnDraw(); + method public void dispatchOnGlobalLayout(); + method public boolean dispatchOnPreDraw(); method public boolean isAlive(); method public deprecated void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener); method public void removeOnDrawListener(android.view.ViewTreeObserver.OnDrawListener); @@ -50593,7 +50723,7 @@ package android.webkit { ctor public URLUtil(); method public static java.lang.String composeSearchUrl(java.lang.String, java.lang.String, java.lang.String); method public static byte[] decode(byte[]) throws java.lang.IllegalArgumentException; - method public static final java.lang.String guessFileName(java.lang.String, java.lang.String, java.lang.String); + method public static java.lang.String guessFileName(java.lang.String, java.lang.String, java.lang.String); method public static java.lang.String guessUrl(java.lang.String); method public static boolean isAboutUrl(java.lang.String); method public static boolean isAssetUrl(java.lang.String); @@ -55662,7 +55792,7 @@ package java.lang { } public static final class Character.UnicodeBlock extends java.lang.Character.Subset { - method public static final java.lang.Character.UnicodeBlock forName(java.lang.String); + method public static java.lang.Character.UnicodeBlock forName(java.lang.String); method public static java.lang.Character.UnicodeBlock of(char); method public static java.lang.Character.UnicodeBlock of(int); field public static final java.lang.Character.UnicodeBlock AEGEAN_NUMBERS; @@ -55889,7 +56019,7 @@ package java.lang { } public static final class Character.UnicodeScript extends java.lang.Enum { - method public static final java.lang.Character.UnicodeScript forName(java.lang.String); + method public static java.lang.Character.UnicodeScript forName(java.lang.String); method public static java.lang.Character.UnicodeScript of(int); method public static java.lang.Character.UnicodeScript valueOf(java.lang.String); method public static final java.lang.Character.UnicodeScript[] values(); @@ -57552,6 +57682,7 @@ package java.lang.ref { method public boolean enqueue(); method public T get(); method public boolean isEnqueued(); + method public static void reachabilityFence(java.lang.Object); } public class ReferenceQueue<T> { @@ -58704,8 +58835,8 @@ package java.net { ctor public URL(java.net.URL, java.lang.String) throws java.net.MalformedURLException; ctor public URL(java.net.URL, java.lang.String, java.net.URLStreamHandler) throws java.net.MalformedURLException; method public java.lang.String getAuthority(); - method public final java.lang.Object getContent() throws java.io.IOException; - method public final java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException; + method public java.lang.Object getContent() throws java.io.IOException; + method public java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException; method public int getDefaultPort(); method public java.lang.String getFile(); method public java.lang.String getHost(); @@ -58718,7 +58849,7 @@ package java.net { method public synchronized int hashCode(); method public java.net.URLConnection openConnection() throws java.io.IOException; method public java.net.URLConnection openConnection(java.net.Proxy) throws java.io.IOException; - method public final java.io.InputStream openStream() throws java.io.IOException; + method public java.io.InputStream openStream() throws java.io.IOException; method public boolean sameFile(java.net.URL); method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory); method public java.lang.String toExternalForm(); @@ -63374,13 +63505,13 @@ package java.text { method public int getOffset(); method public int next(); method public int previous(); - method public static final int primaryOrder(int); + method public static int primaryOrder(int); method public void reset(); - method public static final short secondaryOrder(int); + method public static short secondaryOrder(int); method public void setOffset(int); method public void setText(java.lang.String); method public void setText(java.text.CharacterIterator); - method public static final short tertiaryOrder(int); + method public static short tertiaryOrder(int); field public static final int NULLORDER = -1; // 0xffffffff } @@ -64661,7 +64792,7 @@ package java.time.chrono { } public final class HijrahDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster { - method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.HijrahDate> atTime(java.time.LocalTime); + method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.HijrahDate> atTime(java.time.LocalTime); method public static java.time.chrono.HijrahDate from(java.time.temporal.TemporalAccessor); method public java.time.chrono.HijrahChronology getChronology(); method public java.time.chrono.HijrahEra getEra(); @@ -64748,7 +64879,7 @@ package java.time.chrono { } public final class JapaneseDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster { - method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.JapaneseDate> atTime(java.time.LocalTime); + method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.JapaneseDate> atTime(java.time.LocalTime); method public static java.time.chrono.JapaneseDate from(java.time.temporal.TemporalAccessor); method public java.time.chrono.JapaneseChronology getChronology(); method public java.time.chrono.JapaneseEra getEra(); @@ -64804,7 +64935,7 @@ package java.time.chrono { } public final class MinguoDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster { - method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.MinguoDate> atTime(java.time.LocalTime); + method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.MinguoDate> atTime(java.time.LocalTime); method public static java.time.chrono.MinguoDate from(java.time.temporal.TemporalAccessor); method public java.time.chrono.MinguoChronology getChronology(); method public java.time.chrono.MinguoEra getEra(); @@ -64857,7 +64988,7 @@ package java.time.chrono { } public final class ThaiBuddhistDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster { - method public final java.time.chrono.ChronoLocalDateTime<java.time.chrono.ThaiBuddhistDate> atTime(java.time.LocalTime); + method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.ThaiBuddhistDate> atTime(java.time.LocalTime); method public static java.time.chrono.ThaiBuddhistDate from(java.time.temporal.TemporalAccessor); method public java.time.chrono.ThaiBuddhistChronology getChronology(); method public java.time.chrono.ThaiBuddhistEra getEra(); @@ -64909,8 +65040,8 @@ package java.time.format { method public <T> T parse(java.lang.CharSequence, java.time.temporal.TemporalQuery<T>); method public java.time.temporal.TemporalAccessor parseBest(java.lang.CharSequence, java.time.temporal.TemporalQuery<?>...); method public java.time.temporal.TemporalAccessor parseUnresolved(java.lang.CharSequence, java.text.ParsePosition); - method public static final java.time.temporal.TemporalQuery<java.time.Period> parsedExcessDays(); - method public static final java.time.temporal.TemporalQuery<java.lang.Boolean> parsedLeapSecond(); + method public static java.time.temporal.TemporalQuery<java.time.Period> parsedExcessDays(); + method public static java.time.temporal.TemporalQuery<java.lang.Boolean> parsedLeapSecond(); method public java.text.Format toFormat(); method public java.text.Format toFormat(java.time.temporal.TemporalQuery<?>); method public java.time.format.DateTimeFormatter withChronology(java.time.chrono.Chronology); @@ -66359,15 +66490,15 @@ package java.util { method public java.lang.String getCountry(); method public static java.util.Locale getDefault(); method public static java.util.Locale getDefault(java.util.Locale.Category); - method public final java.lang.String getDisplayCountry(); + method public java.lang.String getDisplayCountry(); method public java.lang.String getDisplayCountry(java.util.Locale); - method public final java.lang.String getDisplayLanguage(); + method public java.lang.String getDisplayLanguage(); method public java.lang.String getDisplayLanguage(java.util.Locale); - method public final java.lang.String getDisplayName(); + method public java.lang.String getDisplayName(); method public java.lang.String getDisplayName(java.util.Locale); method public java.lang.String getDisplayScript(); method public java.lang.String getDisplayScript(java.util.Locale); - method public final java.lang.String getDisplayVariant(); + method public java.lang.String getDisplayVariant(); method public java.lang.String getDisplayVariant(java.util.Locale); method public java.lang.String getExtension(char); method public java.util.Set<java.lang.Character> getExtensionKeys(); @@ -66388,7 +66519,6 @@ package java.util { method public static synchronized void setDefault(java.util.Locale.Category, java.util.Locale); method public java.util.Locale stripExtensions(); method public java.lang.String toLanguageTag(); - method public final java.lang.String toString(); field public static final java.util.Locale CANADA; field public static final java.util.Locale CANADA_FRENCH; field public static final java.util.Locale CHINA; @@ -70574,7 +70704,6 @@ package javax.crypto { public class ExemptionMechanism { ctor protected ExemptionMechanism(javax.crypto.ExemptionMechanismSpi, java.security.Provider, java.lang.String); - method protected void finalize(); method public final byte[] genExemptionBlob() throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException; method public final int genExemptionBlob(byte[]) throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException, javax.crypto.ShortBufferException; method public final int genExemptionBlob(byte[], int) throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException, javax.crypto.ShortBufferException; diff --git a/api/system-current.txt b/api/system-current.txt index 034ee3090fc4..2a257b128c09 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -19,6 +19,7 @@ package android { field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE"; + field public static final java.lang.String BIND_DATA_SERVICE = "android.permission.BIND_DATA_SERVICE"; field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH"; field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE"; field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET"; @@ -311,8 +312,10 @@ package android.app { ctor public InstantAppResolverService(); method public final void attachBaseContext(android.content.Context); method public final android.os.IBinder onBind(android.content.Intent); - method public void onGetInstantAppIntentFilter(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); - method public void onGetInstantAppResolveInfo(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); + method public deprecated void onGetInstantAppIntentFilter(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); + method public void onGetInstantAppIntentFilter(android.content.Intent, int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); + method public deprecated void onGetInstantAppResolveInfo(int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); + method public void onGetInstantAppResolveInfo(android.content.Intent, int[], java.lang.String, android.app.InstantAppResolverService.InstantAppResolutionCallback); } public static final class InstantAppResolverService.InstantAppResolutionCallback { @@ -820,13 +823,24 @@ package android.content { field public static final java.lang.String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED"; field public static final java.lang.String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST"; field public static final java.lang.String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS"; + field public static final java.lang.String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE"; field public static final java.lang.String EXTRA_FORCE_FACTORY_RESET = "android.intent.extra.FORCE_FACTORY_RESET"; + field public static final java.lang.String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION"; + field public static final java.lang.String EXTRA_INSTANT_APP_BUNDLES = "android.intent.extra.INSTANT_APP_BUNDLES"; + field public static final java.lang.String EXTRA_INSTANT_APP_EXTRAS = "android.intent.extra.INSTANT_APP_EXTRAS"; + field public static final java.lang.String EXTRA_INSTANT_APP_FAILURE = "android.intent.extra.INSTANT_APP_FAILURE"; + field public static final java.lang.String EXTRA_INSTANT_APP_HOSTNAME = "android.intent.extra.INSTANT_APP_HOSTNAME"; + field public static final java.lang.String EXTRA_INSTANT_APP_SUCCESS = "android.intent.extra.INSTANT_APP_SUCCESS"; + field public static final java.lang.String EXTRA_INSTANT_APP_TOKEN = "android.intent.extra.INSTANT_APP_TOKEN"; + field public static final java.lang.String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE"; field public static final java.lang.String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID"; field public static final java.lang.String EXTRA_PACKAGES = "android.intent.extra.PACKAGES"; field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME"; field public static final java.lang.String EXTRA_REASON = "android.intent.extra.REASON"; field public static final java.lang.String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK"; field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED"; + field public static final java.lang.String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP"; + field public static final java.lang.String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE"; } public class IntentFilter implements android.os.Parcelable { @@ -839,7 +853,9 @@ package android.content { package android.content.pm { public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + method public boolean isInstantApp(); field public java.lang.String credentialProtectedDataDir; + field public int targetSandboxVersion; } public final class InstantAppInfo implements android.os.Parcelable { @@ -869,6 +885,7 @@ package android.content.pm { ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, int); ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, long, android.os.Bundle); ctor public InstantAppResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>); + ctor public InstantAppResolveInfo(android.os.Bundle); method public int describeContents(); method public byte[] getDigestBytes(); method public int getDigestPrefix(); @@ -877,6 +894,7 @@ package android.content.pm { method public long getLongVersionCode(); method public java.lang.String getPackageName(); method public deprecated int getVersionCode(); + method public boolean shouldLetInstallerDecide(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppResolveInfo> CREATOR; } @@ -888,6 +906,7 @@ package android.content.pm { method public int[] getDigestPrefix(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppResolveInfo.InstantAppDigest> CREATOR; + field public static final android.content.pm.InstantAppResolveInfo.InstantAppDigest UNDEFINED; } public final class IntentFilterVerificationInfo implements android.os.Parcelable { @@ -1122,6 +1141,15 @@ package android.hardware.camera2.params { package android.hardware.display { + public final class AmbientBrightnessDayStats implements android.os.Parcelable { + method public int describeContents(); + method public float[] getBucketBoundaries(); + method public java.time.LocalDate getLocalDate(); + method public float[] getStats(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR; + } + public final class BrightnessChangeEvent implements android.os.Parcelable { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); @@ -1129,11 +1157,14 @@ package android.hardware.display { field public final float batteryLevel; field public final float brightness; field public final int colorTemperature; + field public final boolean isDefaultBrightnessConfig; + field public final boolean isUserSetBrightness; field public final float lastBrightness; field public final long[] luxTimestamps; field public final float[] luxValues; field public final boolean nightMode; field public final java.lang.String packageName; + field public final float powerBrightnessFactor; field public final long timeStamp; } @@ -1991,6 +2022,7 @@ package android.hardware.radio { method public int describeContents(); method public android.hardware.radio.RadioManager.BandDescriptor[] getBands(); method public int getClassId(); + method public java.util.Map<java.lang.String, java.lang.Integer> getDabFrequencyTable(); method public int getId(); method public java.lang.String getImplementor(); method public int getNumAudioSources(); @@ -2673,10 +2705,10 @@ package android.media.soundtrigger { package android.media.tv { public final class TvContentRatingSystemInfo implements android.os.Parcelable { - method public static final android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo); + method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo); method public int describeContents(); - method public final android.net.Uri getXmlUri(); - method public final boolean isSystemDefined(); + method public android.net.Uri getXmlUri(); + method public boolean isSystemDefined(); method public void writeToParcel(android.os.Parcel, int); } @@ -4657,15 +4689,15 @@ package android.telecom { } public final deprecated class Phone { - method public final void addListener(android.telecom.Phone.Listener); - method public final boolean canAddCall(); - method public final deprecated android.telecom.AudioState getAudioState(); - method public final android.telecom.CallAudioState getCallAudioState(); - method public final java.util.List<android.telecom.Call> getCalls(); - method public final void removeListener(android.telecom.Phone.Listener); + method public void addListener(android.telecom.Phone.Listener); + method public boolean canAddCall(); + method public deprecated android.telecom.AudioState getAudioState(); + method public android.telecom.CallAudioState getCallAudioState(); + method public java.util.List<android.telecom.Call> getCalls(); + method public void removeListener(android.telecom.Phone.Listener); method public void requestBluetoothAudio(java.lang.String); - method public final void setAudioRoute(int); - method public final void setMuted(boolean); + method public void setAudioRoute(int); + method public void setMuted(boolean); } public static abstract class Phone.Listener { diff --git a/api/system-removed.txt b/api/system-removed.txt index b63703dc0035..48f43e0880da 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -1,13 +1,5 @@ package android.app { - public abstract deprecated class EphemeralResolverService extends android.app.InstantAppResolverService { - ctor public EphemeralResolverService(); - method public android.os.Looper getLooper(); - method public abstract deprecated java.util.List<android.content.pm.EphemeralResolveInfo> onEphemeralResolveInfoList(int[], int); - method public android.content.pm.EphemeralResolveInfo onGetEphemeralIntentFilter(java.lang.String); - method public java.util.List<android.content.pm.EphemeralResolveInfo> onGetEphemeralResolveInfo(int[]); - } - public class Notification implements android.os.Parcelable { method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String); } @@ -31,10 +23,7 @@ package android.content { public class Intent implements java.lang.Cloneable android.os.Parcelable { field public static final deprecated java.lang.String ACTION_DEVICE_INITIALIZATION_WIZARD = "android.intent.action.DEVICE_INITIALIZATION_WIZARD"; - field public static final deprecated java.lang.String ACTION_EPHEMERAL_RESOLVER_SETTINGS = "android.intent.action.EPHEMERAL_RESOLVER_SETTINGS"; - field public static final deprecated java.lang.String ACTION_INSTALL_EPHEMERAL_PACKAGE = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE"; field public static final deprecated java.lang.String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR"; - field public static final deprecated java.lang.String ACTION_RESOLVE_EPHEMERAL_PACKAGE = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE"; field public static final deprecated java.lang.String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE"; field public static final deprecated java.lang.String EXTRA_CDMA_DEFAULT_ROAMING_INDICATOR = "cdmaDefaultRoamingIndicator"; field public static final deprecated java.lang.String EXTRA_CDMA_ROAMING_INDICATOR = "cdmaRoamingIndicator"; @@ -62,45 +51,6 @@ package android.content { } -package android.content.pm { - - public final deprecated class EphemeralIntentFilter implements android.os.Parcelable { - ctor public EphemeralIntentFilter(java.lang.String, java.util.List<android.content.IntentFilter>); - method public int describeContents(); - method public java.util.List<android.content.IntentFilter> getFilters(); - method public java.lang.String getSplitName(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralIntentFilter> CREATOR; - } - - public final deprecated class EphemeralResolveInfo implements android.os.Parcelable { - ctor public deprecated EphemeralResolveInfo(android.net.Uri, java.lang.String, java.util.List<android.content.IntentFilter>); - ctor public deprecated EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>); - ctor public EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>, int); - ctor public EphemeralResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>); - method public int describeContents(); - method public byte[] getDigestBytes(); - method public int getDigestPrefix(); - method public deprecated java.util.List<android.content.IntentFilter> getFilters(); - method public java.util.List<android.content.pm.EphemeralIntentFilter> getIntentFilters(); - method public java.lang.String getPackageName(); - method public int getVersionCode(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo> CREATOR; - field public static final java.lang.String SHA_ALGORITHM = "SHA-256"; - } - - public static final class EphemeralResolveInfo.EphemeralDigest implements android.os.Parcelable { - ctor public EphemeralResolveInfo.EphemeralDigest(java.lang.String); - method public int describeContents(); - method public byte[][] getDigestBytes(); - method public int[] getDigestPrefix(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo.EphemeralDigest> CREATOR; - } - -} - package android.media.tv { public final class TvInputManager { diff --git a/api/test-current.txt b/api/test-current.txt index d834cf7d62ab..b331ba0c7205 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -298,6 +298,15 @@ package android.hardware.camera2 { package android.hardware.display { + public final class AmbientBrightnessDayStats implements android.os.Parcelable { + method public int describeContents(); + method public float[] getBucketBoundaries(); + method public java.time.LocalDate getLocalDate(); + method public float[] getStats(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR; + } + public final class BrightnessChangeEvent implements android.os.Parcelable { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); @@ -305,11 +314,14 @@ package android.hardware.display { field public final float batteryLevel; field public final float brightness; field public final int colorTemperature; + field public final boolean isDefaultBrightnessConfig; + field public final boolean isUserSetBrightness; field public final float lastBrightness; field public final long[] luxTimestamps; field public final float[] luxValues; field public final boolean nightMode; field public final java.lang.String packageName; + field public final float powerBrightnessFactor; field public final long timeStamp; } @@ -995,8 +1007,8 @@ package android.view { } public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable { - method public final void setActionButton(int); - method public final void setButtonState(int); + method public void setActionButton(int); + method public void setButtonState(int); } public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback { @@ -1044,6 +1056,14 @@ package android.view.autofill { } +package android.view.inputmethod { + + public final class InputMethodManager { + method public boolean isInputMethodPickerShown(); + } + +} + package android.widget { public abstract class AbsListView extends android.widget.AdapterView implements android.widget.Filter.FilterListener android.text.TextWatcher android.view.ViewTreeObserver.OnGlobalLayoutListener android.view.ViewTreeObserver.OnTouchModeChangeListener { diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java index f75678b7fa1e..6e0bd3a81d84 100644 --- a/cmds/content/src/com/android/commands/content/Content.java +++ b/cmds/content/src/com/android/commands/content/Content.java @@ -26,6 +26,7 @@ import android.database.Cursor; import android.net.Uri; import android.os.Binder; import android.os.Bundle; +import android.os.FileUtils; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.Process; @@ -34,6 +35,7 @@ import android.text.TextUtils; import libcore.io.Streams; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -583,7 +585,7 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null)) { - Streams.copy(new FileInputStream(fd.getFileDescriptor()), System.out); + FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out); } } } @@ -596,7 +598,7 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "w", null, null)) { - Streams.copy(System.in, new FileOutputStream(fd.getFileDescriptor())); + FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor()); } } } diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index eabbb96a392e..b0019ac90708 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -38,8 +38,11 @@ statsd_common_src := \ src/external/StatsPuller.cpp \ src/external/StatsCompanionServicePuller.cpp \ src/external/SubsystemSleepStatePuller.cpp \ + src/external/ResourceHealthManagerPuller.cpp \ src/external/CpuTimePerUidPuller.cpp \ src/external/CpuTimePerUidFreqPuller.cpp \ + src/external/KernelUidCpuActiveTimeReader.cpp \ + src/external/KernelUidCpuClusterTimeReader.cpp \ src/external/StatsPullerManagerImpl.cpp \ src/logd/LogEvent.cpp \ src/logd/LogListener.cpp \ @@ -75,7 +78,8 @@ statsd_common_aidl_includes := \ $(LOCAL_PATH)/../../core/java statsd_common_static_libraries := \ - libplatformprotos + libhealthhalutils \ + libplatformprotos \ statsd_common_shared_libraries := \ libbase \ @@ -93,6 +97,7 @@ statsd_common_shared_libraries := \ libhidlbase \ libhidltransport \ libhwbinder \ + android.hardware.health@2.0 \ android.hardware.power@1.0 \ android.hardware.power@1.1 \ libmemunreachable @@ -165,6 +170,7 @@ LOCAL_CFLAGS += \ LOCAL_SRC_FILES := \ $(statsd_common_src) \ + tests/dimension_test.cpp \ tests/AnomalyMonitor_test.cpp \ tests/anomaly/AnomalyTracker_test.cpp \ tests/ConfigManager_test.cpp \ @@ -190,7 +196,8 @@ LOCAL_SRC_FILES := \ tests/e2e/WakelockDuration_e2e_test.cpp \ tests/e2e/MetricConditionLink_e2e_test.cpp \ tests/e2e/Attribution_e2e_test.cpp \ - tests/e2e/GaugeMetric_e2e_test.cpp + tests/e2e/GaugeMetric_e2e_test.cpp \ + tests/e2e/DimensionInCondition_e2e_test.cpp LOCAL_STATIC_LIBRARIES := \ $(statsd_common_static_libraries) \ diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp index 857a6ddad0be..f0eaeff88d34 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -67,12 +67,16 @@ android::hash_t hashDimensionsValue(const DimensionsValue& value) { return hashDimensionsValue(0, value); } +android::hash_t hashMetricDimensionKey(int64_t seed, const MetricDimensionKey& dimensionKey) { + android::hash_t hash = seed; + hash = android::JenkinsHashMix(hash, std::hash<MetricDimensionKey>{}(dimensionKey)); + return JenkinsHashWhiten(hash); +} + using std::string; string HashableDimensionKey::toString() const { - string flattened; - DimensionsValueToString(getDimensionsValue(), &flattened); - return flattened; + return DimensionsValueToString(getDimensionsValue()); } bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) { @@ -162,6 +166,22 @@ bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const { return LessThan(getDimensionsValue(), that.getDimensionsValue()); }; +string MetricDimensionKey::toString() const { + string flattened = mDimensionKeyInWhat.toString(); + flattened += mDimensionKeyInCondition.toString(); + return flattened; +} + +bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const { + return mDimensionKeyInWhat == that.getDimensionKeyInWhat() && + mDimensionKeyInCondition == that.getDimensionKeyInCondition(); +}; + +bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const { + return toString().compare(that.toString()) < 0; +}; + + } // namespace statsd } // namespace os } // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h index 85c317f8cf1f..a31d7a6d85c6 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -41,6 +41,10 @@ public: return mDimensionsValue; } + inline DimensionsValue* getMutableDimensionsValue() { + return &mDimensionsValue; + } + bool operator==(const HashableDimensionKey& that) const; bool operator<(const HashableDimensionKey& that) const; @@ -53,8 +57,52 @@ private: DimensionsValue mDimensionsValue; }; +class MetricDimensionKey { + public: + explicit MetricDimensionKey(const HashableDimensionKey& dimensionKeyInWhat, + const HashableDimensionKey& dimensionKeyInCondition) + : mDimensionKeyInWhat(dimensionKeyInWhat), + mDimensionKeyInCondition(dimensionKeyInCondition) {}; + + MetricDimensionKey(){}; + + MetricDimensionKey(const MetricDimensionKey& that) + : mDimensionKeyInWhat(that.getDimensionKeyInWhat()), + mDimensionKeyInCondition(that.getDimensionKeyInCondition()) {}; + + MetricDimensionKey& operator=(const MetricDimensionKey& from) = default; + + std::string toString() const; + + inline const HashableDimensionKey& getDimensionKeyInWhat() const { + return mDimensionKeyInWhat; + } + + inline const HashableDimensionKey& getDimensionKeyInCondition() const { + return mDimensionKeyInCondition; + } + + bool hasDimensionKeyInCondition() const { + return mDimensionKeyInCondition.getDimensionsValue().has_field(); + } + + bool operator==(const MetricDimensionKey& that) const; + + bool operator<(const MetricDimensionKey& that) const; + + inline const char* c_str() const { + return toString().c_str(); + } + private: + HashableDimensionKey mDimensionKeyInWhat; + HashableDimensionKey mDimensionKeyInCondition; +}; + +bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2); + android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value); android::hash_t hashDimensionsValue(const DimensionsValue& value); +android::hash_t hashMetricDimensionKey(int64_t see, const MetricDimensionKey& dimensionKey); } // namespace statsd } // namespace os @@ -63,6 +111,7 @@ android::hash_t hashDimensionsValue(const DimensionsValue& value); namespace std { using android::os::statsd::HashableDimensionKey; +using android::os::statsd::MetricDimensionKey; template <> struct hash<HashableDimensionKey> { @@ -71,4 +120,14 @@ struct hash<HashableDimensionKey> { } }; -} // namespace std +template <> +struct hash<MetricDimensionKey> { + std::size_t operator()(const MetricDimensionKey& key) const { + android::hash_t hash = hashDimensionsValue( + key.getDimensionKeyInWhat().getDimensionsValue()); + hash = android::JenkinsHashMix(hash, + hashDimensionsValue(key.getDimensionKeyInCondition().getDimensionsValue())); + return android::JenkinsHashWhiten(hash); + } +}; +} // namespace std
\ No newline at end of file diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index c19ff63e2858..7642aafaab43 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -104,7 +104,10 @@ private: FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice); FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent); - + FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink); + FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink); }; } // namespace statsd diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index f545bb0738e9..4e4145439e25 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -551,6 +551,12 @@ status_t StatsService::cmd_dump_memory_info(FILE* out) { return NO_ERROR; } +status_t StatsService::cmd_clear_puller_cache(FILE* out) { + mStatsPullerManager.ClearPullerCache(); + fprintf(out, "Puller cached data removed!\n"); + return NO_ERROR; +} + Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version, const vector<String16>& app) { VLOG("StatsService::informAllUidData was called"); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index be20893994f1..fd3ed1dbed72 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -197,6 +197,11 @@ private: */ status_t cmd_dump_memory_info(FILE* out); + /* + * Clear all puller cached data + */ + status_t cmd_clear_puller_cache(FILE* out); + /** * Update a configuration. */ diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index ded6c4c660be..c84a5b4509b0 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -96,7 +96,7 @@ void AnomalyTracker::flushPastBuckets(const int64_t& latestPastBucketNum) { } } -void AnomalyTracker::addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue, +void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue, const int64_t& bucketNum) { flushPastBuckets(bucketNum); @@ -147,7 +147,7 @@ void AnomalyTracker::addBucketToSum(const shared_ptr<DimToValMap>& bucket) { } } -int64_t AnomalyTracker::getPastBucketValue(const HashableDimensionKey& key, +int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const { const auto& bucket = mPastBuckets[index(bucketNum)]; if (bucket == nullptr) { @@ -157,7 +157,7 @@ int64_t AnomalyTracker::getPastBucketValue(const HashableDimensionKey& key, return itr == bucket->end() ? 0 : itr->second; } -int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) const { +int64_t AnomalyTracker::getSumOverPastBuckets(const MetricDimensionKey& key) const { const auto& itr = mSumOverPastBuckets.find(key); if (itr != mSumOverPastBuckets.end()) { return itr->second; @@ -165,7 +165,7 @@ int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) c return 0; } -bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const HashableDimensionKey& key, +bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const MetricDimensionKey& key, const int64_t& currentBucketValue) { if (currentBucketNum > mMostRecentBucketNum + 1) { // TODO: This creates a needless 0 entry in mSumOverPastBuckets. Fix this. @@ -175,7 +175,7 @@ bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const Hashab && getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt(); } -void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key) { +void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key) { // TODO: Why receive timestamp? RefractoryPeriod should always be based on real time right now. if (isInRefractoryPeriod(timestampNs, key)) { VLOG("Skipping anomaly declaration since within refractory period"); @@ -199,14 +199,14 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const HashableD StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id()); - // TODO: This should also take in the const HashableDimensionKey& key? + // TODO: This should also take in the const MetricDimensionKey& key? android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(), mConfigKey.GetId(), mAlert.id()); } void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, - const HashableDimensionKey& key, + const MetricDimensionKey& key, const int64_t& currentBucketValue) { if (detectAnomaly(currBucketNum, key, currentBucketValue)) { declareAnomaly(timestampNs, key); @@ -214,7 +214,7 @@ void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs, } bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs, - const HashableDimensionKey& key) { + const MetricDimensionKey& key) { const auto& it = mRefractoryPeriodEndsSec.find(key); if (it != mRefractoryPeriodEndsSec.end()) { if ((timestampNs / NS_PER_SEC) <= it->second) { @@ -226,7 +226,7 @@ bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs, return false; } -void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) { +void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) { VLOG("informSubscribers called."); if (mSubscriptions.empty()) { ALOGE("Attempt to call with no subscribers."); diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h index 472c02c7ee7f..f01a97f86cf6 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ b/cmds/statsd/src/anomaly/AnomalyTracker.h @@ -48,19 +48,19 @@ public: // Adds a bucket. // Bucket index starts from 0. void addPastBucket(std::shared_ptr<DimToValMap> bucketValues, const int64_t& bucketNum); - void addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue, + void addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue, const int64_t& bucketNum); // Returns true if detected anomaly for the existing buckets on one or more dimension keys. - bool detectAnomaly(const int64_t& currBucketNum, const HashableDimensionKey& key, + bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key, const int64_t& currentBucketValue); // Informs incidentd about the detected alert. - void declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key); + void declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key); // Detects the alert and informs the incidentd when applicable. void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, - const HashableDimensionKey& key, + const MetricDimensionKey& key, const int64_t& currentBucketValue); // Init the AnomalyMonitor which is shared across anomaly trackers. @@ -69,10 +69,10 @@ public: } // Helper function to return the sum value of past buckets at given dimension. - int64_t getSumOverPastBuckets(const HashableDimensionKey& key) const; + int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const; // Helper function to return the value for a past bucket. - int64_t getPastBucketValue(const HashableDimensionKey& key, const int64_t& bucketNum) const; + int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const; // Returns the anomaly threshold. inline int64_t getAnomalyThreshold() const { @@ -81,7 +81,7 @@ public: // Returns the refractory period timestamp (in seconds) for the given key. // If there is no stored refractory period ending timestamp, returns 0. - uint32_t getRefractoryPeriodEndsSec(const HashableDimensionKey& key) const { + uint32_t getRefractoryPeriodEndsSec(const MetricDimensionKey& key) const { const auto& it = mRefractoryPeriodEndsSec.find(key); return it != mRefractoryPeriodEndsSec.end() ? it->second : 0; } @@ -124,7 +124,7 @@ protected: // declared for that dimension) ends, in seconds. Only anomalies that occur after this period // ends will be declared. // Entries may be, but are not guaranteed to be, removed after the period is finished. - unordered_map<HashableDimensionKey, uint32_t> mRefractoryPeriodEndsSec; + unordered_map<MetricDimensionKey, uint32_t> mRefractoryPeriodEndsSec; void flushPastBuckets(const int64_t& currBucketNum); @@ -135,7 +135,7 @@ protected: // and remove any items with value 0. void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket); - bool isInRefractoryPeriod(const uint64_t& timestampNs, const HashableDimensionKey& key); + bool isInRefractoryPeriod(const uint64_t& timestampNs, const MetricDimensionKey& key); // Calculates the corresponding bucket index within the circular array. size_t index(int64_t bucketNum) const; @@ -144,7 +144,7 @@ protected: virtual void resetStorage(); // Informs the subscribers that an anomaly has occurred. - void informSubscribers(const HashableDimensionKey& key); + void informSubscribers(const MetricDimensionKey& key); FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets); FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets); diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp index 7576a38db51d..bbee9fa5358c 100644 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp @@ -37,8 +37,8 @@ void DurationAnomalyTracker::resetStorage() { if (!mAlarms.empty()) VLOG("AnomalyTracker.resetStorage() called but mAlarms is NOT empty!"); } -void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey, - const uint64_t& timestampNs) { +void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey, + const uint64_t& timestampNs) { auto itr = mAlarms.find(dimensionKey); if (itr == mAlarms.end()) { return; @@ -51,7 +51,7 @@ void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensio } } -void DurationAnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey, +void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey, const uint64_t& timestampNs) { uint32_t timestampSec = static_cast<uint32_t>(timestampNs / NS_PER_SEC); @@ -66,7 +66,7 @@ void DurationAnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey } } -void DurationAnomalyTracker::stopAlarm(const HashableDimensionKey& dimensionKey) { +void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey) { auto itr = mAlarms.find(dimensionKey); if (itr != mAlarms.end()) { mAlarms.erase(dimensionKey); @@ -77,7 +77,7 @@ void DurationAnomalyTracker::stopAlarm(const HashableDimensionKey& dimensionKey) } void DurationAnomalyTracker::stopAllAlarms() { - std::set<HashableDimensionKey> keys; + std::set<MetricDimensionKey> keys; for (auto itr = mAlarms.begin(); itr != mAlarms.end(); ++itr) { keys.insert(itr->first); } @@ -95,7 +95,7 @@ void DurationAnomalyTracker::informAlarmsFired(const uint64_t& timestampNs, // seldomly called. The alternative would be having AnomalyAlarms store information about the // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that is // rarely ever called. - unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> matchedAlarms; + unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> matchedAlarms; for (const auto& kv : mAlarms) { if (firedAlarms.count(kv.second) > 0) { matchedAlarms.insert({kv.first, kv.second}); diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h index 33e55ab850c9..052fdf576289 100644 --- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h +++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h @@ -32,10 +32,10 @@ public: virtual ~DurationAnomalyTracker(); // Starts the alarm at the given timestamp. - void startAlarm(const HashableDimensionKey& dimensionKey, const uint64_t& eventTime); + void startAlarm(const MetricDimensionKey& dimensionKey, const uint64_t& eventTime); // Stops the alarm. - void stopAlarm(const HashableDimensionKey& dimensionKey); + void stopAlarm(const MetricDimensionKey& dimensionKey); // Stop all the alarms owned by this tracker. void stopAllAlarms(); @@ -46,7 +46,7 @@ public: } // Declares the anomaly when the alarm expired given the current timestamp. - void declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey, + void declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey, const uint64_t& timestampNs); // Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker @@ -59,7 +59,7 @@ public: protected: // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they // are still active. - std::unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> mAlarms; + std::unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> mAlarms; // Anomaly alarm monitor. sp<AnomalyMonitor> mAnomalyMonitor; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 4e570a684a06..e64b631b1738 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -95,11 +95,12 @@ message Atom { AppStartMemoryStateCaptured app_start_memory_state_captured = 55; ShutdownSequenceReported shutdown_sequence_reported = 56; BootSequenceReported boot_sequence_reported = 57; + DaveyOccurred davey_occurred = 58; // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15. } // Pulled events will start at field 10000. - // Next: 10019 + // Next: 10021 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001; @@ -120,6 +121,8 @@ message Atom { CpuActiveTime cpu_active_time = 10016; CpuClusterTime cpu_cluster_time = 10017; DiskSpace disk_space = 10018; + RemainingBatteryCapacity remaining_battery_capacity = 10019; + FullBatteryCapacity full_battery_capacity = 10020; } } @@ -721,6 +724,17 @@ message BootSequenceReported { } /** + * Logs the duration of a davey (jank of >=700ms) when it occurs + * + * Logged from: + * frameworks/base/libs/hwui/JankTracker.cpp + */ +message DaveyOccurred { + // Amount of time it took to render the frame. Should be >=700ms. + optional int64 jank_duration_ms = 1; +} + +/** * Logs phone signal strength changes. * * Logged from: @@ -757,8 +771,8 @@ message SettingChanged { // The tag used with the is_default for resetting sets of settings. This is generally null. optional string tag = 5; - // 1 indicates that this setting with tag should be resettable. - optional int32 is_default = 6; + // True if this setting with tag should be resettable. + optional bool is_default = 6; // The user ID associated. Defined in android/os/UserHandle.java optional int32 user = 7; @@ -1105,9 +1119,12 @@ message IsolatedUidChanged { optional int32 isolated_uid = 2; - // 1 denotes we're creating an isolated uid and 0 denotes removal. We expect an isolated uid to - // be removed before if it's used for another parent uid. - optional int32 is_create = 3; + // We expect an isolated uid to be removed before if it's used for another parent uid. + enum Event { + REMOVED = 0; + CREATED = 1; + } + optional Event event = 3; } /** @@ -1410,3 +1427,19 @@ message DiskSpace { // available bytes in download cache or temp directories optional uint64 temp_available_bytes = 3; } + +/** + * Pulls battery coulomb counter, which is the remaining battery charge in uAh. + * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp + */ +message RemainingBatteryCapacity { + optional int32 charge_uAh = 1; +} + +/** + * Pulls battery capacity, which is the battery capacity when full in uAh. + * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp + */ +message FullBatteryCapacity { + optional int32 capacity_uAh = 1; +} diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index ea6586c25d80..4c20ccb61afe 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -78,6 +78,7 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf return false; } + bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers, conditionIdIndexMap, stack); @@ -88,8 +89,10 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf ALOGW("Child initialization success %lld ", (long long)child); } + if (allConditionTrackers[childIndex]->isSliced()) { + setSliced(true); + } mChildren.push_back(childIndex); - mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(), childTracker->getLogTrackerIndex().end()); } @@ -105,11 +108,15 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf void CombinationConditionTracker::isConditionMet( const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, - vector<ConditionState>& conditionCache) const { + const FieldMatcher& dimensionFields, + vector<ConditionState>& conditionCache, + std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const { + // So far, this is fine as there is at most one child having sliced output. for (const int childIndex : mChildren) { if (conditionCache[childIndex] == ConditionState::kNotEvaluated) { allConditions[childIndex]->isConditionMet(conditionParameters, allConditions, - conditionCache); + dimensionFields, conditionCache, + dimensionsKeySet); } } conditionCache[mIndex] = @@ -127,6 +134,7 @@ void CombinationConditionTracker::evaluateCondition( } for (const int childIndex : mChildren) { + // So far, this is fine as there is at most one child having sliced output. if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) { const sp<ConditionTracker>& child = mAllConditions[childIndex]; child->evaluateCondition(event, eventMatcherValues, mAllConditions, @@ -159,6 +167,24 @@ void CombinationConditionTracker::evaluateCondition( } } +ConditionState CombinationConditionTracker::getMetConditionDimension( + const std::vector<sp<ConditionTracker>>& allConditions, + const FieldMatcher& dimensionFields, + std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const { + vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated); + // So far, this is fine as there is at most one child having sliced output. + for (const int childIndex : mChildren) { + conditionCache[childIndex] = conditionCache[childIndex] | + allConditions[childIndex]->getMetConditionDimension( + allConditions, dimensionFields, dimensionsKeySet); + } + evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); + if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) { + dimensionsKeySet.insert(DEFAULT_DIMENSION_KEY); + } + return conditionCache[mIndex]; +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index dfd3837f31f4..0b7f9492d628 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -41,12 +41,20 @@ public: std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache) override; - void isConditionMet(const ConditionKey& conditionParameters, - const std::vector<sp<ConditionTracker>>& allConditions, - std::vector<ConditionState>& conditionCache) const override; + void isConditionMet( + const ConditionKey& conditionParameters, + const std::vector<sp<ConditionTracker>>& allConditions, + const FieldMatcher& dimensionFields, + std::vector<ConditionState>& conditionCache, + std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override; + ConditionState getMetConditionDimension( + const std::vector<sp<ConditionTracker>>& allConditions, + const FieldMatcher& dimensionFields, + std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override; private: LogicalOperation mLogicalOperation; + // Store index of the children Predicates. // We don't store string name of the Children, because we want to get rid of the hash map to // map the name to object. We don't want to store smart pointers to children, because it diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 773860f429b1..81abbdb36ee4 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -24,6 +24,7 @@ #include <log/logprint.h> #include <utils/RefBase.h> +#include <unordered_set> #include <unordered_map> namespace android { @@ -84,10 +85,19 @@ public: // [allConditions]: all condition trackers. This is needed because the condition evaluation is // done recursively // [conditionCache]: the cache holding the condition evaluation values. + // [dimensionsKeySet]: the dimensions where the sliced condition is true. For combination + // condition, it assumes that only one child predicate is sliced. virtual void isConditionMet( const ConditionKey& conditionParameters, const std::vector<sp<ConditionTracker>>& allConditions, - std::vector<ConditionState>& conditionCache) const = 0; + const FieldMatcher& dimensionFields, + std::vector<ConditionState>& conditionCache, + std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0; + + virtual ConditionState getMetConditionDimension( + const std::vector<sp<ConditionTracker>>& allConditions, + const FieldMatcher& dimensionFields, + std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0; // return the list of LogMatchingTracker index that this ConditionTracker uses. virtual const std::set<int>& getLogTrackerIndex() const { @@ -98,6 +108,10 @@ public: mSliced = mSliced | sliced; } + bool isSliced() const { + return mSliced; + } + protected: const int64_t mConditionId; diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp index d99c2ccd1fda..0427700fec91 100644 --- a/cmds/statsd/src/condition/ConditionWizard.cpp +++ b/cmds/statsd/src/condition/ConditionWizard.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ #include "ConditionWizard.h" +#include <unordered_set> namespace android { namespace os { @@ -23,14 +24,26 @@ using std::map; using std::string; using std::vector; -ConditionState ConditionWizard::query(const int index, - const ConditionKey& parameters) { +ConditionState ConditionWizard::query( + const int index, const ConditionKey& parameters, + const FieldMatcher& dimensionFields, + std::unordered_set<HashableDimensionKey> *dimensionKeySet) { + vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated); - mAllConditions[index]->isConditionMet(parameters, mAllConditions, cache); + mAllConditions[index]->isConditionMet( + parameters, mAllConditions, dimensionFields, cache, *dimensionKeySet); return cache[index]; } +ConditionState ConditionWizard::getMetConditionDimension( + const int index, const FieldMatcher& dimensionFields, + std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const { + + return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields, + *dimensionsKeySet); +} + } // namespace statsd } // namespace os } // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h index 4ff5c07e210f..b38b59ff4cd0 100644 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ b/cmds/statsd/src/condition/ConditionWizard.h @@ -41,7 +41,14 @@ public: // the conditionParameters contains the parameters for it's children SimpleConditionTrackers. virtual ConditionState query( const int conditionIndex, - const ConditionKey& conditionParameters); + const ConditionKey& conditionParameters, + const FieldMatcher& dimensionFields, + std::unordered_set<HashableDimensionKey> *dimensionKeySet); + + virtual ConditionState getMetConditionDimension( + const int index, + const FieldMatcher& dimensionFields, + std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const; private: std::vector<sp<ConditionTracker>> mAllConditions; diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index 5cfc349ea46a..25265d5dabd7 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -104,6 +104,9 @@ bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig, vector<bool>& stack) { // SimpleConditionTracker does not have dependency on other conditions, thus we just return // if the initialization was successful. + if (mOutputDimensions.has_field() || mOutputDimensions.child_size() > 0) { + setSliced(true); + } return mInitialized; } @@ -234,11 +237,12 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou conditionChangedCache[mIndex] == true); } -void SimpleConditionTracker::evaluateCondition(const LogEvent& event, - const vector<MatchingState>& eventMatcherValues, - const vector<sp<ConditionTracker>>& mAllConditions, - vector<ConditionState>& conditionCache, - vector<bool>& conditionChangedCache) { +void SimpleConditionTracker::evaluateCondition( + const LogEvent& event, + const vector<MatchingState>& eventMatcherValues, + const vector<sp<ConditionTracker>>& mAllConditions, + vector<ConditionState>& conditionCache, + vector<bool>& conditionChangedCache) { if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { // it has been evaluated. VLOG("Yes, already evaluated, %lld %d", @@ -271,7 +275,7 @@ void SimpleConditionTracker::evaluateCondition(const LogEvent& event, if (mSliced) { // if the condition result is sliced. metrics won't directly get value from the // cache, so just set any value other than kNotEvaluated. - conditionCache[mIndex] = ConditionState::kUnknown; + conditionCache[mIndex] = mInitialValue; } else { const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY); if (itr == mSlicedConditionState.end()) { @@ -310,10 +314,8 @@ void SimpleConditionTracker::evaluateCondition(const LogEvent& event, vector<ConditionState> dimensionalConditionCache(conditionCache.size(), ConditionState::kNotEvaluated); vector<bool> dimensionalConditionChangedCache(conditionChangedCache.size(), false); - handleConditionEvent(HashableDimensionKey(outputValue), matchedState == 1, dimensionalConditionCache, dimensionalConditionChangedCache); - OrConditionState(dimensionalConditionCache, &conditionCache); OrBooleanVector(dimensionalConditionChangedCache, &conditionChangedCache); } @@ -323,49 +325,112 @@ void SimpleConditionTracker::evaluateCondition(const LogEvent& event, void SimpleConditionTracker::isConditionMet( const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, - vector<ConditionState>& conditionCache) const { + const FieldMatcher& dimensionFields, + vector<ConditionState>& conditionCache, + std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const { + if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { + // it has been evaluated. + VLOG("Yes, already evaluated, %lld %d", + (long long)mConditionId, conditionCache[mIndex]); + return; + } const auto pair = conditionParameters.find(mConditionId); - if (pair == conditionParameters.end() && mOutputDimensions.child_size() > 0) { - ALOGE("Predicate %lld output has dimension, but it's not specified in the query!", - (long long)mConditionId); - conditionCache[mIndex] = mInitialValue; + if (pair == conditionParameters.end()) { + ConditionState conditionState = ConditionState::kNotEvaluated; + if (dimensionFields.has_field() && dimensionFields.child_size() > 0 && + dimensionFields.field() == mOutputDimensions.field()) { + conditionState = conditionState | getMetConditionDimension( + allConditions, dimensionFields, dimensionsKeySet); + } else { + conditionState = conditionState | mInitialValue; + if (!mSliced) { + const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY); + if (itr != mSlicedConditionState.end()) { + ConditionState sliceState = + itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; + conditionState = conditionState | sliceState; + } + } + } + conditionCache[mIndex] = conditionState; return; } - std::vector<HashableDimensionKey> defaultKeys = {DEFAULT_DIMENSION_KEY}; + std::vector<HashableDimensionKey> defaultKeys = { DEFAULT_DIMENSION_KEY }; const std::vector<HashableDimensionKey> &keys = (pair == conditionParameters.end()) ? defaultKeys : pair->second; ConditionState conditionState = ConditionState::kNotEvaluated; - for (const auto& key : keys) { + for (size_t i = 0; i < keys.size(); ++i) { + const HashableDimensionKey& key = keys[i]; auto startedCountIt = mSlicedConditionState.find(key); if (startedCountIt != mSlicedConditionState.end()) { - conditionState = conditionState | - (startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse); + ConditionState sliceState = + startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; + conditionState = conditionState | sliceState; + if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) { + HashableDimensionKey dimensionKey; + if (getSubDimension(startedCountIt->first.getDimensionsValue(), dimensionFields, + dimensionKey.getMutableDimensionsValue())) { + dimensionsKeySet.insert(dimensionKey); + } + } } else { // For unseen key, check whether the require dimensions are subset of sliced condition // output. - bool seenDimension = false; + conditionState = conditionState | mInitialValue; for (const auto& slice : mSlicedConditionState) { - if (IsSubDimension(slice.first.getDimensionsValue(), - key.getDimensionsValue())) { - seenDimension = true; - conditionState = conditionState | - (slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse); - } - if (conditionState == ConditionState::kTrue) { - break; + ConditionState sliceState = + slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse; + if (IsSubDimension(slice.first.getDimensionsValue(), key.getDimensionsValue())) { + conditionState = conditionState | sliceState; + if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) { + HashableDimensionKey dimensionKey; + if (getSubDimension(slice.first.getDimensionsValue(), + dimensionFields, dimensionKey.getMutableDimensionsValue())) { + dimensionsKeySet.insert(dimensionKey); + } + } } } - if (!seenDimension) { - conditionState = conditionState | mInitialValue; - } } } conditionCache[mIndex] = conditionState; VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]); } +ConditionState SimpleConditionTracker::getMetConditionDimension( + const std::vector<sp<ConditionTracker>>& allConditions, + const FieldMatcher& dimensionFields, + std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const { + ConditionState conditionState = mInitialValue; + if (!dimensionFields.has_field() || + !mOutputDimensions.has_field() || + dimensionFields.field() != mOutputDimensions.field()) { + const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY); + if (itr != mSlicedConditionState.end()) { + ConditionState sliceState = + itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; + conditionState = conditionState | sliceState; + } + return conditionState; + } + + for (const auto& slice : mSlicedConditionState) { + ConditionState sliceState = + slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse; + DimensionsValue dimensionsValue; + conditionState = conditionState | sliceState; + HashableDimensionKey dimensionKey; + if (sliceState == ConditionState::kTrue && + getSubDimension(slice.first.getDimensionsValue(), dimensionFields, + dimensionKey.getMutableDimensionsValue())) { + dimensionsKeySet.insert(dimensionKey); + } + } + return conditionState; +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index 815b445a8c5b..ce9a02d4a795 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -48,7 +48,14 @@ public: void isConditionMet(const ConditionKey& conditionParameters, const std::vector<sp<ConditionTracker>>& allConditions, - std::vector<ConditionState>& conditionCache) const override; + const FieldMatcher& dimensionFields, + std::vector<ConditionState>& conditionCache, + std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override; + + ConditionState getMetConditionDimension( + const std::vector<sp<ConditionTracker>>& allConditions, + const FieldMatcher& dimensionFields, + std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override; private: const ConfigKey mConfigKey; @@ -73,7 +80,8 @@ private: void handleStopAll(std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache); - void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart, + void handleConditionEvent(const HashableDimensionKey& outputKey, + bool matchStart, std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache); diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp index 3b2d480b3ebf..0ab33cfbaea1 100644 --- a/cmds/statsd/src/condition/condition_util.cpp +++ b/cmds/statsd/src/condition/condition_util.cpp @@ -118,6 +118,9 @@ void OrBooleanVector(const std::vector<bool>& ref, vector<bool> * ored) { void getFieldsFromFieldMatcher(const FieldMatcher& matcher, Field* rootField, Field* leafField, std::vector<Field> *allFields) { + if (matcher.has_position()) { + leafField->set_position_index(0); + } if (matcher.child_size() == 0) { allFields->push_back(*rootField); return; diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp index 04445ca0e230..8a2e87128319 100644 --- a/cmds/statsd/src/dimension.cpp +++ b/cmds/statsd/src/dimension.cpp @@ -253,6 +253,9 @@ void buildAttributionFieldMatcher(const int tagId, const Position position, Fiel } void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) { + if (!value.has_field()) { + return; + } *flattened += std::to_string(value.field()); *flattened += ":"; switch (value.value_case()) { @@ -352,6 +355,46 @@ long getLongFromDimenValue(const DimensionsValue& dimensionValue) { } } +bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher, + DimensionsValue* subDimension) { + if (!matcher.has_field()) { + return false; + } + if (matcher.field() != dimension.field()) { + return false; + } + if (matcher.child_size() <= 0) { + if (dimension.value_case() == DimensionsValue::ValueCase::kValueTuple || + dimension.value_case() == DimensionsValue::ValueCase::VALUE_NOT_SET) { + return false; + } + *subDimension = dimension; + return true; + } else { + if (dimension.value_case() != DimensionsValue::ValueCase::kValueTuple) { + return false; + } + bool found_value = true; + auto value_tuple = dimension.value_tuple(); + subDimension->set_field(dimension.field()); + for (int i = 0; found_value && i < matcher.child_size(); ++i) { + int j = 0; + for (; j < value_tuple.dimensions_value_size(); ++j) { + if (value_tuple.dimensions_value(j).field() == matcher.child(i).field()) { + break; + } + } + if (j < value_tuple.dimensions_value_size()) { + found_value &= getSubDimension(value_tuple.dimensions_value(j), matcher.child(i), + subDimension->mutable_value_tuple()->add_dimensions_value()); + } else { + found_value = false; + } + } + return found_value; + } +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h index e900c5e87227..138c6e9b0160 100644 --- a/cmds/statsd/src/dimension.h +++ b/cmds/statsd/src/dimension.h @@ -63,6 +63,9 @@ bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub // Helper function to get long value from the DimensionsValue proto. long getLongFromDimenValue(const DimensionsValue& dimensionValue); + +bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher, + DimensionsValue* subDimension); } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp new file mode 100644 index 000000000000..72fb5ffd4b90 --- /dev/null +++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG true // STOPSHIP if true +#include "Log.h" + +#include <android/hardware/health/2.0/IHealth.h> +#include <healthhalutils/HealthHalUtils.h> +#include "external/ResourceHealthManagerPuller.h" +#include "external/StatsPuller.h" + +#include "ResourceHealthManagerPuller.h" +#include "logd/LogEvent.h" +#include "statslog.h" + +using android::hardware::hidl_vec; +using android::hardware::health::V2_0::get_health_service; +using android::hardware::health::V2_0::HealthInfo; +using android::hardware::health::V2_0::IHealth; +using android::hardware::health::V2_0::Result; +using android::hardware::Return; +using android::hardware::Void; + +using std::make_shared; +using std::shared_ptr; + +namespace android { +namespace os { +namespace statsd { + +sp<android::hardware::health::V2_0::IHealth> gHealthHal = nullptr; + +bool getHealthHal() { + if (gHealthHal == nullptr) { + gHealthHal = get_health_service(); + + } + return gHealthHal != nullptr; +} + +ResourceHealthManagerPuller::ResourceHealthManagerPuller(int tagId) : StatsPuller(tagId) { +} + +// TODO: add other health atoms (eg. Temperature). +bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { + if (!getHealthHal()) { + ALOGE("Health Hal not loaded"); + return false; + } + + uint64_t timestamp = time(nullptr) * NS_PER_SEC; + + data->clear(); + bool result_success = true; + + Return<void> ret = gHealthHal->getHealthInfo([&](Result r, HealthInfo v) { + if (r != Result::SUCCESS) { + result_success = false; + return; + } + if (mTagId == android::util::REMAINING_BATTERY_CAPACITY) { + auto ptr = make_shared<LogEvent>(android::util::REMAINING_BATTERY_CAPACITY, timestamp); + ptr->write(v.legacy.batteryChargeCounter); + ptr->init(); + data->push_back(ptr); + } else if (mTagId == android::util::FULL_BATTERY_CAPACITY) { + auto ptr = make_shared<LogEvent>(android::util::FULL_BATTERY_CAPACITY, timestamp); + ptr->write(v.legacy.batteryFullCharge); + ptr->init(); + data->push_back(ptr); + } else { + ALOGE("Unsupported tag in ResourceHealthManagerPuller: %d", mTagId); + } + }); + if (!result_success || !ret.isOk()) { + ALOGE("getHealthHal() failed: health HAL service not available. Description: %s", + ret.description().c_str()); + if (!ret.isOk() && ret.isDeadObject()) { + gHealthHal = nullptr; + } + return false; + } + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.h b/cmds/statsd/src/external/ResourceHealthManagerPuller.h new file mode 100644 index 000000000000..9b238eaf5f83 --- /dev/null +++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#pragma once + +#include <utils/String16.h> +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +/** + * Reads Ihealth.hal + */ +class ResourceHealthManagerPuller : public StatsPuller { +public: + ResourceHealthManagerPuller(int tagId); + bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp index cadc535f2469..da14434737af 100644 --- a/cmds/statsd/src/external/StatsPuller.cpp +++ b/cmds/statsd/src/external/StatsPuller.cpp @@ -59,6 +59,11 @@ bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) { return ret; } +void StatsPuller::ClearCache() { + lock_guard<std::mutex> lock(mLock); + mCachedData.clear(); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h index 47cc9f01e054..bc7c45f535d1 100644 --- a/cmds/statsd/src/external/StatsPuller.h +++ b/cmds/statsd/src/external/StatsPuller.h @@ -38,6 +38,8 @@ public: bool Pull(std::vector<std::shared_ptr<LogEvent>>* data); + void ClearCache(); + protected: // The atom tag id this puller pulls const int mTagId; diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h index 00a1475c0829..4826d963a3ed 100644 --- a/cmds/statsd/src/external/StatsPullerManager.h +++ b/cmds/statsd/src/external/StatsPullerManager.h @@ -50,10 +50,14 @@ class StatsPullerManager { return mPullerManager.Pull(tagId, data); } - virtual void SetTimeBaseSec(const long timeBaseSec) { + void SetTimeBaseSec(const long timeBaseSec) { mPullerManager.SetTimeBaseSec(timeBaseSec); } + void ClearPullerCache() { + mPullerManager.ClearPullerCache(); + } + private: StatsPullerManagerImpl & mPullerManager = StatsPullerManagerImpl::GetInstance(); diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp index 148c9ae9b249..71b0abe25d28 100644 --- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp +++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp @@ -23,10 +23,11 @@ #include <climits> #include "CpuTimePerUidFreqPuller.h" #include "CpuTimePerUidPuller.h" -#include "SubsystemSleepStatePuller.h" +#include "ResourceHealthManagerPuller.h" #include "StatsCompanionServicePuller.h" #include "StatsPullerManagerImpl.h" #include "StatsService.h" +#include "SubsystemSleepStatePuller.h" #include "logd/LogEvent.h" #include "statslog.h" @@ -83,7 +84,10 @@ StatsPullerManagerImpl::StatsPullerManagerImpl() make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)}); mPullers.insert({android::util::MODEM_ACTIVITY_INFO, make_shared<StatsCompanionServicePuller>(android::util::MODEM_ACTIVITY_INFO)}); - + mPullers.insert({android::util::REMAINING_BATTERY_CAPACITY, + make_shared<ResourceHealthManagerPuller>(android::util::REMAINING_BATTERY_CAPACITY)}); + mPullers.insert({android::util::FULL_BATTERY_CAPACITY, + make_shared<ResourceHealthManagerPuller>(android::util::FULL_BATTERY_CAPACITY)}); mStatsCompanionService = StatsService::getStatsCompanionService(); } @@ -195,6 +199,12 @@ void StatsPullerManagerImpl::OnAlarmFired() { } } +void StatsPullerManagerImpl::ClearPullerCache() { + for (auto puller : mPullers) { + puller.second->ClearCache(); + } +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h index 7c59f6659d1f..fba3ade8c1bb 100644 --- a/cmds/statsd/src/external/StatsPullerManagerImpl.h +++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h @@ -49,6 +49,8 @@ public: void SetTimeBaseSec(long timeBaseSec) {mTimeBaseSec = timeBaseSec;}; + void ClearPullerCache(); + private: StatsPullerManagerImpl(); diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 9e72f5bd4b72..1dcd8534db79 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -62,13 +62,14 @@ void LogEvent::init() { const char* buffer; size_t len = android_log_write_list_buffer(mContext, &buffer); // turns to reader mode - mContext = create_android_log_parser(buffer, len); - init(mContext); - // destroy the context to save memory. - if (mContext) { + android_log_context contextForRead = create_android_log_parser(buffer, len); + if (contextForRead) { + init(contextForRead); + // destroy the context to save memory. // android_log_destroy will set mContext to NULL - android_log_destroy(&mContext); + android_log_destroy(&contextForRead); } + android_log_destroy(&mContext); } } @@ -188,6 +189,9 @@ void increaseField(Field *field, bool is_child) { * of the elements that are written to the log. */ void LogEvent::init(android_log_context context) { + if (!context) { + return; + } android_log_list_element elem; // TODO: The log is actually structured inside one list. This is convenient // because we'll be able to use it to put the attribution (WorkSource) block first diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 0455f6a210a9..ae4df3ee92f0 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -21,6 +21,7 @@ #include "guardrail/StatsdStats.h" #include "stats_util.h" #include "stats_log_util.h" +#include "dimension.h" #include <limits.h> #include <stdlib.h> @@ -71,13 +72,15 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric } // TODO: use UidMap if uid->pkg_name is required - mDimensions = metric.dimensions_in_what(); + mDimensionsInWhat = metric.dimensions_in_what(); + mDimensionsInCondition = metric.dimensions_in_condition(); if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), metric.links().end()); - mConditionSliced = true; } + mConditionSliced = (metric.links().size() > 0)|| + (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0); VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); @@ -99,7 +102,10 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLog auto count_metrics = report->mutable_count_metrics(); for (const auto& counter : mPastBuckets) { CountMetricData* metricData = count_metrics->add_data(); - *metricData->mutable_dimensions_in_what() = counter.first.getDimensionsValue(); + *metricData->mutable_dimensions_in_what() = + counter.first.getDimensionKeyInWhat().getDimensionsValue(); + *metricData->mutable_dimensions_in_condition() = + counter.first.getDimensionKeyInCondition().getDimensionsValue(); for (const auto& bucket : counter.second) { CountBucketInfo* bucketInfo = metricData->add_bucket_info(); bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); @@ -123,17 +129,26 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, VLOG("metric %lld dump report now...",(long long)mMetricId); for (const auto& counter : mPastBuckets) { - const HashableDimensionKey& hashableKey = counter.first; - VLOG(" dimension key %s", hashableKey.c_str()); + const MetricDimensionKey& dimensionKey = counter.first; + VLOG(" dimension key %s", dimensionKey.c_str()); long long wrapperToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); // First fill dimension. - long long dimensionToken = protoOutput->start( + long long dimensionInWhatToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput); - protoOutput->end(dimensionToken); + writeDimensionsValueProtoToStream( + dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput); + protoOutput->end(dimensionInWhatToken); + + if (dimensionKey.hasDimensionKeyInCondition()) { + long long dimensionInConditionToken = protoOutput->start( + FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); + writeDimensionsValueProtoToStream( + dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput); + protoOutput->end(dimensionInConditionToken); + } // Then fill bucket_info (CountBucketInfo). for (const auto& bucket : counter.second) { @@ -166,7 +181,7 @@ void CountMetricProducer::onConditionChangedLocked(const bool conditionMet, mCondition = conditionMet; } -bool CountMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) { +bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) { return false; } @@ -187,7 +202,7 @@ bool CountMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) } void CountMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const HashableDimensionKey& eventKey, + const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) { uint64_t eventTimeNs = event.GetTimestampNs(); diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 061b7a36817c..8659d4773568 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -50,7 +50,7 @@ public: protected: void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const HashableDimensionKey& eventKey, + const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) override; @@ -74,14 +74,14 @@ private: void flushIfNeededLocked(const uint64_t& newEventTime); // TODO: Add a lock to mPastBuckets. - std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets; + std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets; // The current bucket. std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>(); static const size_t kBucketSize = sizeof(CountBucket{}); - bool hitGuardRailLocked(const HashableDimensionKey& newKey); + bool hitGuardRailLocked(const MetricDimensionKey& newKey); FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents); FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition); diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 000874cf8f44..efbdae150792 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -21,6 +21,7 @@ #include "guardrail/StatsdStats.h" #include "stats_util.h" #include "stats_log_util.h" +#include "dimension.h" #include <limits.h> #include <stdlib.h> @@ -81,13 +82,15 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat } // TODO: use UidMap if uid->pkg_name is required - mDimensions = metric.dimensions_in_what(); + mDimensionsInWhat = metric.dimensions_in_what(); + mDimensionsInCondition = metric.dimensions_in_condition(); if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), metric.links().end()); - mConditionSliced = true; } + mConditionSliced = (metric.links().size() > 0)|| + (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0); VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs); @@ -113,15 +116,17 @@ sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(const Alert &alert) } unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( - const HashableDimensionKey& eventKey) const { + const MetricDimensionKey& eventKey) const { switch (mAggregationType) { case DurationMetric_AggregationType_SUM: return make_unique<OringDurationTracker>( - mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested, + mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, + mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers); case DurationMetric_AggregationType_MAX_SPARSE: return make_unique<MaxDurationTracker>( - mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested, + mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, + mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers); } } @@ -129,10 +134,34 @@ unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) { VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); flushIfNeededLocked(eventTime); + // Now for each of the on-going event, check if the condition has changed for them. - for (auto& pair : mCurrentSlicedDuration) { + for (auto& pair : mCurrentSlicedDurationTrackerMap) { pair.second->onSlicedConditionMayChange(eventTime); } + + + std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet; + ConditionState conditionState = mWizard->getMetConditionDimension( + mConditionTrackerIndex, mDimensionsInCondition, &conditionDimensionsKeySet); + + bool condition = (conditionState == ConditionState::kTrue); + for (auto& pair : mCurrentSlicedDurationTrackerMap) { + conditionDimensionsKeySet.erase(pair.first.getDimensionKeyInCondition()); + } + std::unordered_set<MetricDimensionKey> newKeys; + for (const auto& conditionDimensionsKey : conditionDimensionsKeySet) { + for (auto& pair : mCurrentSlicedDurationTrackerMap) { + auto newKey = + MetricDimensionKey(pair.first.getDimensionKeyInWhat(), conditionDimensionsKey); + if (newKeys.find(newKey) == newKeys.end()) { + mCurrentSlicedDurationTrackerMap[newKey] = pair.second->clone(eventTime); + mCurrentSlicedDurationTrackerMap[newKey]->setEventKey(newKey); + mCurrentSlicedDurationTrackerMap[newKey]->onSlicedConditionMayChange(eventTime); + } + newKeys.insert(newKey); + } + } } void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, @@ -142,7 +171,7 @@ void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, flushIfNeededLocked(eventTime); // TODO: need to populate the condition change time from the event which triggers the condition // change, instead of using current time. - for (auto& pair : mCurrentSlicedDuration) { + for (auto& pair : mCurrentSlicedDurationTrackerMap) { pair.second->onConditionChanged(conditionMet, eventTime); } } @@ -155,7 +184,10 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, Stats auto duration_metrics = report->mutable_duration_metrics(); for (const auto& pair : mPastBuckets) { DurationMetricData* metricData = duration_metrics->add_data(); - *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue(); + *metricData->mutable_dimensions_in_what() = + pair.first.getDimensionKeyInWhat().getDimensionsValue(); + *metricData->mutable_dimensions_in_condition() = + pair.first.getDimensionKeyInCondition().getDimensionsValue(); for (const auto& bucket : pair.second) { auto bucketInfo = metricData->add_bucket_info(); bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); @@ -179,8 +211,8 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, VLOG("metric %lld dump report now...", (long long)mMetricId); for (const auto& pair : mPastBuckets) { - const HashableDimensionKey& hashableKey = pair.first; - VLOG(" dimension key %s", hashableKey.c_str()); + const MetricDimensionKey& dimensionKey = pair.first; + VLOG(" dimension key %s", dimensionKey.c_str()); long long wrapperToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); @@ -188,9 +220,18 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput); + writeDimensionsValueProtoToStream( + dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput); protoOutput->end(dimensionToken); + if (dimensionKey.hasDimensionKeyInCondition()) { + long long dimensionInConditionToken = protoOutput->start( + FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); + writeDimensionsValueProtoToStream( + dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput); + protoOutput->end(dimensionInConditionToken); + } + // Then fill bucket_info (DurationBucketInfo). for (const auto& bucket : pair.second) { long long bucketInfoToken = protoOutput->start( @@ -219,10 +260,11 @@ void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTime) { return; } VLOG("flushing..........."); - for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) { + for (auto it = mCurrentSlicedDurationTrackerMap.begin(); + it != mCurrentSlicedDurationTrackerMap.end();) { if (it->second->flushIfNeeded(eventTime, &mPastBuckets)) { VLOG("erase bucket for key %s", it->first.c_str()); - it = mCurrentSlicedDuration.erase(it); + it = mCurrentSlicedDurationTrackerMap.erase(it); } else { ++it; } @@ -234,28 +276,28 @@ void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTime) { } void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { - if (mCurrentSlicedDuration.size() == 0) { + if (mCurrentSlicedDurationTrackerMap.size() == 0) { return; } fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId, - (unsigned long)mCurrentSlicedDuration.size()); + (unsigned long)mCurrentSlicedDurationTrackerMap.size()); if (verbose) { - for (const auto& slice : mCurrentSlicedDuration) { + for (const auto& slice : mCurrentSlicedDurationTrackerMap) { fprintf(out, "\t%s\n", slice.first.c_str()); slice.second->dumpStates(out, verbose); } } } -bool DurationMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) { +bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { // the key is not new, we are good. - if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) { + if (mCurrentSlicedDurationTrackerMap.find(newKey) != mCurrentSlicedDurationTrackerMap.end()) { return false; } // 1. Report the tuple count if the tuple count > soft limit - if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { - size_t newTupleCount = mCurrentSlicedDuration.size() + 1; + if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { + size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1; StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { @@ -268,27 +310,26 @@ bool DurationMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newK } void DurationMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const HashableDimensionKey& eventKey, + const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, bool condition, const LogEvent& event) { flushIfNeededLocked(event.GetTimestampNs()); if (matcherIndex == mStopAllIndex) { - for (auto& pair : mCurrentSlicedDuration) { + for (auto& pair : mCurrentSlicedDurationTrackerMap) { pair.second->noteStopAll(event.GetTimestampNs()); } return; } - - if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) { + if (mCurrentSlicedDurationTrackerMap.find(eventKey) == mCurrentSlicedDurationTrackerMap.end()) { if (hitGuardRailLocked(eventKey)) { return; } - mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey); + mCurrentSlicedDurationTrackerMap[eventKey] = createDurationTracker(eventKey); } - auto it = mCurrentSlicedDuration.find(eventKey); + auto it = mCurrentSlicedDurationTrackerMap.find(eventKey); std::vector<DimensionsValue> values; getDimensionKeys(event, mInternalDimensions, &values); @@ -302,10 +343,11 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( } else { for (const DimensionsValue& value : values) { if (matcherIndex == mStartIndex) { - it->second->noteStart(HashableDimensionKey(value), condition, - event.GetTimestampNs(), conditionKeys); + it->second->noteStart( + HashableDimensionKey(value), condition, event.GetTimestampNs(), conditionKeys); } else if (matcherIndex == mStopIndex) { - it->second->noteStop(HashableDimensionKey(value), event.GetTimestampNs(), false); + it->second->noteStop( + HashableDimensionKey(value), event.GetTimestampNs(), false); } } } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index d8cab92a2b84..152e570df467 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -50,7 +50,7 @@ public: protected: void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const HashableDimensionKey& eventKey, + const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, bool condition, const LogEvent& event) override; @@ -92,21 +92,21 @@ private: // Save the past buckets and we can clear when the StatsLogReport is dumped. // TODO: Add a lock to mPastBuckets. - std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>> mPastBuckets; + std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets; // The current bucket. - std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>> - mCurrentSlicedDuration; + std::unordered_map<MetricDimensionKey, std::unique_ptr<DurationTracker>> + mCurrentSlicedDurationTrackerMap; // Helper function to create a duration tracker given the metric aggregation type. std::unique_ptr<DurationTracker> createDurationTracker( - const HashableDimensionKey& eventKey) const; + const MetricDimensionKey& eventKey) const; // This hides the base class's std::vector<sp<AnomalyTracker>> mAnomalyTrackers std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers; // Util function to check whether the specified dimension hits the guardrail. - bool hitGuardRailLocked(const HashableDimensionKey& newKey); + bool hitGuardRailLocked(const MetricDimensionKey& newKey); static const size_t kBucketSize = sizeof(DurationBucket{}); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 25c86d0d46f1..820d5918a92a 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -128,7 +128,7 @@ void EventMetricProducer::onConditionChangedLocked(const bool conditionMet, } void EventMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const HashableDimensionKey& eventKey, + const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) { if (!condition) { diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 9da0dd0569d6..935f206017fa 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -45,7 +45,7 @@ protected: private: void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const HashableDimensionKey& eventKey, + const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) override; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 1072c5aae6e4..d6cb1891288a 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -82,13 +82,15 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric mFieldFilter = metric.gauge_fields_filter(); // TODO: use UidMap if uid->pkg_name is required - mDimensions = metric.dimensions_in_what(); + mDimensionsInWhat = metric.dimensions_in_what(); + mDimensionsInCondition = metric.dimensions_in_condition(); if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), metric.links().end()); - mConditionSliced = true; } + mConditionSliced = (metric.links().size() > 0)|| + (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0); // Kicks off the puller immediately. if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { @@ -136,18 +138,27 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS); for (const auto& pair : mPastBuckets) { - const HashableDimensionKey& hashableKey = pair.first; + const MetricDimensionKey& dimensionKey = pair.first; - VLOG(" dimension key %s", hashableKey.c_str()); + VLOG(" dimension key %s", dimensionKey.c_str()); long long wrapperToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); // First fill dimension. long long dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput); + writeDimensionsValueProtoToStream( + dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput); protoOutput->end(dimensionToken); + if (dimensionKey.hasDimensionKeyInCondition()) { + long long dimensionInConditionToken = protoOutput->start( + FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); + writeDimensionsValueProtoToStream( + dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput); + protoOutput->end(dimensionInConditionToken); + } + // Then fill bucket_info (GaugeBucketInfo). for (const auto& bucket : pair.second) { long long bucketInfoToken = protoOutput->start( @@ -248,7 +259,7 @@ void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven } } -bool GaugeMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) { +bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) { return false; } @@ -268,7 +279,7 @@ bool GaugeMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) } void GaugeMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const HashableDimensionKey& eventKey, + const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) { if (condition == false) { diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index 6c013477af37..86d0ccd241eb 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -44,7 +44,7 @@ struct GaugeBucket { uint64_t mBucketNum; }; -typedef std::unordered_map<HashableDimensionKey, std::vector<GaugeAtom>> +typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>> DimToGaugeAtomsMap; // This gauge metric producer first register the puller to automatically pull the gauge at the @@ -64,7 +64,7 @@ public: protected: void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const HashableDimensionKey& eventKey, + const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) override; @@ -99,7 +99,7 @@ private: // Save the past buckets and we can clear when the StatsLogReport is dumped. // TODO: Add a lock to mPastBuckets. - std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets; + std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets; // The current bucket. std::shared_ptr<DimToGaugeAtomsMap> mCurrentSlicedBucket; @@ -119,7 +119,7 @@ private: std::shared_ptr<FieldValueMap> getGaugeFields(const LogEvent& event); // Util function to check whether the specified dimension hits the guardrail. - bool hitGuardRailLocked(const HashableDimensionKey& newKey); + bool hitGuardRailLocked(const MetricDimensionKey& newKey); static const size_t kBucketSize = sizeof(GaugeBucket{}); diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index e74924a81fbf..85e655b08f4d 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -15,6 +15,8 @@ */ #include "MetricProducer.h" +#include "dimension.h" + namespace android { namespace os { namespace statsd { @@ -30,29 +32,51 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo bool condition; ConditionKey conditionKey; + + std::unordered_set<HashableDimensionKey> dimensionKeysInCondition; if (mConditionSliced) { for (const auto& link : mConditionLinks) { getDimensionKeysForCondition(event, link, &conditionKey[link.condition()]); } - if (mWizard->query(mConditionTrackerIndex, conditionKey) != ConditionState::kTrue) { - condition = false; - } else { - condition = true; - } + auto conditionState = + mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition, + &dimensionKeysInCondition); + condition = (conditionState == ConditionState::kTrue); } else { condition = mCondition; } - if (mDimensions.has_field() && mDimensions.child_size() > 0) { - vector<DimensionsValue> dimensionValues; - getDimensionKeys(event, mDimensions, &dimensionValues); - for (const DimensionsValue& dimensionValue : dimensionValues) { + vector<DimensionsValue> dimensionInWhatValues; + if (mDimensionsInWhat.has_field() && mDimensionsInWhat.child_size() > 0) { + getDimensionKeys(event, mDimensionsInWhat, &dimensionInWhatValues); + } + + if (dimensionInWhatValues.empty() && dimensionKeysInCondition.empty()) { + onMatchedLogEventInternalLocked( + matcherIndex, DEFAULT_METRIC_DIMENSION_KEY, conditionKey, condition, event); + } else if (dimensionKeysInCondition.empty()) { + for (const DimensionsValue& whatValue : dimensionInWhatValues) { + onMatchedLogEventInternalLocked( + matcherIndex, + MetricDimensionKey(HashableDimensionKey(whatValue), DEFAULT_DIMENSION_KEY), + conditionKey, condition, event); + } + } else if (dimensionInWhatValues.empty()) { + for (const auto& conditionDimensionKey : dimensionKeysInCondition) { onMatchedLogEventInternalLocked( - matcherIndex, HashableDimensionKey(dimensionValue), conditionKey, condition, event); + matcherIndex, + MetricDimensionKey(DEFAULT_DIMENSION_KEY, conditionDimensionKey), + conditionKey, condition, event); } } else { - onMatchedLogEventInternalLocked( - matcherIndex, DEFAULT_DIMENSION_KEY, conditionKey, condition, event); + for (const DimensionsValue& whatValue : dimensionInWhatValues) { + for (const auto& conditionDimensionKey : dimensionKeysInCondition) { + onMatchedLogEventInternalLocked( + matcherIndex, + MetricDimensionKey(HashableDimensionKey(whatValue), conditionDimensionKey), + conditionKey, condition, event); + } + } } } diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 6f33073c633c..3b1498f07632 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -91,6 +91,7 @@ public: std::lock_guard<std::mutex> lock(mMutex); return onDumpReportLocked(dumpTimeNs, protoOutput); } + void onDumpReport(const uint64_t dumpTimeNs, StatsLogReport* report) { std::lock_guard<std::mutex> lock(mMutex); return onDumpReportLocked(dumpTimeNs, report); @@ -156,7 +157,8 @@ protected: int mConditionTrackerIndex; - FieldMatcher mDimensions; // The dimension defined in statsd_config + FieldMatcher mDimensionsInWhat; // The dimensions_in_what defined in statsd_config + FieldMatcher mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config std::vector<MetricConditionLink> mConditionLinks; @@ -178,7 +180,7 @@ protected: * [event]: the log event, just in case the metric needs its data, e.g., EventMetric. */ virtual void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const HashableDimensionKey& eventKey, + const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) = 0; diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index d0737de8acf3..636289522780 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -189,13 +189,7 @@ void MetricsManager::onLogEvent(const LogEvent& event) { return; } - if (event.GetTagId() != android::util::APP_HOOK) { - std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); - if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) { - VLOG("log source %d not on the whitelist", event.GetUid()); - return; - } - } else { // Check that app hook fields are valid. + if (event.GetTagId() == android::util::APP_HOOK) { // Check that app hook fields are valid. // TODO: Find a way to make these checks easier to maintain if the app hooks get changed. // Label is 2nd from last field and must be from [0, 15]. @@ -211,6 +205,21 @@ void MetricsManager::onLogEvent(const LogEvent& event) { VLOG("App hook does not have valid state %ld", apphookState); return; } + } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) { + // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp. + // Check that the davey duration is reasonable. Max length check is for privacy. + status_t err = NO_ERROR; + long duration = event.GetLong(event.size(), &err); + if (err != NO_ERROR || duration > 100000) { + VLOG("Davey duration is unreasonably long: %ld", duration); + return; + } + } else { + std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); + if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) { + VLOG("log source %d not on the whitelist", event.GetUid()); + return; + } } int tagId = event.GetTagId(); diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 9cdbafc75fb1..d4b9102d5ddc 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -143,6 +143,10 @@ private: FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice); FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent); + FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink); + FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink); + FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index ae0c673e3490..c9cc7bb9c1d4 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -81,13 +81,15 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric } mBucketSizeNs = bucketSizeMills * 1000000; - mDimensions = metric.dimensions_in_what(); + mDimensionsInWhat = metric.dimensions_in_what(); + mDimensionsInCondition = metric.dimensions_in_condition(); if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), metric.links().end()); - mConditionSliced = true; } + mConditionSliced = (metric.links().size() > 0)|| + (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0); if (!metric.has_condition() && mPullTagId != -1) { VLOG("Setting up periodic pulling for %d", mPullTagId); @@ -124,7 +126,10 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLog auto value_metrics = report->mutable_value_metrics(); for (const auto& pair : mPastBuckets) { ValueMetricData* metricData = value_metrics->add_data(); - *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue(); + *metricData->mutable_dimensions_in_what() = + pair.first.getDimensionKeyInWhat().getDimensionsValue(); + *metricData->mutable_dimensions_in_condition() = + pair.first.getDimensionKeyInCondition().getDimensionsValue(); for (const auto& bucket : pair.second) { ValueBucketInfo* bucketInfo = metricData->add_bucket_info(); bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); @@ -146,16 +151,24 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS); for (const auto& pair : mPastBuckets) { - const HashableDimensionKey& hashableKey = pair.first; - VLOG(" dimension key %s", hashableKey.c_str()); + const MetricDimensionKey& dimensionKey = pair.first; + VLOG(" dimension key %s", dimensionKey.c_str()); long long wrapperToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); // First fill dimension. long long dimensionToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); - writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput); + writeDimensionsValueProtoToStream( + dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput); protoOutput->end(dimensionToken); + if (dimensionKey.hasDimensionKeyInCondition()) { + long long dimensionInConditionToken = protoOutput->start( + FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION); + writeDimensionsValueProtoToStream( + dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput); + protoOutput->end(dimensionInConditionToken); + } // Then fill bucket_info (ValueBucketInfo). for (const auto& bucket : pair.second) { @@ -239,7 +252,7 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven } } -bool ValueMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) { +bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { // ===========GuardRail============== // 1. Report the tuple count if the tuple count > soft limit if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) { @@ -260,7 +273,7 @@ bool ValueMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) } void ValueMetricProducer::onMatchedLogEventInternalLocked( - const size_t matcherIndex, const HashableDimensionKey& eventKey, + const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) { uint64_t eventTimeNs = event.GetTimestampNs(); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 9f750cf419b5..121ec7d18515 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -49,7 +49,7 @@ public: protected: void onMatchedLogEventInternalLocked( - const size_t matcherIndex, const HashableDimensionKey& eventKey, + const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) override; @@ -99,16 +99,16 @@ private: long sum; } Interval; - std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket; + std::unordered_map<MetricDimensionKey, Interval> mCurrentSlicedBucket; // Save the past buckets and we can clear when the StatsLogReport is dumped. // TODO: Add a lock to mPastBuckets. - std::unordered_map<HashableDimensionKey, std::vector<ValueBucket>> mPastBuckets; + std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets; std::shared_ptr<FieldValueMap> getValueFields(const LogEvent& event); // Util function to check whether the specified dimension hits the guardrail. - bool hitGuardRailLocked(const HashableDimensionKey& newKey); + bool hitGuardRailLocked(const MetricDimensionKey& newKey); static const size_t kBucketSize = sizeof(ValueBucket{}); diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index c2d2cea2a1ff..45735a866978 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -60,8 +60,9 @@ struct DurationBucket { class DurationTracker { public: - DurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey, - sp<ConditionWizard> wizard, int conditionIndex, bool nesting, + DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, + sp<ConditionWizard> wizard, int conditionIndex, + const FieldMatcher& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced, const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers) : mConfigKey(key), @@ -70,6 +71,7 @@ public: mWizard(wizard), mConditionTrackerIndex(conditionIndex), mBucketSizeNs(bucketSizeNs), + mDimensionInCondition(dimensionInCondition), mNested(nesting), mCurrentBucketStartTimeNs(currentBucketStartNs), mDuration(0), @@ -79,6 +81,8 @@ public: virtual ~DurationTracker(){}; + virtual unique_ptr<DurationTracker> clone(const uint64_t eventTime) = 0; + virtual void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) = 0; virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime, @@ -92,7 +96,7 @@ public: // events, so that the owner can safely remove the tracker. virtual bool flushIfNeeded( uint64_t timestampNs, - std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) = 0; + std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0; // Predict the anomaly timestamp given the current status. virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker, @@ -100,6 +104,10 @@ public: // Dump internal states for debugging virtual void dumpStates(FILE* out, bool verbose) const = 0; + void setEventKey(const MetricDimensionKey& eventKey) { + mEventKey = eventKey; + } + protected: // Starts the anomaly alarm. void startAnomalyAlarm(const uint64_t eventTime) { @@ -150,7 +158,7 @@ protected: const int64_t mTrackerId; - HashableDimensionKey mEventKey; + MetricDimensionKey mEventKey; sp<ConditionWizard> mWizard; @@ -158,6 +166,8 @@ protected: const int64_t mBucketSizeNs; + const FieldMatcher mDimensionInCondition; + const bool mNested; uint64_t mCurrentBucketStartTimeNs; diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp index 412a0c935766..db7dea4afd04 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -25,13 +25,23 @@ namespace os { namespace statsd { MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id, - const HashableDimensionKey& eventKey, - sp<ConditionWizard> wizard, int conditionIndex, bool nesting, + const MetricDimensionKey& eventKey, + sp<ConditionWizard> wizard, int conditionIndex, + const FieldMatcher& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers) - : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, - bucketSizeNs, conditionSliced, anomalyTrackers) { + : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting, + currentBucketStartNs, bucketSizeNs, conditionSliced, anomalyTrackers) { +} + +unique_ptr<DurationTracker> MaxDurationTracker::clone(const uint64_t eventTime) { + auto clonedTracker = make_unique<MaxDurationTracker>(*this); + for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end(); ++it) { + it->second.lastStartTime = eventTime; + it->second.lastDuration = 0; + } + return clonedTracker; } bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { @@ -44,7 +54,7 @@ bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mInfos.size() + 1; StatsdStats::getInstance().noteMetricDimensionSize( - mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()), + mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey), newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { @@ -149,7 +159,7 @@ void MaxDurationTracker::noteStopAll(const uint64_t eventTime) { } bool MaxDurationTracker::flushIfNeeded( - uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) { + uint64_t eventTime, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) { if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) { return false; } @@ -236,8 +246,14 @@ void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) { if (pair.second.state == kStopped) { continue; } - bool conditionMet = mWizard->query(mConditionTrackerIndex, pair.second.conditionKeys) == - ConditionState::kTrue; + std::unordered_set<HashableDimensionKey> conditionDimensionKeySet; + ConditionState conditionState = mWizard->query( + mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition, + &conditionDimensionKeySet); + bool conditionMet = (conditionState == ConditionState::kTrue) && + (!mDimensionInCondition.has_field() || + conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) != + conditionDimensionKeySet.end()); VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet); noteConditionChanged(pair.first, conditionMet, timestamp); } diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index 661d1311293a..4d32a0637d56 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -29,10 +29,15 @@ namespace statsd { class MaxDurationTracker : public DurationTracker { public: MaxDurationTracker(const ConfigKey& key, const int64_t& id, - const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, - int conditionIndex, bool nesting, uint64_t currentBucketStartNs, - uint64_t bucketSizeNs, bool conditionSliced, + const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard, + int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting, + uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced, const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers); + + MaxDurationTracker(const MaxDurationTracker& tracker) = default; + + unique_ptr<DurationTracker> clone(const uint64_t eventTime) override; + void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) override; void noteStop(const HashableDimensionKey& key, const uint64_t eventTime, @@ -41,7 +46,7 @@ public: bool flushIfNeeded( uint64_t timestampNs, - std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override; + std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override; void onSlicedConditionMayChange(const uint64_t timestamp) override; void onConditionChanged(bool condition, const uint64_t timestamp) override; diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index 75d7c0898d78..0feae369b0d5 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -25,17 +25,25 @@ namespace statsd { using std::pair; OringDurationTracker::OringDurationTracker( - const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey, - sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, + const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, + sp<ConditionWizard> wizard, int conditionIndex, + const FieldMatcher& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers) - : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, - bucketSizeNs, conditionSliced, anomalyTrackers), + : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting, + currentBucketStartNs, bucketSizeNs, conditionSliced, anomalyTrackers), mStarted(), mPaused() { mLastStartTime = 0; } +unique_ptr<DurationTracker> OringDurationTracker::clone(const uint64_t eventTime) { + auto clonedTracker = make_unique<OringDurationTracker>(*this); + clonedTracker->mLastStartTime = eventTime; + clonedTracker->mDuration = 0; + return clonedTracker; +} + bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { // ===========GuardRail============== // 1. Report the tuple count if the tuple count > soft limit @@ -45,7 +53,7 @@ bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { size_t newTupleCount = mConditionKeyMap.size() + 1; StatsdStats::getInstance().noteMetricDimensionSize( - mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()), + mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey), newTupleCount); // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { @@ -76,7 +84,6 @@ void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condi if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) { mConditionKeyMap[key] = conditionKey; } - VLOG("Oring: %s start, condition %d", key.c_str(), condition); } @@ -128,7 +135,7 @@ void OringDurationTracker::noteStopAll(const uint64_t timestamp) { } bool OringDurationTracker::flushIfNeeded( - uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) { + uint64_t eventTime, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) { if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) { return false; } @@ -184,8 +191,14 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) ++it; continue; } - if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) != - ConditionState::kTrue) { + std::unordered_set<HashableDimensionKey> conditionDimensionKeySet; + ConditionState conditionState = + mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key], + mDimensionInCondition, &conditionDimensionKeySet); + if (conditionState != ConditionState::kTrue || + (mDimensionInCondition.has_field() && + conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) == + conditionDimensionKeySet.end())) { startedToPaused.push_back(*it); it = mStarted.erase(it); VLOG("Key %s started -> paused", key.c_str()); @@ -210,8 +223,14 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) ++it; continue; } - if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) == - ConditionState::kTrue) { + std::unordered_set<HashableDimensionKey> conditionDimensionKeySet; + ConditionState conditionState = + mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key], + mDimensionInCondition, &conditionDimensionKeySet); + if (conditionState == ConditionState::kTrue && + (!mDimensionInCondition.has_field() || + conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) + != conditionDimensionKeySet.end())) { pausedToStarted.push_back(*it); it = mPaused.erase(it); VLOG("Key %s paused -> started", key.c_str()); diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h index 43469ca9a551..75b5a815de9c 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h @@ -28,11 +28,15 @@ namespace statsd { class OringDurationTracker : public DurationTracker { public: OringDurationTracker(const ConfigKey& key, const int64_t& id, - const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, - int conditionIndex, bool nesting, uint64_t currentBucketStartNs, - uint64_t bucketSizeNs, bool conditionSliced, + const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard, + int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting, + uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced, const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers); + OringDurationTracker(const OringDurationTracker& tracker) = default; + + unique_ptr<DurationTracker> clone(const uint64_t eventTime) override; + void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) override; void noteStop(const HashableDimensionKey& key, const uint64_t eventTime, @@ -44,7 +48,7 @@ public: bool flushIfNeeded( uint64_t timestampNs, - std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override; + std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override; int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker, const uint64_t currentTimestamp) const override; diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index a41f30c2bece..6c6140081bd8 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -54,6 +54,9 @@ const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4; void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue, ProtoOutputStream* protoOutput) { + if (!dimensionsValue.has_field()) { + return; + } protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field()); switch (dimensionsValue.value_case()) { case DimensionsValue::ValueCase::kValueStr: @@ -103,6 +106,9 @@ const int FIELD_CHILD = 3; void writeFieldProtoToStream( const Field& field, util::ProtoOutputStream* protoOutput) { + if (!field.has_field()) { + return; + } protoOutput->write(FIELD_TYPE_INT32 | FIELD_FIELD, field.field()); if (field.has_position_index()) { protoOutput->write(FIELD_TYPE_INT32 | FIELD_POSITION_INDEX, field.position_index()); diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h index 160b1f40243f..31f51a7ac0a4 100644 --- a/cmds/statsd/src/stats_util.h +++ b/cmds/statsd/src/stats_util.h @@ -28,13 +28,14 @@ namespace os { namespace statsd { const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey(); +const MetricDimensionKey DEFAULT_METRIC_DIMENSION_KEY = MetricDimensionKey(); // Minimum bucket size in seconds const long kMinBucketSizeSec = 5 * 60; typedef std::map<int64_t, std::vector<HashableDimensionKey>> ConditionKey; -typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap; +typedef std::unordered_map<MetricDimensionKey, int64_t> DimToValMap; } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp index f912e4b2cd24..3af684fa6069 100644 --- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp +++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp @@ -56,7 +56,7 @@ void SubscriberReporter::removeConfig(const ConfigKey& configKey) { void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey, const Subscription& subscription, - const HashableDimensionKey& dimKey) const { + const MetricDimensionKey& dimKey) const { // Reminder about ids: // subscription id - name of the Subscription (that ties the Alert to the broadcast) // subscription rule_id - the name of the Alert (that triggers the broadcast) @@ -92,7 +92,7 @@ void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey, void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender, const ConfigKey& configKey, const Subscription& subscription, - const HashableDimensionKey& dimKey) const { + const MetricDimensionKey& dimKey) const { VLOG("SubscriberReporter::sendBroadcastLocked called."); if (mStatsCompanionService == nullptr) { ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService."); @@ -107,8 +107,8 @@ void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender, } StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue( - const HashableDimensionKey& dimKey) { - return protoToStatsDimensionsValue(dimKey.getDimensionsValue()); + const MetricDimensionKey& dimKey) { + return protoToStatsDimensionsValue(dimKey.getDimensionKeyInWhat().getDimensionsValue()); } StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue( diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h index 5bb458a8b1d8..13fc7fd06279 100644 --- a/cmds/statsd/src/subscriber/SubscriberReporter.h +++ b/cmds/statsd/src/subscriber/SubscriberReporter.h @@ -80,7 +80,7 @@ public: */ void alertBroadcastSubscriber(const ConfigKey& configKey, const Subscription& subscription, - const HashableDimensionKey& dimKey) const; + const MetricDimensionKey& dimKey) const; private: SubscriberReporter() {}; @@ -101,7 +101,7 @@ private: void sendBroadcastLocked(const sp<android::IBinder>& intentSender, const ConfigKey& configKey, const Subscription& subscription, - const HashableDimensionKey& dimKey) const; + const MetricDimensionKey& dimKey) const; /** Converts a stats_log.proto DimensionsValue to a StatsDimensionsValue. */ static StatsDimensionsValue protoToStatsDimensionsValue( @@ -109,7 +109,7 @@ private: /** Converts a HashableDimensionKey to a StatsDimensionsValue. */ static StatsDimensionsValue protoToStatsDimensionsValue( - const HashableDimensionKey& dimKey); + const MetricDimensionKey& dimKey); }; } // namespace statsd diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp index 66bfa68ecc55..a415ea1a3069 100644 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -33,14 +33,14 @@ namespace statsd { const ConfigKey kConfigKey(0, 12345); -HashableDimensionKey getMockDimensionKey(int key, string value) { +MetricDimensionKey getMockMetricDimensionKey(int key, string value) { DimensionsValue dimensionsValue; dimensionsValue.set_field(key); dimensionsValue.set_value_str(value); - return HashableDimensionKey(dimensionsValue); + return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY); } -void AddValueToBucket(const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list, +void AddValueToBucket(const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list, std::shared_ptr<DimToValMap> bucket) { for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) { (*bucket)[itr->first] += itr->second; @@ -48,7 +48,7 @@ void AddValueToBucket(const std::vector<std::pair<HashableDimensionKey, long>>& } std::shared_ptr<DimToValMap> MockBucket( - const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list) { + const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list) { std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>(); AddValueToBucket(key_value_pair_list, bucket); return bucket; @@ -56,7 +56,7 @@ std::shared_ptr<DimToValMap> MockBucket( // Returns the value, for the given key, in that bucket, or 0 if not present. int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket, - const HashableDimensionKey& key) { + const MetricDimensionKey& key) { const auto& itr = bucket->find(key); if (itr != bucket->end()) { return itr->second; @@ -68,14 +68,14 @@ int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket, bool detectAnomaliesPass(AnomalyTracker& tracker, const int64_t& bucketNum, const std::shared_ptr<DimToValMap>& currentBucket, - const std::set<const HashableDimensionKey>& trueList, - const std::set<const HashableDimensionKey>& falseList) { - for (HashableDimensionKey key : trueList) { + const std::set<const MetricDimensionKey>& trueList, + const std::set<const MetricDimensionKey>& falseList) { + for (MetricDimensionKey key : trueList) { if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) { return false; } } - for (HashableDimensionKey key : falseList) { + for (MetricDimensionKey key : falseList) { if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) { return false; } @@ -100,7 +100,7 @@ void detectAndDeclareAnomalies(AnomalyTracker& tracker, void checkRefractoryTimes(AnomalyTracker& tracker, const int64_t& currTimestampNs, const int32_t& refractoryPeriodSec, - const std::unordered_map<HashableDimensionKey, int64_t>& timestamps) { + const std::unordered_map<MetricDimensionKey, int64_t>& timestamps) { for (const auto& kv : timestamps) { if (kv.second < 0) { // Make sure that, if there is a refractory period, it is already past. @@ -124,9 +124,9 @@ TEST(AnomalyTrackerTest, TestConsecutiveBuckets) { alert.set_trigger_if_sum_gt(2); AnomalyTracker anomalyTracker(alert, kConfigKey); - HashableDimensionKey keyA = getMockDimensionKey(1, "a"); - HashableDimensionKey keyB = getMockDimensionKey(1, "b"); - HashableDimensionKey keyC = getMockDimensionKey(1, "c"); + MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a"); + MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b"); + MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c"); int64_t eventTimestamp0 = 10 * NS_PER_SEC; int64_t eventTimestamp1 = bucketSizeNs + 11 * NS_PER_SEC; @@ -269,11 +269,11 @@ TEST(AnomalyTrackerTest, TestSparseBuckets) { alert.set_trigger_if_sum_gt(2); AnomalyTracker anomalyTracker(alert, kConfigKey); - HashableDimensionKey keyA = getMockDimensionKey(1, "a"); - HashableDimensionKey keyB = getMockDimensionKey(1, "b"); - HashableDimensionKey keyC = getMockDimensionKey(1, "c"); - HashableDimensionKey keyD = getMockDimensionKey(1, "d"); - HashableDimensionKey keyE = getMockDimensionKey(1, "e"); + MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a"); + MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b"); + MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c"); + MetricDimensionKey keyD = getMockMetricDimensionKey(1, "d"); + MetricDimensionKey keyE = getMockMetricDimensionKey(1, "e"); std::shared_ptr<DimToValMap> bucket9 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}}); std::shared_ptr<DimToValMap> bucket16 = MockBucket({{keyB, 4}}); diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index 819f2bebf327..d1b7b2842fd2 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -78,7 +78,7 @@ void makeWakeLockEvent( std::map<int64_t, std::vector<HashableDimensionKey>> getWakeLockQueryKey( const Position position, const std::vector<int> &uids, const string& conditionName) { - std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap; + std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap; std::vector<int> uid_indexes; switch(position) { case Position::FIRST: @@ -265,6 +265,9 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { TEST(SimpleConditionTrackerTest, TestSlicedCondition) { for (Position position : { Position::ANY, Position::FIRST, Position::LAST}) { + FieldMatcher dimensionInCondition; + std::unordered_set<HashableDimensionKey> dimensionKeys; + SimplePredicate simplePredicate = getWakeLockHeldCondition( true /*nesting*/, true /*default to false*/, true /*output slice by uid*/, position); @@ -307,7 +310,8 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { const auto queryKey = getWakeLockQueryKey(position, uids, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); // another wake lock acquired by this uid @@ -361,7 +365,8 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { // query again conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); } @@ -369,6 +374,9 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { } TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { + FieldMatcher dimensionInCondition; + std::unordered_set<HashableDimensionKey> dimensionKeys; + SimplePredicate simplePredicate = getWakeLockHeldCondition( true /*nesting*/, true /*default to false*/, false /*slice output by uid*/, Position::ANY /* position */); @@ -410,7 +418,8 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { ConditionKey queryKey; conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); // another wake lock acquired by this uid @@ -452,13 +461,17 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { // query again conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + dimensionKeys.clear(); + conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); } TEST(SimpleConditionTrackerTest, TestStopAll) { for (Position position : {Position::ANY, Position::FIRST, Position::LAST}) { + FieldMatcher dimensionInCondition; + std::unordered_set<HashableDimensionKey> dimensionKeys; SimplePredicate simplePredicate = getWakeLockHeldCondition( true /*nesting*/, true /*default to false*/, true /*output slice by uid*/, position); @@ -502,7 +515,8 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); // another wake lock acquired by uid2 @@ -528,8 +542,9 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { // TEST QUERY const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; + conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + conditionCache, dimensionKeys); - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); @@ -550,15 +565,15 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { // TEST QUERY const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; - - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); // TEST QUERY const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName); conditionCache[0] = ConditionState::kNotEvaluated; - - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition, + conditionCache, dimensionKeys); EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); } diff --git a/cmds/statsd/tests/dimension_test.cpp b/cmds/statsd/tests/dimension_test.cpp new file mode 100644 index 000000000000..678abaed5fd8 --- /dev/null +++ b/cmds/statsd/tests/dimension_test.cpp @@ -0,0 +1,149 @@ +// 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 "dimension.h" + +#include <gtest/gtest.h> + +using namespace android::os::statsd; + +#ifdef __ANDROID__ + +TEST(DimensionTest, subLeafNodes) { + DimensionsValue dimension; + int tagId = 100; + dimension.set_field(tagId); + auto child = dimension.mutable_value_tuple()->add_dimensions_value(); + child->set_field(1); + child->set_value_int(2000); + + child = dimension.mutable_value_tuple()->add_dimensions_value(); + child->set_field(3); + child->set_value_str("test"); + + child = dimension.mutable_value_tuple()->add_dimensions_value(); + child->set_field(4); + auto grandChild = child->mutable_value_tuple()->add_dimensions_value(); + grandChild->set_field(1); + grandChild->set_value_float(1.3f); + grandChild = child->mutable_value_tuple()->add_dimensions_value(); + grandChild->set_field(3); + grandChild->set_value_str("tag"); + + child = dimension.mutable_value_tuple()->add_dimensions_value(); + child->set_field(6); + child->set_value_bool(false); + + DimensionsValue sub_dimension; + FieldMatcher matcher; + + // Tag id not matched. + matcher.set_field(tagId + 1); + EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); + + // Field not exist. + matcher.Clear(); + matcher.set_field(tagId); + matcher.add_child()->set_field(5); + EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); + + // Field exists. + matcher.Clear(); + matcher.set_field(tagId); + matcher.add_child()->set_field(3); + EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); + + // Field exists. + matcher.Clear(); + sub_dimension.Clear(); + matcher.set_field(tagId); + matcher.add_child()->set_field(6); + EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); + + // Field exists. + matcher.Clear(); + sub_dimension.Clear(); + matcher.set_field(tagId); + matcher.add_child()->set_field(1); + EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); + + // Not leaf field. + matcher.Clear(); + sub_dimension.Clear(); + matcher.set_field(tagId); + matcher.add_child()->set_field(4); + EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); + + // Grand-child leaf field not exist. + matcher.Clear(); + sub_dimension.Clear(); + matcher.set_field(tagId); + auto childMatcher = matcher.add_child(); + childMatcher->set_field(4); + childMatcher->add_child()->set_field(2); + EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); + + // Grand-child leaf field. + matcher.Clear(); + sub_dimension.Clear(); + matcher.set_field(tagId); + childMatcher = matcher.add_child(); + childMatcher->set_field(4); + childMatcher->add_child()->set_field(1); + EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); + + matcher.Clear(); + sub_dimension.Clear(); + matcher.set_field(tagId); + childMatcher = matcher.add_child(); + childMatcher->set_field(4); + childMatcher->add_child()->set_field(3); + EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); + + // Multiple grand-child fields. + matcher.Clear(); + sub_dimension.Clear(); + matcher.set_field(tagId); + childMatcher = matcher.add_child(); + childMatcher->set_field(4); + childMatcher->add_child()->set_field(3); + childMatcher->add_child()->set_field(1); + EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); + + // Multiple fields. + matcher.Clear(); + sub_dimension.Clear(); + matcher.set_field(tagId); + childMatcher = matcher.add_child(); + childMatcher->set_field(4); + childMatcher->add_child()->set_field(3); + childMatcher->add_child()->set_field(1); + matcher.add_child()->set_field(3); + EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension)); + + // Subset of the fields not exist. + matcher.Clear(); + sub_dimension.Clear(); + matcher.set_field(tagId); + childMatcher = matcher.add_child(); + childMatcher->set_field(4); + childMatcher->add_child()->set_field(3); + childMatcher->add_child()->set_field(1); + matcher.add_child()->set_field(2); + EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension)); +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp new file mode 100644 index 000000000000..b5d48efb55f6 --- /dev/null +++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp @@ -0,0 +1,725 @@ +// 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 <gtest/gtest.h> + +#include "src/StatsLogProcessor.h" +#include "src/stats_log_util.h" +#include "tests/statsd_test_util.h" + +#include <vector> + +namespace android { +namespace os { +namespace statsd { + +#ifdef __ANDROID__ + +namespace { + +StatsdConfig CreateCountMetricWithNoLinkConfig() { + StatsdConfig config; + auto screenBrightnessChangeAtomMatcher = CreateScreenBrightnessChangedAtomMatcher(); + *config.add_atom_matcher() = screenBrightnessChangeAtomMatcher; + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + + auto screenIsOffPredicate = CreateScreenIsOffPredicate(); + *config.add_predicate() = screenIsOffPredicate; + + auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by any attribution node and both by uid and tag. + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateAttributionUidAndTagDimensions( + android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *config.add_predicate() = holdingWakelockPredicate; + + auto combinationPredicate = config.add_predicate(); + combinationPredicate->set_id(987654); + combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR); + addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); + addPredicateToPredicateCombination(holdingWakelockPredicate, combinationPredicate); + + auto metric = config.add_count_metric(); + metric->set_id(StringToId("ScreenBrightnessChangeMetric")); + metric->set_what(screenBrightnessChangeAtomMatcher.id()); + metric->set_condition(combinationPredicate->id()); + *metric->mutable_dimensions_in_what() = CreateDimensions( + android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */}); + *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions( + android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + metric->set_bucket(ONE_MINUTE); + return config; +} + +} // namespace + +TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { + ConfigKey cfgKey; + auto config = CreateCountMetricWithNoLinkConfig(); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + std::vector<AttributionNode> attributions1 = + {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; + + std::vector<AttributionNode> attributions2 = + {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; + + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100)); + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_ON, bucketStartTimeNs + bucketSizeNs + 1)); + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * bucketSizeNs - 10)); + + events.push_back(CreateAcquireWakelockEvent( + attributions1, "wl1", bucketStartTimeNs + 200)); + events.push_back(CreateReleaseWakelockEvent( + attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1)); + + events.push_back(CreateAcquireWakelockEvent( + attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 100)); + events.push_back(CreateReleaseWakelockEvent( + attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 50)); + + events.push_back(CreateScreenBrightnessChangedEvent( + 123, bucketStartTimeNs + 11)); + events.push_back(CreateScreenBrightnessChangedEvent( + 123, bucketStartTimeNs + 101)); + events.push_back(CreateScreenBrightnessChangedEvent( + 123, bucketStartTimeNs + 201)); + events.push_back(CreateScreenBrightnessChangedEvent( + 456, bucketStartTimeNs + 203)); + events.push_back(CreateScreenBrightnessChangedEvent( + 456, bucketStartTimeNs + bucketSizeNs - 99)); + events.push_back(CreateScreenBrightnessChangedEvent( + 456, bucketStartTimeNs + bucketSizeNs - 2)); + events.push_back(CreateScreenBrightnessChangedEvent( + 789, bucketStartTimeNs + bucketSizeNs - 1)); + events.push_back(CreateScreenBrightnessChangedEvent( + 456, bucketStartTimeNs + bucketSizeNs + 2)); + events.push_back(CreateScreenBrightnessChangedEvent( + 789, bucketStartTimeNs + 2 * bucketSizeNs - 11)); + events.push_back(CreateScreenBrightnessChangedEvent( + 789, bucketStartTimeNs + 2 * bucketSizeNs - 9)); + events.push_back(CreateScreenBrightnessChangedEvent( + 789, bucketStartTimeNs + 2 * bucketSizeNs - 1)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + + EXPECT_EQ(countMetrics.data_size(), 7); + auto data = countMetrics.data(0); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs ); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123); + EXPECT_FALSE(data.dimensions_in_condition().has_field()); + + data = countMetrics.data(1); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123); + ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111); + + data = countMetrics.data(2); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 3); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456); + ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111); + + data = countMetrics.data(3); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).count(), 2); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).count(), 1); + EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456); + ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333); + + data = countMetrics.data(4); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 2); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789); + EXPECT_FALSE(data.dimensions_in_condition().has_field()); + + data = countMetrics.data(5); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789); + ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111); + + data = countMetrics.data(6); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789); + ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333); +} + +namespace { + +StatsdConfig CreateCountMetricWithLinkConfig() { + StatsdConfig config; + auto appCrashMatcher = CreateProcessCrashAtomMatcher(); + *config.add_atom_matcher() = appCrashMatcher; + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); + + auto screenIsOffPredicate = CreateScreenIsOffPredicate(); + auto isSyncingPredicate = CreateIsSyncingPredicate(); + auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); + *syncDimension = CreateAttributionUidAndTagDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + syncDimension->add_child()->set_field(2 /* name field*/); + + *config.add_predicate() = screenIsOffPredicate; + *config.add_predicate() = isSyncingPredicate; + auto combinationPredicate = config.add_predicate(); + combinationPredicate->set_id(987654); + combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR); + addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); + addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); + + auto metric = config.add_count_metric(); + metric->set_bucket(ONE_MINUTE); + metric->set_id(StringToId("AppCrashMetric")); + metric->set_what(appCrashMatcher.id()); + metric->set_condition(combinationPredicate->id()); + *metric->mutable_dimensions_in_what() = CreateDimensions( + android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */}); + *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + + // Links between crash atom and condition of app is in syncing. + auto links = metric->add_links(); + links->set_condition(isSyncingPredicate.id()); + auto dimensionWhat = links->mutable_fields_in_what(); + dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + dimensionWhat->add_child()->set_field(1); // uid field. + *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + return config; +} + +} // namespace + +TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { + ConfigKey cfgKey; + auto config = CreateCountMetricWithLinkConfig(); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL; + + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + std::vector<AttributionNode> attributions1 = + {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; + + std::vector<AttributionNode> attributions2 = + {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; + + std::vector<std::unique_ptr<LogEvent>> events; + + events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 11)); + events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 101)); + events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 101)); + + events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 201)); + events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 211)); + events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 211)); + + events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 401)); + events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 401)); + events.push_back(CreateAppCrashEvent(555, bucketStartTimeNs + 401)); + + events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + bucketSizeNs + 301)); + events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + bucketSizeNs + 301)); + + events.push_back(CreateAppCrashEvent(777, bucketStartTimeNs + bucketSizeNs + 701)); + + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100)); + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202)); + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 700)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 300)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + + events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 400)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 600)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + StatsLogReport::CountMetricDataWrapper countMetrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics); + + EXPECT_EQ(countMetrics.data_size(), 5); + auto data = countMetrics.data(0); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111); + EXPECT_FALSE(data.dimensions_in_condition().has_field()); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + + data = countMetrics.data(1); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111); + ValidateAttributionUidAndTagDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1"); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 2); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + + data = countMetrics.data(2); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 222); + EXPECT_FALSE(data.dimensions_in_condition().has_field()); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 2); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + + data = countMetrics.data(3); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333); + ValidateAttributionUidAndTagDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2"); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).count(), 1); + EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + + data = countMetrics.data(4); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 777); + EXPECT_FALSE(data.dimensions_in_condition().has_field()); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).count(), 1); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); +} + +namespace { + +StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType aggregationType) { + StatsdConfig config; + *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher(); + *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); + + auto inBatterySaverModePredicate = CreateBatterySaverModePredicate(); + + auto screenIsOffPredicate = CreateScreenIsOffPredicate(); + auto isSyncingPredicate = CreateIsSyncingPredicate(); + auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); + *syncDimension = CreateAttributionUidAndTagDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + syncDimension->add_child()->set_field(2 /* name field */); + + *config.add_predicate() = inBatterySaverModePredicate; + *config.add_predicate() = screenIsOffPredicate; + *config.add_predicate() = isSyncingPredicate; + auto combinationPredicate = config.add_predicate(); + combinationPredicate->set_id(987654); + combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR); + addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); + addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); + + auto metric = config.add_duration_metric(); + metric->set_bucket(ONE_MINUTE); + metric->set_id(StringToId("BatterySaverModeDurationMetric")); + metric->set_what(inBatterySaverModePredicate.id()); + metric->set_condition(combinationPredicate->id()); + *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + return config; +} + +} // namespace + + +TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { + for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { + ConfigKey cfgKey; + auto config = CreateDurationMetricConfigNoLink(aggregationType); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + std::vector<AttributionNode> attributions1 = + {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; + + std::vector<AttributionNode> attributions2 = + {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; + + std::vector<std::unique_ptr<LogEvent>> events; + + events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 1)); + events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 101)); + events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 110)); + + events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 201)); + events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 500)); + + events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 600)); + events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 850)); + + events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + bucketSizeNs + 870)); + events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 900)); + + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100)); + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202)); + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 800)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 300)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + + events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 700)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + StatsLogReport::DurationMetricDataWrapper metrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics); + + EXPECT_EQ(metrics.data_size(), 3); + auto data = metrics.data(0); + EXPECT_FALSE(data.dimensions_in_what().has_field()); + EXPECT_FALSE(data.dimensions_in_condition().has_field()); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30); + EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + + data = metrics.data(1); + EXPECT_FALSE(data.dimensions_in_what().has_field()); + ValidateAttributionUidAndTagDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1"); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300); + EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + + data = metrics.data(2); + EXPECT_FALSE(data.dimensions_in_what().has_field()); + ValidateAttributionUidAndTagDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2"); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700); + EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + } +} + +namespace { + +StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType aggregationType) { + StatsdConfig config; + *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); + + auto screenIsOffPredicate = CreateScreenIsOffPredicate(); + auto isSyncingPredicate = CreateIsSyncingPredicate(); + auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); + *syncDimension = CreateAttributionUidAndTagDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + syncDimension->add_child()->set_field(2 /* name field */); + + auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); + *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ }); + + *config.add_predicate() = screenIsOffPredicate; + *config.add_predicate() = isSyncingPredicate; + *config.add_predicate() = isInBackgroundPredicate; + auto combinationPredicate = config.add_predicate(); + combinationPredicate->set_id(987654); + combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR); + addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); + addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); + + auto metric = config.add_duration_metric(); + metric->set_bucket(ONE_MINUTE); + metric->set_id(StringToId("AppInBackgroundMetric")); + metric->set_what(isInBackgroundPredicate.id()); + metric->set_condition(combinationPredicate->id()); + *metric->mutable_dimensions_in_what() = CreateDimensions( + android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ }); + *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + + // Links between crash atom and condition of app is in syncing. + auto links = metric->add_links(); + links->set_condition(isSyncingPredicate.id()); + auto dimensionWhat = links->mutable_fields_in_what(); + dimensionWhat->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); + dimensionWhat->add_child()->set_field(1); // uid field. + *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + return config; +} + +} // namespace + +TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { + for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) { + ConfigKey cfgKey; + auto config = CreateDurationMetricConfigWithLink(aggregationType); + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; + + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + std::vector<AttributionNode> attributions1 = + {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; + + std::vector<AttributionNode> attributions2 = + {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(555, "GMSCoreModule2")}; + + std::vector<std::unique_ptr<LogEvent>> events; + + events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 101)); + events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + 110)); + + events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 201)); + events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + bucketSizeNs + 100)); + + events.push_back(CreateMoveToBackgroundEvent(333, bucketStartTimeNs + 399)); + events.push_back(CreateMoveToForegroundEvent(333, bucketStartTimeNs + bucketSizeNs + 800)); + + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10)); + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100)); + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202)); + events.push_back(CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 801)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 300)); + + events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400)); + events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc", + bucketStartTimeNs + bucketSizeNs - 1)); + + events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401)); + events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", + bucketStartTimeNs + bucketSizeNs + 700)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(event.get()); + } + + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + StatsLogReport::DurationMetricDataWrapper metrics; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics); + + EXPECT_EQ(metrics.data_size(), 3); + auto data = metrics.data(0); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111); + EXPECT_FALSE(data.dimensions_in_condition().has_field()); + EXPECT_EQ(data.bucket_info_size(), 1); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + + data = metrics.data(1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111); + ValidateAttributionUidAndTagDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1"); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100); + EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + + data = metrics.data(2); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333); + ValidateAttributionUidAndTagDimension( + data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2"); + EXPECT_EQ(data.bucket_info_size(), 2); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401); + EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700); + EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + } +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp index 4504a95c8ef0..233031c5c8da 100644 --- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp @@ -44,9 +44,10 @@ StatsdConfig CreateStatsdConfig() { auto screenIsOffPredicate = CreateScreenIsOffPredicate(); auto isSyncingPredicate = CreateIsSyncingPredicate(); - *isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions() = - CreateDimensions( - android::util::SYNC_STATE_CHANGED, {1 /* uid field */, 2 /* name field*/}); + auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions(); + *syncDimension = CreateAttributionUidDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); + syncDimension->add_child()->set_field(2 /* name field*/); auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = @@ -78,9 +79,8 @@ StatsdConfig CreateStatsdConfig() { auto dimensionWhat = links->mutable_fields_in_what(); dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. - auto dimensionCondition = links->mutable_fields_in_condition(); - dimensionCondition->set_field(android::util::SYNC_STATE_CHANGED); - dimensionCondition->add_child()->set_field(1); // uid field. + *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( + android::util::SYNC_STATE_CHANGED, {Position::FIRST}); // Links between crash atom and condition of app is in background. links = countMetric->add_links(); @@ -88,7 +88,7 @@ StatsdConfig CreateStatsdConfig() { dimensionWhat = links->mutable_fields_in_what(); dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. - dimensionCondition = links->mutable_fields_in_condition(); + auto dimensionCondition = links->mutable_fields_in_condition(); dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); dimensionCondition->add_child()->set_field(1); // uid field. return config; @@ -132,12 +132,14 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) { CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2 * bucketSizeNs - 100); + std::vector<AttributionNode> attributions = + {CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")}; auto syncOnEvent1 = - CreateSyncStartEvent(appUid, "ReadEmail", bucketStartTimeNs + 50); + CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50); auto syncOffEvent1 = - CreateSyncEndEvent(appUid, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300); + CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300); auto syncOnEvent2 = - CreateSyncStartEvent(appUid, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000); + CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000); auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15); diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 4ad209712905..50b3532e827a 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -67,9 +67,9 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) { // Flushes. countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); - EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) != + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != countProducer.mPastBuckets.end()); - const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY]; + const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; EXPECT_EQ(1UL, buckets.size()); EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); @@ -80,10 +80,10 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) { countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); - EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) != + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != countProducer.mPastBuckets.end()); - EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY].size()); - const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY][1]; + EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1]; EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs); EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs); EXPECT_EQ(1LL, bucketInfo2.mCount); @@ -91,9 +91,9 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) { // nothing happens in bucket 3. we should not record anything for bucket 3. countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); - EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) != + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != countProducer.mPastBuckets.end()); - const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY]; + const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; EXPECT_EQ(2UL, buckets3.size()); } @@ -124,10 +124,10 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); - EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) != + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != countProducer.mPastBuckets.end()); { - const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY]; + const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; EXPECT_EQ(1UL, buckets.size()); const auto& bucketInfo = buckets[0]; EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); @@ -167,9 +167,9 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { {getMockedDimensionKey(conditionTagId, 2, "222")}; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse)); + EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse)); - EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue)); + EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue)); CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard, bucketStartTimeNs); @@ -181,9 +181,9 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); - EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) != + EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != countProducer.mPastBuckets.end()); - const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY]; + const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; EXPECT_EQ(1UL, buckets.size()); const auto& bucketInfo = buckets[0]; EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs); @@ -229,13 +229,13 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); // One event in bucket #2. No alarm as bucket #0 is trashed out. countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); // Two events in bucket #3. countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); @@ -244,13 +244,13 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second); // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6 - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), event5.GetTimestampNs() / NS_PER_SEC + refPeriodSec); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7); EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), event7.GetTimestampNs() / NS_PER_SEC + refPeriodSec); } diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index a59f1fe66354..c9fe2523c577 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -62,9 +62,9 @@ TEST(DurationMetricTrackerTest, TestNoCondition) { durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); - EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) != + EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != durationProducer.mPastBuckets.end()); - const auto& buckets = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY]; + const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; EXPECT_EQ(2UL, buckets.size()); EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); @@ -107,9 +107,9 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); - EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) != + EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != durationProducer.mPastBuckets.end()); - const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY]; + const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; EXPECT_EQ(1UL, buckets2.size()); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index da00cae125c7..3deab3710dd1 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -114,9 +114,9 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")}; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse)); + EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse)); - EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue)); + EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue)); EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs); diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 4533ac610057..58be5b071b53 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -218,7 +218,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()-> second.front().mFields->begin()->second.value_int()); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 20); @@ -231,7 +231,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()-> second.front().mFields->begin()->second.value_int()); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec); std::shared_ptr<LogEvent> event3 = @@ -245,7 +245,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin()-> second.front().mFields->begin()->second.value_int()); - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec); // The event4 does not have the gauge field. Thus the current bucket value is 0. diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index 0772b0d4001d..203f028080d2 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -41,22 +41,24 @@ const ConfigKey kConfigKey(0, 12345); const int TagId = 1; -const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "1"); -const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; -const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); -const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); + const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; + const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); + const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); + + FieldMatcher dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, - bucketSizeNs, false, {}); + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, + false, bucketStartTimeNs, bucketSizeNs, false, {}); tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey()); // Event starts again. This would not change anything as it already starts. @@ -75,16 +77,22 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { } TEST(MaxDurationTrackerTest, TestStopAll) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); + const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; + const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); + const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); + + FieldMatcher dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, - bucketSizeNs, false, {}); + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, + false, bucketStartTimeNs, bucketSizeNs, false, {}); tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey()); @@ -105,21 +113,26 @@ TEST(MaxDurationTrackerTest, TestStopAll) { } TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); + const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; + const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); + const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); + FieldMatcher dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, - bucketSizeNs, false, {}); + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, + false, bucketStartTimeNs, bucketSizeNs, false, {}); // The event starts. tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey()); - // Starts again. Does not change anything. + // Starts again. Does not DEFAULT_DIMENSION_KEY anything. tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1, ConditionKey()); @@ -135,16 +148,21 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { } TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); + const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; + const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); + const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); + FieldMatcher dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs, - bucketSizeNs, false, {}); + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, + true, bucketStartTimeNs, bucketSizeNs, false, {}); // 2 starts tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey()); @@ -160,7 +178,8 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); // real stop now. - tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (2 * bucketSizeNs) + 5, false); + tracker.noteStop(DEFAULT_DIMENSION_KEY, + bucketStartTimeNs + (2 * bucketSizeNs) + 5, false); tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets); EXPECT_EQ(3u, buckets[eventKey].size()); @@ -170,16 +189,20 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { } TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { + const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; + const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); + + FieldMatcher dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey conditionKey1; - HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 2, "maps"); + MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps"); conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; - EXPECT_CALL(*wizard, query(_, conditionKey1)) // #4 + EXPECT_CALL(*wizard, query(_, conditionKey1, _, _)) // #4 .WillOnce(Return(ConditionState::kFalse)); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; @@ -187,8 +210,8 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { int64_t durationTimeNs = 2 * 1000; int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, - bucketSizeNs, true, {}); + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + false, bucketStartTimeNs, bucketSizeNs, true, {}); EXPECT_TRUE(tracker.mAnomalyTrackers.empty()); tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1); @@ -204,6 +227,10 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { } TEST(MaxDurationTrackerTest, TestAnomalyDetection) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1"); + const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); + const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); + FieldMatcher dimensionInCondition; int64_t metricId = 1; Alert alert; alert.set_id(101); @@ -213,7 +240,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { const int32_t refPeriodSec = 1; alert.set_refractory_period_secs(refPeriodSec); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; @@ -221,8 +248,8 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs, - bucketSizeNs, false, {anomalyTracker}); + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, + true, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker}); tracker.noteStart(key1, true, eventStartTimeNs, ConditionKey()); tracker.noteStop(key1, eventStartTimeNs + 10, false); diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index 6b8893e5973f..80e16a1f3b55 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -38,24 +38,26 @@ namespace statsd { const ConfigKey kConfigKey(0, 12345); const int TagId = 1; const int64_t metricId = 123; -const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event"); - -const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")}; -const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); -const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); TEST(OringDurationTrackerTest, TestDurationOverlap) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); + + const std::vector<HashableDimensionKey> kConditionKey1 = + {getMockedDimensionKey(TagId, 1, "maps")}; + const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); + const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); + FieldMatcher dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, - bucketStartTimeNs, bucketSizeNs, false, {}); + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + false, bucketStartTimeNs, bucketSizeNs, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); @@ -71,16 +73,23 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { } TEST(OringDurationTrackerTest, TestDurationNested) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); + + const std::vector<HashableDimensionKey> kConditionKey1 = + {getMockedDimensionKey(TagId, 1, "maps")}; + const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); + const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); + FieldMatcher dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, false, {}); + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + true, bucketStartTimeNs, bucketSizeNs, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl @@ -95,16 +104,23 @@ TEST(OringDurationTrackerTest, TestDurationNested) { } TEST(OringDurationTrackerTest, TestStopAll) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); + + const std::vector<HashableDimensionKey> kConditionKey1 = + {getMockedDimensionKey(TagId, 1, "maps")}; + const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); + const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); + FieldMatcher dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, false, {}); + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + true, bucketStartTimeNs, bucketSizeNs, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl @@ -118,17 +134,24 @@ TEST(OringDurationTrackerTest, TestStopAll) { } TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); + + const std::vector<HashableDimensionKey> kConditionKey1 = + {getMockedDimensionKey(TagId, 1, "maps")}; + const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); + const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); + FieldMatcher dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, false, {}); + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + true, bucketStartTimeNs, bucketSizeNs, false, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); @@ -150,23 +173,30 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { } TEST(OringDurationTrackerTest, TestDurationConditionChange) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); + + const std::vector<HashableDimensionKey> kConditionKey1 = + {getMockedDimensionKey(TagId, 1, "maps")}; + const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); + const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); + FieldMatcher dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - EXPECT_CALL(*wizard, query(_, key1)) // #4 + EXPECT_CALL(*wizard, query(_, key1, _, _)) // #4 .WillOnce(Return(ConditionState::kFalse)); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, - bucketStartTimeNs, bucketSizeNs, true, {}); + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + false, bucketStartTimeNs, bucketSizeNs, true, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); @@ -181,25 +211,32 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { } TEST(OringDurationTrackerTest, TestDurationConditionChange2) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); + + const std::vector<HashableDimensionKey> kConditionKey1 = + {getMockedDimensionKey(TagId, 1, "maps")}; + const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); + const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); + FieldMatcher dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - EXPECT_CALL(*wizard, query(_, key1)) + EXPECT_CALL(*wizard, query(_, key1, _, _)) .Times(2) .WillOnce(Return(ConditionState::kFalse)) .WillOnce(Return(ConditionState::kTrue)); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, - bucketStartTimeNs, bucketSizeNs, true, {}); + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + false, bucketStartTimeNs, bucketSizeNs, true, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); // condition to false; record duration 5n @@ -216,22 +253,29 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { } TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); + + const std::vector<HashableDimensionKey> kConditionKey1 = + {getMockedDimensionKey(TagId, 1, "maps")}; + const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); + const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); + FieldMatcher dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - EXPECT_CALL(*wizard, query(_, key1)) // #4 + EXPECT_CALL(*wizard, query(_, key1, _, _)) // #4 .WillOnce(Return(ConditionState::kFalse)); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, true, {}); + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + true, bucketStartTimeNs, bucketSizeNs, true, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1); @@ -249,6 +293,13 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { } TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); + + const std::vector<HashableDimensionKey> kConditionKey1 = + {getMockedDimensionKey(TagId, 1, "maps")}; + const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); + const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); + FieldMatcher dimensionInCondition; Alert alert; alert.set_id(101); alert.set_metric_id(1); @@ -256,7 +307,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { alert.set_num_buckets(2); alert.set_refractory_period_secs(1); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; @@ -264,8 +315,8 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, true, {anomalyTracker}); + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + true, bucketStartTimeNs, bucketSizeNs, true, {anomalyTracker}); // Nothing in the past bucket. tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey()); @@ -310,6 +361,12 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { } TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); + + const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")}; + const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); + const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); + FieldMatcher dimensionInCondition; Alert alert; alert.set_id(101); alert.set_metric_id(1); @@ -318,7 +375,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { const int32_t refPeriodSec = 45; alert.set_refractory_period_secs(refPeriodSec); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; @@ -326,8 +383,8 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/, - bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker}); + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + true /*nesting*/, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false); @@ -352,6 +409,13 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { } TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { + const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event"); + + const std::vector<HashableDimensionKey> kConditionKey1 = + {getMockedDimensionKey(TagId, 1, "maps")}; + const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); + const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); + FieldMatcher dimensionInCondition; Alert alert; alert.set_id(101); alert.set_metric_id(1); @@ -360,7 +424,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { const int32_t refPeriodSec = 45; alert.set_refractory_period_secs(refPeriodSec); - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; + unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey conkey; conkey[StringToId("APP_BACKGROUND")] = kConditionKey1; @@ -369,8 +433,9 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/, - bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker}); + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + true /*nesting*/, bucketStartTimeNs, bucketSizeNs, false, + {anomalyTracker}); tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1 EXPECT_EQ(1u, anomalyTracker->mAlarms.size()); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index fff3dbf5428d..55c078dcbab8 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -299,26 +299,26 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); // Value sum == 30 <= 130. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); // One event in bucket #2. No alarm as bucket #0 is trashed out. valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); // Value sum == 130 <= 130. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); // Three events in bucket #3. valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); // Anomaly at event 4 since Value sum == 131 > 130! - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5); // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6); // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period. - EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), event6->GetTimestampNs() / NS_PER_SEC + refPeriodSec); } diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp index fc7245ca54e3..ab9345af172e 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp +++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp @@ -26,6 +26,13 @@ HashableDimensionKey getMockedDimensionKey(int tagId, int key, string value) { return HashableDimensionKey(dimensionsValue); } +MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, string value) { + DimensionsValue dimensionsValue; + dimensionsValue.set_field(tagId); + dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key); + dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value); + return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY); +} } // namespace statsd } // namespace os } // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h index 23e86f92f844..0a97456c5684 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.h +++ b/cmds/statsd/tests/metrics/metrics_test_helper.h @@ -25,10 +25,12 @@ namespace statsd { class MockConditionWizard : public ConditionWizard { public: - MOCK_METHOD2( + MOCK_METHOD4( query, ConditionState(const int conditionIndex, - const ConditionKey& conditionParameters)); + const ConditionKey& conditionParameters, + const FieldMatcher& dimensionFields, + std::unordered_set<HashableDimensionKey> *dimensionKeySet)); }; class MockStatsPullerManager : public StatsPullerManager { @@ -39,6 +41,7 @@ public: }; HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value); +MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value); } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 9f4582dc6994..13055cb9e7a3 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include <gtest/gtest.h> #include "statsd_test_util.h" namespace android { @@ -27,6 +26,22 @@ AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) { return atom_matcher; } +AtomMatcher CreateScreenBrightnessChangedAtomMatcher() { + AtomMatcher atom_matcher; + atom_matcher.set_id(StringToId("ScreenBrightnessChanged")); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED); + return atom_matcher; +} + +AtomMatcher CreateUidProcessStateChangedAtomMatcher() { + AtomMatcher atom_matcher; + atom_matcher.set_id(StringToId("UidProcessStateChanged")); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED); + return atom_matcher; +} + AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name, WakelockStateChanged::State state) { AtomMatcher atom_matcher; @@ -47,6 +62,30 @@ AtomMatcher CreateReleaseWakelockAtomMatcher() { return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE); } +AtomMatcher CreateBatterySaverModeStateChangedAtomMatcher( + const string& name, BatterySaverModeStateChanged::State state) { + AtomMatcher atom_matcher; + atom_matcher.set_id(StringToId(name)); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::BATTERY_SAVER_MODE_STATE_CHANGED); + auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); + field_value_matcher->set_field(1); // State field. + field_value_matcher->set_eq_int(state); + return atom_matcher; +} + +AtomMatcher CreateBatterySaverModeStartAtomMatcher() { + return CreateBatterySaverModeStateChangedAtomMatcher( + "BatterySaverModeStart", BatterySaverModeStateChanged::ON); +} + + +AtomMatcher CreateBatterySaverModeStopAtomMatcher() { + return CreateBatterySaverModeStateChangedAtomMatcher( + "BatterySaverModeStop", BatterySaverModeStateChanged::OFF); +} + + AtomMatcher CreateScreenStateChangedAtomMatcher( const string& name, android::view::DisplayStateEnum state) { AtomMatcher atom_matcher; @@ -59,6 +98,7 @@ AtomMatcher CreateScreenStateChangedAtomMatcher( return atom_matcher; } + AtomMatcher CreateScreenTurnedOnAtomMatcher() { return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn", android::view::DisplayStateEnum::DISPLAY_STATE_ON); @@ -128,6 +168,13 @@ AtomMatcher CreateProcessCrashAtomMatcher() { "ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED); } +Predicate CreateBatterySaverModePredicate() { + Predicate predicate; + predicate.set_id(StringToId("BatterySaverIsOn")); + predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart")); + predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop")); + return predicate; +} Predicate CreateScreenIsOnPredicate() { Predicate predicate; @@ -218,6 +265,31 @@ std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( return event; } +std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) { + auto event = std::make_unique<LogEvent>( + android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs); + EXPECT_TRUE(event->write(BatterySaverModeStateChanged::ON)); + event->init(); + return event; +} + +std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) { + auto event = std::make_unique<LogEvent>( + android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs); + EXPECT_TRUE(event->write(BatterySaverModeStateChanged::OFF)); + event->init(); + return event; +} + +std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent( + int level, uint64_t timestampNs) { + auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs); + EXPECT_TRUE(event->write(level)); + event->init(); + return event; + +} + std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent( const std::vector<AttributionNode>& attributions, const string& wakelockName, const WakelockStateChanged::State state, uint64_t timestampNs) { @@ -267,9 +339,10 @@ std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t ti } std::unique_ptr<LogEvent> CreateSyncStateChangedEvent( - const int uid, const string& name, const SyncStateChanged::State state, uint64_t timestampNs) { + const std::vector<AttributionNode>& attributions, + const string& name, const SyncStateChanged::State state, uint64_t timestampNs) { auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs); - event->write(uid); + event->write(attributions); event->write(name); event->write(state); event->init(); @@ -277,13 +350,13 @@ std::unique_ptr<LogEvent> CreateSyncStateChangedEvent( } std::unique_ptr<LogEvent> CreateSyncStartEvent( - const int uid, const string& name, uint64_t timestampNs){ - return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::ON, timestampNs); + const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs){ + return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs); } std::unique_ptr<LogEvent> CreateSyncEndEvent( - const int uid, const string& name, uint64_t timestampNs) { - return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::OFF, timestampNs); + const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs) { + return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs); } std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent( diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index ff8fef0c46b6..6638893f6aeb 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -14,6 +14,7 @@ #pragma once +#include <gtest/gtest.h> #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "statslog.h" #include "src/logd/LogEvent.h" @@ -26,6 +27,18 @@ namespace statsd { // Create AtomMatcher proto to simply match a specific atom type. AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId); +// Create AtomMatcher proto for screen brightness state changed. +AtomMatcher CreateScreenBrightnessChangedAtomMatcher(); + +// Create AtomMatcher proto for starting battery save mode. +AtomMatcher CreateBatterySaverModeStartAtomMatcher(); + +// Create AtomMatcher proto for stopping battery save mode. +AtomMatcher CreateBatterySaverModeStopAtomMatcher(); + +// Create AtomMatcher proto for process state changed. +AtomMatcher CreateUidProcessStateChangedAtomMatcher(); + // Create AtomMatcher proto for acquiring wakelock. AtomMatcher CreateAcquireWakelockAtomMatcher(); @@ -59,6 +72,9 @@ Predicate CreateScreenIsOnPredicate(); // Create Predicate proto for screen is off. Predicate CreateScreenIsOffPredicate(); +// Create Predicate proto for battery saver mode. +Predicate CreateBatterySaverModePredicate(); + // Create Predicate proto for holding wakelock. Predicate CreateHoldingWakelockPredicate(); @@ -86,6 +102,15 @@ FieldMatcher CreateAttributionUidDimensions(const int atomId, std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( const android::view::DisplayStateEnum state, uint64_t timestampNs); +// Create log event for screen brightness state changed. +std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent( + int level, uint64_t timestampNs); + +// Create log event when battery saver starts. +std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs); +// Create log event when battery saver stops. +std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs); + // Create log event for app moving to background. std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs); @@ -94,11 +119,11 @@ std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t ti // Create log event when the app sync starts. std::unique_ptr<LogEvent> CreateSyncStartEvent( - const int uid, const string& name, uint64_t timestampNs); + const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs); // Create log event when the app sync ends. std::unique_ptr<LogEvent> CreateSyncEndEvent( - const int uid, const string& name, uint64_t timestampNs); + const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs); // Create log event when the app sync ends. std::unique_ptr<LogEvent> CreateAppCrashEvent( @@ -136,9 +161,12 @@ void ValidateAttributionUidAndTagDimension( template <typename T> void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) { - std::map<HashableDimensionKey, int> dimensionIndexMap; + std::map<MetricDimensionKey, int> dimensionIndexMap; for (int i = 0; i < metricData.data_size(); ++i) { - dimensionIndexMap.insert(std::make_pair(metricData.data(i).dimensions_in_what(), i)); + dimensionIndexMap.insert(std::make_pair( + MetricDimensionKey(HashableDimensionKey(metricData.data(i).dimensions_in_what()), + HashableDimensionKey(metricData.data(i).dimensions_in_condition())), + i)); } for (const auto& itr : dimensionIndexMap) { *sortedMetricData->add_data() = metricData.data(itr.second); diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk index 32a85b136023..a65095f4188d 100644 --- a/cmds/statsd/tools/dogfood/Android.mk +++ b/cmds/statsd/tools/dogfood/Android.mk @@ -27,6 +27,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \ LOCAL_PROTOC_OPTIMIZE_TYPE := lite LOCAL_PRIVILEGED_MODULE := true LOCAL_DEX_PREOPT := false +LOCAL_CERTIFICATE := platform LOCAL_PROGUARD_ENABLED := disabled include $(BUILD_PACKAGE) diff --git a/cmds/statsd/tools/dogfood/AndroidManifest.xml b/cmds/statsd/tools/dogfood/AndroidManifest.xml index 7bfde4050197..cd76c9d379b5 100644 --- a/cmds/statsd/tools/dogfood/AndroidManifest.xml +++ b/cmds/statsd/tools/dogfood/AndroidManifest.xml @@ -18,6 +18,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.statsd.dogfood" + android:sharedUserId="android.uid.system" android:versionCode="1" android:versionName="1.0" > diff --git a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config Binary files differindex c1c391483855..d05006124994 100644 --- a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config +++ b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java index d39aa1d314e6..57575ae145f6 100644 --- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java +++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java @@ -116,28 +116,32 @@ public class MainActivity extends Activity { findViewById(R.id.plug).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, 1); + StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, + StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_AC); } }); findViewById(R.id.unplug).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, 0); + StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, + StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_NONE); } }); findViewById(R.id.screen_on).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, 2); + StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, + StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON); } }); findViewById(R.id.screen_off).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, 1); + StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, + StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF); } }); @@ -255,7 +259,9 @@ public class MainActivity extends Activity { } int[] uids = new int[] {mUids[id]}; String[] tags = new String[] {"acquire"}; - StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, 0, name, 1); + StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, + StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name, + StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE); StringBuilder sb = new StringBuilder(); sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0) .append(", ").append(name).append(", 1);"); @@ -269,7 +275,9 @@ public class MainActivity extends Activity { } int[] uids = new int[] {mUids[id]}; String[] tags = new String[] {"release"}; - StatsLog.write(10, uids, tags, 0, name, 0); + StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, + StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name, + StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE); StringBuilder sb = new StringBuilder(); sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0) .append(", ").append(name).append(", 0);"); diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java index 34f6d7de0cc9..3893be49e739 100644 --- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java +++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java @@ -21,7 +21,6 @@ import android.hardware.usb.IUsbManager; import android.hardware.usb.UsbManager; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemProperties; public class UsbCommand extends Svc.Command { public UsbCommand() { @@ -37,41 +36,41 @@ public class UsbCommand extends Svc.Command { public String longHelp() { return shortHelp() + "\n" + "\n" - + "usage: svc usb setFunction [function] [usbDataUnlocked=false]\n" - + " Set the current usb function and optionally the data lock state.\n\n" + + "usage: svc usb setFunctions [function]\n" + + " Set the current usb function. If function is blank, sets to charging.\n" + " svc usb setScreenUnlockedFunctions [function]\n" - + " Sets the functions which, if the device was charging," - + " become current on screen unlock.\n" - + " svc usb getFunction\n" - + " Gets the list of currently enabled functions\n"; + + " Sets the functions which, if the device was charging, become current on" + + "screen unlock. If function is blank, turn off this feature.\n" + + " svc usb getFunctions\n" + + " Gets the list of currently enabled functions\n\n" + + "possible values of [function] are any of 'mtp', 'ptp', 'rndis', 'midi'\n"; } @Override public void run(String[] args) { - boolean validCommand = false; if (args.length >= 2) { - if ("setFunction".equals(args[1])) { - IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService( - Context.USB_SERVICE)); - boolean unlockData = false; - if (args.length >= 4) { - unlockData = Boolean.valueOf(args[3]); - } + IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService( + Context.USB_SERVICE)); + if ("setFunctions".equals(args[1])) { try { - usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null), unlockData); + usbMgr.setCurrentFunctions(UsbManager.usbFunctionsFromString( + args.length >= 3 ? args[2] : "")); } catch (RemoteException e) { System.err.println("Error communicating with UsbManager: " + e); } return; - } else if ("getFunction".equals(args[1])) { - System.err.println(SystemProperties.get("sys.usb.config")); + } else if ("getFunctions".equals(args[1])) { + try { + System.err.println( + UsbManager.usbFunctionsToString(usbMgr.getCurrentFunctions())); + } catch (RemoteException e) { + System.err.println("Error communicating with UsbManager: " + e); + } return; } else if ("setScreenUnlockedFunctions".equals(args[1])) { - IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService( - Context.USB_SERVICE)); try { - usbMgr.setScreenUnlockedFunctions((args.length >= 3 ? args[2] : - UsbManager.USB_FUNCTION_NONE)); + usbMgr.setScreenUnlockedFunctions(UsbManager.usbFunctionsFromString( + args.length >= 3 ? args[2] : "")); } catch (RemoteException e) { System.err.println("Error communicating with UsbManager: " + e); } diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt new file mode 100644 index 000000000000..58936fcc4452 --- /dev/null +++ b/config/hiddenapi-light-greylist.txt @@ -0,0 +1,1164 @@ +Landroid/accounts/AccountManager;->mContext:Landroid/content/Context; +Landroid/animation/ValueAnimator;->animateValue(F)V +Landroid/animation/ValueAnimator;->getDurationScale()F +Landroid/animation/ValueAnimator;->sDurationScale:F +Landroid/app/Activity;->convertFromTranslucent()V +Landroid/app/Activity;->convertToTranslucent(Landroid/app/Activity$TranslucentConversionListener;Landroid/app/ActivityOptions;)Z +Landroid/app/Activity;->getActivityOptions()Landroid/app/ActivityOptions; +Landroid/app/Activity;->getActivityToken()Landroid/os/IBinder; +Landroid/app/Activity;->mActivityInfo:Landroid/content/pm/ActivityInfo; +Landroid/app/ActivityManager;->clearApplicationUserData(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)Z +Landroid/app/ActivityManager;->forceStopPackage(Ljava/lang/String;)V +Landroid/app/ActivityManager;->getCurrentUser()I +Landroid/app/ActivityManager;->isLowRamDeviceStatic()Z +Landroid/app/ActivityManager;->isUserRunning(I)Z +Landroid/app/ActivityManager;->mContext:Landroid/content/Context; +Landroid/app/ActivityManagerNative;->getDefault()Landroid/app/IActivityManager; +Landroid/app/ActivityManager;->PROCESS_STATE_TOP:I +Landroid/app/ActivityManager$RecentTaskInfo;->firstActiveTime:J +Landroid/app/ActivityManager$RunningAppProcessInfo;->flags:I +Landroid/app/ActivityManager$RunningAppProcessInfo;->processState:I +Landroid/app/Activity;->mApplication:Landroid/app/Application; +Landroid/app/Activity;->mHandler:Landroid/os/Handler; +Landroid/app/Activity;->mResultCode:I +Landroid/app/Activity;->mResultData:Landroid/content/Intent; +Landroid/app/Activity;->mToken:Landroid/os/IBinder; +Landroid/app/Activity;->mWindow:Landroid/view/Window; +Landroid/app/Activity;->mWindowManager:Landroid/view/WindowManager; +Landroid/app/Activity;->setDisablePreviewScreenshots(Z)V +Landroid/app/ActivityThread$ActivityClientRecord;->activityInfo:Landroid/content/pm/ActivityInfo; +Landroid/app/ActivityThread$ActivityClientRecord;->token:Landroid/os/IBinder; +Landroid/app/ActivityThread$AppBindData;->info:Landroid/app/LoadedApk; +Landroid/app/ActivityThread$AppBindData;->instrumentationArgs:Landroid/os/Bundle; +Landroid/app/ActivityThread;->currentActivityThread()Landroid/app/ActivityThread; +Landroid/app/ActivityThread;->currentApplication()Landroid/app/Application; +Landroid/app/ActivityThread;->currentPackageName()Ljava/lang/String; +Landroid/app/ActivityThread;->currentProcessName()Ljava/lang/String; +Landroid/app/ActivityThread;->getApplication()Landroid/app/Application; +Landroid/app/ActivityThread;->getApplicationThread()Landroid/app/ActivityThread$ApplicationThread; +Landroid/app/ActivityThread;->getHandler()Landroid/os/Handler; +Landroid/app/ActivityThread;->getPackageManager()Landroid/content/pm/IPackageManager; +Landroid/app/ActivityThread;->getProcessName()Ljava/lang/String; +Landroid/app/ActivityThread$H;->BIND_SERVICE:I +Landroid/app/ActivityThread$H;->CREATE_SERVICE:I +Landroid/app/ActivityThread$H;->EXIT_APPLICATION:I +Landroid/app/ActivityThread$H;->RECEIVER:I +Landroid/app/ActivityThread$H;->RELAUNCH_ACTIVITY:I +Landroid/app/ActivityThread$H;->REMOVE_PROVIDER:I +Landroid/app/ActivityThread$H;->SERVICE_ARGS:I +Landroid/app/ActivityThread$H;->STOP_SERVICE:I +Landroid/app/ActivityThread$H;->UNBIND_SERVICE:I +Landroid/app/ActivityThread;->installContentProviders(Landroid/content/Context;Ljava/util/List;)V +Landroid/app/ActivityThread;->mActivities:Landroid/util/ArrayMap; +Landroid/app/ActivityThread;->mAllApplications:Ljava/util/ArrayList; +Landroid/app/ActivityThread;->mBoundApplication:Landroid/app/ActivityThread$AppBindData; +Landroid/app/ActivityThread;->mInitialApplication:Landroid/app/Application; +Landroid/app/ActivityThread;->mInstrumentation:Landroid/app/Instrumentation; +Landroid/app/ActivityThread;->mNumVisibleActivities:I +Landroid/app/ActivityThread;->mPackages:Landroid/util/ArrayMap; +Landroid/app/ActivityThread;->performStopActivity(Landroid/os/IBinder;ZLjava/lang/String;)V +Landroid/app/ActivityThread;->sPackageManager:Landroid/content/pm/IPackageManager; +Landroid/app/admin/DevicePolicyManager;->getTrustAgentConfiguration(Landroid/content/ComponentName;Landroid/content/ComponentName;I)Ljava/util/List; +Landroid/app/admin/DevicePolicyManager;->notifyPendingSystemUpdate(J)V +Landroid/app/admin/DevicePolicyManager;->notifyPendingSystemUpdate(JZ)V +Landroid/app/admin/DevicePolicyManager;->packageHasActiveAdmins(Ljava/lang/String;I)Z +Landroid/app/admin/DevicePolicyManager;->packageHasActiveAdmins(Ljava/lang/String;)Z +Landroid/app/admin/DevicePolicyManager;->setActiveAdmin(Landroid/content/ComponentName;ZI)V +Landroid/app/admin/DevicePolicyManager;->setActiveAdmin(Landroid/content/ComponentName;Z)V +Landroid/app/AlertDialog$Builder;->P:Lcom/android/internal/app/AlertController$AlertParams; +Landroid/app/AlertDialog;->mAlert:Lcom/android/internal/app/AlertController; +Landroid/app/AppGlobals;->getInitialApplication()Landroid/app/Application; +Landroid/app/AppGlobals;->getPackageManager()Landroid/content/pm/IPackageManager; +Landroid/app/Application;->attach(Landroid/content/Context;)V +Landroid/app/ApplicationLoaders;->getDefault()Landroid/app/ApplicationLoaders; +Landroid/app/ApplicationLoaders;->mLoaders:Landroid/util/ArrayMap; +Landroid/app/Application;->mComponentCallbacks:Ljava/util/ArrayList; +Landroid/app/Application;->mLoadedApk:Landroid/app/LoadedApk; +Landroid/app/ApplicationPackageManager;->deletePackage(Ljava/lang/String;Landroid/content/pm/IPackageDeleteObserver;I)V +Landroid/app/ApplicationPackageManager;->installExistingPackage(Ljava/lang/String;)I +Landroid/app/ApplicationPackageManager;->installExistingPackage(Ljava/lang/String;I)I +Landroid/app/AppOpsManager;->checkOp(IILjava/lang/String;)I +Landroid/app/AppOpsManager;->checkOpNoThrow(IILjava/lang/String;)I +Landroid/app/AppOpsManager;->noteOp(I)I +Landroid/app/AppOpsManager;->noteOp(IILjava/lang/String;)I +Landroid/app/AppOpsManager;->OP_POST_NOTIFICATION:I +Landroid/app/AppOpsManager;->OP_WIFI_SCAN:I +Landroid/app/AppOpsManager;->OP_WRITE_SMS:I +Landroid/app/AppOpsManager;->setMode(IILjava/lang/String;I)V +Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I +Landroid/app/backup/BackupDataInputStream;->dataSize:I +Landroid/app/backup/BackupDataInputStream;->key:Ljava/lang/String; +Landroid/app/backup/FullBackup;->backupToTar(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/FullBackupDataOutput;)I +Landroid/app/ContextImpl;->createActivityContext(Landroid/app/ActivityThread;Landroid/app/LoadedApk;Landroid/content/pm/ActivityInfo;Landroid/os/IBinder;ILandroid/content/res/Configuration;)Landroid/app/ContextImpl; +Landroid/app/ContextImpl;->getActivityToken()Landroid/os/IBinder; +Landroid/app/ContextImpl;->mMainThread:Landroid/app/ActivityThread; +Landroid/app/ContextImpl;->mPackageInfo:Landroid/app/LoadedApk; +Landroid/app/ContextImpl;->setOuterContext(Landroid/content/Context;)V +Landroid/app/DatePickerDialog;->mDatePicker:Landroid/widget/DatePicker; +Landroid/app/Dialog;->CANCEL:I +Landroid/app/Dialog;->mCancelMessage:Landroid/os/Message; +Landroid/app/Dialog;->mDismissMessage:Landroid/os/Message; +Landroid/app/Dialog;->mListenersHandler:Landroid/os/Handler; +Landroid/app/Dialog;->mOwnerActivity:Landroid/app/Activity; +Landroid/app/Dialog;->mShowMessage:Landroid/os/Message; +Landroid/app/DownloadManager$Request;->mUri:Landroid/net/Uri; +Landroid/app/Fragment;->mChildFragmentManager:Landroid/app/FragmentManagerImpl; +Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V +Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String; +Landroid/app/IActivityManager;->resumeAppSwitches()V +Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V +Landroid/app/IntentService;->mServiceHandler:Landroid/app/IntentService$ServiceHandler; +Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor; +Landroid/app/LoadedApk;->mApplication:Landroid/app/Application; +Landroid/app/LoadedApk;->mReceivers:Landroid/util/ArrayMap; +Landroid/app/LoadedApk;->mResDir:Ljava/lang/String; +Landroid/app/LoadedApk;->mResources:Landroid/content/res/Resources; +Landroid/app/LocalActivityManager;->mActivities:Ljava/util/Map; +Landroid/app/LocalActivityManager;->mActivityArray:Ljava/util/ArrayList; +Landroid/app/Notification$Builder;->mActions:Ljava/util/ArrayList; +Landroid/app/Notification;->EXTRA_SUBSTITUTE_APP_NAME:Ljava/lang/String; +Landroid/app/Notification;->isGroupSummary()Z +Landroid/app/NotificationManager;->getService()Landroid/app/INotificationManager; +Landroid/app/NotificationManager;->notifyAsUser(Ljava/lang/String;ILandroid/app/Notification;Landroid/os/UserHandle;)V +Landroid/app/Notification;->setLatestEventInfo(Landroid/content/Context;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/app/PendingIntent;)V +Landroid/app/PendingIntent;->getActivityAsUser(Landroid/content/Context;ILandroid/content/Intent;ILandroid/os/Bundle;Landroid/os/UserHandle;)Landroid/app/PendingIntent; +Landroid/app/PendingIntent;->getIntent()Landroid/content/Intent; +Landroid/app/PendingIntent;->isActivity()Z +Landroid/app/Presentation;->createPresentationContext(Landroid/content/Context;Landroid/view/Display;I)Landroid/content/Context; +Landroid/app/ProgressDialog;->mProgressNumber:Landroid/widget/TextView; +Landroid/app/Service;->setForeground(Z)V +Landroid/app/StatusBarManager;->collapsePanels()V +Landroid/app/StatusBarManager;->disable(I)V +Landroid/app/StatusBarManager;->expandNotificationsPanel()V +Landroid/app/StatusBarManager;->expandSettingsPanel(Ljava/lang/String;)V +Landroid/app/StatusBarManager;->expandSettingsPanel()V +Landroid/app/TimePickerDialog;->mTimePicker:Landroid/widget/TimePicker; +Landroid/app/WallpaperManager;->getBitmap()Landroid/graphics/Bitmap; +Landroid/app/WallpaperManager;->getBitmap(Z)Landroid/graphics/Bitmap; +Landroid/app/WallpaperManager;->getIWallpaperManager()Landroid/app/IWallpaperManager; +Landroid/app/WallpaperManager;->openDefaultWallpaper(Landroid/content/Context;I)Ljava/io/InputStream; +Landroid/app/WallpaperManager;->setBitmap(Landroid/graphics/Bitmap;Landroid/graphics/Rect;ZII)I +Landroid/app/WallpaperManager;->sGlobals:Landroid/app/WallpaperManager$Globals; +Landroid/appwidget/AppWidgetManager;->bindAppWidgetIdIfAllowed(IILandroid/content/ComponentName;Landroid/os/Bundle;)Z +Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;Landroid/os/Bundle;)V +Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;)V +Landroid/bluetooth/BluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z +Landroid/bluetooth/BluetoothAdapter;->disable(Z)Z +Landroid/bluetooth/BluetoothAdapter;->enableNoAutoConnect()Z +Landroid/bluetooth/BluetoothAdapter;->getDiscoverableTimeout()I +Landroid/bluetooth/BluetoothAdapter;->setScanMode(II)Z +Landroid/bluetooth/BluetoothAdapter;->setScanMode(I)Z +Landroid/bluetooth/BluetoothDevice;->cancelBondProcess()Z +Landroid/bluetooth/BluetoothDevice;->createBond(I)Z +Landroid/bluetooth/BluetoothDevice;->getAliasName()Ljava/lang/String; +Landroid/bluetooth/BluetoothDevice;->isConnected()Z +Landroid/bluetooth/BluetoothDevice;->isEncrypted()Z +Landroid/bluetooth/BluetoothDevice;->removeBond()Z +Landroid/bluetooth/BluetoothDevice;->setPhonebookAccessPermission(I)Z +Landroid/bluetooth/BluetoothGattCharacteristic;->mInstance:I +Landroid/bluetooth/BluetoothGattCharacteristic;->mService:Landroid/bluetooth/BluetoothGattService; +Landroid/bluetooth/BluetoothGattDescriptor;->mCharacteristic:Landroid/bluetooth/BluetoothGattCharacteristic; +Landroid/bluetooth/BluetoothGattDescriptor;->mInstance:I +Landroid/bluetooth/BluetoothGatt;->refresh()Z +Landroid/bluetooth/BluetoothHeadset;->close()V +Landroid/bluetooth/BluetoothHeadset;->setPriority(Landroid/bluetooth/BluetoothDevice;I)Z +Landroid/bluetooth/BluetoothUuid;->RESERVED_UUIDS:[Landroid/os/ParcelUuid; +Landroid/bluetooth/IBluetooth$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetooth; +Landroid/content/AsyncTaskLoader;->mExecutor:Ljava/util/concurrent/Executor; +Landroid/content/ContentProviderOperation;->mSelection:Ljava/lang/String; +Landroid/content/ContentProviderOperation;->mType:I +Landroid/content/ContentResolver;->getContentService()Landroid/content/IContentService; +Landroid/content/ContentResolver;->getSyncStatus(Landroid/accounts/Account;Ljava/lang/String;)Landroid/content/SyncStatusInfo; +Landroid/content/ContentResolver;->mContext:Landroid/content/Context; +Landroid/content/ContentValues;->mValues:Ljava/util/HashMap; +Landroid/content/Context;->createPackageContextAsUser(Ljava/lang/String;ILandroid/os/UserHandle;)Landroid/content/Context; +Landroid/content/Context;->getSharedPrefsFile(Ljava/lang/String;)Ljava/io/File; +Landroid/content/Context;->getThemeResId()I +Landroid/content/Context;->sendBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;I)V +Landroid/content/Context;->sendBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;Landroid/os/Bundle;)V +Landroid/content/Context;->sendOrderedBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;ILandroid/content/BroadcastReceiver;Landroid/os/Handler;ILjava/lang/String;Landroid/os/Bundle;)V +Landroid/content/Context;->sendOrderedBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;ILandroid/os/Bundle;Landroid/content/BroadcastReceiver;Landroid/os/Handler;ILjava/lang/String;Landroid/os/Bundle;)V +Landroid/content/ContextWrapper;->mBase:Landroid/content/Context; +Landroid/content/CursorLoader;->mCancellationSignal:Landroid/os/CancellationSignal; +Landroid/content/CursorLoader;->mObserver:Landroid/content/Loader$ForceLoadContentObserver; +Landroid/content/IContentService;->cancelSync(Landroid/accounts/Account;Ljava/lang/String;Landroid/content/ComponentName;)V +Landroid/content/IContentService;->getMasterSyncAutomatically()Z +Landroid/content/IContentService;->setMasterSyncAutomatically(Z)V +Landroid/content/Intent;->ACTION_ALARM_CHANGED:Ljava/lang/String; +Landroid/content/Intent;->putExtra(Ljava/lang/String;Landroid/os/IBinder;)Landroid/content/Intent; +Landroid/content/pm/ApplicationInfo;->installLocation:I +Landroid/content/pm/ApplicationInfo;->isForwardLocked()Z +Landroid/content/pm/ApplicationInfo;->isInstantApp()Z +Landroid/content/pm/ApplicationInfo;->isPrivilegedApp()Z +Landroid/content/pm/ApplicationInfo;->primaryCpuAbi:Ljava/lang/String; +Landroid/content/pm/ApplicationInfo;->privateFlags:I +Landroid/content/pm/ApplicationInfo;->targetSandboxVersion:I +Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo; +Landroid/content/pm/IPackageManager;->setLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;ILandroid/content/IntentFilter;ILandroid/content/ComponentName;)V +Landroid/content/pm/LauncherApps;->mPm:Landroid/content/pm/PackageManager; +Landroid/content/pm/LauncherApps;->startShortcut(Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Rect;Landroid/os/Bundle;I)V +Landroid/content/pm/PackageInstaller$SessionParams;->setGrantedRuntimePermissions([Ljava/lang/String;)V +Landroid/content/pm/PackageInstaller$SessionParams;->setInstallAsInstantApp(Z)V +Landroid/content/pm/PackageManager;->freeStorageAndNotify(JLandroid/content/pm/IPackageDataObserver;)V +Landroid/content/pm/PackageManager;->freeStorageAndNotify(Ljava/lang/String;JLandroid/content/pm/IPackageDataObserver;)V +Landroid/content/pm/PackageManager;->freeStorage(JLandroid/content/IntentSender;)V +Landroid/content/pm/PackageManager;->freeStorage(Ljava/lang/String;JLandroid/content/IntentSender;)V +Landroid/content/pm/PackageManager;->getApplicationInfoAsUser(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo; +Landroid/content/pm/PackageManager;->getPackageCandidateVolumes(Landroid/content/pm/ApplicationInfo;)Ljava/util/List; +Landroid/content/pm/PackageManager;->getPackageInfoAsUser(Ljava/lang/String;II)Landroid/content/pm/PackageInfo; +Landroid/content/pm/PackageManager;->getPackageSizeInfo(Ljava/lang/String;Landroid/content/pm/IPackageStatsObserver;)V +Landroid/content/pm/PackageManager;->getResourcesForApplicationAsUser(Ljava/lang/String;I)Landroid/content/res/Resources; +Landroid/content/pm/PackageManager;->movePackage(Ljava/lang/String;Landroid/os/storage/VolumeInfo;)I +Landroid/content/pm/PackageManager;->queryBroadcastReceivers(Landroid/content/Intent;II)Ljava/util/List; +Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Ljava/io/File;Z)V +Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Z)V +Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;I)Landroid/content/pm/PackageInfo; +Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;)Landroid/content/pm/PackageInfo; +Landroid/content/pm/PackageParser;->parseMonolithicPackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package; +Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package; +Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/content/pm/PackageParser$Package; +Landroid/content/pm/UserInfo;->id:I +Landroid/content/pm/UserInfo;->isPrimary()Z +Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I +Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I +Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V +Landroid/content/res/AssetManager;->getArraySize(I)I +Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray; +Landroid/content/res/AssetManager;->getCookieName(I)Ljava/lang/String; +Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence; +Landroid/content/res/AssetManager;->loadResourceBagValue(IILandroid/util/TypedValue;Z)I +Landroid/content/res/AssetManager;->loadResourceValue(ISLandroid/util/TypedValue;Z)I +Landroid/content/res/AssetManager;->loadThemeAttributeValue(JILandroid/util/TypedValue;Z)I +Landroid/content/res/AssetManager;->mObject:J +Landroid/content/res/AssetManager;->openNonAssetFdNative(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor; +Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;I)Ljava/io/InputStream; +Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;)Ljava/io/InputStream; +Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;I)Ljava/io/InputStream; +Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/InputStream; +Landroid/content/res/AssetManager;->openNonAssetNative(ILjava/lang/String;I)J +Landroid/content/res/AssetManager;->openXmlAssetNative(ILjava/lang/String;)J +Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z +Landroid/content/res/AssetManager;->retrieveArray(I[I)I +Landroid/content/res/AssetManager;->retrieveAttributes(J[I[I[I)Z +Landroid/content/res/AssetManager;->STYLE_NUM_ENTRIES:I +Landroid/content/res/AssetManager;->STYLE_RESOURCE_ID:I +Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable; +Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo; +Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object; +Landroid/content/res/ResourcesImpl;->mAssets:Landroid/content/res/AssetManager; +Landroid/content/res/ResourcesImpl;->mColorDrawableCache:Landroid/content/res/DrawableCache; +Landroid/content/res/ResourcesImpl;->mConfiguration:Landroid/content/res/Configuration; +Landroid/content/res/ResourcesImpl;->mDrawableCache:Landroid/content/res/DrawableCache; +Landroid/content/res/ResourcesImpl;->mPreloading:Z +Landroid/content/res/ResourcesImpl;->sPreloadedColorDrawables:Landroid/util/LongSparseArray; +Landroid/content/res/ResourcesImpl;->sPreloadedDrawables:[Landroid/util/LongSparseArray; +Landroid/content/res/ResourcesImpl;->TRACE_FOR_MISS_PRELOAD:Z +Landroid/content/res/ResourcesImpl;->TRACE_FOR_PRELOAD:Z +Landroid/content/res/Resources;->loadXmlResourceParser(ILjava/lang/String;)Landroid/content/res/XmlResourceParser; +Landroid/content/res/Resources;->loadXmlResourceParser(Ljava/lang/String;IILjava/lang/String;)Landroid/content/res/XmlResourceParser; +Landroid/content/res/Resources;->mResourcesImpl:Landroid/content/res/ResourcesImpl; +Landroid/content/res/Resources;->mTmpValue:Landroid/util/TypedValue; +Landroid/content/res/Resources;->mTypedArrayPool:Landroid/util/Pools$SynchronizedPool; +Landroid/content/res/Resources;->setCompatibilityInfo(Landroid/content/res/CompatibilityInfo;)V +Landroid/content/res/TypedArray;->getValueAt(ILandroid/util/TypedValue;)Z +Landroid/content/res/TypedArray;->mAssets:Landroid/content/res/AssetManager; +Landroid/content/res/TypedArray;->mData:[I +Landroid/content/res/TypedArray;->mIndices:[I +Landroid/content/res/TypedArray;->mLength:I +Landroid/content/res/TypedArray;->mMetrics:Landroid/util/DisplayMetrics; +Landroid/content/res/TypedArray;->mRecycled:Z +Landroid/content/res/TypedArray;->mResources:Landroid/content/res/Resources; +Landroid/content/res/TypedArray;->mTheme:Landroid/content/res/Resources$Theme; +Landroid/content/res/TypedArray;->mValue:Landroid/util/TypedValue; +Landroid/content/res/TypedArray;->mXml:Landroid/content/res/XmlBlock$Parser; +Landroid/content/res/XmlBlock;->close()V +Landroid/content/res/XmlBlock;->newParser()Landroid/content/res/XmlResourceParser; +Landroid/content/res/XmlBlock$Parser;->mParseState:J +Landroid/content/SyncStatusInfo;->lastSuccessTime:J +Landroid/database/AbstractCursor;->mNotifyUri:Landroid/net/Uri; +Landroid/database/CursorWindow;->mWindowPtr:J +Landroid/database/CursorWindow;->sCursorWindowSize:I +Landroid/database/CursorWindow;->sWindowToPidMap:Landroid/util/LongSparseArray; +Landroid/database/CursorWrapper;->mCursor:Landroid/database/Cursor; +Landroid/graphics/Bitmap$Config;->nativeInt:I +Landroid/graphics/Bitmap;->createAshmemBitmap()Landroid/graphics/Bitmap; +Landroid/graphics/Bitmap;->createAshmemBitmap(Landroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap; +Landroid/graphics/Bitmap;->getDefaultDensity()I +Landroid/graphics/drawable/AnimationDrawable;->mCurFrame:I +Landroid/graphics/drawable/BitmapDrawable;->getTint()Landroid/content/res/ColorStateList; +Landroid/graphics/drawable/BitmapDrawable;->getTintMode()Landroid/graphics/PorterDuff$Mode; +Landroid/graphics/drawable/BitmapDrawable;->setBitmap(Landroid/graphics/Bitmap;)V +Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mConstantPadding:Landroid/graphics/Rect; +Landroid/graphics/drawable/DrawableContainer;->getOpticalInsets()Landroid/graphics/Insets; +Landroid/graphics/drawable/DrawableContainer;->mDrawableContainerState:Landroid/graphics/drawable/DrawableContainer$DrawableContainerState; +Landroid/graphics/drawable/Drawable;->inflateWithAttributes(Landroid/content/res/Resources;Lorg/xmlpull/v1/XmlPullParser;Landroid/content/res/TypedArray;I)V +Landroid/graphics/drawable/Drawable;->mCallback:Ljava/lang/ref/WeakReference; +Landroid/graphics/drawable/NinePatchDrawable;->mNinePatchState:Landroid/graphics/drawable/NinePatchDrawable$NinePatchState; +Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;->mNinePatch:Landroid/graphics/NinePatch; +Landroid/graphics/drawable/StateListDrawable;->extractStateSet(Landroid/util/AttributeSet;)[I +Landroid/graphics/drawable/StateListDrawable;->getStateCount()I +Landroid/graphics/drawable/StateListDrawable;->getStateDrawable(I)Landroid/graphics/drawable/Drawable; +Landroid/graphics/drawable/StateListDrawable;->getStateSet(I)[I +Landroid/graphics/drawable/StateListDrawable;->mStateListState:Landroid/graphics/drawable/StateListDrawable$StateListState; +Landroid/graphics/drawable/StateListDrawable;->updateStateFromTypedArray(Landroid/content/res/TypedArray;)V +Landroid/graphics/LinearGradient;->mColors:[I +Landroid/graphics/NinePatch;->mBitmap:Landroid/graphics/Bitmap; +Landroid/graphics/SurfaceTexture;->nativeDetachFromGLContext()I +Landroid/graphics/Typeface;->mStyle:I +Landroid/graphics/Typeface;->sDefaults:[Landroid/graphics/Typeface; +Landroid/graphics/Typeface;->setDefault(Landroid/graphics/Typeface;)V +Landroid/graphics/Typeface;->sSystemFontMap:Ljava/util/Map; +Landroid/hardware/Camera;->addCallbackBuffer([BI)V +Landroid/hardware/Camera;->openLegacy(II)Landroid/hardware/Camera; +Landroid/hardware/display/WifiDisplayStatus;->mActiveDisplay:Landroid/hardware/display/WifiDisplay; +Landroid/hardware/display/WifiDisplayStatus;->mDisplays:[Landroid/hardware/display/WifiDisplay; +Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager; +Landroid/hardware/input/InputManager;->getInstance()Landroid/hardware/input/InputManager; +Landroid/hardware/input/InputManager;->mIm:Landroid/hardware/input/IInputManager; +Landroid/hardware/usb/UsbManager;->setCurrentFunction(Ljava/lang/String;Z)V +Landroid/location/LocationRequest;->createFromDeprecatedProvider(Ljava/lang/String;JFZ)Landroid/location/LocationRequest; +Landroid/location/LocationRequest;->setWorkSource(Landroid/os/WorkSource;)V +Landroid/location/Location;->setIsFromMockProvider(Z)V +Landroid/media/AudioAttributes$Builder;->setInternalCapturePreset(I)Landroid/media/AudioAttributes$Builder; +Landroid/media/AudioManager;->abandonAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;)I +Landroid/media/AudioManager;->mAudioFocusIdListenerMap:Ljava/util/concurrent/ConcurrentHashMap; +Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioFocusRequest;Landroid/media/audiopolicy/AudioPolicy;)I +Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;II)I +Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;IILandroid/media/audiopolicy/AudioPolicy;)I +Landroid/media/AudioManager;->setMasterMute(ZI)V +Landroid/media/AudioManager;->STREAM_BLUETOOTH_SCO:I +Landroid/media/AudioManager;->STREAM_SYSTEM_ENFORCED:I +Landroid/media/AudioManager;->STREAM_TTS:I +Landroid/media/AudioSystem;->setDeviceConnectionState(IILjava/lang/String;Ljava/lang/String;)I +Landroid/media/AudioTrack;->getLatency()I +Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService; +Landroid/media/MediaCodec;->releaseOutputBuffer(IZZJ)V +Landroid/media/MediaFile;->getFileType(Ljava/lang/String;)Landroid/media/MediaFile$MediaFileType; +Landroid/media/MediaFile;->getMimeTypeForFile(Ljava/lang/String;)Ljava/lang/String; +Landroid/media/MediaFile;->isVideoFileType(I)Z +Landroid/media/MediaFile$MediaFileType;->fileType:I +Landroid/media/MediaFile;->sFileTypeMap:Ljava/util/HashMap; +Landroid/media/MediaMetadataRetriever;->getEmbeddedPicture(I)[B +Landroid/media/MediaPlayer;->getMetadata(ZZ)Landroid/media/Metadata; +Landroid/media/MediaPlayer;->invoke(Landroid/os/Parcel;Landroid/os/Parcel;)V +Landroid/media/MediaPlayer;->newRequest()Landroid/os/Parcel; +Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/util/List;)V +Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;Ljava/util/Map;Ljava/util/List;)V +Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;Ljava/util/Map;)V +Landroid/media/MediaPlayer;->setRetransmitEndpoint(Ljava/net/InetSocketAddress;)V +Landroid/media/MediaRecorder$AudioSource;->RADIO_TUNER:I +Landroid/media/MediaRouter$RouteInfo;->getStatusCode()I +Landroid/media/MediaRouter$RouteInfo;->STATUS_CONNECTING:I +Landroid/media/MediaRouter;->selectRouteInt(ILandroid/media/MediaRouter$RouteInfo;Z)V +Landroid/media/MediaScanner;->mClient:Landroid/media/MediaScanner$MyMediaScannerClient; +Landroid/media/MediaScanner;->scanSingleFile(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri; +Landroid/media/MiniThumbFile;->reset()V +Landroid/media/RingtoneManager;->getRingtone(Landroid/content/Context;Landroid/net/Uri;I)Landroid/media/Ringtone; +Landroid/media/Ringtone;->setLooping(Z)V +Landroid/media/Ringtone;->setVolume(F)V +Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler; +Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap; +Landroid/net/ConnectivityManager;->ACTION_TETHER_STATE_CHANGED:Ljava/lang/String; +Landroid/net/ConnectivityManager;->EXTRA_ACTIVE_TETHER:Ljava/lang/String; +Landroid/net/ConnectivityManager;->getActiveLinkProperties()Landroid/net/LinkProperties; +Landroid/net/ConnectivityManager;->getLinkProperties(I)Landroid/net/LinkProperties; +Landroid/net/ConnectivityManager;->getMobileDataEnabled()Z +Landroid/net/ConnectivityManager;->getTetherableUsbRegexs()[Ljava/lang/String; +Landroid/net/ConnectivityManager;->getTetherableWifiRegexs()[Ljava/lang/String; +Landroid/net/ConnectivityManager;->getTetheredIfaces()[Ljava/lang/String; +Landroid/net/ConnectivityManager;->isNetworkSupported(I)Z +Landroid/net/ConnectivityManager;->isNetworkTypeMobile(I)Z +Landroid/net/ConnectivityManager;->isTetheringSupported()Z +Landroid/net/ConnectivityManager;->mService:Landroid/net/IConnectivityManager; +Landroid/net/ConnectivityManager;->requestRouteToHostAddress(ILjava/net/InetAddress;)Z +Landroid/net/ConnectivityManager;->requestRouteToHost(II)Z +Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String; +Landroid/net/LinkProperties;->setHttpProxy(Landroid/net/ProxyInfo;)V +Landroid/net/NetworkPolicyManager;->mService:Landroid/net/INetworkPolicyManager; +Landroid/net/NetworkStats;->capacity:I +Landroid/net/NetworkStats;->defaultNetwork:[I +Landroid/net/NetworkStats;->iface:[Ljava/lang/String; +Landroid/net/NetworkStats;->metered:[I +Landroid/net/NetworkStats;->operations:[J +Landroid/net/NetworkStats;->roaming:[I +Landroid/net/NetworkStats;->rxBytes:[J +Landroid/net/NetworkStats;->rxPackets:[J +Landroid/net/NetworkStats;->set:[I +Landroid/net/NetworkStats;->size:I +Landroid/net/NetworkStats;->tag:[I +Landroid/net/NetworkStats;->txBytes:[J +Landroid/net/NetworkStats;->txPackets:[J +Landroid/net/NetworkStats;->uid:[I +Landroid/net/SSLCertificateSocketFactory;->getHttpSocketFactory(ILandroid/net/SSLSessionCache;)Lorg/apache/http/conn/ssl/SSLSocketFactory; +Landroid/net/TrafficStats;->getStatsService()Landroid/net/INetworkStatsService; +Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I +Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection; +Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V +Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V +Landroid/net/wifi/p2p/WifiP2pManager;->setDeviceName(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Ljava/lang/String;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V +Landroid/net/wifi/WifiConfiguration;->apBand:I +Landroid/net/wifi/WifiConfiguration;->apChannel:I +Landroid/net/wifi/WifiConfiguration;->hasNoInternetAccess()Z +Landroid/net/wifi/WifiConfiguration;->mIpConfiguration:Landroid/net/IpConfiguration; +Landroid/net/wifi/WifiConfiguration;->validatedInternetAccess:Z +Landroid/net/wifi/WifiManager;->connect(ILandroid/net/wifi/WifiManager$ActionListener;)V +Landroid/net/wifi/WifiManager;->connect(Landroid/net/wifi/WifiConfiguration;Landroid/net/wifi/WifiManager$ActionListener;)V +Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_STATE:Ljava/lang/String; +Landroid/net/wifi/WifiManager;->forget(ILandroid/net/wifi/WifiManager$ActionListener;)V +Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration; +Landroid/net/wifi/WifiManager;->getWifiApState()I +Landroid/net/wifi/WifiManager;->isDualBandSupported()Z +Landroid/net/wifi/WifiManager;->isWifiApEnabled()Z +Landroid/net/wifi/WifiManager;->mService:Landroid/net/wifi/IWifiManager; +Landroid/net/wifi/WifiManager;->save(Landroid/net/wifi/WifiConfiguration;Landroid/net/wifi/WifiManager$ActionListener;)V +Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z +Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_CHANGED_ACTION:Ljava/lang/String; +Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLED:I +Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLING:I +Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLED:I +Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLING:I +Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I +Landroid/nfc/NfcAdapter;->getDefaultAdapter()Landroid/nfc/NfcAdapter; +Landroid/nfc/NfcAdapter;->setNdefPushMessageCallback(Landroid/nfc/NfcAdapter$CreateNdefMessageCallback;Landroid/app/Activity;I)V +Landroid/opengl/GLSurfaceView$EglHelper;->mEglContext:Ljavax/microedition/khronos/egl/EGLContext; +Landroid/opengl/GLSurfaceView$GLThread;->mEglHelper:Landroid/opengl/GLSurfaceView$EglHelper; +Landroid/opengl/GLSurfaceView;->mGLThread:Landroid/opengl/GLSurfaceView$GLThread; +Landroid/os/AsyncTask;->mFuture:Ljava/util/concurrent/FutureTask; +Landroid/os/AsyncTask;->mStatus:Landroid/os/AsyncTask$Status; +Landroid/os/AsyncTask;->mTaskInvoked:Ljava/util/concurrent/atomic/AtomicBoolean; +Landroid/os/AsyncTask;->mWorker:Landroid/os/AsyncTask$WorkerRunnable; +Landroid/os/AsyncTask;->sDefaultExecutor:Ljava/util/concurrent/Executor; +Landroid/os/AsyncTask;->setDefaultExecutor(Ljava/util/concurrent/Executor;)V +Landroid/os/BatteryStats;->NUM_DATA_CONNECTION_TYPES:I +Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J +Landroid/os/BatteryStats$Uid;->getFullWifiLockTime(JI)J +Landroid/os/BatteryStats$Uid;->getProcessStats()Landroid/util/ArrayMap; +Landroid/os/BatteryStats$Uid;->getSensorStats()Landroid/util/SparseArray; +Landroid/os/BatteryStats$Uid;->getUid()I +Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J +Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J +Landroid/os/BatteryStats$Uid$Proc;->getForegroundTime(I)J +Landroid/os/BatteryStats$Uid$Proc;->getSystemTime(I)J +Landroid/os/BatteryStats$Uid$Proc;->getUserTime(I)J +Landroid/os/BatteryStats$Uid$Sensor;->getHandle()I +Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Timer; +Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String; +Landroid/os/Bundle;->getIBinder(Ljava/lang/String;)Landroid/os/IBinder; +Landroid/os/Bundle;->putIBinder(Ljava/lang/String;Landroid/os/IBinder;)V +Landroid/os/Debug;->countInstancesOfClass(Ljava/lang/Class;)J +Landroid/os/Debug;->dumpReferenceTables()V +Landroid/os/Debug$MemoryInfo;->getOtherLabel(I)Ljava/lang/String; +Landroid/os/Debug$MemoryInfo;->getOtherPrivateDirty(I)I +Landroid/os/Debug$MemoryInfo;->getOtherPss(I)I +Landroid/os/Debug$MemoryInfo;->getOtherSharedDirty(I)I +Landroid/os/Debug$MemoryInfo;->NUM_DVK_STATS:I +Landroid/os/Debug$MemoryInfo;->NUM_OTHER_STATS:I +Landroid/os/Environment;->buildExternalStorageAppDataDirs(Ljava/lang/String;)[Ljava/io/File; +Landroid/os/FileUtils;->checksumCrc32(Ljava/io/File;)J +Landroid/os/FileUtils;->copyFile(Ljava/io/File;Ljava/io/File;)Z +Landroid/os/FileUtils;->copyToFile(Ljava/io/InputStream;Ljava/io/File;)Z +Landroid/os/FileUtils;->deleteOlderFiles(Ljava/io/File;IJ)Z +Landroid/os/FileUtils;->readTextFile(Ljava/io/File;ILjava/lang/String;)Ljava/lang/String; +Landroid/os/FileUtils;->setPermissions(Ljava/io/FileDescriptor;III)I +Landroid/os/FileUtils;->setPermissions(Ljava/io/File;III)I +Landroid/os/FileUtils;->setPermissions(Ljava/lang/String;III)I +Landroid/os/FileUtils;->stringToFile(Ljava/io/File;Ljava/lang/String;)V +Landroid/os/FileUtils;->stringToFile(Ljava/lang/String;Ljava/lang/String;)V +Landroid/os/Handler;->getIMessenger()Landroid/os/IMessenger; +Landroid/os/Handler;->hasCallbacks(Ljava/lang/Runnable;)Z +Landroid/os/Handler;->mCallback:Landroid/os/Handler$Callback; +Landroid/os/Handler;->mMessenger:Landroid/os/IMessenger; +Landroid/os/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPowerManager; +Landroid/os/IPowerManager;->userActivity(JII)V +Landroid/os/Looper;->mQueue:Landroid/os/MessageQueue; +Landroid/os/MemoryFile;->getFileDescriptor()Ljava/io/FileDescriptor; +Landroid/os/Message;->callback:Ljava/lang/Runnable; +Landroid/os/Message;->flags:I +Landroid/os/Message;->next:Landroid/os/Message; +Landroid/os/MessageQueue;->mIdleHandlers:Ljava/util/ArrayList; +Landroid/os/MessageQueue;->mMessages:Landroid/os/Message; +Landroid/os/MessageQueue;->mQuitAllowed:Z +Landroid/os/MessageQueue;->next()Landroid/os/Message; +Landroid/os/Message;->target:Landroid/os/Handler; +Landroid/os/PowerManager;->getMaximumScreenBrightnessSetting()I +Landroid/os/PowerManager;->getMinimumScreenBrightnessSetting()I +Landroid/os/PowerManager;->isLightDeviceIdleMode()Z +Landroid/os/PowerManager;->userActivity(JII)V +Landroid/os/PowerManager;->userActivity(JZ)V +Landroid/os/PowerManager;->validateWakeLockParameters(ILjava/lang/String;)V +Landroid/os/PowerManager;->wakeUp(JLjava/lang/String;)V +Landroid/os/PowerManager;->wakeUp(J)V +Landroid/os/Process;->getParentPid(I)I +Landroid/os/Process;->getPids(Ljava/lang/String;[I)[I +Landroid/os/Process;->getUidForPid(I)I +Landroid/os/Process;->isIsolated(I)Z +Landroid/os/Process;->isIsolated()Z +Landroid/os/Process;->readProcFile(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z +Landroid/os/Process;->readProcLines(Ljava/lang/String;[Ljava/lang/String;[J)V +Landroid/os/RecoverySystem;->cancelScheduledUpdate(Landroid/content/Context;)V +Landroid/os/RecoverySystem;->installPackage(Landroid/content/Context;Ljava/io/File;Z)V +Landroid/os/RecoverySystem;->processPackage(Landroid/content/Context;Ljava/io/File;Landroid/os/RecoverySystem$ProgressListener;Landroid/os/Handler;)V +Landroid/os/RecoverySystem;->processPackage(Landroid/content/Context;Ljava/io/File;Landroid/os/RecoverySystem$ProgressListener;)V +Landroid/os/RecoverySystem;->rebootWipeAb(Landroid/content/Context;Ljava/io/File;Ljava/lang/String;)V +Landroid/os/RecoverySystem;->scheduleUpdateOnBoot(Landroid/content/Context;Ljava/io/File;)V +Landroid/os/SELinux;->isSELinuxEnabled()Z +Landroid/os/SELinux;->isSELinuxEnforced()Z +Landroid/os/ServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder; +Landroid/os/ServiceManager;->getService(Ljava/lang/String;)Landroid/os/IBinder; +Landroid/os/storage/DiskInfo;->getDescription()Ljava/lang/String; +Landroid/os/storage/StorageManager;->findVolumeByUuid(Ljava/lang/String;)Landroid/os/storage/VolumeInfo; +Landroid/os/storage/StorageManager;->getBestVolumeDescription(Landroid/os/storage/VolumeInfo;)Ljava/lang/String; +Landroid/os/storage/StorageManager;->getDisks()Ljava/util/List; +Landroid/os/storage/StorageManager;->getStorageBytesUntilLow(Ljava/io/File;)J +Landroid/os/storage/StorageManager;->getVolumeList(II)[Landroid/os/storage/StorageVolume; +Landroid/os/storage/StorageManager;->getVolumeList()[Landroid/os/storage/StorageVolume; +Landroid/os/storage/StorageManager;->getVolumePaths()[Ljava/lang/String; +Landroid/os/storage/StorageManager;->getVolumes()Ljava/util/List; +Landroid/os/storage/StorageManager;->getVolumeState(Ljava/lang/String;)Ljava/lang/String; +Landroid/os/storage/StorageVolume;->getPathFile()Ljava/io/File; +Landroid/os/storage/StorageVolume;->getPath()Ljava/lang/String; +Landroid/os/storage/StorageVolume;->getUserLabel()Ljava/lang/String; +Landroid/os/storage/VolumeInfo;->getDisk()Landroid/os/storage/DiskInfo; +Landroid/os/storage/VolumeInfo;->getFsUuid()Ljava/lang/String; +Landroid/os/storage/VolumeInfo;->getPath()Ljava/io/File; +Landroid/os/storage/VolumeInfo;->getState()I +Landroid/os/storage/VolumeInfo;->getType()I +Landroid/os/storage/VolumeInfo;->isPrimary()Z +Landroid/os/storage/VolumeInfo;->isVisible()Z +Landroid/os/StrictMode;->violationsBeingTimed:Ljava/lang/ThreadLocal; +Landroid/os/SystemProperties;->addChangeCallback(Ljava/lang/Runnable;)V +Landroid/os/SystemProperties;->getBoolean(Ljava/lang/String;Z)Z +Landroid/os/SystemProperties;->getInt(Ljava/lang/String;I)I +Landroid/os/SystemProperties;->get(Ljava/lang/String;)Ljava/lang/String; +Landroid/os/SystemProperties;->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; +Landroid/os/SystemProperties;->getLong(Ljava/lang/String;J)J +Landroid/os/SystemProperties;->PROP_NAME_MAX:I +Landroid/os/SystemProperties;->set(Ljava/lang/String;Ljava/lang/String;)V +Landroid/os/Trace;->asyncTraceBegin(JLjava/lang/String;I)V +Landroid/os/Trace;->asyncTraceEnd(JLjava/lang/String;I)V +Landroid/os/Trace;->isTagEnabled(J)Z +Landroid/os/Trace;->setAppTracingAllowed(Z)V +Landroid/os/Trace;->traceBegin(JLjava/lang/String;)V +Landroid/os/Trace;->traceCounter(JLjava/lang/String;I)V +Landroid/os/Trace;->traceEnd(J)V +Landroid/os/Trace;->TRACE_TAG_APP:J +Landroid/os/Trace;->TRACE_TAG_VIEW:J +Landroid/os/UpdateEngine;->applyPayload(Ljava/lang/String;JJ[Ljava/lang/String;)V +Landroid/os/UpdateEngine;->bind(Landroid/os/UpdateEngineCallback;Landroid/os/Handler;)Z +Landroid/os/UpdateEngine;->bind(Landroid/os/UpdateEngineCallback;)Z +Landroid/os/UpdateEngine;->cancel()V +Landroid/os/UpdateEngine;->resetStatus()V +Landroid/os/UpdateLock;->acquire()V +Landroid/os/UpdateLock;->isHeld()Z +Landroid/os/UpdateLock;->release()V +Landroid/os/UserHandle;->ALL:Landroid/os/UserHandle; +Landroid/os/UserHandle;->getIdentifier()I +Landroid/os/UserHandle;->getUserId(I)I +Landroid/os/UserHandle;->isOwner()Z +Landroid/os/UserHandle;->myUserId()I +Landroid/os/UserHandle;->of(I)Landroid/os/UserHandle; +Landroid/os/UserManager;->get(Landroid/content/Context;)Landroid/os/UserManager; +Landroid/os/UserManager;->getMaxSupportedUsers()I +Landroid/os/UserManager;->getProfiles(I)Ljava/util/List; +Landroid/os/UserManager;->getUserHandle()I +Landroid/os/UserManager;->getUserHandle(I)I +Landroid/os/UserManager;->getUserInfo(I)Landroid/content/pm/UserInfo; +Landroid/os/UserManager;->getUserSerialNumber(I)I +Landroid/os/UserManager;->getUsers()Ljava/util/List; +Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z +Landroid/os/UserManager;->isLinkedUser()Z +Landroid/os/UserManager;->isUserUnlocked(I)Z +Landroid/os/WorkSource;->add(ILjava/lang/String;)Z +Landroid/os/WorkSource;->add(I)Z +Landroid/os/WorkSource;->get(I)I +Landroid/os/WorkSource;->getName(I)Ljava/lang/String; +Landroid/os/WorkSource;->size()I +Landroid/preference/DialogPreference;->mBuilder:Landroid/app/AlertDialog$Builder; +Landroid/preference/DialogPreference;->mDialogIcon:Landroid/graphics/drawable/Drawable; +Landroid/preference/DialogPreference;->mDialog:Landroid/app/Dialog; +Landroid/preference/DialogPreference;->mDialogMessage:Ljava/lang/CharSequence; +Landroid/preference/DialogPreference;->mDialogTitle:Ljava/lang/CharSequence; +Landroid/preference/DialogPreference;->mNegativeButtonText:Ljava/lang/CharSequence; +Landroid/preference/DialogPreference;->mPositiveButtonText:Ljava/lang/CharSequence; +Landroid/preference/DialogPreference;->mWhichButtonClicked:I +Landroid/preference/ListPreference;->mClickedDialogEntryIndex:I +Landroid/preference/PreferenceActivity;->mPreferenceManager:Landroid/preference/PreferenceManager; +Landroid/preference/PreferenceActivity;->mPrefsContainer:Landroid/view/ViewGroup; +Landroid/preference/PreferenceManager;->dispatchActivityDestroy()V +Landroid/preference/PreferenceManager;->dispatchActivityResult(IILandroid/content/Intent;)V +Landroid/preference/PreferenceManager;->dispatchActivityStop()V +Landroid/preference/PreferenceManager;->getEditor()Landroid/content/SharedPreferences$Editor; +Landroid/preference/PreferenceManager;->getPreferenceScreen()Landroid/preference/PreferenceScreen; +Landroid/preference/PreferenceManager;->inflateFromIntent(Landroid/content/Intent;Landroid/preference/PreferenceScreen;)Landroid/preference/PreferenceScreen; +Landroid/preference/PreferenceManager;->inflateFromResource(Landroid/content/Context;ILandroid/preference/PreferenceScreen;)Landroid/preference/PreferenceScreen; +Landroid/preference/PreferenceManager;->mActivityDestroyListeners:Ljava/util/List; +Landroid/preference/PreferenceManager;->mOnPreferenceTreeClickListener:Landroid/preference/PreferenceManager$OnPreferenceTreeClickListener; +Landroid/preference/PreferenceManager;->mSharedPreferences:Landroid/content/SharedPreferences; +Landroid/preference/PreferenceManager;->registerOnActivityDestroyListener(Landroid/preference/PreferenceManager$OnActivityDestroyListener;)V +Landroid/preference/PreferenceManager;->registerOnActivityStopListener(Landroid/preference/PreferenceManager$OnActivityStopListener;)V +Landroid/preference/PreferenceManager;->setFragment(Landroid/preference/PreferenceFragment;)V +Landroid/preference/PreferenceManager;->setPreferences(Landroid/preference/PreferenceScreen;)Z +Landroid/preference/PreferenceManager;->shouldCommit()Z +Landroid/preference/PreferenceManager;->unregisterOnActivityDestroyListener(Landroid/preference/PreferenceManager$OnActivityDestroyListener;)V +Landroid/preference/PreferenceManager;->unregisterOnActivityStopListener(Landroid/preference/PreferenceManager$OnActivityStopListener;)V +Landroid/preference/Preference;->onKey(Landroid/view/View;ILandroid/view/KeyEvent;)Z +Landroid/preference/Preference;->performClick(Landroid/preference/PreferenceScreen;)V +Landroid/preference/PreferenceScreen;->mRootAdapter:Landroid/widget/ListAdapter; +Landroid/print/PrinterId;->getServiceName()Landroid/content/ComponentName; +Landroid/print/PrintJobInfo;->getAdvancedOptions()Landroid/os/Bundle; +Landroid/print/PrintJobInfo;->getDocumentInfo()Landroid/print/PrintDocumentInfo; +Landroid/provider/Browser;->canClearHistory(Landroid/content/ContentResolver;)Z +Landroid/provider/Browser;->clearHistory(Landroid/content/ContentResolver;)V +Landroid/provider/Browser;->clearSearches(Landroid/content/ContentResolver;)V +Landroid/provider/Browser;->deleteFromHistory(Landroid/content/ContentResolver;Ljava/lang/String;)V +Landroid/provider/Browser;->getVisitedHistory(Landroid/content/ContentResolver;)[Ljava/lang/String; +Landroid/provider/Browser;->sendString(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V +Landroid/provider/Settings$Global;->OTA_DISABLE_AUTOMATIC_UPDATE:Ljava/lang/String; +Landroid/provider/Settings$Global;->PACKAGE_VERIFIER_ENABLE:Ljava/lang/String; +Landroid/provider/Settings$Secure;->ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:Ljava/lang/String; +Landroid/provider/Settings$Secure;->PACKAGE_VERIFIER_USER_CONSENT:Ljava/lang/String; +Landroid/provider/Settings$Secure;->USER_SETUP_COMPLETE:Ljava/lang/String; +Landroid/provider/Settings$System;->AIRPLANE_MODE_TOGGLEABLE_RADIOS:Ljava/lang/String; +Landroid/provider/Settings$System;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String; +Landroid/provider/Settings$System;->putStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;I)Z +Landroid/provider/Telephony$Sms;->addMessageToUri(ILandroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZJ)Landroid/net/Uri; +Landroid/provider/Telephony$Sms;->addMessageToUri(ILandroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZ)Landroid/net/Uri; +Landroid/provider/Telephony$Sms;->addMessageToUri(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZJ)Landroid/net/Uri; +Landroid/provider/Telephony$Sms;->addMessageToUri(Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;ZZ)Landroid/net/Uri; +Landroid/provider/Telephony$Sms$Inbox;->addMessage(ILandroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Z)Landroid/net/Uri; +Landroid/provider/Telephony$Sms$Inbox;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Z)Landroid/net/Uri; +Landroid/provider/Telephony$Sms$Sent;->addMessage(ILandroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)Landroid/net/Uri; +Landroid/provider/Telephony$Sms$Sent;->addMessage(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;)Landroid/net/Uri; +Landroid/renderscript/RenderScript;->create(Landroid/content/Context;I)Landroid/renderscript/RenderScript; +Landroid/renderscript/RenderScript;->create(Landroid/content/Context;ILandroid/renderscript/RenderScript$ContextType;I)Landroid/renderscript/RenderScript; +Landroid/renderscript/RenderScript;->getMinorID()J +Landroid/R$styleable;->TextAppearance:[I +Landroid/R$styleable;->TextAppearance_textColor:I +Landroid/R$styleable;->TextAppearance_textSize:I +Landroid/security/KeyStore;->getInstance()Landroid/security/KeyStore; +Landroid/service/media/IMediaBrowserServiceCallbacks;->onConnectFailed()V +Landroid/service/media/IMediaBrowserServiceCallbacks;->onConnect(Ljava/lang/String;Landroid/media/session/MediaSession$Token;Landroid/os/Bundle;)V +Landroid/service/media/IMediaBrowserServiceCallbacks;->onLoadChildren(Ljava/lang/String;Landroid/content/pm/ParceledListSlice;)V +Landroid/service/media/IMediaBrowserServiceCallbacks;->onLoadChildrenWithOptions(Ljava/lang/String;Landroid/content/pm/ParceledListSlice;Landroid/os/Bundle;)V +Landroid/service/media/MediaBrowserService;->KEY_MEDIA_ITEM:Ljava/lang/String; +Landroid/service/notification/NotificationListenerService;->registerAsSystemService(Landroid/content/Context;Landroid/content/ComponentName;I)V +Landroid/service/notification/NotificationListenerService;->unregisterAsSystemService()V +Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V +Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String; +Landroid/telephony/PhoneStateListener;->mSubId:Ljava/lang/Integer; +Landroid/telephony/ServiceState;->newFromBundle(Landroid/os/Bundle;)Landroid/telephony/ServiceState; +Landroid/telephony/SignalStrength;->getCdmaLevel()I +Landroid/telephony/SignalStrength;->getLteDbm()I +Landroid/telephony/SignalStrength;->getLteRsrp()I +Landroid/telephony/SignalStrength;->getLteRssnr()I +Landroid/telephony/SignalStrength;->getLteSignalStrength()I +Landroid/telephony/SmsManager;->RESULT_ERROR_FDN_CHECK_FAILURE:I +Landroid/telephony/SmsMessage;->getSubId()I +Landroid/telephony/SmsMessage;->mWrappedSmsMessage:Lcom/android/internal/telephony/SmsMessageBase; +Landroid/telephony/SubscriptionManager;->getDefaultSmsPhoneId()I +Landroid/telephony/SubscriptionManager;->getPhoneId(I)I +Landroid/telephony/SubscriptionManager;->getSubId(I)[I +Landroid/telephony/SubscriptionManager;->setDefaultSmsSubId(I)V +Landroid/telephony/TelephonyManager;->checkCarrierPrivilegesForPackage(Ljava/lang/String;)I +Landroid/telephony/TelephonyManager;->from(Landroid/content/Context;)Landroid/telephony/TelephonyManager; +Landroid/telephony/TelephonyManager;->getCurrentPhoneType()I +Landroid/telephony/TelephonyManager;->getCurrentPhoneType(I)I +Landroid/telephony/TelephonyManager;->getDataEnabled(I)Z +Landroid/telephony/TelephonyManager;->getDataEnabled()Z +Landroid/telephony/TelephonyManager;->getDefault()Landroid/telephony/TelephonyManager; +Landroid/telephony/TelephonyManager;->getITelephony()Lcom/android/internal/telephony/ITelephony; +Landroid/telephony/TelephonyManager;->getLine1Number(I)Ljava/lang/String; +Landroid/telephony/TelephonyManager;->getNetworkClass(I)I +Landroid/telephony/TelephonyManager;->getNetworkCountryIso(I)Ljava/lang/String; +Landroid/telephony/TelephonyManager;->getNetworkOperator(I)Ljava/lang/String; +Landroid/telephony/TelephonyManager;->getNetworkOperatorName(I)Ljava/lang/String; +Landroid/telephony/TelephonyManager;->getNetworkType(I)I +Landroid/telephony/TelephonyManager;->getSimOperator(I)Ljava/lang/String; +Landroid/telephony/TelephonyManager;->getSimOperatorName(I)Ljava/lang/String; +Landroid/telephony/TelephonyManager;->getSimSerialNumber(I)Ljava/lang/String; +Landroid/telephony/TelephonyManager;->getSubscriberId(I)Ljava/lang/String; +Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z +Landroid/telephony/TelephonyManager;->setDataEnabled(IZ)V +Landroid/text/AndroidBidi;->bidi(I[C[B)I +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/method/LinkMovementMethod;->sInstance:Landroid/text/method/LinkMovementMethod; +Landroid/text/SpannableStringBuilder;->mGapLength:I +Landroid/text/SpannableStringBuilder;->mGapStart:I +Landroid/text/SpannableStringBuilder;->mSpanCount:I +Landroid/text/SpannableStringBuilder;->mSpanEnds:[I +Landroid/text/SpannableStringBuilder;->mSpanFlags:[I +Landroid/text/SpannableStringBuilder;->mSpans:[Ljava/lang/Object; +Landroid/text/SpannableStringBuilder;->mSpanStarts:[I +Landroid/text/StaticLayout;->mColumns:I +Landroid/text/StaticLayout;->mLineCount:I +Landroid/text/StaticLayout;->mLines:[I +Landroid/text/TextLine;->obtain()Landroid/text/TextLine; +Landroid/text/TextLine;->sCached:[Landroid/text/TextLine; +Landroid/text/TextPaint;->setUnderlineText(IF)V +Landroid/util/Log;->wtf(ILjava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;ZZ)I +Landroid/util/Pools$SynchronizedPool;->acquire()Ljava/lang/Object; +Landroid/view/accessibility/AccessibilityManager;->getInstance(Landroid/content/Context;)Landroid/view/accessibility/AccessibilityManager; +Landroid/view/accessibility/AccessibilityManager;->isHighTextContrastEnabled()Z +Landroid/view/accessibility/AccessibilityManager;->sInstance:Landroid/view/accessibility/AccessibilityManager; +Landroid/view/accessibility/AccessibilityManager;->sInstanceSync:Ljava/lang/Object; +Landroid/view/accessibility/AccessibilityNodeInfo;->refresh(Landroid/os/Bundle;Z)Z +Landroid/view/ActionMode;->isUiFocusable()Z +Landroid/view/animation/Animation;->mListener:Landroid/view/animation/Animation$AnimationListener; +Landroid/view/Choreographer;->doFrame(JI)V +Landroid/view/Choreographer;->getFrameTime()J +Landroid/view/Choreographer;->mCallbackQueues:[Landroid/view/Choreographer$CallbackQueue; +Landroid/view/Choreographer;->postCallback(ILjava/lang/Runnable;Ljava/lang/Object;)V +Landroid/view/Choreographer;->removeCallbacks(ILjava/lang/Runnable;Ljava/lang/Object;)V +Landroid/view/Choreographer;->scheduleVsyncLocked()V +Landroid/view/ContextThemeWrapper;->mResources:Landroid/content/res/Resources; +Landroid/view/ContextThemeWrapper;->mTheme:Landroid/content/res/Resources$Theme; +Landroid/view/ContextThemeWrapper;->mThemeResource:I +Landroid/view/InputDevice;->isExternal()Z +Landroid/view/inputmethod/InputMethodManager;->finishInputLocked()V +Landroid/view/inputmethod/InputMethodManager;->focusIn(Landroid/view/View;)V +Landroid/view/inputmethod/InputMethodManager;->getInputMethodWindowVisibleHeight()I +Landroid/view/inputmethod/InputMethodManager;->mCurId:Ljava/lang/String; +Landroid/view/inputmethod/InputMethodManager;->mCurRootView:Landroid/view/View; +Landroid/view/inputmethod/InputMethodManager;->mNextServedView:Landroid/view/View; +Landroid/view/inputmethod/InputMethodManager;->mServedView:Landroid/view/View; +Landroid/view/inputmethod/InputMethodManager;->notifyUserAction()V +Landroid/view/inputmethod/InputMethodManager;->showSoftInputUnchecked(ILandroid/os/ResultReceiver;)V +Landroid/view/inputmethod/InputMethodManager;->windowDismissed(Landroid/os/IBinder;)V +Landroid/view/IWindowManager;->getAnimationScale(I)F +Landroid/view/IWindowManager;->hasNavigationBar()Z +Landroid/view/IWindowManager;->setAnimationScale(IF)V +Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V +Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager; +Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I +Landroid/view/LayoutInflater;->mConstructorArgs:[Ljava/lang/Object; +Landroid/view/LayoutInflater;->mFactory2:Landroid/view/LayoutInflater$Factory2; +Landroid/view/LayoutInflater;->mFactory:Landroid/view/LayoutInflater$Factory; +Landroid/view/LayoutInflater;->mFactorySet:Z +Landroid/view/LayoutInflater;->sConstructorMap:Ljava/util/HashMap; +Landroid/view/MotionEvent;->HISTORY_CURRENT:I +Landroid/view/MotionEvent;->mNativePtr:J +Landroid/view/MotionEvent;->nativeGetRawAxisValue(JIII)F +Landroid/view/MotionEvent;->scale(F)V +Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener; +Landroid/view/SurfaceView;->mCallbacks:Ljava/util/ArrayList; +Landroid/view/SurfaceView;->mFormat:I +Landroid/view/SurfaceView;->mRequestedFormat:I +Landroid/view/SurfaceView;->mSurfaceHolder:Landroid/view/SurfaceHolder; +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; +Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionAction(IIILandroid/view/textclassifier/TextClassification;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent; +Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(II)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent; +Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextClassification;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent; +Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextSelection;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent; +Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionStarted(I)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent; +Landroid/view/textservice/TextServicesManager;->isSpellCheckerEnabled()Z +Landroid/view/TextureView;->mLayer:Landroid/view/HardwareLayer; +Landroid/view/TextureView;->mSurface:Landroid/graphics/SurfaceTexture; +Landroid/view/TextureView;->mUpdateListener:Landroid/graphics/SurfaceTexture$OnFrameAvailableListener; +Landroid/view/TouchDelegate;->mDelegateTargeted:Z +Landroid/view/VelocityTracker;->obtain(Ljava/lang/String;)Landroid/view/VelocityTracker; +Landroid/view/View;->clearAccessibilityFocus()V +Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z +Landroid/view/View;->computeOpaqueFlags()V +Landroid/view/ViewConfiguration;->mFadingMarqueeEnabled:Z +Landroid/view/ViewConfiguration;->sHasPermanentMenuKeySet:Z +Landroid/view/ViewConfiguration;->sHasPermanentMenuKey:Z +Landroid/view/View;->createSnapshot(Landroid/view/ViewDebug$CanvasProvider;Z)Landroid/graphics/Bitmap; +Landroid/view/ViewDebug;->dispatchCommand(Landroid/view/View;Ljava/lang/String;Ljava/lang/String;Ljava/io/OutputStream;)V +Landroid/view/ViewDebug;->dump(Landroid/view/View;ZZLjava/io/OutputStream;)V +Landroid/view/View;->dispatchAttachedToWindow(Landroid/view/View$AttachInfo;I)V +Landroid/view/View;->dispatchDetachedFromWindow()V +Landroid/view/View;->fitsSystemWindows()Z +Landroid/view/View;->getListenerInfo()Landroid/view/View$ListenerInfo; +Landroid/view/View;->getViewRootImpl()Landroid/view/ViewRootImpl; +Landroid/view/ViewGroup;->FLAG_SUPPORT_STATIC_TRANSFORMATIONS:I +Landroid/view/ViewGroup;->FLAG_USE_CHILD_DRAWING_ORDER:I +Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V +Landroid/view/ViewGroup$MarginLayoutParams;->endMargin:I +Landroid/view/ViewGroup$MarginLayoutParams;->startMargin:I +Landroid/view/ViewGroup;->mChildren:[Landroid/view/View; +Landroid/view/ViewGroup;->mFirstTouchTarget:Landroid/view/ViewGroup$TouchTarget; +Landroid/view/ViewGroup;->mGroupFlags:I +Landroid/view/ViewGroup;->mOnHierarchyChangeListener:Landroid/view/ViewGroup$OnHierarchyChangeListener; +Landroid/view/View;->initializeScrollbars(Landroid/content/res/TypedArray;)V +Landroid/view/View;->isPaddingResolved()Z +Landroid/view/View;->isVisibleToUser(Landroid/graphics/Rect;)Z +Landroid/view/View;->isVisibleToUser()Z +Landroid/view/View$ListenerInfo;->mOnClickListener:Landroid/view/View$OnClickListener; +Landroid/view/View$ListenerInfo;->mOnTouchListener:Landroid/view/View$OnTouchListener; +Landroid/view/View;->mAccessibilityDelegate:Landroid/view/View$AccessibilityDelegate; +Landroid/view/View;->mAttachInfo:Landroid/view/View$AttachInfo; +Landroid/view/View;->mBottom:I +Landroid/view/View;->mContext:Landroid/content/Context; +Landroid/view/View;->mDrawingCache:Landroid/graphics/Bitmap; +Landroid/view/View;->mLeft:I +Landroid/view/View;->mListenerInfo:Landroid/view/View$ListenerInfo; +Landroid/view/View;->mMinHeight:I +Landroid/view/View;->mMinWidth:I +Landroid/view/View;->mPaddingLeft:I +Landroid/view/View;->mPaddingRight:I +Landroid/view/View;->mParent:Landroid/view/ViewParent; +Landroid/view/View;->mPrivateFlags3:I +Landroid/view/View;->mRecreateDisplayList:Z +Landroid/view/View;->mResources:Landroid/content/res/Resources; +Landroid/view/View;->mRight:I +Landroid/view/View;->mScrollCache:Landroid/view/View$ScrollabilityCache; +Landroid/view/View;->mScrollX:I +Landroid/view/View;->mScrollY:I +Landroid/view/View;->mStartActivityRequestWho:Ljava/lang/String; +Landroid/view/View;->mTop:I +Landroid/view/View;->mUnscaledDrawingCache:Landroid/graphics/Bitmap; +Landroid/view/View;->notifySubtreeAccessibilityStateChangedIfNeeded()V +Landroid/view/View;->recomputePadding()V +Landroid/view/View;->requestAccessibilityFocus()Z +Landroid/view/View;->resetPaddingToInitialValues()V +Landroid/view/ViewRootImpl;->detachFunctor(J)V +Landroid/view/ViewRootImpl;->invokeFunctor(JZ)V +Landroid/view/ViewRootImpl;->mStopped:Z +Landroid/view/View;->setAssistBlocked(Z)V +Landroid/view/View;->setFrame(IIII)Z +Landroid/view/View;->setIsRootNamespace(Z)V +Landroid/view/View;->startActivityForResult(Landroid/content/Intent;I)V +Landroid/view/View;->STATUS_BAR_DISABLE_BACK:I +Landroid/view/View;->STATUS_BAR_DISABLE_EXPAND:I +Landroid/view/View;->STATUS_BAR_DISABLE_HOME:I +Landroid/view/View;->STATUS_BAR_DISABLE_RECENT:I +Landroid/view/View;->toGlobalMotionEvent(Landroid/view/MotionEvent;)Z +Landroid/view/View;->toLocalMotionEvent(Landroid/view/MotionEvent;)Z +Landroid/view/ViewTreeObserver;->addOnComputeInternalInsetsListener(Landroid/view/ViewTreeObserver$OnComputeInternalInsetsListener;)V +Landroid/view/ViewTreeObserver$InternalInsetsInfo;->setTouchableInsets(I)V +Landroid/view/ViewTreeObserver$InternalInsetsInfo;->TOUCHABLE_INSETS_REGION:I +Landroid/view/ViewTreeObserver$InternalInsetsInfo;->touchableRegion:Landroid/graphics/Region; +Landroid/view/ViewTreeObserver;->removeOnComputeInternalInsetsListener(Landroid/view/ViewTreeObserver$OnComputeInternalInsetsListener;)V +Landroid/view/WindowManagerGlobal;->getInstance()Landroid/view/WindowManagerGlobal; +Landroid/view/WindowManagerGlobal;->getRootView(Ljava/lang/String;)Landroid/view/View; +Landroid/view/WindowManagerGlobal;->getViewRootNames()[Ljava/lang/String; +Landroid/view/WindowManagerGlobal;->getWindowManagerService()Landroid/view/IWindowManager; +Landroid/view/WindowManagerGlobal;->mLock:Ljava/lang/Object; +Landroid/view/WindowManagerGlobal;->mViews:Ljava/util/ArrayList; +Landroid/view/WindowManagerGlobal;->trimMemory(I)V +Landroid/view/WindowManager$LayoutParams;->needsMenuKey:I +Landroid/view/WindowManager$LayoutParams;->NEEDS_MENU_SET_TRUE:I +Landroid/view/WindowManager$LayoutParams;->privateFlags:I +Landroid/view/WindowManager$LayoutParams;->userActivityTimeout:J +Landroid/view/Window;->mAppName:Ljava/lang/String; +Landroid/view/Window;->mAppToken:Landroid/os/IBinder; +Landroid/view/Window;->mHardwareAccelerated:Z +Landroid/webkit/WebSettings;->setNavDump(Z)V +Landroid/webkit/WebSettings;->setPluginsEnabled(Z)V +Landroid/webkit/WebView;->debugDump()V +Landroid/webkit/WebView;->disablePlatformNotifications()V +Landroid/webkit/WebView;->emulateShiftHeld()V +Landroid/webkit/WebView;->enablePlatformNotifications()V +Landroid/webkit/WebViewFactory;->getLoadedPackageInfo()Landroid/content/pm/PackageInfo; +Landroid/webkit/WebViewFactory;->getProvider()Landroid/webkit/WebViewFactoryProvider; +Landroid/webkit/WebViewFactory;->sPackageInfo:Landroid/content/pm/PackageInfo; +Landroid/webkit/WebView;->getVisibleTitleHeight()I +Landroid/webkit/WebView;->getWebViewProvider()Landroid/webkit/WebViewProvider; +Landroid/webkit/WebView;->isPaused()Z +Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider; +Landroid/webkit/WebView;->notifyFindDialogDismissed()V +Landroid/webkit/WebView;->restorePicture(Landroid/os/Bundle;Ljava/io/File;)Z +Landroid/webkit/WebView;->savePicture(Landroid/os/Bundle;Ljava/io/File;)Z +Landroid/webkit/WebView;->sEnforceThreadChecking:Z +Landroid/widget/AbsListView$FlingRunnable;->endFling()V +Landroid/widget/AbsListView;->invokeOnItemScrollListener()V +Landroid/widget/AbsListView;->isVerticalScrollBarHidden()Z +Landroid/widget/AbsListView;->mAdapter:Landroid/widget/ListAdapter; +Landroid/widget/AbsListView;->mEdgeGlowBottom:Landroid/widget/EdgeEffect; +Landroid/widget/AbsListView;->mEdgeGlowTop:Landroid/widget/EdgeEffect; +Landroid/widget/AbsListView;->mFastScroll:Landroid/widget/FastScroller; +Landroid/widget/AbsListView;->mFlingRunnable:Landroid/widget/AbsListView$FlingRunnable; +Landroid/widget/AbsListView;->mIsChildViewEnabled:Z +Landroid/widget/AbsListView;->mMaximumVelocity:I +Landroid/widget/AbsListView;->mMotionPosition:I +Landroid/widget/AbsListView;->mOnScrollListener:Landroid/widget/AbsListView$OnScrollListener; +Landroid/widget/AbsListView;->mRecycler:Landroid/widget/AbsListView$RecycleBin; +Landroid/widget/AbsListView;->mSelectionTopPadding:I +Landroid/widget/AbsListView;->mSelectorPosition:I +Landroid/widget/AbsListView;->mSelectorRect:Landroid/graphics/Rect; +Landroid/widget/AbsListView;->mTouchMode:I +Landroid/widget/AbsListView;->mTouchSlop:I +Landroid/widget/AbsListView;->mVelocityTracker:Landroid/view/VelocityTracker; +Landroid/widget/AbsListView;->performLongPress(Landroid/view/View;IJFF)Z +Landroid/widget/AbsListView;->performLongPress(Landroid/view/View;IJ)Z +Landroid/widget/AbsListView;->smoothScrollBy(IIZZ)V +Landroid/widget/AbsListView;->trackMotionScroll(II)Z +Landroid/widget/AbsSeekBar;->mIsDragging:Z +Landroid/widget/AbsSeekBar;->mSplitTrack:Z +Landroid/widget/AbsSeekBar;->mThumb:Landroid/graphics/drawable/Drawable; +Landroid/widget/AbsSeekBar;->mTouchProgressOffset:F +Landroid/widget/ActivityChooserView;->setExpandActivityOverflowButtonDrawable(Landroid/graphics/drawable/Drawable;)V +Landroid/widget/AdapterView;->mDataChanged:Z +Landroid/widget/AdapterView;->setNextSelectedPositionInt(I)V +Landroid/widget/AdapterView;->setSelectedPositionInt(I)V +Landroid/widget/AutoCompleteTextView;->doAfterTextChanged()V +Landroid/widget/AutoCompleteTextView;->doBeforeTextChanged()V +Landroid/widget/AutoCompleteTextView;->ensureImeVisible(Z)V +Landroid/widget/AutoCompleteTextView;->mPopup:Landroid/widget/ListPopupWindow; +Landroid/widget/AutoCompleteTextView;->setDropDownAlwaysVisible(Z)V +Landroid/widget/CompoundButton;->mButtonDrawable:Landroid/graphics/drawable/Drawable; +Landroid/widget/DatePicker;->mDelegate:Landroid/widget/DatePicker$DatePickerDelegate; +Landroid/widget/EdgeEffect;->mPaint:Landroid/graphics/Paint; +Landroid/widget/Editor;->mShowCursor:J +Landroid/widget/ExpandableListView;->mChildDivider:Landroid/graphics/drawable/Drawable; +Landroid/widget/FastScroller;->mThumbDrawable:Landroid/graphics/drawable/Drawable; +Landroid/widget/FastScroller;->mTrackDrawable:Landroid/graphics/drawable/Drawable; +Landroid/widget/Gallery;->mDownTouchView:Landroid/view/View; +Landroid/widget/GridView;->mColumnWidth:I +Landroid/widget/GridView;->mHorizontalSpacing:I +Landroid/widget/GridView;->mNumColumns:I +Landroid/widget/GridView;->mRequestedNumColumns:I +Landroid/widget/GridView;->mVerticalSpacing:I +Landroid/widget/HorizontalScrollView;->mEdgeGlowLeft:Landroid/widget/EdgeEffect; +Landroid/widget/HorizontalScrollView;->mEdgeGlowRight:Landroid/widget/EdgeEffect; +Landroid/widget/HorizontalScrollView;->mScroller:Landroid/widget/OverScroller; +Landroid/widget/ImageView;->mAdjustViewBounds:Z +Landroid/widget/ImageView;->mAlpha:I +Landroid/widget/ImageView;->mMaxHeight:I +Landroid/widget/ImageView;->mMaxWidth:I +Landroid/widget/LinearLayout;->mGravity:I +Landroid/widget/LinearLayout;->mUseLargestChild:Z +Landroid/widget/ListPopupWindow;->mPopup:Landroid/widget/PopupWindow; +Landroid/widget/ListPopupWindow;->setForceIgnoreOutsideTouch(Z)V +Landroid/widget/ListView;->mAreAllItemsSelectable:Z +Landroid/widget/ListView;->setSelectionInt(I)V +Landroid/widget/MediaController;->mAnchor:Landroid/view/View; +Landroid/widget/MediaController;->mDecor:Landroid/view/View; +Landroid/widget/MediaController;->mDecorLayoutParams:Landroid/view/WindowManager$LayoutParams; +Landroid/widget/MediaController;->mWindowManager:Landroid/view/WindowManager; +Landroid/widget/NumberPicker;->mInputText:Landroid/widget/EditText; +Landroid/widget/NumberPicker;->mSelectionDivider:Landroid/graphics/drawable/Drawable; +Landroid/widget/NumberPicker;->mSelectorWheelPaint:Landroid/graphics/Paint; +Landroid/widget/PopupMenu;->mPopup:Lcom/android/internal/view/menu/MenuPopupHelper; +Landroid/widget/PopupWindow;->computeAnimationResource()I +Landroid/widget/PopupWindow;->createPopupLayoutParams(Landroid/os/IBinder;)Landroid/view/WindowManager$LayoutParams; +Landroid/widget/PopupWindow;->invokePopup(Landroid/view/WindowManager$LayoutParams;)V +Landroid/widget/PopupWindow;->mAboveAnchor:Z +Landroid/widget/PopupWindow;->mAnchor:Ljava/lang/ref/WeakReference; +Landroid/widget/PopupWindow;->mAnimationStyle:I +Landroid/widget/PopupWindow;->mBackgroundView:Landroid/view/View; +Landroid/widget/PopupWindow;->mContentView:Landroid/view/View; +Landroid/widget/PopupWindow;->mHeightMode:I +Landroid/widget/PopupWindow;->mIsDropdown:Z +Landroid/widget/PopupWindow;->mIsShowing:Z +Landroid/widget/PopupWindow;->mLastHeight:I +Landroid/widget/PopupWindow;->mLastWidth:I +Landroid/widget/PopupWindow;->mOnScrollChangedListener:Landroid/view/ViewTreeObserver$OnScrollChangedListener; +Landroid/widget/PopupWindow;->mWidthMode:I +Landroid/widget/PopupWindow;->preparePopup(Landroid/view/WindowManager$LayoutParams;)V +Landroid/widget/PopupWindow;->setLayoutInScreenEnabled(Z)V +Landroid/widget/PopupWindow;->setLayoutInsetDecor(Z)V +Landroid/widget/PopupWindow;->setTouchModal(Z)V +Landroid/widget/ProgressBar;->mCurrentDrawable:Landroid/graphics/drawable/Drawable; +Landroid/widget/ProgressBar;->mDuration:I +Landroid/widget/ProgressBar;->mIndeterminate:Z +Landroid/widget/ProgressBar;->mMaxHeight:I +Landroid/widget/ProgressBar;->mMinHeight:I +Landroid/widget/ProgressBar;->mOnlyIndeterminate:Z +Landroid/widget/RelativeLayout$LayoutParams;->mBottom:I +Landroid/widget/RelativeLayout$LayoutParams;->mLeft:I +Landroid/widget/RelativeLayout$LayoutParams;->mRight:I +Landroid/widget/RelativeLayout$LayoutParams;->mTop:I +Landroid/widget/RelativeLayout;->mGravity:I +Landroid/widget/RemoteViews$Action;->mergeBehavior()I +Landroid/widget/RemoteViews$Action;->viewId:I +Landroid/widget/RemoteViews;->mActions:Ljava/util/ArrayList; +Landroid/widget/RemoteViews;->mApplication:Landroid/content/pm/ApplicationInfo; +Landroid/widget/RemoteViews$ReflectionAction;->methodName:Ljava/lang/String; +Landroid/widget/ScrollView;->mEdgeGlowBottom:Landroid/widget/EdgeEffect; +Landroid/widget/ScrollView;->mEdgeGlowTop:Landroid/widget/EdgeEffect; +Landroid/widget/ScrollView;->mIsBeingDragged:Z +Landroid/widget/ScrollView;->mOverflingDistance:I +Landroid/widget/ScrollView;->mOverscrollDistance:I +Landroid/widget/ScrollView;->mScroller:Landroid/widget/OverScroller; +Landroid/widget/SearchView;->mCloseButton:Landroid/widget/ImageView; +Landroid/widget/SearchView;->mSearchButton:Landroid/widget/ImageView; +Landroid/widget/SearchView;->mSearchPlate:Landroid/view/View; +Landroid/widget/SearchView;->onCloseClicked()V +Landroid/widget/SearchView;->setQuery(Ljava/lang/CharSequence;)V +Landroid/widget/SlidingDrawer;->mTopOffset:I +Landroid/widget/Spinner;->mPopup:Landroid/widget/Spinner$SpinnerPopup; +Landroid/widget/Switch;->mThumbDrawable:Landroid/graphics/drawable/Drawable; +Landroid/widget/Switch;->mTrackDrawable:Landroid/graphics/drawable/Drawable; +Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V +Landroid/widget/TextView;->assumeLayout()V +Landroid/widget/TextView;->createEditorIfNeeded()V +Landroid/widget/TextView;->mCursorDrawableRes:I +Landroid/widget/TextView;->mCurTextColor:I +Landroid/widget/TextView;->mEditor:Landroid/widget/Editor; +Landroid/widget/TextView;->mListeners:Ljava/util/ArrayList; +Landroid/widget/TextView;->mMarquee:Landroid/widget/TextView$Marquee; +Landroid/widget/TextView;->mMaximum:I +Landroid/widget/TextView;->mMaxMode:I +Landroid/widget/TextView;->mSingleLine:Z +Landroid/widget/VideoView;->mCurrentBufferPercentage:I +Landroid/widget/VideoView;->mMediaController:Landroid/widget/MediaController; +Landroid/widget/VideoView;->mSHCallback:Landroid/view/SurfaceHolder$Callback; +Landroid/widget/VideoView;->mUri:Landroid/net/Uri; +Landroid/widget/VideoView;->mVideoHeight:I +Landroid/widget/VideoView;->mVideoWidth:I +Lcom/android/internal/app/IBatteryStats;->getStatistics()[B +Lcom/android/internal/app/IBatteryStats$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IBatteryStats; +Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryRealtime(JI)J +Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryUptime(JI)J +Lcom/android/internal/os/BatteryStatsImpl;->CREATOR:Landroid/os/Parcelable$Creator; +Lcom/android/internal/os/BatteryStatsImpl;->getGlobalWifiRunningTime(JI)J +Lcom/android/internal/os/BatteryStatsImpl;->getScreenOnTime(JI)J +Lcom/android/internal/os/BatteryStatsImpl;->getUidStats()Landroid/util/SparseArray; +Lcom/android/internal/os/BatteryStatsImpl$Timer;->getCountLocked(I)I +Lcom/android/internal/os/BatteryStatsImpl$Timer;->getTotalTimeLocked(JI)J +Lcom/android/internal/os/BatteryStatsImpl$Uid;->getProcessStats()Landroid/util/ArrayMap; +Lcom/android/internal/os/BatteryStatsImpl$Uid;->getSensorStats()Landroid/util/SparseArray; +Lcom/android/internal/os/BatteryStatsImpl$Uid;->getUid()I +Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWakelockStats()Landroid/util/ArrayMap; +Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWifiRunningTime(JI)J +Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWifiScanTime(JI)J +Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getForegroundTime(I)J +Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getStarts(I)I +Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getSystemTime(I)J +Lcom/android/internal/os/BatteryStatsImpl$Uid$Proc;->getUserTime(I)J +Lcom/android/internal/os/BatteryStatsImpl$Uid$Sensor;->getHandle()I +Lcom/android/internal/os/BatteryStatsImpl$Uid$Sensor;->getSensorTime()Lcom/android/internal/os/BatteryStatsImpl$Timer; +Lcom/android/internal/os/BatteryStatsImpl$Uid$Wakelock;->getWakeTime(I)Lcom/android/internal/os/BatteryStatsImpl$Timer; +Lcom/android/internal/os/FuseAppLoop;->onCommand(IJJJI[B)V +Lcom/android/internal/os/FuseAppLoop;->onOpen(JJ)[B +Lcom/android/internal/os/PowerProfile;->getAveragePower(Ljava/lang/String;)D +Lcom/android/internal/os/PowerProfile;->getAveragePower(Ljava/lang/String;I)D +Lcom/android/internal/os/PowerProfile;->getBatteryCapacity()D +Lcom/android/internal/R$array;->config_mobile_hotspot_provision_app:I +Lcom/android/internal/R$array;->config_tether_wifi_regexs:I +Lcom/android/internal/R$attr;->switchStyle:I +Lcom/android/internal/R$bool;->config_mms_content_disposition_support:I +Lcom/android/internal/R$bool;->config_showNavigationBar:I +Lcom/android/internal/R$dimen;->navigation_bar_height:I +Lcom/android/internal/R$dimen;->navigation_bar_height_landscape:I +Lcom/android/internal/R$dimen;->status_bar_height:I +Lcom/android/internal/R$drawable;->ic_menu_close_clear_cancel:I +Lcom/android/internal/R$id;->amPm:I +Lcom/android/internal/R$id;->edittext_container:I +Lcom/android/internal/R$id;->icon:I +Lcom/android/internal/R$id;->message:I +Lcom/android/internal/R$id;->minute:I +Lcom/android/internal/R$id;->shortcut:I +Lcom/android/internal/R$id;->text:I +Lcom/android/internal/R$id;->time:I +Lcom/android/internal/R$id;->timePicker:I +Lcom/android/internal/R$id;->title_container:I +Lcom/android/internal/R$id;->title:I +Lcom/android/internal/R$integer;->config_screenBrightnessDim:I +Lcom/android/internal/R$layout;->screen_title:I +Lcom/android/internal/R$styleable;->CompoundButton_button:I +Lcom/android/internal/R$styleable;->CompoundButton:[I +Lcom/android/internal/R$styleable;->IconMenuView:[I +Lcom/android/internal/R$styleable;->ImageView:[I +Lcom/android/internal/R$styleable;->ImageView_src:I +Lcom/android/internal/R$styleable;->ScrollView:[I +Lcom/android/internal/R$styleable;->TabWidget:[I +Lcom/android/internal/R$styleable;->TextView_drawableBottom:I +Lcom/android/internal/R$styleable;->TextView_drawableLeft:I +Lcom/android/internal/R$styleable;->TextView_drawableRight:I +Lcom/android/internal/R$styleable;->TextView_drawableTop:I +Lcom/android/internal/R$styleable;->TextView:[I +Lcom/android/internal/R$styleable;->View_background:I +Lcom/android/internal/R$styleable;->View:[I +Lcom/android/internal/R$styleable;->View_id:I +Lcom/android/internal/R$styleable;->ViewStub:[I +Lcom/android/internal/R$styleable;->ViewStub_inflatedId:I +Lcom/android/internal/R$styleable;->ViewStub_layout:I +Lcom/android/internal/R$styleable;->Window_windowActionBarFullscreenDecorLayout:I +Lcom/android/internal/R$xml;->power_profile:I +Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneSubInfo; +Lcom/android/internal/telephony/ISms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISms; +Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V +Lcom/android/internal/telephony/ITelephony;->endCall()Z +Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z +Lcom/android/internal/telephony/ITelephony;->silenceRinger()V +Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony; +Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap; +Lcom/android/internal/view/InputBindResult;->CREATOR:Landroid/os/Parcelable$Creator; +Lcom/android/internal/view/menu/MenuBuilder;->mContext:Landroid/content/Context; +Lcom/android/internal/view/menu/MenuBuilder;->setCurrentMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V +Lcom/android/internal/view/menu/MenuBuilder;->setOptionalIconsVisible(Z)V +Lcom/android/internal/view/menu/MenuItemImpl;->mIconResId:I +Lcom/android/internal/view/menu/MenuItemImpl;->setMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V +Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I +Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool; +Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B +Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([B)V +Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String; +Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList; +Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard; +Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V +Ldalvik/system/CloseGuard;->warnIfOpen()V +Ldalvik/system/DexFile;->mCookie:Ljava/lang/Object; +Ldalvik/system/DexFile;->mFileName:Ljava/lang/String; +Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object; +Ldalvik/system/DexPathList;->dexElements:[Ldalvik/system/DexPathList$Element; +Ldalvik/system/DexPathList$Element;->dexFile:Ldalvik/system/DexFile; +Ldalvik/system/DexPathList;->makeDexElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;Ljava/lang/ClassLoader;)[Ldalvik/system/DexPathList$Element; +Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;)[Ldalvik/system/DexPathList$NativeLibraryElement; +Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;)[Ldalvik/system/DexPathList$Element; +Ldalvik/system/DexPathList;->nativeLibraryDirectories:Ljava/util/List; +Ldalvik/system/DexPathList;->nativeLibraryPathElements:[Ldalvik/system/DexPathList$NativeLibraryElement; +Ldalvik/system/DexPathList;->systemNativeLibraryDirectories:Ljava/util/List; +Ldalvik/system/VMDebug;->dumpReferenceTables()V +Ldalvik/system/VMRuntime;->clearGrowthLimit()V +Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String; +Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime; +Ldalvik/system/VMRuntime;->is64Bit()Z +Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object; +Ldalvik/system/VMRuntime;->registerNativeAllocation(I)V +Ldalvik/system/VMRuntime;->registerNativeFree(I)V +Ldalvik/system/VMRuntime;->setMinimumHeapSize(J)J +Ldalvik/system/VMRuntime;->setTargetHeapUtilization(F)F +Ldalvik/system/VMRuntime;->trackExternalAllocation(J)Z +Ldalvik/system/VMRuntime;->trackExternalFree(J)V +Ldalvik/system/VMStack;->getCallingClassLoader()Ljava/lang/ClassLoader; +Ldalvik/system/VMStack;->getStackClass2()Ljava/lang/Class; +Ljava/io/FileDescriptor;->descriptor:I +Ljava/io/FileDescriptor;->getInt$()I +Ljava/io/FileDescriptor;->setInt$(I)V +Ljava/io/ObjectStreamClass;->getConstructorId(Ljava/lang/Class;)J +Ljava/io/ObjectStreamClass;->newInstance(Ljava/lang/Class;J)Ljava/lang/Object; +Ljava/io/ObjectStreamClass;->newInstance()Ljava/lang/Object; +Ljava/lang/Class;->dexCache:Ljava/lang/Object; +Ljava/lang/Class;->dexClassDefIndex:I +Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader; +Ljava/lang/Daemons$Daemon;->stop()V +Ljava/lang/Daemons$Daemon;->thread:Ljava/lang/Thread; +Ljava/lang/Daemons$FinalizerDaemon;->finalizingObject:Ljava/lang/Object; +Ljava/lang/Daemons$FinalizerDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerDaemon; +Ljava/lang/Daemons$FinalizerWatchdogDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon; +Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V +Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V +Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String; +Ljava/lang/ThreadGroup;->parent:Ljava/lang/ThreadGroup; +Ljava/lang/ThreadGroup;->systemThreadGroup:Ljava/lang/ThreadGroup; +Ljava/lang/Thread;->inheritableThreadLocals:Ljava/lang/ThreadLocal$ThreadLocalMap; +Ljava/lang/Throwable;->detailMessage:Ljava/lang/String; +Ljava/net/Authenticator;->theAuthenticator:Ljava/net/Authenticator; +Ljava/net/DatagramSocket;->impl:Ljava/net/DatagramSocketImpl; +Ljava/net/InetAddress;->clearDnsCache()V +Ljava/net/InetAddress;->isNumeric(Ljava/lang/String;)Z +Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress; +Ljava/net/Socket;->impl:Ljava/net/SocketImpl; +Ljava/net/URI;->host:Ljava/lang/String; +Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z +Ljava/security/spec/ECParameterSpec;->getCurveName()Ljava/lang/String; +Ljava/security/spec/ECParameterSpec;->setCurveName(Ljava/lang/String;)V +Ljava/util/ArrayList$SubList;->parent:Ljava/util/AbstractList; +Ljava/util/ArrayList$SubList;->parentOffset:I +Ljava/util/ArrayList$SubList;->size:I +Ljava/util/Arrays$ArrayList;->a:[Ljava/lang/Object; +Ljava/util/Calendar;->zone:Ljava/util/TimeZone; +Ljava/util/concurrent/FutureTask;->callable:Ljava/util/concurrent/Callable; +Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale; +Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory; +Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory; +Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>(Ljavax/net/ssl/SSLSocketFactory;)V +Lorg/json/JSONArray;->values:Ljava/util/List; +Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 7bfb20f8cc35..04c44a3818a3 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -425,6 +425,12 @@ import java.util.List; * safely called after {@link #onPause()} and allows and application to safely * wait until {@link #onStop()} to save persistent state.</p> * + * <p class="note">For applications targeting platforms starting with + * {@link android.os.Build.VERSION_CODES#P} {@link #onSaveInstanceState(Bundle)} + * will always be called after {@link #onStop}, so an application may safely + * perform fragment transactions in {@link #onStop} and will be able to save + * persistent state later.</p> + * * <p>For those methods that are not marked as being killable, the activity's * process will not be killed by the system starting from the time the method * is called and continuing after it returns. Thus an activity is in the killable @@ -1577,8 +1583,11 @@ public class Activity extends ContextThemeWrapper * call through to the default implementation, otherwise be prepared to save * all of the state of each view yourself. * - * <p>If called, this method will occur before {@link #onStop}. There are - * no guarantees about whether it will occur before or after {@link #onPause}. + * <p>If called, this method will occur after {@link #onStop} for applications + * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}. + * For applications targeting earlier platform versions this method will occur + * before {@link #onStop} and there are no guarantees about whether it will + * occur before or after {@link #onPause}. * * @param outState Bundle in which to place your saved state. * @@ -7143,7 +7152,10 @@ public class Activity extends ContextThemeWrapper boolean isApiWarningEnabled = SystemProperties.getInt("ro.art.hiddenapi.warning", 0) == 1; if (isAppDebuggable || isApiWarningEnabled) { - if (VMRuntime.getRuntime().hasUsedHiddenApi()) { + if (!mMainThread.mHiddenApiWarningShown && VMRuntime.getRuntime().hasUsedHiddenApi()) { + // Only show the warning once per process. + mMainThread.mHiddenApiWarningShown = true; + String appName = getApplicationInfo().loadLabel(getPackageManager()) .toString(); String warning = "Detected problems with API compatiblity\n" diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e8535cd1bca2..42825f0ad8d4 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -291,6 +291,7 @@ public final class ActivityThread extends ClientTransactionHandler { boolean mJitEnabled = false; boolean mSomeActivitiesChanged = false; boolean mUpdatingSystemConfig = false; + /* package */ boolean mHiddenApiWarningShown = false; // These can be accessed by multiple threads; mResourcesManager is the lock. // XXX For now we keep around information about all packages we have @@ -487,12 +488,14 @@ public final class ActivityThread extends ClientTransactionHandler { } } - public boolean isPreHoneycomb() { - if (activity != null) { - return activity.getApplicationInfo().targetSdkVersion - < android.os.Build.VERSION_CODES.HONEYCOMB; - } - return false; + private boolean isPreHoneycomb() { + return activity != null && activity.getApplicationInfo().targetSdkVersion + < android.os.Build.VERSION_CODES.HONEYCOMB; + } + + private boolean isPreP() { + return activity != null && activity.getApplicationInfo().targetSdkVersion + < android.os.Build.VERSION_CODES.P; } public boolean isPersistable() { @@ -4164,9 +4167,12 @@ public final class ActivityThread extends ClientTransactionHandler { * {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call. */ private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) { + // Before P onSaveInstanceState was called before onStop, starting with P it's + // called after. Before Honeycomb state was always saved before onPause. final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null && !r.isPreHoneycomb(); - if (shouldSaveState) { + final boolean isPreP = r.isPreP(); + if (shouldSaveState && isPreP) { callActivityOnSaveInstanceState(r); } @@ -4185,6 +4191,10 @@ public final class ActivityThread extends ClientTransactionHandler { r.setState(ON_STOP); EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(), r.activity.getComponentName().getClassName(), reason); + + if (shouldSaveState && !isPreP) { + callActivityOnSaveInstanceState(r); + } } private void updateVisibility(ActivityClientRecord r, boolean show) { diff --git a/core/java/android/app/EphemeralResolverService.java b/core/java/android/app/EphemeralResolverService.java deleted file mode 100644 index 427a0386e87c..000000000000 --- a/core/java/android/app/EphemeralResolverService.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2015 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; - -import android.annotation.SystemApi; -import android.app.Service; -import android.app.InstantAppResolverService.InstantAppResolutionCallback; -import android.content.Context; -import android.content.Intent; -import android.content.pm.EphemeralResolveInfo; -import android.content.pm.InstantAppResolveInfo; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.IRemoteCallback; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Base class for implementing the resolver service. - * @hide - * @removed - * @deprecated use InstantAppResolverService instead - */ -@Deprecated -@SystemApi -public abstract class EphemeralResolverService extends InstantAppResolverService { - private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE; - private static final String TAG = "PackageManager"; - - /** - * Called to retrieve resolve info for ephemeral applications. - * - * @param digestPrefix The hash prefix of the ephemeral's domain. - * @param prefixMask A mask that was applied to each digest prefix. This should - * be used when comparing against the digest prefixes as all bits might - * not be set. - * @deprecated use {@link #onGetEphemeralResolveInfo(int[])} instead - */ - @Deprecated - public abstract List<EphemeralResolveInfo> onEphemeralResolveInfoList( - int digestPrefix[], int prefix); - - /** - * Called to retrieve resolve info for ephemeral applications. - * - * @param digestPrefix The hash prefix of the ephemeral's domain. - */ - public List<EphemeralResolveInfo> onGetEphemeralResolveInfo(int digestPrefix[]) { - return onEphemeralResolveInfoList(digestPrefix, 0xFFFFF000); - } - - /** - * Called to retrieve intent filters for ephemeral applications. - * - * @param hostName The name of the host to get intent filters for. - */ - public EphemeralResolveInfo onGetEphemeralIntentFilter(String hostName) { - throw new IllegalStateException("Must define"); - } - - @Override - public Looper getLooper() { - return super.getLooper(); - } - - @Override - void _onGetInstantAppResolveInfo(int[] digestPrefix, String token, - InstantAppResolutionCallback callback) { - if (DEBUG_EPHEMERAL) { - Log.d(TAG, "Legacy resolver; getInstantAppResolveInfo;" - + " prefix: " + Arrays.toString(digestPrefix)); - } - final List<EphemeralResolveInfo> response = onGetEphemeralResolveInfo(digestPrefix); - final int responseSize = response == null ? 0 : response.size(); - final List<InstantAppResolveInfo> resultList = new ArrayList<>(responseSize); - for (int i = 0; i < responseSize; i++) { - resultList.add(response.get(i).getInstantAppResolveInfo()); - } - callback.onInstantAppResolveInfo(resultList); - } - - @Override - void _onGetInstantAppIntentFilter(int[] digestPrefix, String token, - String hostName, InstantAppResolutionCallback callback) { - if (DEBUG_EPHEMERAL) { - Log.d(TAG, "Legacy resolver; getInstantAppIntentFilter;" - + " prefix: " + Arrays.toString(digestPrefix)); - } - final EphemeralResolveInfo response = onGetEphemeralIntentFilter(hostName); - final List<InstantAppResolveInfo> resultList = new ArrayList<>(1); - resultList.add(response.getInstantAppResolveInfo()); - callback.onInstantAppResolveInfo(resultList); - } -} diff --git a/core/java/android/app/IInstantAppResolver.aidl b/core/java/android/app/IInstantAppResolver.aidl index 805d8c057d27..ae200578d829 100644 --- a/core/java/android/app/IInstantAppResolver.aidl +++ b/core/java/android/app/IInstantAppResolver.aidl @@ -16,13 +16,15 @@ package android.app; +import android.content.Intent; import android.os.IRemoteCallback; /** @hide */ oneway interface IInstantAppResolver { - void getInstantAppResolveInfoList(in int[] digestPrefix, + void getInstantAppResolveInfoList(in Intent sanitizedIntent, in int[] hostDigestPrefix, String token, int sequence, IRemoteCallback callback); - void getInstantAppIntentFilterList(in int[] digestPrefix, - String token, String hostName, IRemoteCallback callback); + void getInstantAppIntentFilterList(in Intent sanitizedIntent, in int[] hostDigestPrefix, + String token, IRemoteCallback callback); + } diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java index c5dc86c79ef9..76a36820ed83 100644 --- a/core/java/android/app/InstantAppResolverService.java +++ b/core/java/android/app/InstantAppResolverService.java @@ -17,7 +17,6 @@ package android.app; import android.annotation.SystemApi; -import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.InstantAppResolveInfo; @@ -35,6 +34,7 @@ import android.util.Slog; import com.android.internal.os.SomeArgs; import java.util.Arrays; +import java.util.Collections; import java.util.List; /** @@ -43,7 +43,7 @@ import java.util.List; */ @SystemApi public abstract class InstantAppResolverService extends Service { - private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE; + private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE; private static final String TAG = "PackageManager"; /** @hide */ @@ -53,23 +53,65 @@ public abstract class InstantAppResolverService extends Service { Handler mHandler; /** - * Called to retrieve resolve info for instant applications. + * Called to retrieve resolve info for instant applications immediately. * * @param digestPrefix The hash prefix of the instant app's domain. + * @deprecated should implement {@link #onGetInstantAppResolveInfo(Intent, int[], String, + * InstantAppResolutionCallback)} */ + @Deprecated public void onGetInstantAppResolveInfo( int digestPrefix[], String token, InstantAppResolutionCallback callback) { throw new IllegalStateException("Must define"); } /** - * Called to retrieve intent filters for instant applications. + * Called to retrieve intent filters for instant applications from potentially expensive + * sources. * * @param digestPrefix The hash prefix of the instant app's domain. + * @deprecated should implement {@link #onGetInstantAppIntentFilter(Intent, int[], String, + * InstantAppResolutionCallback)} */ + @Deprecated public void onGetInstantAppIntentFilter( int digestPrefix[], String token, InstantAppResolutionCallback callback) { - throw new IllegalStateException("Must define"); + throw new IllegalStateException("Must define onGetInstantAppIntentFilter"); + } + + /** + * Called to retrieve resolve info for instant applications immediately. + * + * @param sanitizedIntent The sanitized {@link Intent} used for resolution. + * @param hostDigestPrefix The hash prefix of the instant app's domain. + */ + public void onGetInstantAppResolveInfo(Intent sanitizedIntent, int[] hostDigestPrefix, + String token, InstantAppResolutionCallback callback) { + // if not overridden, forward to old methods and filter out non-web intents + if (sanitizedIntent.isBrowsableWebIntent()) { + onGetInstantAppResolveInfo(hostDigestPrefix, token, callback); + } else { + callback.onInstantAppResolveInfo(Collections.emptyList()); + } + } + + /** + * Called to retrieve intent filters for instant applications from potentially expensive + * sources. + * + * @param sanitizedIntent The sanitized {@link Intent} used for resolution. + * @param hostDigestPrefix The hash prefix of the instant app's domain or null if no host is + * defined. + */ + public void onGetInstantAppIntentFilter(Intent sanitizedIntent, int[] hostDigestPrefix, + String token, InstantAppResolutionCallback callback) { + Log.e(TAG, "New onGetInstantAppIntentFilter is not overridden"); + // if not overridden, forward to old methods and filter out non-web intents + if (sanitizedIntent.isBrowsableWebIntent()) { + onGetInstantAppIntentFilter(hostDigestPrefix, token, callback); + } else { + callback.onInstantAppResolveInfo(Collections.emptyList()); + } } /** @@ -89,33 +131,33 @@ public abstract class InstantAppResolverService extends Service { public final IBinder onBind(Intent intent) { return new IInstantAppResolver.Stub() { @Override - public void getInstantAppResolveInfoList( - int digestPrefix[], String token, int sequence, IRemoteCallback callback) { - if (DEBUG_EPHEMERAL) { + public void getInstantAppResolveInfoList(Intent sanitizedIntent, int[] digestPrefix, + String token, int sequence, IRemoteCallback callback) { + if (DEBUG_INSTANT) { Slog.v(TAG, "[" + token + "] Phase1 called; posting"); } final SomeArgs args = SomeArgs.obtain(); args.arg1 = callback; args.arg2 = digestPrefix; args.arg3 = token; - mHandler.obtainMessage( - ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, sequence, 0, args) - .sendToTarget(); + args.arg4 = sanitizedIntent; + mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, + sequence, 0, args).sendToTarget(); } @Override - public void getInstantAppIntentFilterList( - int digestPrefix[], String token, String hostName, IRemoteCallback callback) { - if (DEBUG_EPHEMERAL) { + public void getInstantAppIntentFilterList(Intent sanitizedIntent, + int[] digestPrefix, String token, IRemoteCallback callback) { + if (DEBUG_INSTANT) { Slog.v(TAG, "[" + token + "] Phase2 called; posting"); } final SomeArgs args = SomeArgs.obtain(); args.arg1 = callback; args.arg2 = digestPrefix; args.arg3 = token; - args.arg4 = hostName; - mHandler.obtainMessage( - ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, callback).sendToTarget(); + args.arg4 = sanitizedIntent; + mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, + callback).sendToTarget(); } }; } @@ -142,29 +184,9 @@ public abstract class InstantAppResolverService extends Service { } } - @Deprecated - void _onGetInstantAppResolveInfo(int[] digestPrefix, String token, - InstantAppResolutionCallback callback) { - if (DEBUG_EPHEMERAL) { - Slog.d(TAG, "[" + token + "] Phase1 request;" - + " prefix: " + Arrays.toString(digestPrefix)); - } - onGetInstantAppResolveInfo(digestPrefix, token, callback); - } - @Deprecated - void _onGetInstantAppIntentFilter(int digestPrefix[], String token, String hostName, - InstantAppResolutionCallback callback) { - if (DEBUG_EPHEMERAL) { - Slog.d(TAG, "[" + token + "] Phase2 request;" - + " prefix: " + Arrays.toString(digestPrefix)); - } - onGetInstantAppIntentFilter(digestPrefix, token, callback); - } - private final class ServiceHandler extends Handler { public static final int MSG_GET_INSTANT_APP_RESOLVE_INFO = 1; public static final int MSG_GET_INSTANT_APP_INTENT_FILTER = 2; - public ServiceHandler(Looper looper) { super(looper, null /*callback*/, true /*async*/); } @@ -179,9 +201,13 @@ public abstract class InstantAppResolverService extends Service { final IRemoteCallback callback = (IRemoteCallback) args.arg1; final int[] digestPrefix = (int[]) args.arg2; final String token = (String) args.arg3; + final Intent intent = (Intent) args.arg4; final int sequence = message.arg1; - _onGetInstantAppResolveInfo( - digestPrefix, token, + if (DEBUG_INSTANT) { + Slog.d(TAG, "[" + token + "] Phase1 request;" + + " prefix: " + Arrays.toString(digestPrefix)); + } + onGetInstantAppResolveInfo(intent, digestPrefix, token, new InstantAppResolutionCallback(sequence, callback)); } break; @@ -190,9 +216,12 @@ public abstract class InstantAppResolverService extends Service { final IRemoteCallback callback = (IRemoteCallback) args.arg1; final int[] digestPrefix = (int[]) args.arg2; final String token = (String) args.arg3; - final String hostName = (String) args.arg4; - _onGetInstantAppIntentFilter( - digestPrefix, token, hostName, + final Intent intent = (Intent) args.arg4; + if (DEBUG_INSTANT) { + Slog.d(TAG, "[" + token + "] Phase2 request;" + + " prefix: " + Arrays.toString(digestPrefix)); + } + onGetInstantAppIntentFilter(intent, digestPrefix, token, new InstantAppResolutionCallback(-1 /*sequence*/, callback)); } break; diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index f21746cdd275..39bccc37d2b1 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -51,6 +51,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.DeadSystemException; +import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -1329,11 +1330,7 @@ public class WallpaperManager { private void copyStreamToWallpaperFile(InputStream data, FileOutputStream fos) throws IOException { - byte[] buffer = new byte[32768]; - int amt; - while ((amt=data.read(buffer)) > 0) { - fos.write(buffer, 0, amt); - } + FileUtils.copy(data, fos); } /** diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index 085fc79f58e4..46566e79d99c 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -146,6 +146,9 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu }) public @interface WindowConfig {} + /** @hide */ + public static final int PINNED_WINDOWING_MODE_ELEVATION_IN_DIP = 5; + public WindowConfiguration() { unset(); } diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl index 4461b16fe15c..38d9025cc82f 100644 --- a/core/java/android/app/slice/ISliceManager.aidl +++ b/core/java/android/app/slice/ISliceManager.aidl @@ -31,4 +31,7 @@ interface ISliceManager { SliceSpec[] getPinnedSpecs(in Uri uri, String pkg); int checkSlicePermission(in Uri uri, String pkg, int pid, int uid); void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices); + + byte[] getBackupPayload(int user); + void applyRestore(in byte[] payload, int user); } diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java index 2fa9d8e0882f..3f13fffb3857 100644 --- a/core/java/android/app/slice/SliceManager.java +++ b/core/java/android/app/slice/SliceManager.java @@ -20,9 +20,9 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; +import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; -import android.content.IContentProvider; import android.content.Intent; import android.content.pm.ResolveInfo; import android.net.Uri; @@ -193,10 +193,15 @@ public class SliceManager { * <p> * Pinned state is not persisted across reboots, so apps are expected to re-pin any slices * they still care about after a reboot. + * <p> + * This may only be called by apps that are the default launcher for the device + * or the default voice interaction service. Otherwise will throw {@link SecurityException}. * * @param uri The uri of the slice being pinned. * @param specs The list of supported {@link SliceSpec}s of the callback. * @see SliceProvider#onSlicePinned(Uri) + * @see Intent#ACTION_ASSIST + * @see Intent#CATEGORY_HOME */ public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) { try { @@ -211,10 +216,15 @@ public class SliceManager { * Remove a pin for a slice. * <p> * If the slice has no other pins/callbacks then the slice will be unpinned. + * <p> + * This may only be called by apps that are the default launcher for the device + * or the default voice interaction service. Otherwise will throw {@link SecurityException}. * * @param uri The uri of the slice being unpinned. * @see #pinSlice * @see SliceProvider#onSliceUnpinned(Uri) + * @see Intent#ACTION_ASSIST + * @see Intent#CATEGORY_HOME */ public void unpinSlice(@NonNull Uri uri) { try { @@ -262,17 +272,13 @@ public class SliceManager { */ public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) { ContentResolver resolver = mContext.getContentResolver(); - IContentProvider provider = resolver.acquireProvider(uri); - try { + try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) { Bundle extras = new Bundle(); extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri); - final Bundle res = provider.call(resolver.getPackageName(), - SliceProvider.METHOD_GET_DESCENDANTS, null, extras); + final Bundle res = provider.call(SliceProvider.METHOD_GET_DESCENDANTS, null, extras); return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS); } catch (RemoteException e) { Log.e(TAG, "Unable to get slice descendants", e); - } finally { - resolver.releaseProvider(provider); } return Collections.emptyList(); } @@ -288,17 +294,15 @@ public class SliceManager { public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) { Preconditions.checkNotNull(uri, "uri"); ContentResolver resolver = mContext.getContentResolver(); - IContentProvider provider = resolver.acquireProvider(uri); - if (provider == null) { - throw new IllegalArgumentException("Unknown URI " + uri); - } - try { + try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) { + if (provider == null) { + throw new IllegalArgumentException("Unknown URI " + uri); + } Bundle extras = new Bundle(); extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri); extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS, new ArrayList<>(supportedSpecs)); - final Bundle res = provider.call(mContext.getPackageName(), SliceProvider.METHOD_SLICE, - null, extras); + final Bundle res = provider.call(SliceProvider.METHOD_SLICE, null, extras); Bundle.setDefusable(res, true); if (res == null) { return null; @@ -308,8 +312,55 @@ public class SliceManager { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. return null; - } finally { - resolver.releaseProvider(provider); + } + } + + /** + * Turns a slice intent into a slice uri. Expects an explicit intent. If there is no + * {@link android.content.ContentProvider} associated with the given intent this will throw + * {@link IllegalArgumentException}. + * + * @param intent The intent associated with a slice. + * @return The Slice Uri provided by the app or null if none is given. + * @see Slice + * @see SliceProvider#onMapIntentToUri(Intent) + * @see Intent + */ + public @Nullable Uri mapIntentToUri(@NonNull Intent intent) { + Preconditions.checkNotNull(intent, "intent"); + Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null, + "Slice intent must be explicit %s", intent); + ContentResolver resolver = mContext.getContentResolver(); + + // Check if the intent has data for the slice uri on it and use that + final Uri intentData = intent.getData(); + if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) { + return intentData; + } + // Otherwise ask the app + List<ResolveInfo> providers = + mContext.getPackageManager().queryIntentContentProviders(intent, 0); + if (providers == null || providers.isEmpty()) { + throw new IllegalArgumentException("Unable to resolve intent " + intent); + } + String authority = providers.get(0).providerInfo.authority; + Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) + .authority(authority).build(); + try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) { + if (provider == null) { + throw new IllegalArgumentException("Unknown URI " + uri); + } + Bundle extras = new Bundle(); + extras.putParcelable(SliceProvider.EXTRA_INTENT, intent); + final Bundle res = provider.call(SliceProvider.METHOD_MAP_ONLY_INTENT, null, extras); + if (res == null) { + return null; + } + return res.getParcelable(SliceProvider.EXTRA_SLICE); + } catch (RemoteException e) { + // Arbitrary and not worth documenting, as Activity + // Manager will kill this process shortly anyway. + return null; } } @@ -329,7 +380,7 @@ public class SliceManager { @NonNull List<SliceSpec> supportedSpecs) { Preconditions.checkNotNull(intent, "intent"); Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null, - "Slice intent must be explicit " + intent); + "Slice intent must be explicit %s", intent); ContentResolver resolver = mContext.getContentResolver(); // Check if the intent has data for the slice uri on it and use that @@ -340,23 +391,21 @@ public class SliceManager { // Otherwise ask the app List<ResolveInfo> providers = mContext.getPackageManager().queryIntentContentProviders(intent, 0); - if (providers == null) { + if (providers == null || providers.isEmpty()) { throw new IllegalArgumentException("Unable to resolve intent " + intent); } String authority = providers.get(0).providerInfo.authority; Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) .authority(authority).build(); - IContentProvider provider = resolver.acquireProvider(uri); - if (provider == null) { - throw new IllegalArgumentException("Unknown URI " + uri); - } - try { + try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) { + if (provider == null) { + throw new IllegalArgumentException("Unknown URI " + uri); + } Bundle extras = new Bundle(); extras.putParcelable(SliceProvider.EXTRA_INTENT, intent); extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS, new ArrayList<>(supportedSpecs)); - final Bundle res = provider.call(mContext.getPackageName(), - SliceProvider.METHOD_MAP_INTENT, null, extras); + final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras); if (res == null) { return null; } @@ -365,8 +414,6 @@ public class SliceManager { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. return null; - } finally { - resolver.releaseProvider(provider); } } diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index 336bd4782156..af4303263da5 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -114,6 +114,10 @@ public abstract class SliceProvider extends ContentProvider { /** * @hide */ + public static final String METHOD_MAP_ONLY_INTENT = "map_only"; + /** + * @hide + */ public static final String METHOD_PIN = "pin"; /** * @hide @@ -341,6 +345,13 @@ public abstract class SliceProvider extends ContentProvider { b.putParcelable(EXTRA_SLICE, null); } return b; + } else if (method.equals(METHOD_MAP_ONLY_INTENT)) { + Intent intent = extras.getParcelable(EXTRA_INTENT); + if (intent == null) return null; + Uri uri = onMapIntentToUri(intent); + Bundle b = new Bundle(); + b.putParcelable(EXTRA_SLICE, uri); + return b; } else if (method.equals(METHOD_PIN)) { Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); if (Binder.getCallingUid() != Process.SYSTEM_UID) { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e02a29494296..9b62f192ae62 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -50,6 +50,7 @@ import android.provider.DocumentsContract; import android.provider.DocumentsProvider; import android.provider.MediaStore; import android.provider.OpenableColumns; +import android.text.TextUtils; import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; @@ -1553,16 +1554,6 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_INSTALL_FAILURE = "android.intent.action.INSTALL_FAILURE"; /** - * @hide - * @removed - * @deprecated Do not use. This will go away. - * Replace with {@link #ACTION_INSTALL_INSTANT_APP_PACKAGE}. - */ - @SystemApi - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_INSTALL_EPHEMERAL_PACKAGE - = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE"; - /** * Activity Action: Launch instant application installer. * <p class="note"> * This is a protected intent that can only be sent by the system. @@ -1576,16 +1567,6 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE"; /** - * @hide - * @removed - * @deprecated Do not use. This will go away. - * Replace with {@link #ACTION_RESOLVE_INSTANT_APP_PACKAGE}. - */ - @SystemApi - @SdkConstant(SdkConstantType.SERVICE_ACTION) - public static final String ACTION_RESOLVE_EPHEMERAL_PACKAGE - = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE"; - /** * Service Action: Resolve instant application. * <p> * The system will have a persistent connection to this service. @@ -1600,16 +1581,6 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE"; /** - * @hide - * @removed - * @deprecated Do not use. This will go away. - * Replace with {@link #ACTION_INSTANT_APP_RESOLVER_SETTINGS}. - */ - @SystemApi - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_EPHEMERAL_RESOLVER_SETTINGS - = "android.intent.action.EPHEMERAL_RESOLVER_SETTINGS"; - /** * Activity Action: Launch instant app settings. * * <p class="note"> @@ -4443,45 +4414,109 @@ public class Intent implements Parcelable, Cloneable { /** * A {@link IntentSender} to start after ephemeral installation success. + * @deprecated Use {@link #EXTRA_INSTANT_APP_SUCCESS). + * @removed * @hide */ + @Deprecated public static final String EXTRA_EPHEMERAL_SUCCESS = "android.intent.extra.EPHEMERAL_SUCCESS"; /** + * A {@link IntentSender} to start after instant app installation success. + * @hide + */ + @SystemApi + public static final String EXTRA_INSTANT_APP_SUCCESS = + "android.intent.extra.INSTANT_APP_SUCCESS"; + + /** * A {@link IntentSender} to start after ephemeral installation failure. + * @deprecated Use {@link #EXTRA_INSTANT_APP_FAILURE). + * @removed * @hide */ + @Deprecated public static final String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE"; /** + * A {@link IntentSender} to start after instant app installation failure. + * @hide + */ + @SystemApi + public static final String EXTRA_INSTANT_APP_FAILURE = + "android.intent.extra.INSTANT_APP_FAILURE"; + + /** * The host name that triggered an ephemeral resolution. + * @deprecated Use {@link #EXTRA_INSTANT_APP_HOSTNAME). + * @removed * @hide */ + @Deprecated public static final String EXTRA_EPHEMERAL_HOSTNAME = "android.intent.extra.EPHEMERAL_HOSTNAME"; /** + * The host name that triggered an instant app resolution. + * @hide + */ + @SystemApi + public static final String EXTRA_INSTANT_APP_HOSTNAME = + "android.intent.extra.INSTANT_APP_HOSTNAME"; + + /** * An opaque token to track ephemeral resolution. + * @deprecated Use {@link #EXTRA_INSTANT_APP_TOKEN). + * @removed * @hide */ + @Deprecated public static final String EXTRA_EPHEMERAL_TOKEN = "android.intent.extra.EPHEMERAL_TOKEN"; /** + * An opaque token to track instant app resolution. + * @hide + */ + @SystemApi + public static final String EXTRA_INSTANT_APP_TOKEN = + "android.intent.extra.INSTANT_APP_TOKEN"; + + /** * The action that triggered an instant application resolution. * @hide */ + @SystemApi public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION"; /** - * A {@link Bundle} of metadata that describes the instanta application that needs to be + * An array of {@link Bundle}s containing details about resolved instant apps.. + * @hide + */ + @SystemApi + public static final String EXTRA_INSTANT_APP_BUNDLES = + "android.intent.extra.INSTANT_APP_BUNDLES"; + + /** + * A {@link Bundle} of metadata that describes the instant application that needs to be * installed. This data is populated from the response to * {@link android.content.pm.InstantAppResolveInfo#getExtras()} as provided by the registered * instant application resolver. * @hide */ + @SystemApi public static final String EXTRA_INSTANT_APP_EXTRAS = "android.intent.extra.INSTANT_APP_EXTRAS"; /** + * A boolean value indicating that the instant app resolver was unable to state with certainty + * that it did or did not have an app for the sanitized {@link Intent} defined at + * {@link #EXTRA_INTENT}. + * @hide + */ + @SystemApi + public static final String EXTRA_UNKNOWN_INSTANT_APP = + "android.intent.extra.UNKNOWN_INSTANT_APP"; + + /** * The version code of the app to install components from. * @deprecated Use {@link #EXTRA_LONG_VERSION_CODE). * @hide @@ -4493,12 +4528,14 @@ public class Intent implements Parcelable, Cloneable { * The version code of the app to install components from. * @hide */ + @SystemApi public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE"; /** - * The app that triggered the ephemeral installation. + * The app that triggered the instant app installation. * @hide */ + @SystemApi public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE"; @@ -4507,6 +4544,7 @@ public class Intent implements Parcelable, Cloneable { * installer may use. * @hide */ + @SystemApi public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE"; @@ -5029,6 +5067,7 @@ public class Intent implements Parcelable, Cloneable { FLAG_GRANT_PREFIX_URI_PERMISSION, FLAG_DEBUG_TRIAGED_MISSING, FLAG_IGNORE_EPHEMERAL, + FLAG_ACTIVITY_MATCH_EXTERNAL, FLAG_ACTIVITY_NO_HISTORY, FLAG_ACTIVITY_SINGLE_TOP, FLAG_ACTIVITY_NEW_TASK, @@ -5072,6 +5111,7 @@ public class Intent implements Parcelable, Cloneable { FLAG_INCLUDE_STOPPED_PACKAGES, FLAG_DEBUG_TRIAGED_MISSING, FLAG_IGNORE_EPHEMERAL, + FLAG_ACTIVITY_MATCH_EXTERNAL, FLAG_ACTIVITY_NO_HISTORY, FLAG_ACTIVITY_SINGLE_TOP, FLAG_ACTIVITY_NEW_TASK, @@ -5475,6 +5515,14 @@ public class Intent implements Parcelable, Cloneable { */ public static final int FLAG_ACTIVITY_LAUNCH_ADJACENT = 0x00001000; + + /** + * If set, resolution of this intent may take place via an instant app not + * yet on the device if there does not yet exist an app on device to + * resolve it. + */ + public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 0x00000800; + /** * If set, when sending a broadcast only registered receivers will be * called -- no BroadcastReceiver components will be launched. @@ -10028,6 +10076,25 @@ public class Intent implements Parcelable, Cloneable { } } + /** @hide */ + public boolean hasWebURI() { + if (getData() == null) { + return false; + } + final String scheme = getScheme(); + if (TextUtils.isEmpty(scheme)) { + return false; + } + return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS); + } + + /** @hide */ + public boolean isBrowsableWebIntent() { + return ACTION_VIEW.equals(mAction) + && hasCategory(CATEGORY_BROWSABLE) + && hasWebURI(); + } + /** * @hide */ diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index f6697e8148a0..b61a6d997b14 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -958,6 +958,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * Version of the sandbox the application wants to run in. * @hide */ + @SystemApi public int targetSandboxVersion; /** @@ -1600,7 +1601,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * @hide */ public boolean isAllowedToUseHiddenApi() { - return isSystemApp(); + return false; } /** @@ -1655,7 +1656,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0; } - /** @hide */ + /** + * True if the application is installed as an instant app. + * @hide + */ + @SystemApi public boolean isInstantApp() { return (privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; } diff --git a/core/java/android/content/pm/AuxiliaryResolveInfo.java b/core/java/android/content/pm/AuxiliaryResolveInfo.java index 6bdcefbe974e..202df50dda6f 100644 --- a/core/java/android/content/pm/AuxiliaryResolveInfo.java +++ b/core/java/android/content/pm/AuxiliaryResolveInfo.java @@ -21,6 +21,10 @@ import android.annotation.Nullable; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; +import android.os.Bundle; + +import java.util.Collections; +import java.util.List; /** * Auxiliary application resolution response. @@ -31,56 +35,95 @@ import android.content.IntentFilter; * hasn't been installed. * @hide */ -public final class AuxiliaryResolveInfo extends IntentFilter { - /** Resolved information returned from the external instant resolver */ - public final InstantAppResolveInfo resolveInfo; - /** The resolved package. Copied from {@link #resolveInfo}. */ - public final String packageName; +public final class AuxiliaryResolveInfo { /** The activity to launch if there's an installation failure. */ public final ComponentName installFailureActivity; - /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */ - public final String splitName; /** Whether or not instant resolution needs the second phase */ public final boolean needsPhaseTwo; /** Opaque token to track the instant application resolution */ public final String token; - /** The version code of the package */ - public final long versionCode; /** An intent to start upon failure to install */ public final Intent failureIntent; + /** The matching filters for this resolve info. */ + public final List<AuxiliaryFilter> filters; /** Create a response for installing an instant application. */ - public AuxiliaryResolveInfo(@NonNull InstantAppResolveInfo resolveInfo, - @NonNull IntentFilter orig, - @Nullable String splitName, - @NonNull String token, + public AuxiliaryResolveInfo(@NonNull String token, boolean needsPhase2, - @Nullable Intent failureIntent) { - super(orig); - this.resolveInfo = resolveInfo; - this.packageName = resolveInfo.getPackageName(); - this.splitName = splitName; + @Nullable Intent failureIntent, + @Nullable List<AuxiliaryFilter> filters) { this.token = token; this.needsPhaseTwo = needsPhase2; - this.versionCode = resolveInfo.getVersionCode(); this.failureIntent = failureIntent; + this.filters = filters; this.installFailureActivity = null; } /** Create a response for installing a split on demand. */ - public AuxiliaryResolveInfo(@NonNull String packageName, - @Nullable String splitName, - @Nullable ComponentName failureActivity, - long versionCode, - @Nullable Intent failureIntent) { + public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity, + @Nullable Intent failureIntent, + @Nullable List<AuxiliaryFilter> filters) { super(); - this.packageName = packageName; this.installFailureActivity = failureActivity; - this.splitName = splitName; - this.versionCode = versionCode; - this.resolveInfo = null; + this.filters = filters; this.token = null; this.needsPhaseTwo = false; this.failureIntent = failureIntent; } + + /** Create a response for installing a split on demand. */ + public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity, + String packageName, long versionCode, String splitName) { + this(failureActivity, null, Collections.singletonList( + new AuxiliaryResolveInfo.AuxiliaryFilter(packageName, versionCode, splitName))); + } + + /** @hide */ + public static final class AuxiliaryFilter extends IntentFilter { + /** Resolved information returned from the external instant resolver */ + public final InstantAppResolveInfo resolveInfo; + /** The resolved package. Copied from {@link #resolveInfo}. */ + public final String packageName; + /** The version code of the package */ + public final long versionCode; + /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */ + public final String splitName; + /** The extras to pass on to the installer for this filter. */ + public final Bundle extras; + + public AuxiliaryFilter(IntentFilter orig, InstantAppResolveInfo resolveInfo, + String splitName, Bundle extras) { + super(orig); + this.resolveInfo = resolveInfo; + this.packageName = resolveInfo.getPackageName(); + this.versionCode = resolveInfo.getLongVersionCode(); + this.splitName = splitName; + this.extras = extras; + } + + public AuxiliaryFilter(InstantAppResolveInfo resolveInfo, + String splitName, Bundle extras) { + this.resolveInfo = resolveInfo; + this.packageName = resolveInfo.getPackageName(); + this.versionCode = resolveInfo.getLongVersionCode(); + this.splitName = splitName; + this.extras = extras; + } + + public AuxiliaryFilter(String packageName, long versionCode, String splitName) { + this.resolveInfo = null; + this.packageName = packageName; + this.versionCode = versionCode; + this.splitName = splitName; + this.extras = null; + } + + @Override + public String toString() { + return "AuxiliaryFilter{" + + "packageName='" + packageName + '\'' + + ", versionCode=" + versionCode + + ", splitName='" + splitName + '\'' + '}'; + } + } }
\ No newline at end of file diff --git a/core/java/android/content/pm/EphemeralIntentFilter.java b/core/java/android/content/pm/EphemeralIntentFilter.java deleted file mode 100644 index 1dbbf816ed93..000000000000 --- a/core/java/android/content/pm/EphemeralIntentFilter.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content.pm; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.content.IntentFilter; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.ArrayList; -import java.util.List; - -/** - * Information about an ephemeral application intent filter. - * @hide - * @removed - */ -@Deprecated -@SystemApi -public final class EphemeralIntentFilter implements Parcelable { - private final InstantAppIntentFilter mInstantAppIntentFilter; - - public EphemeralIntentFilter(@Nullable String splitName, @NonNull List<IntentFilter> filters) { - mInstantAppIntentFilter = new InstantAppIntentFilter(splitName, filters); - } - - EphemeralIntentFilter(@NonNull InstantAppIntentFilter intentFilter) { - mInstantAppIntentFilter = intentFilter; - } - - EphemeralIntentFilter(Parcel in) { - mInstantAppIntentFilter = in.readParcelable(null /*loader*/); - } - - public String getSplitName() { - return mInstantAppIntentFilter.getSplitName(); - } - - public List<IntentFilter> getFilters() { - return mInstantAppIntentFilter.getFilters(); - } - - /** @hide */ - InstantAppIntentFilter getInstantAppIntentFilter() { - return mInstantAppIntentFilter; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeParcelable(mInstantAppIntentFilter, flags); - } - - public static final Parcelable.Creator<EphemeralIntentFilter> CREATOR - = new Parcelable.Creator<EphemeralIntentFilter>() { - @Override - public EphemeralIntentFilter createFromParcel(Parcel in) { - return new EphemeralIntentFilter(in); - } - @Override - public EphemeralIntentFilter[] newArray(int size) { - return new EphemeralIntentFilter[size]; - } - }; -} diff --git a/core/java/android/content/pm/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java deleted file mode 100644 index 12131a3ebc98..000000000000 --- a/core/java/android/content/pm/EphemeralResolveInfo.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2015 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.content.pm; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.content.IntentFilter; -import android.content.pm.InstantAppResolveInfo.InstantAppDigest; -import android.net.Uri; -import android.os.Parcel; -import android.os.Parcelable; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -/** - * Information about an ephemeral application. - * @hide - * @removed - */ -@Deprecated -@SystemApi -public final class EphemeralResolveInfo implements Parcelable { - /** Algorithm that will be used to generate the domain digest */ - public static final String SHA_ALGORITHM = "SHA-256"; - - private final InstantAppResolveInfo mInstantAppResolveInfo; - @Deprecated - private final List<IntentFilter> mLegacyFilters; - - @Deprecated - public EphemeralResolveInfo(@NonNull Uri uri, @NonNull String packageName, - @NonNull List<IntentFilter> filters) { - if (uri == null || packageName == null || filters == null || filters.isEmpty()) { - throw new IllegalArgumentException(); - } - final List<EphemeralIntentFilter> ephemeralFilters = new ArrayList<>(1); - ephemeralFilters.add(new EphemeralIntentFilter(packageName, filters)); - mInstantAppResolveInfo = new InstantAppResolveInfo(uri.getHost(), packageName, - createInstantAppIntentFilterList(ephemeralFilters)); - mLegacyFilters = new ArrayList<IntentFilter>(filters.size()); - mLegacyFilters.addAll(filters); - } - - @Deprecated - public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName, - @Nullable List<EphemeralIntentFilter> filters) { - this(digest, packageName, filters, -1 /*versionCode*/); - } - - public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName, - @Nullable List<EphemeralIntentFilter> filters, int versionCode) { - mInstantAppResolveInfo = new InstantAppResolveInfo( - digest.getInstantAppDigest(), packageName, - createInstantAppIntentFilterList(filters), versionCode); - mLegacyFilters = null; - } - - public EphemeralResolveInfo(@NonNull String hostName, @Nullable String packageName, - @Nullable List<EphemeralIntentFilter> filters) { - this(new EphemeralDigest(hostName), packageName, filters); - } - - EphemeralResolveInfo(Parcel in) { - mInstantAppResolveInfo = in.readParcelable(null /*loader*/); - mLegacyFilters = new ArrayList<IntentFilter>(); - in.readList(mLegacyFilters, null /*loader*/); - } - - /** @hide */ - public InstantAppResolveInfo getInstantAppResolveInfo() { - return mInstantAppResolveInfo; - } - - private static List<InstantAppIntentFilter> createInstantAppIntentFilterList( - List<EphemeralIntentFilter> filters) { - if (filters == null) { - return null; - } - final int filterCount = filters.size(); - final List<InstantAppIntentFilter> returnList = new ArrayList<>(filterCount); - for (int i = 0; i < filterCount; i++) { - returnList.add(filters.get(i).getInstantAppIntentFilter()); - } - return returnList; - } - - public byte[] getDigestBytes() { - return mInstantAppResolveInfo.getDigestBytes(); - } - - public int getDigestPrefix() { - return mInstantAppResolveInfo.getDigestPrefix(); - } - - public String getPackageName() { - return mInstantAppResolveInfo.getPackageName(); - } - - public List<EphemeralIntentFilter> getIntentFilters() { - final List<InstantAppIntentFilter> filters = mInstantAppResolveInfo.getIntentFilters(); - final int filterCount = filters.size(); - final List<EphemeralIntentFilter> returnList = new ArrayList<>(filterCount); - for (int i = 0; i < filterCount; i++) { - returnList.add(new EphemeralIntentFilter(filters.get(i))); - } - return returnList; - } - - public int getVersionCode() { - return mInstantAppResolveInfo.getVersionCode(); - } - - @Deprecated - public List<IntentFilter> getFilters() { - return mLegacyFilters; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeParcelable(mInstantAppResolveInfo, flags); - out.writeList(mLegacyFilters); - } - - public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR - = new Parcelable.Creator<EphemeralResolveInfo>() { - @Override - public EphemeralResolveInfo createFromParcel(Parcel in) { - return new EphemeralResolveInfo(in); - } - @Override - public EphemeralResolveInfo[] newArray(int size) { - return new EphemeralResolveInfo[size]; - } - }; - - /** - * Helper class to generate and store each of the digests and prefixes - * sent to the Ephemeral Resolver. - * <p> - * Since intent filters may want to handle multiple hosts within a - * domain [eg “*.google.com”], the resolver is presented with multiple - * hash prefixes. For example, "a.b.c.d.e" generates digests for - * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e". - * - * @hide - */ - @SystemApi - public static final class EphemeralDigest implements Parcelable { - private final InstantAppDigest mInstantAppDigest; - - public EphemeralDigest(@NonNull String hostName) { - this(hostName, -1 /*maxDigests*/); - } - - /** @hide */ - public EphemeralDigest(@NonNull String hostName, int maxDigests) { - mInstantAppDigest = new InstantAppDigest(hostName, maxDigests); - } - - EphemeralDigest(Parcel in) { - mInstantAppDigest = in.readParcelable(null /*loader*/); - } - - /** @hide */ - InstantAppDigest getInstantAppDigest() { - return mInstantAppDigest; - } - - public byte[][] getDigestBytes() { - return mInstantAppDigest.getDigestBytes(); - } - - public int[] getDigestPrefix() { - return mInstantAppDigest.getDigestPrefix(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeParcelable(mInstantAppDigest, flags); - } - - @SuppressWarnings("hiding") - public static final Parcelable.Creator<EphemeralDigest> CREATOR = - new Parcelable.Creator<EphemeralDigest>() { - @Override - public EphemeralDigest createFromParcel(Parcel in) { - return new EphemeralDigest(in); - } - @Override - public EphemeralDigest[] newArray(int size) { - return new EphemeralDigest[size]; - } - }; - } -} diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl index 0b16852246f8..8fddb99b35a8 100644 --- a/core/java/android/content/pm/IPackageInstallerSession.aidl +++ b/core/java/android/content/pm/IPackageInstallerSession.aidl @@ -26,9 +26,12 @@ interface IPackageInstallerSession { void addClientProgress(float progress); String[] getNames(); + ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes); ParcelFileDescriptor openRead(String name); + void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd); + void removeSplit(String splitName); void close(); diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java index 19cb9323ba93..112c5dae6731 100644 --- a/core/java/android/content/pm/InstantAppResolveInfo.java +++ b/core/java/android/content/pm/InstantAppResolveInfo.java @@ -19,6 +19,7 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.content.Intent; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -26,11 +27,35 @@ import android.os.Parcelable; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; /** - * Information about an instant application. + * Describes an externally resolvable instant application. There are three states that this class + * can represent: <p/> + * <ul> + * <li> + * The first, usable only for non http/s intents, implies that the resolver cannot + * immediately resolve this intent and would prefer that resolution be deferred to the + * instant app installer. Represent this state with {@link #InstantAppResolveInfo(Bundle)}. + * If the {@link android.content.Intent} has the scheme set to http/s and a set of digest + * prefixes were passed into one of the resolve methods in + * {@link android.app.InstantAppResolverService}, this state cannot be used. + * </li> + * <li> + * The second represents a partial match and is constructed with any of the other + * constructors. By setting one or more of the {@link Nullable}arguments to null, you + * communicate to the resolver in response to + * {@link android.app.InstantAppResolverService#onGetInstantAppResolveInfo(Intent, int[], + * String, InstantAppResolverService.InstantAppResolutionCallback)} + * that you need a 2nd round of resolution to complete the request. + * </li> + * <li> + * The third represents a complete match and is constructed with all @Nullable parameters + * populated. + * </li> + * </ul> * @hide */ @SystemApi @@ -38,6 +63,8 @@ public final class InstantAppResolveInfo implements Parcelable { /** Algorithm that will be used to generate the domain digest */ private static final String SHA_ALGORITHM = "SHA-256"; + private static final byte[] EMPTY_DIGEST = new byte[0]; + private final InstantAppDigest mDigest; private final String mPackageName; /** The filters used to match domain */ @@ -46,15 +73,30 @@ public final class InstantAppResolveInfo implements Parcelable { private final long mVersionCode; /** Data about the app that should be passed along to the Instant App installer on resolve */ private final Bundle mExtras; + /** + * A flag that indicates that the resolver is aware that an app may match, but would prefer + * that the installer get the sanitized intent to decide. This should not be used for + * resolutions that include a host and will be ignored in such cases. + */ + private final boolean mShouldLetInstallerDecide; + /** Constructor for intent-based InstantApp resolution results. */ public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, @Nullable List<InstantAppIntentFilter> filters, int versionCode) { this(digest, packageName, filters, (long) versionCode, null /* extras */); } + /** Constructor for intent-based InstantApp resolution results with extras. */ public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, @Nullable List<InstantAppIntentFilter> filters, long versionCode, @Nullable Bundle extras) { + this(digest, packageName, filters, versionCode, extras, false); + } + + /** Constructor for intent-based InstantApp resolution results with extras. */ + private InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, + @Nullable List<InstantAppIntentFilter> filters, long versionCode, + @Nullable Bundle extras, boolean shouldLetInstallerDecide) { // validate arguments if ((packageName == null && (filters != null && filters.size() != 0)) || (packageName != null && (filters == null || filters.size() == 0))) { @@ -62,7 +104,7 @@ public final class InstantAppResolveInfo implements Parcelable { } mDigest = digest; if (filters != null) { - mFilters = new ArrayList<InstantAppIntentFilter>(filters.size()); + mFilters = new ArrayList<>(filters.size()); mFilters.addAll(filters); } else { mFilters = null; @@ -70,25 +112,48 @@ public final class InstantAppResolveInfo implements Parcelable { mPackageName = packageName; mVersionCode = versionCode; mExtras = extras; + mShouldLetInstallerDecide = shouldLetInstallerDecide; } + /** Constructor for intent-based InstantApp resolution results by hostname. */ public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName, @Nullable List<InstantAppIntentFilter> filters) { this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/, null /* extras */); } + /** + * Constructor that creates a "let the installer decide" response with optional included + * extras. + */ + public InstantAppResolveInfo(@Nullable Bundle extras) { + this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true); + } + InstantAppResolveInfo(Parcel in) { - mDigest = in.readParcelable(null /*loader*/); - mPackageName = in.readString(); - mFilters = new ArrayList<InstantAppIntentFilter>(); - in.readList(mFilters, null /*loader*/); - mVersionCode = in.readLong(); + mShouldLetInstallerDecide = in.readBoolean(); mExtras = in.readBundle(); + if (mShouldLetInstallerDecide) { + mDigest = InstantAppDigest.UNDEFINED; + mPackageName = null; + mFilters = Collections.emptyList(); + mVersionCode = -1; + } else { + mDigest = in.readParcelable(null /*loader*/); + mPackageName = in.readString(); + mFilters = new ArrayList<>(); + in.readList(mFilters, null /*loader*/); + mVersionCode = in.readLong(); + } + } + + /** Returns true if the installer should be notified that it should query for packages. */ + public boolean shouldLetInstallerDecide() { + return mShouldLetInstallerDecide; } public byte[] getDigestBytes() { - return mDigest.getDigestBytes()[0]; + return mDigest.mDigestBytes.length > 0 ? mDigest.getDigestBytes()[0] : EMPTY_DIGEST; } public int getDigestPrefix() { @@ -127,11 +192,15 @@ public final class InstantAppResolveInfo implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { + out.writeBoolean(mShouldLetInstallerDecide); + out.writeBundle(mExtras); + if (mShouldLetInstallerDecide) { + return; + } out.writeParcelable(mDigest, flags); out.writeString(mPackageName); out.writeList(mFilters); out.writeLong(mVersionCode); - out.writeBundle(mExtras); } public static final Parcelable.Creator<InstantAppResolveInfo> CREATOR @@ -159,7 +228,9 @@ public final class InstantAppResolveInfo implements Parcelable { @SystemApi public static final class InstantAppDigest implements Parcelable { private static final int DIGEST_MASK = 0xfffff000; - private static final int DIGEST_PREFIX_COUNT = 5; + + public static final InstantAppDigest UNDEFINED = + new InstantAppDigest(new byte[][]{}, new int[]{}); /** Full digest of the domain hashes */ private final byte[][] mDigestBytes; /** The first 4 bytes of the domain hashes */ @@ -186,6 +257,11 @@ public final class InstantAppResolveInfo implements Parcelable { } } + private InstantAppDigest(byte[][] digestBytes, int[] prefix) { + this.mDigestPrefix = prefix; + this.mDigestBytes = digestBytes; + } + private static byte[][] generateDigest(String hostName, int maxDigests) { ArrayList<byte[]> digests = new ArrayList<>(); try { diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index df677d208d36..d0be6c854020 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -829,7 +829,19 @@ public class PackageInstaller { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } + } + /** {@hide} */ + public void write(@NonNull String name, long offsetBytes, long lengthBytes, + @NonNull ParcelFileDescriptor fd) throws IOException { + try { + mSession.write(name, offsetBytes, lengthBytes, fd); + } catch (RuntimeException e) { + ExceptionUtils.maybeUnwrapIOException(e); + throw e; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java new file mode 100644 index 000000000000..c811999ce304 --- /dev/null +++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java @@ -0,0 +1,184 @@ +/* + * 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.hardware.biometrics; + +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.os.CancellationSignal; +import android.os.Parcelable; + +import java.util.concurrent.Executor; + +/** + * This is the common interface that all biometric authentication classes should implement. + * @hide + */ +public interface BiometricAuthenticator { + + /** + * Container for biometric data + * @hide + */ + abstract class BiometricIdentifier implements Parcelable {} + + /** + * Container for callback data from {@link BiometricAuthenticator#authenticate( + * CancellationSignal, Executor, AuthenticationCallback)} and + * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor, + * AuthenticationCallback)} + */ + class AuthenticationResult { + private BiometricIdentifier mIdentifier; + private CryptoObject mCryptoObject; + private int mUserId; + + /** + * @hide + */ + public AuthenticationResult() { } + + /** + * Authentication result + * @param crypto + * @param identifier + * @param userId + * @hide + */ + public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier, + int userId) { + mCryptoObject = crypto; + mIdentifier = identifier; + mUserId = userId; + } + + /** + * Obtain the crypto object associated with this transaction + * @return crypto object provided to {@link BiometricAuthenticator#authenticate( + * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)} + */ + public CryptoObject getCryptoObject() { + return mCryptoObject; + } + + /** + * Obtain the biometric identifier associated with this operation. Applications are strongly + * discouraged from associating specific identifiers with specific applications or + * operations. + * @hide + */ + public BiometricIdentifier getId() { + return mIdentifier; + } + + /** + * Obtain the userId for which this biometric was authenticated. + * @hide + */ + public int getUserId() { + return mUserId; + } + }; + + /** + * Callback structure provided to {@link BiometricAuthenticator#authenticate(CancellationSignal, + * Executor, AuthenticationCallback)} or {@link BiometricAuthenticator#authenticate( + * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}. Users must provide + * an implementation of this for listening to biometric events. + */ + abstract class AuthenticationCallback { + /** + * Called when an unrecoverable error has been encountered and the operation is complete. + * No further actions will be made on this object. + * @param errorCode An integer identifying the error message + * @param errString A human-readable error string that can be shown on an UI + */ + public void onAuthenticationError(int errorCode, CharSequence errString) {} + + /** + * Called when a recoverable error has been encountered during authentication. The help + * string is provided to give the user guidance for what went wrong, such as "Sensor dirty, + * please clean it." + * @param helpCode An integer identifying the error message + * @param helpString A human-readable string that can be shown on an UI + */ + public void onAuthenticationHelp(int helpCode, CharSequence helpString) {} + + /** + * Called when a biometric is recognized. + * @param result An object containing authentication-related data + */ + public void onAuthenticationSucceeded(AuthenticationResult result) {} + + /** + * Called when a biometric is valid but not recognized. + */ + public void onAuthenticationFailed() {} + + /** + * Called when a biometric has been acquired, but hasn't been processed yet. + * @hide + */ + public void onAuthenticationAcquired(int acquireInfo) {} + }; + + /** + * This call warms up the hardware and starts scanning for valid biometrics. It terminates + * when {@link AuthenticationCallback#onAuthenticationError(int, + * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded( + * AuthenticationResult)} is called, at which point the crypto object becomes invalid. This + * operation can be canceled by using the provided cancel object. The application wil receive + * authentication errors through {@link AuthenticationCallback}. Calling + * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor, + * AuthenticationCallback)} while an existing authentication attempt is occurring will stop + * the previous client and start a new authentication. The interrupted client will receive a + * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int, + * CharSequence)}. + * + * @throws IllegalArgumentException If any of the arguments are null + * + * @param crypto Object associated with the call + * @param cancel An object that can be used to cancel authentication + * @param executor An executor to handle callback events + * @param callback An object to receive authentication events + */ + void authenticate(@NonNull CryptoObject crypto, + @NonNull CancellationSignal cancel, + @NonNull @CallbackExecutor Executor executor, + @NonNull AuthenticationCallback callback); + + /** + * This call warms up the hardware and starts scanning for valid biometrics. It terminates + * when {@link AuthenticationCallback#onAuthenticationError(int, + * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded( + * AuthenticationResult)} is called. This operation can be canceled by using the provided cancel + * object. The application wil receive authentication errors through + * {@link AuthenticationCallback}. Calling {@link BiometricAuthenticator#authenticate( + * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)} while an existing + * authentication attempt is occurring will stop the previous client and start a new + * authentication. The interrupted client will receive a cancelled notification through + * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}. + * + * @throws IllegalArgumentException If any of the arguments are null + * + * @param cancel An object that can be used to cancel authentication + * @param executor An executor to handle callback events + * @param callback An object to receive authentication events + */ + void authenticate(@NonNull CancellationSignal cancel, + @NonNull @CallbackExecutor Executor executor, + @NonNull AuthenticationCallback callback); +} diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java new file mode 100644 index 000000000000..638f525bfb10 --- /dev/null +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -0,0 +1,168 @@ +/* + * 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.hardware.biometrics; + +import android.hardware.fingerprint.FingerprintManager; + +/** + * Interface containing all of the fingerprint-specific constants. + * @hide + */ +public interface BiometricFingerprintConstants { + // + // Error messages from fingerprint hardware during initilization, enrollment, authentication or + // removal. Must agree with the list in fingerprint.h + // + + /** + * The hardware is unavailable. Try again later. + */ + public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; + + /** + * Error state returned when the sensor was unable to process the current image. + */ + public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; + + /** + * Error state returned when the current request has been running too long. This is intended to + * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is + * platform and sensor-specific, but is generally on the order of 30 seconds. + */ + public static final int FINGERPRINT_ERROR_TIMEOUT = 3; + + /** + * Error state returned for operations like enrollment; the operation cannot be completed + * because there's not enough storage remaining to complete the operation. + */ + public static final int FINGERPRINT_ERROR_NO_SPACE = 4; + + /** + * The operation was canceled because the fingerprint sensor is unavailable. For example, + * this may happen when the user is switched, the device is locked or another pending operation + * prevents or disables it. + */ + public static final int FINGERPRINT_ERROR_CANCELED = 5; + + /** + * The {@link FingerprintManager#remove} call failed. Typically this will happen when the + * provided fingerprint id was incorrect. + * + * @hide + */ + public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6; + + /** + * The operation was canceled because the API is locked out due to too many attempts. + * This occurs after 5 failed attempts, and lasts for 30 seconds. + */ + public static final int FINGERPRINT_ERROR_LOCKOUT = 7; + + /** + * Hardware vendors may extend this list if there are conditions that do not fall under one of + * the above categories. Vendors are responsible for providing error strings for these errors. + * These messages are typically reserved for internal operations such as enrollment, but may be + * used to express vendor errors not covered by the ones in fingerprint.h. Applications are + * expected to show the error message string if they happen, but are advised not to rely on the + * message id since they will be device and vendor-specific + */ + public static final int FINGERPRINT_ERROR_VENDOR = 8; + + /** + * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times. + * Fingerprint authentication is disabled until the user unlocks with strong authentication + * (PIN/Pattern/Password) + */ + public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; + + /** + * The user canceled the operation. Upon receiving this, applications should use alternate + * authentication (e.g. a password). The application should also provide the means to return + * to fingerprint authentication, such as a "use fingerprint" button. + */ + public static final int FINGERPRINT_ERROR_USER_CANCELED = 10; + + /** + * The user does not have any fingerprints enrolled. + */ + public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; + + /** + * The device does not have a fingerprint sensor. + */ + public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; + + /** + * @hide + */ + public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000; + + // + // Image acquisition messages. Must agree with those in fingerprint.h + // + + /** + * The image acquired was good. + */ + public static final int FINGERPRINT_ACQUIRED_GOOD = 0; + + /** + * Only a partial fingerprint image was detected. During enrollment, the user should be + * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor." + */ + public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; + + /** + * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or + * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}). + */ + public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; + + /** + * The fingerprint image was too noisy due to suspected or detected dirt on the sensor. + * For example, it's reasonable return this after multiple + * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor + * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor + * when this is returned. + */ + public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; + + /** + * The fingerprint image was unreadable due to lack of motion. This is most appropriate for + * linear array sensors that require a swipe motion. + */ + public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; + + /** + * The fingerprint image was incomplete due to quick motion. While mostly appropriate for + * linear array sensors, this could also happen if the finger was moved during acquisition. + * The user should be asked to move the finger slower (linear) or leave the finger on the sensor + * longer. + */ + public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; + + /** + * Hardware vendors may extend this list if there are conditions that do not fall under one of + * the above categories. Vendors are responsible for providing error strings for these errors. + * @hide + */ + public static final int FINGERPRINT_ACQUIRED_VENDOR = 6; + /** + * @hide + */ + public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; +} diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java new file mode 100644 index 000000000000..496d9c57f252 --- /dev/null +++ b/core/java/android/hardware/biometrics/CryptoObject.java @@ -0,0 +1,79 @@ +/* + * 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.hardware.biometrics; + +import android.annotation.NonNull; +import android.security.keystore.AndroidKeyStoreProvider; + +import java.security.Signature; + +import javax.crypto.Cipher; +import javax.crypto.Mac; + +/** + * A wrapper class for the crypto objects supported by FingerprintManager. Currently the + * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects. + * @hide + */ +public class CryptoObject { + private final Object mCrypto; + + public CryptoObject(@NonNull Signature signature) { + mCrypto = signature; + } + + public CryptoObject(@NonNull Cipher cipher) { + mCrypto = cipher; + } + + public CryptoObject(@NonNull Mac mac) { + mCrypto = mac; + } + + /** + * Get {@link Signature} object. + * @return {@link Signature} object or null if this doesn't contain one. + */ + public Signature getSignature() { + return mCrypto instanceof Signature ? (Signature) mCrypto : null; + } + + /** + * Get {@link Cipher} object. + * @return {@link Cipher} object or null if this doesn't contain one. + */ + public Cipher getCipher() { + return mCrypto instanceof Cipher ? (Cipher) mCrypto : null; + } + + /** + * Get {@link Mac} object. + * @return {@link Mac} object or null if this doesn't contain one. + */ + public Mac getMac() { + return mCrypto instanceof Mac ? (Mac) mCrypto : null; + } + + /** + * @hide + * @return the opId associated with this object or 0 if none + */ + public final long getOpId() { + return mCrypto != null + ? AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0; + } +}; diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.aidl b/core/java/android/hardware/display/AmbientBrightnessDayStats.aidl new file mode 100644 index 000000000000..9070777bab63 --- /dev/null +++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.aidl @@ -0,0 +1,19 @@ +/* + * 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.hardware.display; + +parcelable AmbientBrightnessDayStats; diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java new file mode 100644 index 000000000000..00f3c36d0361 --- /dev/null +++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java @@ -0,0 +1,211 @@ +/* + * Copyright 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.hardware.display; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.time.LocalDate; +import java.util.Arrays; + +/** + * AmbientBrightnessDayStats stores and manipulates brightness stats over a single day. + * {@see DisplayManager.getAmbientBrightnessStats()} + * + * @hide + */ +@SystemApi +@TestApi +public final class AmbientBrightnessDayStats implements Parcelable { + + /** The localdate for which brightness stats are being tracked */ + private final LocalDate mLocalDate; + + /** Ambient brightness values for creating bucket boundaries from */ + private final float[] mBucketBoundaries; + + /** Stats of how much time (in seconds) was spent in each of the buckets */ + private final float[] mStats; + + /** + * @hide + */ + public AmbientBrightnessDayStats(@NonNull LocalDate localDate, + @NonNull float[] bucketBoundaries) { + this(localDate, bucketBoundaries, null); + } + + /** + * @hide + */ + public AmbientBrightnessDayStats(@NonNull LocalDate localDate, + @NonNull float[] bucketBoundaries, float[] stats) { + Preconditions.checkNotNull(localDate); + Preconditions.checkNotNull(bucketBoundaries); + Preconditions.checkArrayElementsInRange(bucketBoundaries, 0, Float.MAX_VALUE, + "bucketBoundaries"); + if (bucketBoundaries.length < 1) { + throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value"); + } + checkSorted(bucketBoundaries); + if (stats == null) { + stats = new float[bucketBoundaries.length]; + } else { + Preconditions.checkArrayElementsInRange(stats, 0, Float.MAX_VALUE, "stats"); + if (bucketBoundaries.length != stats.length) { + throw new IllegalArgumentException( + "Bucket boundaries and stats must be of same size."); + } + } + mLocalDate = localDate; + mBucketBoundaries = bucketBoundaries; + mStats = stats; + } + + public LocalDate getLocalDate() { + return mLocalDate; + } + + public float[] getStats() { + return mStats; + } + + public float[] getBucketBoundaries() { + return mBucketBoundaries; + } + + private AmbientBrightnessDayStats(Parcel source) { + mLocalDate = LocalDate.parse(source.readString()); + mBucketBoundaries = source.createFloatArray(); + mStats = source.createFloatArray(); + } + + public static final Creator<AmbientBrightnessDayStats> CREATOR = + new Creator<AmbientBrightnessDayStats>() { + + @Override + public AmbientBrightnessDayStats createFromParcel(Parcel source) { + return new AmbientBrightnessDayStats(source); + } + + @Override + public AmbientBrightnessDayStats[] newArray(int size) { + return new AmbientBrightnessDayStats[size]; + } + }; + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + AmbientBrightnessDayStats other = (AmbientBrightnessDayStats) obj; + return mLocalDate.equals(other.mLocalDate) && Arrays.equals(mBucketBoundaries, + other.mBucketBoundaries) && Arrays.equals(mStats, other.mStats); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = result * prime + mLocalDate.hashCode(); + result = result * prime + Arrays.hashCode(mBucketBoundaries); + result = result * prime + Arrays.hashCode(mStats); + return result; + } + + @Override + public String toString() { + StringBuilder bucketBoundariesString = new StringBuilder(); + StringBuilder statsString = new StringBuilder(); + for (int i = 0; i < mBucketBoundaries.length; i++) { + if (i != 0) { + bucketBoundariesString.append(", "); + statsString.append(", "); + } + bucketBoundariesString.append(mBucketBoundaries[i]); + statsString.append(mStats[i]); + } + return new StringBuilder() + .append(mLocalDate).append(" ") + .append("{").append(bucketBoundariesString).append("} ") + .append("{").append(statsString).append("}").toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mLocalDate.toString()); + dest.writeFloatArray(mBucketBoundaries); + dest.writeFloatArray(mStats); + } + + /** @hide */ + public void log(float ambientBrightness, float durationSec) { + int bucketIndex = getBucketIndex(ambientBrightness); + if (bucketIndex >= 0) { + mStats[bucketIndex] += durationSec; + } + } + + private int getBucketIndex(float ambientBrightness) { + if (ambientBrightness < mBucketBoundaries[0]) { + return -1; + } + int low = 0; + int high = mBucketBoundaries.length - 1; + while (low < high) { + int mid = (low + high) / 2; + if (mBucketBoundaries[mid] <= ambientBrightness + && ambientBrightness < mBucketBoundaries[mid + 1]) { + return mid; + } else if (mBucketBoundaries[mid] < ambientBrightness) { + low = mid + 1; + } else if (mBucketBoundaries[mid] > ambientBrightness) { + high = mid - 1; + } + } + return low; + } + + private static void checkSorted(float[] values) { + if (values.length <= 1) { + return; + } + float prevValue = values[0]; + for (int i = 1; i < values.length; i++) { + Preconditions.checkState(prevValue < values[i]); + prevValue = values[i]; + } + return; + } +} diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java index 2301824cb9dc..02eb28ceb4b9 100644 --- a/core/java/android/hardware/display/BrightnessChangeEvent.java +++ b/core/java/android/hardware/display/BrightnessChangeEvent.java @@ -54,19 +54,30 @@ public final class BrightnessChangeEvent implements Parcelable { /** Most recent battery level when brightness was changed or Float.NaN */ public final float batteryLevel; + /** Factor applied to brightness due to battery level, 0.0-1.0 */ + public final float powerBrightnessFactor; + /** Color filter active to provide night mode */ public final boolean nightMode; /** If night mode color filter is active this will be the temperature in kelvin */ public final int colorTemperature; - /** Brightness le vel before slider adjustment */ + /** Brightness level before slider adjustment */ public final float lastBrightness; + /** Whether brightness configuration is default version */ + public final boolean isDefaultBrightnessConfig; + + /** Whether brightness curve includes a user brightness point */ + public final boolean isUserSetBrightness; + + /** @hide */ private BrightnessChangeEvent(float brightness, long timeStamp, String packageName, int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel, - boolean nightMode, int colorTemperature, float lastBrightness) { + float powerBrightnessFactor, boolean nightMode, int colorTemperature, + float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness) { this.brightness = brightness; this.timeStamp = timeStamp; this.packageName = packageName; @@ -74,9 +85,12 @@ public final class BrightnessChangeEvent implements Parcelable { this.luxValues = luxValues; this.luxTimestamps = luxTimestamps; this.batteryLevel = batteryLevel; + this.powerBrightnessFactor = powerBrightnessFactor; this.nightMode = nightMode; this.colorTemperature = colorTemperature; this.lastBrightness = lastBrightness; + this.isDefaultBrightnessConfig = isDefaultBrightnessConfig; + this.isUserSetBrightness = isUserSetBrightness; } /** @hide */ @@ -88,9 +102,12 @@ public final class BrightnessChangeEvent implements Parcelable { this.luxValues = other.luxValues; this.luxTimestamps = other.luxTimestamps; this.batteryLevel = other.batteryLevel; + this.powerBrightnessFactor = other.powerBrightnessFactor; this.nightMode = other.nightMode; this.colorTemperature = other.colorTemperature; this.lastBrightness = other.lastBrightness; + this.isDefaultBrightnessConfig = other.isDefaultBrightnessConfig; + this.isUserSetBrightness = other.isUserSetBrightness; } private BrightnessChangeEvent(Parcel source) { @@ -101,9 +118,12 @@ public final class BrightnessChangeEvent implements Parcelable { luxValues = source.createFloatArray(); luxTimestamps = source.createLongArray(); batteryLevel = source.readFloat(); + powerBrightnessFactor = source.readFloat(); nightMode = source.readBoolean(); colorTemperature = source.readInt(); lastBrightness = source.readFloat(); + isDefaultBrightnessConfig = source.readBoolean(); + isUserSetBrightness = source.readBoolean(); } public static final Creator<BrightnessChangeEvent> CREATOR = @@ -130,9 +150,12 @@ public final class BrightnessChangeEvent implements Parcelable { dest.writeFloatArray(luxValues); dest.writeLongArray(luxTimestamps); dest.writeFloat(batteryLevel); + dest.writeFloat(powerBrightnessFactor); dest.writeBoolean(nightMode); dest.writeInt(colorTemperature); dest.writeFloat(lastBrightness); + dest.writeBoolean(isDefaultBrightnessConfig); + dest.writeBoolean(isUserSetBrightness); } /** @hide */ @@ -144,9 +167,12 @@ public final class BrightnessChangeEvent implements Parcelable { private float[] mLuxValues; private long[] mLuxTimestamps; private float mBatteryLevel; + private float mPowerBrightnessFactor; private boolean mNightMode; private int mColorTemperature; private float mLastBrightness; + private boolean mIsDefaultBrightnessConfig; + private boolean mIsUserSetBrightness; /** {@see BrightnessChangeEvent#brightness} */ public Builder setBrightness(float brightness) { @@ -190,6 +216,12 @@ public final class BrightnessChangeEvent implements Parcelable { return this; } + /** {@see BrightnessChangeEvent#powerSaveBrightness} */ + public Builder setPowerBrightnessFactor(float powerBrightnessFactor) { + mPowerBrightnessFactor = powerBrightnessFactor; + return this; + } + /** {@see BrightnessChangeEvent#nightMode} */ public Builder setNightMode(boolean nightMode) { mNightMode = nightMode; @@ -208,11 +240,24 @@ public final class BrightnessChangeEvent implements Parcelable { return this; } + /** {@see BrightnessChangeEvent#isDefaultBrightnessConfig} */ + public Builder setIsDefaultBrightnessConfig(boolean isDefaultBrightnessConfig) { + mIsDefaultBrightnessConfig = isDefaultBrightnessConfig; + return this; + } + + /** {@see BrightnessChangeEvent#userBrightnessPoint} */ + public Builder setUserBrightnessPoint(boolean isUserSetBrightness) { + mIsUserSetBrightness = isUserSetBrightness; + return this; + } + /** Builds a BrightnessChangeEvent */ public BrightnessChangeEvent build() { return new BrightnessChangeEvent(mBrightness, mTimeStamp, mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel, - mNightMode, mColorTemperature, mLastBrightness); + mPowerBrightnessFactor, mNightMode, mColorTemperature, mLastBrightness, + mIsDefaultBrightnessConfig, mIsUserSetBrightness); } } } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 4de4880b7c17..22fb8e75289a 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -631,6 +631,16 @@ public final class DisplayManager { } /** + * Fetch {@link AmbientBrightnessDayStats}s. + * + * @hide until we make it a system api + */ + @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS) + public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() { + return mGlobal.getAmbientBrightnessStats(); + } + + /** * Sets the global display brightness configuration. * * @hide diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 2d5f5e041486..d7f7c865b8fb 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -525,6 +525,21 @@ public final class DisplayManagerGlobal { } } + /** + * Retrieves ambient brightness stats. + */ + public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() { + try { + ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats(); + if (stats == null) { + return Collections.emptyList(); + } + return stats.getList(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { @Override public void onDisplayEvent(int displayId, int event) { diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 1cfad4f0168f..f468942cc951 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -174,9 +174,9 @@ public abstract class DisplayManagerInternal { public abstract boolean isUidPresentOnDisplay(int uid, int displayId); /** - * Persist brightness slider events. + * Persist brightness slider events and ambient brightness stats. */ - public abstract void persistBrightnessSliderEvents(); + public abstract void persistBrightnessTrackerState(); /** * Notifies the display manager that resource overlays have changed. diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 13599cfa0b7d..0571ae1fe825 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -87,6 +87,9 @@ interface IDisplayManager { // Requires BRIGHTNESS_SLIDER_USAGE permission. ParceledListSlice getBrightnessEvents(String callingPackage); + // Requires ACCESS_AMBIENT_LIGHT_STATS permission. + ParceledListSlice getAmbientBrightnessStats(); + // Sets the global brightness configuration for a given user. Requires // CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user being configured is not // the same as the calling user. diff --git a/core/java/android/hardware/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java index c30763475fa8..c7ce8fad2543 100644 --- a/core/java/android/hardware/fingerprint/Fingerprint.java +++ b/core/java/android/hardware/fingerprint/Fingerprint.java @@ -15,6 +15,7 @@ */ package android.hardware.fingerprint; +import android.hardware.biometrics.BiometricAuthenticator; import android.os.Parcel; import android.os.Parcelable; @@ -22,7 +23,7 @@ import android.os.Parcelable; * Container for fingerprint metadata. * @hide */ -public final class Fingerprint implements Parcelable { +public final class Fingerprint extends BiometricAuthenticator.BiometricIdentifier { private CharSequence mName; private int mGroupId; private int mFingerId; diff --git a/core/java/android/hardware/fingerprint/FingerprintDialog.java b/core/java/android/hardware/fingerprint/FingerprintDialog.java index 6b7fab773b43..49835963a3b1 100644 --- a/core/java/android/hardware/fingerprint/FingerprintDialog.java +++ b/core/java/android/hardware/fingerprint/FingerprintDialog.java @@ -23,19 +23,25 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.content.Context; import android.content.DialogInterface; -import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; -import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; +import android.content.pm.PackageManager; +import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.BiometricFingerprintConstants; +import android.hardware.biometrics.CryptoObject; import android.hardware.fingerprint.IFingerprintDialogReceiver; import android.os.Bundle; import android.os.CancellationSignal; import android.text.TextUtils; +import java.security.Signature; import java.util.concurrent.Executor; +import javax.crypto.Cipher; +import javax.crypto.Mac; + /** * A class that manages a system-provided fingerprint dialog. */ -public class FingerprintDialog { +public class FingerprintDialog implements BiometricAuthenticator, BiometricFingerprintConstants { /** * @hide @@ -200,6 +206,7 @@ public class FingerprintDialog { } } + private PackageManager mPackageManager; private FingerprintManager mFingerprintManager; private Bundle mBundle; private ButtonInfo mPositiveButtonInfo; @@ -227,37 +234,209 @@ public class FingerprintDialog { mPositiveButtonInfo = positiveButtonInfo; mNegativeButtonInfo = negativeButtonInfo; mFingerprintManager = context.getSystemService(FingerprintManager.class); + mPackageManager = context.getPackageManager(); + } + + /** + * A wrapper class for the crypto objects supported by FingerprintManager. Currently the + * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects. + */ + public static final class CryptoObject extends android.hardware.biometrics.CryptoObject { + public CryptoObject(@NonNull Signature signature) { + super(signature); + } + + public CryptoObject(@NonNull Cipher cipher) { + super(cipher); + } + + public CryptoObject(@NonNull Mac mac) { + super(mac); + } + + /** + * Get {@link Signature} object. + * @return {@link Signature} object or null if this doesn't contain one. + */ + public Signature getSignature() { + return super.getSignature(); + } + + /** + * Get {@link Cipher} object. + * @return {@link Cipher} object or null if this doesn't contain one. + */ + public Cipher getCipher() { + return super.getCipher(); + } + + /** + * Get {@link Mac} object. + * @return {@link Mac} object or null if this doesn't contain one. + */ + public Mac getMac() { + return super.getMac(); + } + } + + /** + * Container for callback data from {@link #authenticate( + * CancellationSignal, Executor, AuthenticationCallback)} and + * {@link #authenticate(CryptoObject, CancellationSignal, Executor, + * AuthenticationCallback)} + */ + public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult { + /** + * Authentication result + * @param crypto + * @param identifier + * @param userId + * @hide + */ + public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier, + int userId) { + super(crypto, identifier, userId); + } + /** + * Obtain the crypto object associated with this transaction + * @return crypto object provided to {@link #authenticate( + * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)} + */ + public CryptoObject getCryptoObject() { + return (CryptoObject) super.getCryptoObject(); + } + } + + /** + * Callback structure provided to {@link FingerprintDialog#authenticate(CancellationSignal, + * Executor, AuthenticationCallback)} or {@link FingerprintDialog#authenticate(CryptoObject, + * CancellationSignal, Executor, AuthenticationCallback)}. Users must provide an implementation + * of this for listening to authentication events. + */ + public static abstract class AuthenticationCallback extends + BiometricAuthenticator.AuthenticationCallback { + /** + * Called when an unrecoverable error has been encountered and the operation is complete. + * No further actions will be made on this object. + * @param errorCode An integer identifying the error message + * @param errString A human-readable error string that can be shown on an UI + */ + @Override + public void onAuthenticationError(int errorCode, CharSequence errString) {} + + /** + * Called when a recoverable error has been encountered during authentication. The help + * string is provided to give the user guidance for what went wrong, such as "Sensor dirty, + * please clean it." + * @param helpCode An integer identifying the error message + * @param helpString A human-readable string that can be shown on an UI + */ + @Override + public void onAuthenticationHelp(int helpCode, CharSequence helpString) {} + + /** + * Called when a biometric is recognized. + * @param result An object containing authentication-related data + */ + public void onAuthenticationSucceeded(AuthenticationResult result) {} + + /** + * Called when a biometric is valid but not recognized. + */ + @Override + public void onAuthenticationFailed() {} + + /** + * Called when a biometric has been acquired, but hasn't been processed yet. + * @hide + */ + @Override + public void onAuthenticationAcquired(int acquireInfo) {} + + /** + * @param result An object containing authentication-related data + * @hide + */ + @Override + public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) { + onAuthenticationSucceeded(new AuthenticationResult( + (CryptoObject) result.getCryptoObject(), + result.getId(), + result.getUserId())); + } + } + + + /** + * @param crypto Object associated with the call + * @param cancel An object that can be used to cancel authentication + * @param executor An executor to handle callback events + * @param callback An object to receive authentication events + * @hide + */ + @Override + public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto, + @NonNull CancellationSignal cancel, + @NonNull @CallbackExecutor Executor executor, + @NonNull BiometricAuthenticator.AuthenticationCallback callback) { + if (!(callback instanceof FingerprintDialog.AuthenticationCallback)) { + throw new IllegalArgumentException("Callback cannot be casted"); + } + authenticate(crypto, cancel, executor, (AuthenticationCallback) callback); } /** + * + * @param cancel An object that can be used to cancel authentication + * @param executor An executor to handle callback events + * @param callback An object to receive authentication events + * @hide + */ + @Override + public void authenticate(@NonNull CancellationSignal cancel, + @NonNull @CallbackExecutor Executor executor, + @NonNull BiometricAuthenticator.AuthenticationCallback callback) { + if (!(callback instanceof FingerprintDialog.AuthenticationCallback)) { + throw new IllegalArgumentException("Callback cannot be casted"); + } + authenticate(cancel, executor, (AuthenticationCallback) callback); + } + + + /** * This call warms up the fingerprint hardware, displays a system-provided dialog, * and starts scanning for a fingerprint. It terminates when - * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when - * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, - * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user - * dismisses the system-provided dialog, at which point the crypto object becomes invalid. - * This operation can be canceled by using the provided cancel object. The application will - * receive authentication errors through {@link AuthenticationCallback}, and button events - * through the corresponding callback set in - * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}. - * It is safe to reuse the {@link FingerprintDialog} object, and calling - * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)} - * while an existing authentication attempt is occurring will stop the previous client and - * start a new authentication. The interrupted client will receive a cancelled notification - * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}. + * {@link AuthenticationCallback#onAuthenticationError(int, + * CharSequence)} is called, when + * {@link AuthenticationCallback#onAuthenticationSucceeded( + * AuthenticationResult)}, or when the user dismisses the system-provided dialog, at which point + * the crypto object becomes invalid. This operation can be canceled by using the provided + * cancel object. The application will receive authentication errors through + * {@link AuthenticationCallback}, and button events through the + * corresponding callback set in {@link Builder#setNegativeButton(CharSequence, + * Executor, DialogInterface.OnClickListener)}. It is safe to reuse the + * {@link FingerprintDialog} object, and calling {@link FingerprintDialog#authenticate( + * CancellationSignal, Executor, AuthenticationCallback)} while an + * existing authentication attempt is occurring will stop the previous client and start a + * new authentication. The interrupted client will receive a cancelled notification through + * {@link AuthenticationCallback#onAuthenticationError(int, + * CharSequence)}. * - * @throws IllegalArgumentException if any of the arguments are null + * @throws IllegalArgumentException If any of the arguments are null * - * @param crypto object associated with the call - * @param cancel an object that can be used to cancel authentication - * @param executor an executor to handle callback events - * @param callback an object to receive authentication events + * @param crypto Object associated with the call + * @param cancel An object that can be used to cancel authentication + * @param executor An executor to handle callback events + * @param callback An object to receive authentication events */ @RequiresPermission(USE_FINGERPRINT) - public void authenticate(@NonNull FingerprintManager.CryptoObject crypto, + public void authenticate(@NonNull CryptoObject crypto, @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, - @NonNull FingerprintManager.AuthenticationCallback callback) { + @NonNull AuthenticationCallback callback) { + if (handlePreAuthenticationErrors(callback, executor)) { + return; + } mFingerprintManager.authenticate(crypto, cancel, mBundle, executor, mDialogReceiver, callback); } @@ -265,29 +444,57 @@ public class FingerprintDialog { /** * This call warms up the fingerprint hardware, displays a system-provided dialog, * and starts scanning for a fingerprint. It terminates when - * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when - * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, - * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user - * dismisses the system-provided dialog. This operation can be canceled by using the provided - * cancel object. The application will receive authentication errors through - * {@link AuthenticationCallback}, and button events through the corresponding callback set in + * {@link AuthenticationCallback#onAuthenticationError(int, + * CharSequence)} is called, when + * {@link AuthenticationCallback#onAuthenticationSucceeded( + * AuthenticationResult)} is called, or when the user dismisses the system-provided dialog. + * This operation can be canceled by using the provided cancel object. The application will + * receive authentication errors through {@link AuthenticationCallback}, + * and button events through the corresponding callback set in * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}. * It is safe to reuse the {@link FingerprintDialog} object, and calling - * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)} - * while an existing authentication attempt is occurring will stop the previous client and - * start a new authentication. The interrupted client will receive a cancelled notification - * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}. + * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, + * AuthenticationCallback)} while an existing authentication attempt is + * occurring will stop the previous client and start a new authentication. The interrupted + * client will receive a cancelled notification through + * {@link AuthenticationCallback#onAuthenticationError(int, + * CharSequence)}. * - * @throws IllegalArgumentException if any of the arguments are null + * @throws IllegalArgumentException If any of the arguments are null * - * @param cancel an object that can be used to cancel authentication - * @param executor an executor to handle callback events - * @param callback an object to receive authentication events + * @param cancel An object that can be used to cancel authentication + * @param executor An executor to handle callback events + * @param callback An object to receive authentication events */ @RequiresPermission(USE_FINGERPRINT) public void authenticate(@NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, - @NonNull FingerprintManager.AuthenticationCallback callback) { + @NonNull AuthenticationCallback callback) { + if (handlePreAuthenticationErrors(callback, executor)) { + return; + } mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback); } + + private boolean handlePreAuthenticationErrors(AuthenticationCallback callback, + Executor executor) { + if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { + sendError(FINGERPRINT_ERROR_HW_NOT_PRESENT, callback, executor); + return true; + } else if (!mFingerprintManager.isHardwareDetected()) { + sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, callback, executor); + return true; + } else if (!mFingerprintManager.hasEnrolledFingerprints()) { + sendError(FINGERPRINT_ERROR_NO_FINGERPRINTS, callback, executor); + return true; + } + return false; + } + + private void sendError(int error, AuthenticationCallback callback, Executor executor) { + executor.execute(() -> { + callback.onAuthenticationError(error, mFingerprintManager.getErrorString( + error, 0 /* vendorCode */)); + }); + } } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 62d92c4ac385..2dfe673a1c6c 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -27,6 +27,8 @@ import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.app.ActivityManager; import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.BiometricFingerprintConstants; import android.os.Binder; import android.os.Bundle; import android.os.CancellationSignal; @@ -38,7 +40,6 @@ import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; -import android.security.keystore.AndroidKeyStoreProvider; import android.util.Log; import android.util.Slog; @@ -51,9 +52,14 @@ import javax.crypto.Mac; /** * A class that coordinates access to the fingerprint hardware. + * @deprecated See {@link FingerprintDialog} which shows a system-provided dialog upon starting + * authentication. In a world where devices may have in-display fingerprint sensors, it's much + * more realistic to have a system-provided authentication dialog since the in-display sensor + * location may vary by vendor/device. */ +@Deprecated @SystemService(Context.FINGERPRINT_SERVICE) -public class FingerprintManager { +public class FingerprintManager implements BiometricFingerprintConstants { private static final String TAG = "FingerprintManager"; private static final boolean DEBUG = true; private static final int MSG_ENROLL_RESULT = 100; @@ -64,147 +70,14 @@ public class FingerprintManager { private static final int MSG_REMOVED = 105; private static final int MSG_ENUMERATED = 106; - // - // Error messages from fingerprint hardware during initilization, enrollment, authentication or - // removal. Must agree with the list in fingerprint.h - // - - /** - * The hardware is unavailable. Try again later. - */ - public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; - - /** - * Error state returned when the sensor was unable to process the current image. - */ - public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; - - /** - * Error state returned when the current request has been running too long. This is intended to - * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is - * platform and sensor-specific, but is generally on the order of 30 seconds. - */ - public static final int FINGERPRINT_ERROR_TIMEOUT = 3; - - /** - * Error state returned for operations like enrollment; the operation cannot be completed - * because there's not enough storage remaining to complete the operation. - */ - public static final int FINGERPRINT_ERROR_NO_SPACE = 4; - - /** - * The operation was canceled because the fingerprint sensor is unavailable. For example, - * this may happen when the user is switched, the device is locked or another pending operation - * prevents or disables it. - */ - public static final int FINGERPRINT_ERROR_CANCELED = 5; - - /** - * The {@link FingerprintManager#remove} call failed. Typically this will happen when the - * provided fingerprint id was incorrect. - * - * @hide - */ - public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6; - - /** - * The operation was canceled because the API is locked out due to too many attempts. - * This occurs after 5 failed attempts, and lasts for 30 seconds. - */ - public static final int FINGERPRINT_ERROR_LOCKOUT = 7; - - /** - * Hardware vendors may extend this list if there are conditions that do not fall under one of - * the above categories. Vendors are responsible for providing error strings for these errors. - * These messages are typically reserved for internal operations such as enrollment, but may be - * used to express vendor errors not covered by the ones in fingerprint.h. Applications are - * expected to show the error message string if they happen, but are advised not to rely on the - * message id since they will be device and vendor-specific - */ - public static final int FINGERPRINT_ERROR_VENDOR = 8; - - /** - * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times. - * Fingerprint authentication is disabled until the user unlocks with strong authentication - * (PIN/Pattern/Password) - */ - public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; - - /** - * The user canceled the operation. Upon receiving this, applications should use alternate - * authentication (e.g. a password). The application should also provide the means to return - * to fingerprint authentication, such as a "use fingerprint" button. - */ - public static final int FINGERPRINT_ERROR_USER_CANCELED = 10; - - /** - * @hide - */ - public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000; - - // - // Image acquisition messages. Must agree with those in fingerprint.h - // - - /** - * The image acquired was good. - */ - public static final int FINGERPRINT_ACQUIRED_GOOD = 0; - - /** - * Only a partial fingerprint image was detected. During enrollment, the user should be - * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor." - */ - public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; - - /** - * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or - * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}). - */ - public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; - - /** - * The fingerprint image was too noisy due to suspected or detected dirt on the sensor. - * For example, it's reasonable return this after multiple - * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor - * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor - * when this is returned. - */ - public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; - - /** - * The fingerprint image was unreadable due to lack of motion. This is most appropriate for - * linear array sensors that require a swipe motion. - */ - public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; - - /** - * The fingerprint image was incomplete due to quick motion. While mostly appropriate for - * linear array sensors, this could also happen if the finger was moved during acquisition. - * The user should be asked to move the finger slower (linear) or leave the finger on the sensor - * longer. - */ - public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; - - /** - * Hardware vendors may extend this list if there are conditions that do not fall under one of - * the above categories. Vendors are responsible for providing error strings for these errors. - * @hide - */ - public static final int FINGERPRINT_ACQUIRED_VENDOR = 6; - /** - * @hide - */ - public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; - private IFingerprintService mService; private Context mContext; private IBinder mToken = new Binder(); - private AuthenticationCallback mAuthenticationCallback; + private BiometricAuthenticator.AuthenticationCallback mAuthenticationCallback; private EnrollmentCallback mEnrollmentCallback; private RemovalCallback mRemovalCallback; private EnumerateCallback mEnumerateCallback; - private CryptoObject mCryptoObject; + private android.hardware.biometrics.CryptoObject mCryptoObject; private Fingerprint mRemovalFingerprint; private Handler mHandler; private Executor mExecutor; @@ -217,9 +90,9 @@ public class FingerprintManager { } private class OnAuthenticationCancelListener implements OnCancelListener { - private CryptoObject mCrypto; + private android.hardware.biometrics.CryptoObject mCrypto; - public OnAuthenticationCancelListener(CryptoObject crypto) { + public OnAuthenticationCancelListener(android.hardware.biometrics.CryptoObject crypto) { mCrypto = crypto; } @@ -233,18 +106,17 @@ public class FingerprintManager { * A wrapper class for the crypto objects supported by FingerprintManager. Currently the * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects. */ - public static final class CryptoObject { - + public static final class CryptoObject extends android.hardware.biometrics.CryptoObject { public CryptoObject(@NonNull Signature signature) { - mCrypto = signature; + super(signature); } public CryptoObject(@NonNull Cipher cipher) { - mCrypto = cipher; + super(cipher); } public CryptoObject(@NonNull Mac mac) { - mCrypto = mac; + super(mac); } /** @@ -252,7 +124,7 @@ public class FingerprintManager { * @return {@link Signature} object or null if this doesn't contain one. */ public Signature getSignature() { - return mCrypto instanceof Signature ? (Signature) mCrypto : null; + return super.getSignature(); } /** @@ -260,7 +132,7 @@ public class FingerprintManager { * @return {@link Cipher} object or null if this doesn't contain one. */ public Cipher getCipher() { - return mCrypto instanceof Cipher ? (Cipher) mCrypto : null; + return super.getCipher(); } /** @@ -268,20 +140,9 @@ public class FingerprintManager { * @return {@link Mac} object or null if this doesn't contain one. */ public Mac getMac() { - return mCrypto instanceof Mac ? (Mac) mCrypto : null; + return super.getMac(); } - - /** - * @hide - * @return the opId associated with this object or 0 if none - */ - public long getOpId() { - return mCrypto != null ? - AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0; - } - - private final Object mCrypto; - }; + } /** * Container for callback data from {@link FingerprintManager#authenticate(CryptoObject, @@ -334,13 +195,15 @@ public class FingerprintManager { * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to * fingerprint events. */ - public static abstract class AuthenticationCallback { + public static abstract class AuthenticationCallback + extends BiometricAuthenticator.AuthenticationCallback { /** * Called when an unrecoverable error has been encountered and the operation is complete. * No further callbacks will be made on this object. * @param errorCode An integer identifying the error message * @param errString A human-readable error string that can be shown in UI */ + @Override public void onAuthenticationError(int errorCode, CharSequence errString) { } /** @@ -350,6 +213,7 @@ public class FingerprintManager { * @param helpCode An integer identifying the error message * @param helpString A human-readable string that can be shown in UI */ + @Override public void onAuthenticationHelp(int helpCode, CharSequence helpString) { } /** @@ -361,6 +225,7 @@ public class FingerprintManager { /** * Called when a fingerprint is valid but not recognized. */ + @Override public void onAuthenticationFailed() { } /** @@ -369,7 +234,19 @@ public class FingerprintManager { * @param acquireInfo one of FINGERPRINT_ACQUIRED_* constants * @hide */ + @Override public void onAuthenticationAcquired(int acquireInfo) {} + + /** + * @hide + * @param result + */ + @Override + public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) { + onAuthenticationSucceeded(new AuthenticationResult( + (CryptoObject) result.getCryptoObject(), + (Fingerprint) result.getId(), result.getUserId())); + } }; /** @@ -489,7 +366,12 @@ public class FingerprintManager { * by <a href="{@docRoot}training/articles/keystore.html">Android Keystore * facility</a>. * @throws IllegalStateException if the crypto primitive is not initialized. + * @deprecated See {@link FingerprintDialog#authenticate(CancellationSignal, Executor, + * FingerprintDialog.AuthenticationCallback)} and {@link FingerprintDialog#authenticate( + * FingerprintDialog.CryptoObject, CancellationSignal, Executor, + * FingerprintDialog.AuthenticationCallback)} */ + @Deprecated @RequiresPermission(USE_FINGERPRINT) public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) { @@ -554,12 +436,12 @@ public class FingerprintManager { * @param userId the user ID that the fingerprint hardware will authenticate for. */ private void authenticate(int userId, - @Nullable CryptoObject crypto, + @Nullable android.hardware.biometrics.CryptoObject crypto, @NonNull CancellationSignal cancel, @NonNull Bundle bundle, @NonNull @CallbackExecutor Executor executor, @NonNull IFingerprintDialogReceiver receiver, - @NonNull AuthenticationCallback callback) { + @NonNull BiometricAuthenticator.AuthenticationCallback callback) { mCryptoObject = crypto; if (cancel.isCanceled()) { Log.w(TAG, "authentication already canceled"); @@ -598,7 +480,7 @@ public class FingerprintManager { @NonNull Bundle bundle, @NonNull @CallbackExecutor Executor executor, @NonNull IFingerprintDialogReceiver receiver, - @NonNull AuthenticationCallback callback) { + @NonNull BiometricAuthenticator.AuthenticationCallback callback) { if (cancel == null) { throw new IllegalArgumentException("Must supply a cancellation signal"); } @@ -626,12 +508,12 @@ public class FingerprintManager { * @param callback * @hide */ - public void authenticate(@NonNull CryptoObject crypto, + public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto, @NonNull CancellationSignal cancel, @NonNull Bundle bundle, @NonNull @CallbackExecutor Executor executor, @NonNull IFingerprintDialogReceiver receiver, - @NonNull AuthenticationCallback callback) { + @NonNull BiometricAuthenticator.AuthenticationCallback callback) { if (crypto == null) { throw new IllegalArgumentException("Must supply a crypto object"); } @@ -648,9 +530,10 @@ public class FingerprintManager { throw new IllegalArgumentException("Must supply a receiver"); } if (callback == null) { - throw new IllegalArgumentException("Must supply a calback"); + throw new IllegalArgumentException("Must supply a callback"); } - authenticate(UserHandle.myUserId(), crypto, cancel, bundle, executor, receiver, callback); + authenticate(UserHandle.myUserId(), crypto, cancel, + bundle, executor, receiver, callback); } /** @@ -848,7 +731,10 @@ public class FingerprintManager { * Determine if there is at least one fingerprint enrolled. * * @return true if at least one fingerprint is enrolled, false otherwise + * @deprecated See {@link FingerprintDialog} and + * {@link FingerprintDialog#FINGERPRINT_ERROR_NO_FINGERPRINTS} */ + @Deprecated @RequiresPermission(USE_FINGERPRINT) public boolean hasEnrolledFingerprints() { if (mService != null) try { @@ -879,7 +765,10 @@ public class FingerprintManager { * Determine if fingerprint hardware is present and functional. * * @return true if hardware is present and functional, false otherwise. + * @deprecated See {@link FingerprintDialog} and + * {@link FingerprintDialog#FINGERPRINT_ERROR_HW_UNAVAILABLE} */ + @Deprecated @RequiresPermission(USE_FINGERPRINT) public boolean isHardwareDetected() { if (mService != null) { @@ -1049,8 +938,8 @@ public class FingerprintManager { private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) { if (mAuthenticationCallback != null) { - final AuthenticationResult result = - new AuthenticationResult(mCryptoObject, fp, userId); + final BiometricAuthenticator.AuthenticationResult result = + new BiometricAuthenticator.AuthenticationResult(mCryptoObject, fp, userId); mAuthenticationCallback.onAuthenticationSucceeded(result); } } @@ -1126,7 +1015,7 @@ public class FingerprintManager { } } - private void cancelAuthentication(CryptoObject cryptoObject) { + private void cancelAuthentication(android.hardware.biometrics.CryptoObject cryptoObject) { if (mService != null) try { mService.cancelAuthentication(mToken, mContext.getOpPackageName()); } catch (RemoteException e) { @@ -1160,6 +1049,12 @@ public class FingerprintManager { case FINGERPRINT_ERROR_USER_CANCELED: return mContext.getString( com.android.internal.R.string.fingerprint_error_user_canceled); + case FINGERPRINT_ERROR_NO_FINGERPRINTS: + return mContext.getString( + com.android.internal.R.string.fingerprint_error_no_fingerprints); + case FINGERPRINT_ERROR_HW_NOT_PRESENT: + return mContext.getString( + com.android.internal.R.string.fingerprint_error_hw_not_present); case FINGERPRINT_ERROR_VENDOR: { String[] msgArray = mContext.getResources().getStringArray( com.android.internal.R.array.fingerprint_error_vendor); diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index b00f6033976e..e1d7edfa7d9c 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -213,6 +213,7 @@ public class RadioManager { private final boolean mIsBgScanSupported; private final Set<Integer> mSupportedProgramTypes; private final Set<Integer> mSupportedIdentifierTypes; + @Nullable private final Map<String, Integer> mDabFrequencyTable; @NonNull private final Map<String, String> mVendorInfo; /** @hide */ @@ -221,6 +222,7 @@ public class RadioManager { boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported, @ProgramSelector.ProgramType int[] supportedProgramTypes, @ProgramSelector.IdentifierType int[] supportedIdentifierTypes, + @Nullable Map<String, Integer> dabFrequencyTable, Map<String, String> vendorInfo) { mId = id; mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName; @@ -236,6 +238,13 @@ public class RadioManager { mIsBgScanSupported = isBgScanSupported; mSupportedProgramTypes = arrayToSet(supportedProgramTypes); mSupportedIdentifierTypes = arrayToSet(supportedIdentifierTypes); + if (dabFrequencyTable != null) { + for (Map.Entry<String, Integer> entry : dabFrequencyTable.entrySet()) { + Objects.requireNonNull(entry.getKey()); + Objects.requireNonNull(entry.getValue()); + } + } + mDabFrequencyTable = dabFrequencyTable; mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo; } @@ -363,6 +372,19 @@ public class RadioManager { } /** + * A frequency table for Digital Audio Broadcasting (DAB). + * + * The key is a channel name, i.e. 5A, 7B. + * + * The value is a frequency, in kHz. + * + * @return a frequency table, or {@code null} if the module doesn't support DAB + */ + public @Nullable Map<String, Integer> getDabFrequencyTable() { + return mDabFrequencyTable; + } + + /** * A map of vendor-specific opaque strings, passed from HAL without changes. * Format of these strings can vary across vendors. * @@ -403,6 +425,7 @@ public class RadioManager { mIsBgScanSupported = in.readInt() == 1; mSupportedProgramTypes = arrayToSet(in.createIntArray()); mSupportedIdentifierTypes = arrayToSet(in.createIntArray()); + mDabFrequencyTable = Utils.readStringIntMap(in); mVendorInfo = Utils.readStringMap(in); } @@ -433,6 +456,7 @@ public class RadioManager { dest.writeInt(mIsBgScanSupported ? 1 : 0); dest.writeIntArray(setToArray(mSupportedProgramTypes)); dest.writeIntArray(setToArray(mSupportedIdentifierTypes)); + Utils.writeStringIntMap(dest, mDabFrequencyTable); Utils.writeStringMap(dest, mVendorInfo); } @@ -456,67 +480,31 @@ public class RadioManager { @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + mId; - result = prime * result + mServiceName.hashCode(); - result = prime * result + mClassId; - result = prime * result + ((mImplementor == null) ? 0 : mImplementor.hashCode()); - result = prime * result + ((mProduct == null) ? 0 : mProduct.hashCode()); - result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode()); - result = prime * result + ((mSerial == null) ? 0 : mSerial.hashCode()); - result = prime * result + mNumTuners; - result = prime * result + mNumAudioSources; - result = prime * result + (mIsCaptureSupported ? 1 : 0); - result = prime * result + Arrays.hashCode(mBands); - result = prime * result + (mIsBgScanSupported ? 1 : 0); - result = prime * result + mVendorInfo.hashCode(); - return result; + return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion, + mSerial, mNumTuners, mNumAudioSources, mIsCaptureSupported, mBands, + mIsBgScanSupported, mDabFrequencyTable, mVendorInfo); } @Override public boolean equals(Object obj) { - if (this == obj) - return true; - if (!(obj instanceof ModuleProperties)) - return false; + if (this == obj) return true; + if (!(obj instanceof ModuleProperties)) return false; ModuleProperties other = (ModuleProperties) obj; - if (mId != other.getId()) - return false; + + if (mId != other.getId()) return false; if (!TextUtils.equals(mServiceName, other.mServiceName)) return false; - if (mClassId != other.getClassId()) - return false; - if (mImplementor == null) { - if (other.getImplementor() != null) - return false; - } else if (!mImplementor.equals(other.getImplementor())) - return false; - if (mProduct == null) { - if (other.getProduct() != null) - return false; - } else if (!mProduct.equals(other.getProduct())) - return false; - if (mVersion == null) { - if (other.getVersion() != null) - return false; - } else if (!mVersion.equals(other.getVersion())) - return false; - if (mSerial == null) { - if (other.getSerial() != null) - return false; - } else if (!mSerial.equals(other.getSerial())) - return false; - if (mNumTuners != other.getNumTuners()) - return false; - if (mNumAudioSources != other.getNumAudioSources()) - return false; - if (mIsCaptureSupported != other.isCaptureSupported()) - return false; - if (!Arrays.equals(mBands, other.getBands())) - return false; - if (mIsBgScanSupported != other.isBackgroundScanningSupported()) - return false; - if (!mVendorInfo.equals(other.mVendorInfo)) return false; + if (mClassId != other.mClassId) return false; + if (!Objects.equals(mImplementor, other.mImplementor)) return false; + if (!Objects.equals(mProduct, other.mProduct)) return false; + if (!Objects.equals(mVersion, other.mVersion)) return false; + if (!Objects.equals(mSerial, other.mSerial)) return false; + if (mNumTuners != other.mNumTuners) return false; + if (mNumAudioSources != other.mNumAudioSources) return false; + if (mIsCaptureSupported != other.mIsCaptureSupported) return false; + if (!Objects.equals(mBands, other.mBands)) return false; + if (mIsBgScanSupported != other.mIsBgScanSupported) return false; + if (!Objects.equals(mDabFrequencyTable, other.mDabFrequencyTable)) return false; + if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false; return true; } } diff --git a/core/java/android/hardware/radio/Utils.java b/core/java/android/hardware/radio/Utils.java index f1b589746a14..9887f7823269 100644 --- a/core/java/android/hardware/radio/Utils.java +++ b/core/java/android/hardware/radio/Utils.java @@ -56,6 +56,29 @@ final class Utils { return map; } + static void writeStringIntMap(@NonNull Parcel dest, @Nullable Map<String, Integer> map) { + if (map == null) { + dest.writeInt(0); + return; + } + dest.writeInt(map.size()); + for (Map.Entry<String, Integer> entry : map.entrySet()) { + dest.writeString(entry.getKey()); + dest.writeInt(entry.getValue()); + } + } + + static @NonNull Map<String, Integer> readStringIntMap(@NonNull Parcel in) { + int size = in.readInt(); + Map<String, Integer> map = new HashMap<>(); + while (size-- > 0) { + String key = in.readString(); + int value = in.readInt(); + map.put(key, value); + } + return map; + } + static <T extends Parcelable> void writeSet(@NonNull Parcel dest, @Nullable Set<T> set) { if (set == null) { dest.writeInt(0); diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 398dda174ba1..91bbdc7dde80 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -88,18 +88,22 @@ interface IUsbManager /* Returns true if the specified USB function is enabled. */ boolean isFunctionEnabled(String function); - /* Sets the current USB function as well as whether USB data - * (for example, MTP exposed pictures) should be made available - * on the USB connection. Unlocking data should only be done with - * user involvement, since exposing pictures or other data could - * leak sensitive user information. - */ + /* Sets the current USB function. */ + void setCurrentFunctions(long functions); + + /* Compatibility version of setCurrentFunctions(long). */ void setCurrentFunction(String function, boolean usbDataUnlocked); + /* Gets the current USB functions. */ + long getCurrentFunctions(); + /* Sets the screen unlocked USB function(s), which will be set automatically * when the screen is unlocked. */ - void setScreenUnlockedFunctions(String function); + void setScreenUnlockedFunctions(long functions); + + /* Gets the current screen unlocked functions. */ + long getScreenUnlockedFunctions(); /* Allow USB debugging from the attached host. If alwaysAllow is true, add the * the public key to list of host keys that the user has approved. diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 7617c2bd196f..8daecac5a109 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -28,6 +28,7 @@ import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; +import android.hardware.usb.gadget.V1_0.GadgetFunction; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.Process; @@ -37,6 +38,8 @@ import android.util.Log; import com.android.internal.util.Preconditions; import java.util.HashMap; +import java.util.Map; +import java.util.StringJoiner; /** * This class allows you to access the state of USB and communicate with USB devices. @@ -70,7 +73,7 @@ public class UsbManager { * MTP function is enabled * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the * PTP function is enabled - * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the + * <li> {@link #USB_FUNCTION_ACCESSORY} boolean extra indicating whether the * accessory function is enabled * <li> {@link #USB_FUNCTION_AUDIO_SOURCE} boolean extra indicating whether the * audio source function is enabled @@ -196,8 +199,7 @@ public class UsbManager { /** * A placeholder indicating that no USB function is being specified. - * Used to distinguish between selecting no function vs. the default function in - * {@link #setCurrentFunction(String)}. + * Used for compatibility with old init scripts to indicate no functions vs. charging function. * * {@hide} */ @@ -298,6 +300,69 @@ public class UsbManager { */ public static final String EXTRA_PERMISSION_GRANTED = "permission"; + /** + * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)} + * {@hide} + */ + public static final long FUNCTION_NONE = 0; + + /** + * Code for the mtp usb function. Passed as a mask into {@link #setCurrentFunctions(long)} + * {@hide} + */ + public static final long FUNCTION_MTP = GadgetFunction.MTP; + + /** + * Code for the ptp usb function. Passed as a mask into {@link #setCurrentFunctions(long)} + * {@hide} + */ + public static final long FUNCTION_PTP = GadgetFunction.PTP; + + /** + * Code for the rndis usb function. Passed as a mask into {@link #setCurrentFunctions(long)} + * {@hide} + */ + public static final long FUNCTION_RNDIS = GadgetFunction.RNDIS; + + /** + * Code for the midi usb function. Passed as a mask into {@link #setCurrentFunctions(long)} + * {@hide} + */ + public static final long FUNCTION_MIDI = GadgetFunction.MIDI; + + /** + * Code for the accessory usb function. + * {@hide} + */ + public static final long FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY; + + /** + * Code for the audio source usb function. + * {@hide} + */ + public static final long FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE; + + /** + * Code for the adb usb function. + * {@hide} + */ + public static final long FUNCTION_ADB = GadgetFunction.ADB; + + private static final long SETTABLE_FUNCTIONS = FUNCTION_MTP | FUNCTION_PTP | FUNCTION_RNDIS + | FUNCTION_MIDI; + + private static final Map<String, Long> FUNCTION_NAME_TO_CODE = new HashMap<>(); + + static { + FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_MTP, FUNCTION_MTP); + FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_PTP, FUNCTION_PTP); + FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_RNDIS, FUNCTION_RNDIS); + FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_MIDI, FUNCTION_MIDI); + FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ACCESSORY, FUNCTION_ACCESSORY); + FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, FUNCTION_AUDIO_SOURCE); + FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ADB, FUNCTION_ADB); + } + private final Context mContext; private final IUsbManager mService; @@ -548,15 +613,14 @@ public class UsbManager { * services offered by the device. * </p> * + * @deprecated use getCurrentFunctions() instead. * @param function name of the USB function * @return true if the USB function is enabled * * {@hide} */ + @Deprecated public boolean isFunctionEnabled(String function) { - if (mService == null) { - return false; - } try { return mService.isFunctionEnabled(function); } catch (RemoteException e) { @@ -565,7 +629,7 @@ public class UsbManager { } /** - * Sets the current USB function when in device mode. + * Sets the current USB functions when in device mode. * <p> * USB functions represent interfaces which are published to the host to access * services offered by the device. @@ -574,27 +638,59 @@ public class UsbManager { * automatically activate additional functions such as {@link #USB_FUNCTION_ADB} * or {@link #USB_FUNCTION_ACCESSORY} based on other settings and states. * </p><p> - * The allowed values are: {@link #USB_FUNCTION_NONE}, {@link #USB_FUNCTION_AUDIO_SOURCE}, - * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP}, - * or {@link #USB_FUNCTION_RNDIS}. - * </p><p> - * Also sets whether USB data (for example, MTP exposed pictures) should be made available - * on the USB connection when in device mode. Unlocking usb data should only be done with - * user involvement, since exposing pictures or other data could leak sensitive - * user information. + * An argument of 0 indicates that the device is charging, and can pick any + * appropriate function for that purpose. * </p><p> * Note: This function is asynchronous and may fail silently without applying * the requested changes. * </p> * - * @param function name of the USB function, or null to restore the default function - * @param usbDataUnlocked whether user data is accessible + * @param functions the USB function(s) to set, as a bitwise mask. + * Must satisfy {@link UsbManager#areSettableFunctions} + * + * {@hide} + */ + public void setCurrentFunctions(long functions) { + try { + mService.setCurrentFunctions(functions); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets the current USB functions when in device mode. + * + * @deprecated use setCurrentFunctions(long) instead. + * @param functions the USB function(s) to set. + * @param usbDataUnlocked unused + + * {@hide} + */ + @Deprecated + public void setCurrentFunction(String functions, boolean usbDataUnlocked) { + try { + mService.setCurrentFunction(functions, usbDataUnlocked); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the current USB functions in device mode. + * <p> + * This function returns the state of primary USB functions and can return a + * mask containing any usb function(s) except for ADB. + * </p> + * + * @return The currently enabled functions, in a bitwise mask. + * A zero mask indicates that the current function is the charging function. * * {@hide} */ - public void setCurrentFunction(String function, boolean usbDataUnlocked) { + public long getCurrentFunctions() { try { - mService.setCurrentFunction(function, usbDataUnlocked); + return mService.getCurrentFunctions(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -604,23 +700,37 @@ public class UsbManager { * Sets the screen unlocked functions, which are persisted and set as the current functions * whenever the screen is unlocked. * <p> - * The allowed values are: {@link #USB_FUNCTION_NONE}, - * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP}, - * or {@link #USB_FUNCTION_RNDIS}. - * {@link #USB_FUNCTION_NONE} has the effect of switching off this feature, so functions + * A zero mask has the effect of switching off this feature, so functions * no longer change on screen unlock. * </p><p> * Note: When the screen is on, this method will apply given functions as current functions, * which is asynchronous and may fail silently without applying the requested changes. * </p> * - * @param function function to set as default + * @param functions functions to set, in a bitwise mask. + * Must satisfy {@link UsbManager#areSettableFunctions} * * {@hide} */ - public void setScreenUnlockedFunctions(String function) { + public void setScreenUnlockedFunctions(long functions) { try { - mService.setScreenUnlockedFunctions(function); + mService.setScreenUnlockedFunctions(functions); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Gets the current screen unlocked functions. + * + * @return The currently set screen enabled functions. + * A zero mask indicates that the screen unlocked functions feature is not enabled. + * + * {@hide} + */ + public long getScreenUnlockedFunctions() { + try { + return mService.getScreenUnlockedFunctions(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -719,51 +829,71 @@ public class UsbManager { } } - /** @hide */ - public static String addFunction(String functions, String function) { - if (USB_FUNCTION_NONE.equals(functions)) { - return function; - } - if (!containsFunction(functions, function)) { - if (functions.length() > 0) { - functions += ","; - } - functions += function; - } - return functions; + /** + * Returns whether the given functions are valid inputs to UsbManager. + * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI are accepted. + * + * @return Whether the mask is settable. + * + * {@hide} + */ + public static boolean areSettableFunctions(long functions) { + return functions == FUNCTION_NONE + || ((~SETTABLE_FUNCTIONS & functions) == 0 && Long.bitCount(functions) == 1); } - /** @hide */ - public static String removeFunction(String functions, String function) { - String[] split = functions.split(","); - for (int i = 0; i < split.length; i++) { - if (function.equals(split[i])) { - split[i] = null; - } + /** + * Converts the given function mask to string. Maintains ordering with respect to init scripts. + * + * @return String representation of given mask + * + * {@hide} + */ + public static String usbFunctionsToString(long functions) { + StringJoiner joiner = new StringJoiner(","); + if ((functions & FUNCTION_MTP) != 0) { + joiner.add(UsbManager.USB_FUNCTION_MTP); } - if (split.length == 1 && split[0] == null) { - return USB_FUNCTION_NONE; + if ((functions & FUNCTION_PTP) != 0) { + joiner.add(UsbManager.USB_FUNCTION_PTP); } - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < split.length; i++) { - String s = split[i]; - if (s != null) { - if (builder.length() > 0) { - builder.append(","); - } - builder.append(s); - } + if ((functions & FUNCTION_RNDIS) != 0) { + joiner.add(UsbManager.USB_FUNCTION_RNDIS); + } + if ((functions & FUNCTION_MIDI) != 0) { + joiner.add(UsbManager.USB_FUNCTION_MIDI); + } + if ((functions & FUNCTION_ACCESSORY) != 0) { + joiner.add(UsbManager.USB_FUNCTION_ACCESSORY); + } + if ((functions & FUNCTION_AUDIO_SOURCE) != 0) { + joiner.add(UsbManager.USB_FUNCTION_AUDIO_SOURCE); } - return builder.toString(); + if ((functions & FUNCTION_ADB) != 0) { + joiner.add(UsbManager.USB_FUNCTION_ADB); + } + return joiner.toString(); } - /** @hide */ - public static boolean containsFunction(String functions, String function) { - int index = functions.indexOf(function); - if (index < 0) return false; - if (index > 0 && functions.charAt(index - 1) != ',') return false; - int charAfter = index + function.length(); - if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false; - return true; + /** + * Parses a string of usb functions that are comma separated. + * + * @return A mask of all valid functions in the string + * + * {@hide} + */ + public static long usbFunctionsFromString(String functions) { + if (functions == null || functions.equals(USB_FUNCTION_NONE)) { + return FUNCTION_NONE; + } + long ret = 0; + for (String function : functions.split(",")) { + if (FUNCTION_NAME_TO_CODE.containsKey(function)) { + ret |= FUNCTION_NAME_TO_CODE.get(function); + } else if (function.length() > 0) { + throw new IllegalArgumentException("Invalid usb function " + functions); + } + } + return ret; } } diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index 9ccdbe2b1bdc..0829b4a3e9fe 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -462,7 +462,7 @@ public final class IpSecTransform implements AutoCloseable { mConfig.setMode(MODE_TUNNEL); mConfig.setSourceAddress(sourceAddress.getHostAddress()); mConfig.setSpiResourceId(spi.getResourceId()); - return new IpSecTransform(mContext, mConfig); + return new IpSecTransform(mContext, mConfig).activate(); } /** diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index bda720bb6fdd..7922276edb75 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -439,6 +439,10 @@ public class TrafficStats { } } + private static long addIfSupported(long stat) { + return (stat == UNSUPPORTED) ? 0 : stat; + } + /** * Return number of packets transmitted across mobile networks since device * boot. Counts packets across all mobile network interfaces, and always @@ -451,7 +455,7 @@ public class TrafficStats { public static long getMobileTxPackets() { long total = 0; for (String iface : getMobileIfaces()) { - total += getTxPackets(iface); + total += addIfSupported(getTxPackets(iface)); } return total; } @@ -468,7 +472,7 @@ public class TrafficStats { public static long getMobileRxPackets() { long total = 0; for (String iface : getMobileIfaces()) { - total += getRxPackets(iface); + total += addIfSupported(getRxPackets(iface)); } return total; } @@ -485,7 +489,7 @@ public class TrafficStats { public static long getMobileTxBytes() { long total = 0; for (String iface : getMobileIfaces()) { - total += getTxBytes(iface); + total += addIfSupported(getTxBytes(iface)); } return total; } @@ -502,7 +506,7 @@ public class TrafficStats { public static long getMobileRxBytes() { long total = 0; for (String iface : getMobileIfaces()) { - total += getRxBytes(iface); + total += addIfSupported(getRxBytes(iface)); } return total; } @@ -517,9 +521,7 @@ public class TrafficStats { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - if (stat != UNSUPPORTED) { - total += stat; - } + total += addIfSupported(stat); } return total; } @@ -534,9 +536,7 @@ public class TrafficStats { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - if (stat != UNSUPPORTED) { - total += stat; - } + total += addIfSupported(stat); } return total; } diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 9edcc0e9b8d4..5ca3a4106a2d 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -720,6 +720,10 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { LOOP: while (end < length) { switch (uriString.charAt(end)) { case '/': // Start of path + case '\\':// Start of path + // Per http://url.spec.whatwg.org/#host-state, the \ character + // is treated as if it were a / character when encountered in a + // host case '?': // Start of query case '#': // Start of fragment break LOOP; @@ -758,6 +762,10 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { case '#': // Start of fragment return ""; // Empty path. case '/': // Start of path! + case '\\':// Start of path! + // Per http://url.spec.whatwg.org/#host-state, the \ character + // is treated as if it were a / character when encountered in a + // host break LOOP; } pathStart++; diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 7c53ec198e7d..1160415ce212 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -16,6 +16,11 @@ package android.os; +import static android.system.OsConstants.SPLICE_F_MORE; +import static android.system.OsConstants.SPLICE_F_MOVE; +import static android.system.OsConstants.S_ISFIFO; +import static android.system.OsConstants.S_ISREG; + import android.annotation.NonNull; import android.annotation.Nullable; import android.provider.DocumentsContract.Document; @@ -28,7 +33,9 @@ import android.util.Slog; import android.webkit.MimeTypeMap; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.SizedInputStream; +import libcore.io.IoUtils; import libcore.util.EmptyArray; import java.io.BufferedInputStream; @@ -41,10 +48,12 @@ import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Comparator; import java.util.Objects; +import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import java.util.zip.CRC32; import java.util.zip.CheckedInputStream; @@ -81,6 +90,14 @@ public class FileUtils { private static final File[] EMPTY = new File[0]; + private static final boolean ENABLE_COPY_OPTIMIZATIONS = true; + + private static final long COPY_CHECKPOINT_BYTES = 524288; + + public interface ProgressListener { + public void onProgress(long progress); + } + /** * Set owner and mode of of given {@link File}. * @@ -185,6 +202,9 @@ public class FileUtils { return false; } + /** + * @deprecated use {@link #copy(File, File)} instead. + */ @Deprecated public static boolean copyFile(File srcFile, File destFile) { try { @@ -195,14 +215,19 @@ public class FileUtils { } } - // copy a file from srcFile to destFile, return true if succeed, return - // false if fail + /** + * @deprecated use {@link #copy(File, File)} instead. + */ + @Deprecated public static void copyFileOrThrow(File srcFile, File destFile) throws IOException { try (InputStream in = new FileInputStream(srcFile)) { copyToFileOrThrow(in, destFile); } } + /** + * @deprecated use {@link #copy(InputStream, OutputStream)} instead. + */ @Deprecated public static boolean copyToFile(InputStream inputStream, File destFile) { try { @@ -214,29 +239,247 @@ public class FileUtils { } /** - * Copy data from a source stream to destFile. - * Return true if succeed, return false if failed. + * @deprecated use {@link #copy(InputStream, OutputStream)} instead. */ - public static void copyToFileOrThrow(InputStream inputStream, File destFile) - throws IOException { + @Deprecated + public static void copyToFileOrThrow(InputStream in, File destFile) throws IOException { if (destFile.exists()) { destFile.delete(); } - FileOutputStream out = new FileOutputStream(destFile); - try { - byte[] buffer = new byte[4096]; - int bytesRead; - while ((bytesRead = inputStream.read(buffer)) >= 0) { - out.write(buffer, 0, bytesRead); + try (FileOutputStream out = new FileOutputStream(destFile)) { + copy(in, out); + try { + Os.fsync(out.getFD()); + } catch (ErrnoException e) { + throw e.rethrowAsIOException(); } - } finally { - out.flush(); + } + } + + /** + * Copy the contents of one file to another, replacing any existing content. + * <p> + * Attempts to use several optimization strategies to copy the data in the + * kernel before falling back to a userspace copy as a last resort. + * + * @return number of bytes copied. + */ + public static long copy(@NonNull File from, @NonNull File to) throws IOException { + return copy(from, to, null, null); + } + + /** + * Copy the contents of one file to another, replacing any existing content. + * <p> + * Attempts to use several optimization strategies to copy the data in the + * kernel before falling back to a userspace copy as a last resort. + * + * @param listener to be periodically notified as the copy progresses. + * @param signal to signal if the copy should be cancelled early. + * @return number of bytes copied. + */ + public static long copy(@NonNull File from, @NonNull File to, + @Nullable ProgressListener listener, @Nullable CancellationSignal signal) + throws IOException { + try (FileInputStream in = new FileInputStream(from); + FileOutputStream out = new FileOutputStream(to)) { + return copy(in, out, listener, signal); + } + } + + /** + * Copy the contents of one stream to another. + * <p> + * Attempts to use several optimization strategies to copy the data in the + * kernel before falling back to a userspace copy as a last resort. + * + * @return number of bytes copied. + */ + public static long copy(@NonNull InputStream in, @NonNull OutputStream out) throws IOException { + return copy(in, out, null, null); + } + + /** + * Copy the contents of one stream to another. + * <p> + * Attempts to use several optimization strategies to copy the data in the + * kernel before falling back to a userspace copy as a last resort. + * + * @param listener to be periodically notified as the copy progresses. + * @param signal to signal if the copy should be cancelled early. + * @return number of bytes copied. + */ + public static long copy(@NonNull InputStream in, @NonNull OutputStream out, + @Nullable ProgressListener listener, @Nullable CancellationSignal signal) + throws IOException { + if (ENABLE_COPY_OPTIMIZATIONS) { + if (in instanceof FileInputStream && out instanceof FileOutputStream) { + return copy(((FileInputStream) in).getFD(), ((FileOutputStream) out).getFD(), + listener, signal); + } + } + + // Worse case fallback to userspace + return copyInternalUserspace(in, out, listener, signal); + } + + /** + * Copy the contents of one FD to another. + * <p> + * Attempts to use several optimization strategies to copy the data in the + * kernel before falling back to a userspace copy as a last resort. + * + * @return number of bytes copied. + */ + public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out) + throws IOException { + return copy(in, out, null, null); + } + + /** + * Copy the contents of one FD to another. + * <p> + * Attempts to use several optimization strategies to copy the data in the + * kernel before falling back to a userspace copy as a last resort. + * + * @param listener to be periodically notified as the copy progresses. + * @param signal to signal if the copy should be cancelled early. + * @return number of bytes copied. + */ + public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out, + @Nullable ProgressListener listener, @Nullable CancellationSignal signal) + throws IOException { + return copy(in, out, listener, signal, Long.MAX_VALUE); + } + + /** + * Copy the contents of one FD to another. + * <p> + * Attempts to use several optimization strategies to copy the data in the + * kernel before falling back to a userspace copy as a last resort. + * + * @param listener to be periodically notified as the copy progresses. + * @param signal to signal if the copy should be cancelled early. + * @param count the number of bytes to copy. + * @return number of bytes copied. + */ + public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out, + @Nullable ProgressListener listener, @Nullable CancellationSignal signal, long count) + throws IOException { + if (ENABLE_COPY_OPTIMIZATIONS) { try { - out.getFD().sync(); - } catch (IOException e) { + final StructStat st_in = Os.fstat(in); + final StructStat st_out = Os.fstat(out); + if (S_ISREG(st_in.st_mode) && S_ISREG(st_out.st_mode)) { + return copyInternalSendfile(in, out, listener, signal, count); + } else if (S_ISFIFO(st_in.st_mode) || S_ISFIFO(st_out.st_mode)) { + return copyInternalSplice(in, out, listener, signal, count); + } + } catch (ErrnoException e) { + throw e.rethrowAsIOException(); + } + } + + // Worse case fallback to userspace + return copyInternalUserspace(in, out, listener, signal, count); + } + + /** + * Requires one of input or output to be a pipe. + */ + @VisibleForTesting + public static long copyInternalSplice(FileDescriptor in, FileDescriptor out, + ProgressListener listener, CancellationSignal signal, long count) + throws ErrnoException { + long progress = 0; + long checkpoint = 0; + + long t; + while ((t = Os.splice(in, null, out, null, Math.min(count, COPY_CHECKPOINT_BYTES), + SPLICE_F_MOVE | SPLICE_F_MORE)) != 0) { + progress += t; + checkpoint += t; + count -= t; + + if (checkpoint >= COPY_CHECKPOINT_BYTES) { + if (signal != null) { + signal.throwIfCanceled(); + } + if (listener != null) { + listener.onProgress(progress); + } + checkpoint = 0; } - out.close(); } + return progress; + } + + /** + * Requires both input and output to be a regular file. + */ + @VisibleForTesting + public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out, + ProgressListener listener, CancellationSignal signal, long count) + throws ErrnoException { + long progress = 0; + long checkpoint = 0; + + long t; + while ((t = Os.sendfile(out, in, null, Math.min(count, COPY_CHECKPOINT_BYTES))) != 0) { + progress += t; + checkpoint += t; + count -= t; + + if (checkpoint >= COPY_CHECKPOINT_BYTES) { + if (signal != null) { + signal.throwIfCanceled(); + } + if (listener != null) { + listener.onProgress(progress); + } + checkpoint = 0; + } + } + return progress; + } + + @VisibleForTesting + public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out, + ProgressListener listener, CancellationSignal signal, long count) throws IOException { + if (count != Long.MAX_VALUE) { + return copyInternalUserspace(new SizedInputStream(new FileInputStream(in), count), + new FileOutputStream(out), listener, signal); + } else { + return copyInternalUserspace(new FileInputStream(in), + new FileOutputStream(out), listener, signal); + } + } + + @VisibleForTesting + public static long copyInternalUserspace(InputStream in, OutputStream out, + ProgressListener listener, CancellationSignal signal) throws IOException { + long progress = 0; + long checkpoint = 0; + byte[] buffer = new byte[8192]; + + int t; + while ((t = in.read(buffer)) != -1) { + out.write(buffer, 0, t); + + progress += t; + checkpoint += t; + + if (checkpoint >= COPY_CHECKPOINT_BYTES) { + if (signal != null) { + signal.throwIfCanceled(); + } + if (listener != null) { + listener.onProgress(progress); + } + checkpoint = 0; + } + } + return progress; } /** @@ -797,4 +1040,69 @@ public class FileUtils { } return val * pow; } + + @VisibleForTesting + public static class MemoryPipe extends Thread implements AutoCloseable { + private final FileDescriptor[] pipe; + private final byte[] data; + private final boolean sink; + + private MemoryPipe(byte[] data, boolean sink) throws IOException { + try { + this.pipe = Os.pipe(); + } catch (ErrnoException e) { + throw e.rethrowAsIOException(); + } + this.data = data; + this.sink = sink; + } + + private MemoryPipe startInternal() { + super.start(); + return this; + } + + public static MemoryPipe createSource(byte[] data) throws IOException { + return new MemoryPipe(data, false).startInternal(); + } + + public static MemoryPipe createSink(byte[] data) throws IOException { + return new MemoryPipe(data, true).startInternal(); + } + + public FileDescriptor getFD() { + return sink ? pipe[1] : pipe[0]; + } + + public FileDescriptor getInternalFD() { + return sink ? pipe[0] : pipe[1]; + } + + @Override + public void run() { + final FileDescriptor fd = getInternalFD(); + try { + int i = 0; + while (i < data.length) { + if (sink) { + i += Os.read(fd, data, i, data.length - i); + } else { + i += Os.write(fd, data, i, data.length - i); + } + } + } catch (IOException | ErrnoException e) { + // Ignored + } finally { + if (sink) { + SystemClock.sleep(TimeUnit.SECONDS.toMillis(1)); + } + IoUtils.closeQuietly(fd); + } + } + + @Override + public void close() throws Exception { + IoUtils.closeQuietly(getFD()); + } + } } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index b2b43cfd6ddc..2093cec769da 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -393,8 +393,9 @@ public class UserManager { public static final String DISALLOW_CONFIG_VPN = "no_config_vpn"; /** - * Specifies if a user is disallowed from configuring location mode. Device owner and profile - * owners can set this restriction and it only applies on the managed user. + * Specifies if a user is disallowed from enabling or disabling location providers. As a + * result, user is disallowed from turning on or off location. Device owner and profile owners + * can set this restriction and it only applies on the managed user. * * <p>In a managed profile, location sharing is forced off when it's off on primary user, so * user can still turn off location sharing on managed profile when the restriction is set by @@ -408,11 +409,12 @@ public class UserManager { * * <p>Key for user restrictions. * <p>Type: Boolean + * @see android.location.LocationManager#isProviderEnabled(String) * @see DevicePolicyManager#addUserRestriction(ComponentName, String) * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) * @see #getUserRestrictions() */ - public static final String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode"; + public static final String DISALLOW_CONFIG_LOCATION = "no_config_location"; /** * Specifies if date, time and timezone configuring is disallowed. diff --git a/core/java/android/print/PrintFileDocumentAdapter.java b/core/java/android/print/PrintFileDocumentAdapter.java index 747400d486bf..a5f93050e307 100644 --- a/core/java/android/print/PrintFileDocumentAdapter.java +++ b/core/java/android/print/PrintFileDocumentAdapter.java @@ -21,6 +21,8 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.CancellationSignal; import android.os.CancellationSignal.OnCancelListener; +import android.os.FileUtils; +import android.os.OperationCanceledException; import android.os.ParcelFileDescriptor; import android.util.Log; @@ -114,28 +116,15 @@ public class PrintFileDocumentAdapter extends PrintDocumentAdapter { @Override protected Void doInBackground(Void... params) { - InputStream in = null; - OutputStream out = new FileOutputStream(mDestination.getFileDescriptor()); - final byte[] buffer = new byte[8192]; - try { - in = new FileInputStream(mFile); - while (true) { - if (isCancelled()) { - break; - } - final int readByteCount = in.read(buffer); - if (readByteCount < 0) { - break; - } - out.write(buffer, 0, readByteCount); - } - } catch (IOException ioe) { - Log.e(LOG_TAG, "Error writing data!", ioe); - mResultCallback.onWriteFailed(mContext.getString( - R.string.write_fail_reason_cannot_write)); - } finally { - IoUtils.closeQuietly(in); - IoUtils.closeQuietly(out); + try (InputStream in = new FileInputStream(mFile); + OutputStream out = new FileOutputStream(mDestination.getFileDescriptor())) { + FileUtils.copy(in, out, null, mCancellationSignal); + } catch (OperationCanceledException e) { + // Ignored; already handled below + } catch (IOException e) { + Log.e(LOG_TAG, "Error writing data!", e); + mResultCallback.onWriteFailed(mContext.getString( + R.string.write_fail_reason_cannot_write)); } return null; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1f0d683192d5..e3b8a109bbc7 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1903,7 +1903,7 @@ public final class Settings { cp.call(cr.getPackageName(), mCallSetCommand, name, arg); String newValue = getStringForUser(cr, name, userHandle); StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newValue, prevValue, tag, - makeDefault ? 1 : 0, userHandle); + makeDefault, userHandle); } catch (RemoteException e) { Log.w(TAG, "Can't set key " + name + " in " + mUri, e); return false; @@ -9315,17 +9315,6 @@ public final class Settings { private static final Validator WIFI_WAKEUP_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; /** - * Value to specify if Wi-Fi Wakeup is available. - * - * Wi-Fi Wakeup will only operate if it's available - * and {@link #WIFI_WAKEUP_ENABLED} is true. - * - * Type: int (0 for false, 1 for true) - * @hide - */ - public static final String WIFI_WAKEUP_AVAILABLE = "wifi_wakeup_available"; - - /** * Value to specify whether network quality scores and badging should be shown in the UI. * * Type: int (0 for false, 1 for true) @@ -10750,14 +10739,14 @@ public final class Settings { public static final String LOW_POWER_MODE = "low_power"; /** - * Battery level [1-99] at which low power mode automatically turns on. + * Battery level [1-100] at which low power mode automatically turns on. * If 0, it will not automatically turn on. * @hide */ public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level"; private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR = - new SettingsValidators.InclusiveIntegerRangeValidator(0, 99); + new SettingsValidators.InclusiveIntegerRangeValidator(0, 100); /** * If not 0, the activity manager will aggressively finish activities and diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java index f0b9fa2de5d2..65ce67fcba8f 100644 --- a/core/java/android/se/omapi/Channel.java +++ b/core/java/android/se/omapi/Channel.java @@ -25,6 +25,7 @@ package android.se.omapi; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.util.Log; import java.io.IOException; @@ -168,8 +169,10 @@ public class Channel { throw new IOException("Error in communicating with Secure Element"); } return response; - } catch (RemoteException e) { + } catch (ServiceSpecificException e) { throw new IOException(e.getMessage()); + } catch (RemoteException e) { + throw new IllegalStateException(e.getMessage()); } } } @@ -244,8 +247,10 @@ public class Channel { synchronized (mLock) { return mChannel.selectNext(); } - } catch (RemoteException e) { + } catch (ServiceSpecificException e) { throw new IOException(e.getMessage()); + } catch (RemoteException e) { + throw new IllegalStateException(e.getMessage()); } } } diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java index 9f1573973be4..3dec97631e9c 100644 --- a/core/java/android/se/omapi/Reader.java +++ b/core/java/android/se/omapi/Reader.java @@ -24,6 +24,7 @@ package android.se.omapi; import android.annotation.NonNull; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.util.Log; import java.io.IOException; @@ -45,8 +46,7 @@ public class Reader { private final Object mLock = new Object(); - Reader(SEService service, String name, ISecureElementReader reader) throws - IOException { + Reader(SEService service, String name, ISecureElementReader reader) { if (reader == null || service == null || name == null) { throw new IllegalArgumentException("Parameters cannot be null"); } @@ -96,8 +96,10 @@ public class Reader { ISecureElementSession session; try { session = mReader.openSession(); - } catch (RemoteException e) { + } catch (ServiceSpecificException e) { throw new IOException(e.getMessage()); + } catch (RemoteException e) { + throw new IllegalStateException(e.getMessage()); } if (session == null) { throw new IOException("service session is null."); diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java index 1e37277dcd6d..b8937e69c143 100644 --- a/core/java/android/se/omapi/SEService.java +++ b/core/java/android/se/omapi/SEService.java @@ -42,6 +42,23 @@ import java.util.HashMap; */ public class SEService { + /** + * Error code used with ServiceSpecificException. + * Thrown if there was an error communicating with the Secure Element. + * + * @hide + */ + public static final int IO_ERROR = 1; + + /** + * Error code used with ServiceSpecificException. + * Thrown if AID cannot be selected or is not available when opening + * a logical channel. + * + * @hide + */ + public static final int NO_SUCH_ELEMENT_ERROR = 2; + private static final String TAG = "OMAPI.SEService"; private final Object mLock = new Object(); diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java index bb2a0327ee0d..19a018ee01bb 100644 --- a/core/java/android/se/omapi/Session.java +++ b/core/java/android/se/omapi/Session.java @@ -25,6 +25,7 @@ package android.se.omapi; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.util.Log; import java.io.IOException; @@ -207,8 +208,16 @@ public class Session { return null; } return new Channel(mService, this, channel); + } catch (ServiceSpecificException e) { + if (e.errorCode == SEService.IO_ERROR) { + throw new IOException(e.getMessage()); + } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) { + throw new NoSuchElementException(e.getMessage()); + } else { + throw new IllegalStateException(e.getMessage()); + } } catch (RemoteException e) { - throw new IOException(e.getMessage()); + throw new IllegalStateException(e.getMessage()); } } } @@ -311,8 +320,16 @@ public class Session { return null; } return new Channel(mService, this, channel); + } catch (ServiceSpecificException e) { + if (e.errorCode == SEService.IO_ERROR) { + throw new IOException(e.getMessage()); + } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) { + throw new NoSuchElementException(e.getMessage()); + } else { + throw new IllegalStateException(e.getMessage()); + } } catch (RemoteException e) { - throw new IOException(e.getMessage()); + throw new IllegalStateException(e.getMessage()); } } } diff --git a/core/java/android/security/ConfirmationAlreadyPresentingException.java b/core/java/android/security/ConfirmationAlreadyPresentingException.java new file mode 100644 index 000000000000..ae4ec5a1bf45 --- /dev/null +++ b/core/java/android/security/ConfirmationAlreadyPresentingException.java @@ -0,0 +1,29 @@ +/* + * Copyright 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.security; + +/** + * This exception is thrown when presenting a prompt fails because another prompt is already + * being presented. + */ +public class ConfirmationAlreadyPresentingException extends Exception { + public ConfirmationAlreadyPresentingException() {} + + public ConfirmationAlreadyPresentingException(String message) { + super(message); + } +} diff --git a/core/java/android/security/ConfirmationCallback.java b/core/java/android/security/ConfirmationCallback.java new file mode 100644 index 000000000000..4670bce3a9e0 --- /dev/null +++ b/core/java/android/security/ConfirmationCallback.java @@ -0,0 +1,54 @@ +/* + * Copyright 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.security; + +import android.annotation.NonNull; + +/** + * Callback class used when signaling that a prompt is no longer being presented. + */ +public abstract class ConfirmationCallback { + /** + * Called when the requested prompt was accepted by the user. + * + * The format of 'dataThatWasConfirmed' parameter is a <a href="http://cbor.io/">CBOR</a> + * encoded map (type 5) with (at least) the keys <strong>prompt</strong> and + * <strong>extra</strong>. The keys are encoded as CBOR text string (type 3). The value of + * promptText is encoded as CBOR text string (type 3), and the value of extraData is encoded as + * CBOR byte string (type 2). Other keys may be added in the future. + * + * @param dataThatWasConfirmed the data that was confirmed, see above for the format. + */ + public void onConfirmedByUser(@NonNull byte[] dataThatWasConfirmed) {} + + /** + * Called when the requested prompt was dismissed (not accepted) by the user. + */ + public void onDismissedByUser() {} + + /** + * Called when the requested prompt was dismissed by the application. + */ + public void onDismissedByApplication() {} + + /** + * Called when the requested prompt was dismissed because of a low-level error. + * + * @param e an exception representing the error. + */ + public void onError(Exception e) {} +} diff --git a/core/java/android/security/ConfirmationDialog.java b/core/java/android/security/ConfirmationDialog.java new file mode 100644 index 000000000000..e9df3705db6e --- /dev/null +++ b/core/java/android/security/ConfirmationDialog.java @@ -0,0 +1,283 @@ +/* + * Copyright 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.security; + +import android.annotation.NonNull; +import android.content.Context; +import android.text.TextUtils; +import android.util.Log; + +import java.util.Locale; +import java.util.concurrent.Executor; + +/** + * Class used for displaying confirmation prompts. + * + * <p>Confirmation prompts are prompts shown to the user to confirm a given text and are + * implemented in a way that a positive response indicates with high confidence that the user has + * seen the given text, even if the Android framework (including the kernel) was + * compromised. Implementing confirmation prompts with these guarantees requires dedicated + * hardware-support and may not always be available. + * + * <p>Confirmation prompts are typically used with an external entitity - the <i>Relying Party</i> - + * in the following way. The setup steps are as follows: + * <ul> + * <li> Before first use, the application generates a key-pair with the + * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserConfirmationRequired + * CONFIRMATION tag} set. Device attestation, + * e.g. {@link java.security.KeyStore#getCertificateChain getCertificateChain()}, is used to + * generate a certificate chain that includes the public key (<code>Kpub</code> in the following) + * of the newly generated key. + * <li> The application sends <code>Kpub</code> and the certificate chain resulting from device + * attestation to the <i>Relying Party</i>. + * <li> The <i>Relying Party</i> validates the certificate chain which involves checking the root + * certificate is what is expected (e.g. a certificate from Google), each certificate signs the + * next one in the chain, ending with <code>Kpub</code>, and that the attestation certificate + * asserts that <code>Kpub</code> has the + * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserConfirmationRequired + * CONFIRMATION tag} set. + * Additionally the relying party stores <code>Kpub</code> and associates it with the device + * it was received from. + * </ul> + * + * <p>The <i>Relying Party</i> is typically an external device (for example connected via + * Bluetooth) or application server. + * + * <p>Before executing a transaction which requires a high assurance of user content, the + * application does the following: + * <ul> + * <li> The application gets a cryptographic nonce from the <i>Relying Party</i> and passes this as + * the <code>extraData</code> (via the Builder helper class) to the + * {@link #presentPrompt presentPrompt()} method. The <i>Relying Party</i> stores the nonce locally + * since it'll use it in a later step. + * <li> If the user approves the prompt a <i>Confirmation Response</i> is returned in the + * {@link ConfirmationCallback#onConfirmedByUser onConfirmedByUser(byte[])} callback as the + * <code>dataThatWasConfirmed</code> parameter. This blob contains the text that was shown to the + * user, the <code>extraData</code> parameter, and possibly other data. + * <li> The application signs the <i>Confirmation Response</i> with the previously created key and + * sends the blob and the signature to the <i>Relying Party</i>. + * <li> The <i>Relying Party</i> checks that the signature was made with <code>Kpub</code> and then + * extracts <code>promptText</code> matches what is expected and <code>extraData</code> matches the + * previously created nonce. If all checks passes, the transaction is executed. + * </ul> + * + * <p>A common way of implementing the "<code>promptText</code> is what is expected" check in the + * last bullet, is to have the <i>Relying Party</i> generate <code>promptText</code> and store it + * along the nonce in the <code>extraData</code> blob. + */ +public class ConfirmationDialog { + private static final String TAG = "ConfirmationDialog"; + + private CharSequence mPromptText; + private byte[] mExtraData; + private ConfirmationCallback mCallback; + private Executor mExecutor; + + private final KeyStore mKeyStore = KeyStore.getInstance(); + + private void doCallback(int responseCode, byte[] dataThatWasConfirmed, + ConfirmationCallback callback) { + switch (responseCode) { + case KeyStore.CONFIRMATIONUI_OK: + callback.onConfirmedByUser(dataThatWasConfirmed); + break; + + case KeyStore.CONFIRMATIONUI_CANCELED: + callback.onDismissedByUser(); + break; + + case KeyStore.CONFIRMATIONUI_ABORTED: + callback.onDismissedByApplication(); + break; + + case KeyStore.CONFIRMATIONUI_SYSTEM_ERROR: + callback.onError(new Exception("System error returned by ConfirmationUI.")); + break; + + default: + callback.onError(new Exception("Unexpected responseCode=" + responseCode + + " from onConfirmtionPromptCompleted() callback.")); + break; + } + } + + private final android.os.IBinder mCallbackBinder = + new android.security.IConfirmationPromptCallback.Stub() { + @Override + public void onConfirmationPromptCompleted( + int responseCode, final byte[] dataThatWasConfirmed) + throws android.os.RemoteException { + if (mCallback != null) { + ConfirmationCallback callback = mCallback; + Executor executor = mExecutor; + mCallback = null; + mExecutor = null; + if (executor == null) { + doCallback(responseCode, dataThatWasConfirmed, callback); + } else { + executor.execute(new Runnable() { + @Override + public void run() { + doCallback(responseCode, dataThatWasConfirmed, callback); + } + }); + } + } + } + }; + + /** + * A builder that collects arguments, to be shown on the system-provided confirmation dialog. + */ + public static class Builder { + + private CharSequence mPromptText; + private byte[] mExtraData; + + /** + * Creates a builder for the confirmation dialog. + */ + public Builder() { + } + + /** + * Sets the prompt text for the dialog. + * + * @param promptText the text to present in the prompt. + * @return the builder. + */ + public Builder setPromptText(CharSequence promptText) { + mPromptText = promptText; + return this; + } + + /** + * Sets the extra data for the dialog. + * + * @param extraData data to include in the response data. + * @return the builder. + */ + public Builder setExtraData(byte[] extraData) { + mExtraData = extraData; + return this; + } + + /** + * Creates a {@link ConfirmationDialog} with the arguments supplied to this builder. + * + * @param context the application context + * @return a {@link ConfirmationDialog} + * @throws IllegalArgumentException if any of the required fields are not set. + */ + public ConfirmationDialog build(Context context) { + if (TextUtils.isEmpty(mPromptText)) { + throw new IllegalArgumentException("prompt text must be set and non-empty"); + } + if (mExtraData == null) { + throw new IllegalArgumentException("extraData must be set"); + } + return new ConfirmationDialog(mPromptText, mExtraData); + } + } + + private ConfirmationDialog(CharSequence promptText, byte[] extraData) { + mPromptText = promptText; + mExtraData = extraData; + } + + /** + * Requests a confirmation prompt to be presented to the user. + * + * When the prompt is no longer being presented, one of the methods in + * {@link ConfirmationCallback} is called on the supplied callback object. + * + * @param executor the executor identifying the thread that will receive the callback. + * @param callback the callback to use when the dialog is done showing. + * @throws IllegalArgumentException if the prompt text is too long or malfomed. + * @throws ConfirmationAlreadyPresentingException if another prompt is being presented. + * @throws ConfirmationNotAvailableException if confirmation prompts are not supported. + */ + public void presentPrompt(@NonNull Executor executor, @NonNull ConfirmationCallback callback) + throws ConfirmationAlreadyPresentingException, + ConfirmationNotAvailableException { + if (mCallback != null) { + throw new ConfirmationAlreadyPresentingException(); + } + mCallback = callback; + mExecutor = executor; + + int uiOptionsAsFlags = 0; + // TODO: set AccessibilityInverted, AccessibilityMagnified in uiOptionsAsFlags as needed. + String locale = Locale.getDefault().toLanguageTag(); + int responseCode = mKeyStore.presentConfirmationPrompt( + mCallbackBinder, mPromptText.toString(), mExtraData, locale, uiOptionsAsFlags); + switch (responseCode) { + case KeyStore.CONFIRMATIONUI_OK: + return; + + case KeyStore.CONFIRMATIONUI_OPERATION_PENDING: + throw new ConfirmationAlreadyPresentingException(); + + case KeyStore.CONFIRMATIONUI_UNIMPLEMENTED: + throw new ConfirmationNotAvailableException(); + + case KeyStore.CONFIRMATIONUI_UIERROR: + throw new IllegalArgumentException(); + + default: + // Unexpected error code. + Log.w(TAG, + "Unexpected responseCode=" + responseCode + + " from presentConfirmationPrompt() call."); + throw new IllegalArgumentException(); + } + } + + /** + * Cancels a prompt currently being displayed. + * + * On success, the + * {@link ConfirmationCallback#onDismissedByApplication onDismissedByApplication()} method on + * the supplied callback object will be called asynchronously. + * + * @throws IllegalStateException if no prompt is currently being presented. + */ + public void cancelPrompt() { + int responseCode = mKeyStore.cancelConfirmationPrompt(mCallbackBinder); + if (responseCode == KeyStore.CONFIRMATIONUI_OK) { + return; + } else if (responseCode == KeyStore.CONFIRMATIONUI_OPERATION_PENDING) { + throw new IllegalStateException(); + } else { + // Unexpected error code. + Log.w(TAG, + "Unexpected responseCode=" + responseCode + + " from cancelConfirmationPrompt() call."); + throw new IllegalStateException(); + } + } + + /** + * Checks if the device supports confirmation prompts. + * + * @return true if confirmation prompts are supported by the device. + */ + public static boolean isSupported() { + // TODO: read and return system property. + return true; + } +} diff --git a/core/java/android/security/ConfirmationNotAvailableException.java b/core/java/android/security/ConfirmationNotAvailableException.java new file mode 100644 index 000000000000..8d0e672c0671 --- /dev/null +++ b/core/java/android/security/ConfirmationNotAvailableException.java @@ -0,0 +1,30 @@ +/* + * Copyright 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.security; + +/** + * This exception is thrown when presenting a prompt fails because the the environment lacks + * facilities for showing confirmations. + */ +public class ConfirmationNotAvailableException extends Exception { + public ConfirmationNotAvailableException() { + } + + public ConfirmationNotAvailableException(String message) { + super(message); + } +} diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 346437032845..1d1333504350 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -74,6 +74,7 @@ public final class KeymasterDefs { public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505; public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506; public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507; + public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508; public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600; public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601; diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java index 4e4a0374087e..7cd08f76a47e 100644 --- a/core/java/android/security/keystore/recovery/RecoveryController.java +++ b/core/java/android/security/keystore/recovery/RecoveryController.java @@ -26,9 +26,13 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; +import android.security.KeyStore; +import android.security.keystore.AndroidKeyStoreProvider; import com.android.internal.widget.ILockSettings; +import java.security.Key; +import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.List; @@ -113,9 +117,11 @@ public class RecoveryController { private final ILockSettings mBinder; + private final KeyStore mKeyStore; - private RecoveryController(ILockSettings binder) { + private RecoveryController(ILockSettings binder, KeyStore keystore) { mBinder = binder; + mKeyStore = keystore; } /** @@ -133,7 +139,7 @@ public class RecoveryController { public static RecoveryController getInstance(Context context) { ILockSettings lockSettings = ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings")); - return new RecoveryController(lockSettings); + return new RecoveryController(lockSettings, KeyStore.getInstance()); } /** @@ -430,6 +436,7 @@ public class RecoveryController { } /** + * Deprecated. * Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable * key store. Returns the raw material of the key. * @@ -444,7 +451,6 @@ public class RecoveryController { public byte[] generateAndStoreKey(@NonNull String alias, byte[] account) throws InternalRecoveryServiceException, LockScreenRequiredException { try { - // TODO: add account return mBinder.generateAndStoreKey(alias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -457,6 +463,72 @@ public class RecoveryController { } /** + * Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable + * key store. Returns {@link javax.crypto.SecretKey}. + * + * @param alias The key alias. + * @param account The account associated with the key. + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. + * @throws LockScreenRequiredException if the user has not set a lock screen. This is required + * to generate recoverable keys, as the snapshots are encrypted using a key derived from the + * lock screen. + * @hide + */ + public Key generateKey(@NonNull String alias, byte[] account) + throws InternalRecoveryServiceException, LockScreenRequiredException { + // TODO: update RecoverySession.recoverKeys + try { + String grantAlias = mBinder.generateKey(alias, account); + if (grantAlias == null) { + return null; + } + Key result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( + mKeyStore, + grantAlias, + KeyStore.UID_SELF); + return result; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (UnrecoverableKeyException e) { + throw new InternalRecoveryServiceException("Access to newly generated key failed for"); + } catch (ServiceSpecificException e) { + if (e.errorCode == ERROR_INSECURE_USER) { + throw new LockScreenRequiredException(e.getMessage()); + } + throw wrapUnexpectedServiceSpecificException(e); + } + } + + /** + * Gets a key called {@code alias} from the recoverable key store. + * + * @param alias The key alias. + * @return The key. + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. + * @throws UnrecoverableKeyException if key is permanently invalidated or not found. + * @hide + */ + public @Nullable Key getKey(@NonNull String alias) + throws InternalRecoveryServiceException, UnrecoverableKeyException { + try { + String grantAlias = mBinder.getKey(alias); + if (grantAlias == null) { + return null; + } + return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( + mKeyStore, + grantAlias, + KeyStore.UID_SELF); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (ServiceSpecificException e) { + throw wrapUnexpectedServiceSpecificException(e); + } + } + + /** * Removes a key called {@code alias} from the recoverable key store. * * @param alias The key alias. diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java index 917efa8bd5f4..12aa64e4ecb7 100644 --- a/core/java/android/service/autofill/AutofillService.java +++ b/core/java/android/service/autofill/AutofillService.java @@ -468,9 +468,8 @@ import com.android.internal.os.SomeArgs; * <p>Typically, field classification can be used to detect fields that can be autofilled with * user data that is not associated with a specific app—such as email and physical * address. Once the service identifies that a such field was manually filled by the user, the - * service could use this signal to improve its heuristics, either locally (i.e., in the same - * device) or globally (i.e., by crowdsourcing the results back to the service's server so it can - * be used by other users). + * service could use this signal to improve its heuristics on subsequent requests (for example, by + * infering which resource ids are associated with known fields). * * <p>The field classification workflow involves 4 steps: * @@ -481,8 +480,8 @@ import com.android.internal.os.SomeArgs; * <li>Identify which fields should be analysed by calling * {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)}. * <li>Verify the results through {@link FillEventHistory.Event#getFieldsClassification()}. - * <li>Use the results to dynamically create {@link Dataset} or {@link SaveInfo} objects in future - * requests. + * <li>Use the results to dynamically create {@link Dataset} or {@link SaveInfo} objects in + * subsequent requests. * </ol> * * <p>The field classification is an expensive operation and should be used carefully, otherwise it diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java index b8e8b19f9786..fb468a8dad6f 100644 --- a/core/java/android/service/autofill/CustomDescription.java +++ b/core/java/android/service/autofill/CustomDescription.java @@ -173,7 +173,10 @@ public final class CustomDescription implements Parcelable { } /** - * Updates the {@link RemoteViews presentation template} when a condition is satisfied. + * Updates the {@link RemoteViews presentation template} when a condition is satisfied by + * applying a series of remote view operations. This allows dynamic customization of the + * portion of the save UI that is controlled by the autofill service. Such dynamic + * customization is based on the content of target views. * * <p>The updates are applied in the sequence they are added, after the * {@link #addChild(int, Transformation) transformations} are applied to the children diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 266bcda7797a..f32dee1ec9f2 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -29,7 +29,6 @@ import android.widget.RemoteViews; import com.android.internal.util.Preconditions; -import java.io.Serializable; import java.util.ArrayList; import java.util.regex.Pattern; @@ -99,7 +98,7 @@ public final class Dataset implements Parcelable { private final ArrayList<AutofillId> mFieldIds; private final ArrayList<AutofillValue> mFieldValues; private final ArrayList<RemoteViews> mFieldPresentations; - private final ArrayList<Pattern> mFieldFilters; + private final ArrayList<DatasetFieldFilter> mFieldFilters; private final RemoteViews mPresentation; private final IntentSender mAuthentication; @Nullable String mId; @@ -132,7 +131,7 @@ public final class Dataset implements Parcelable { /** @hide */ @Nullable - public Pattern getFilter(int index) { + public DatasetFieldFilter getFilter(int index) { return mFieldFilters.get(index); } @@ -189,7 +188,7 @@ public final class Dataset implements Parcelable { private ArrayList<AutofillId> mFieldIds; private ArrayList<AutofillValue> mFieldValues; private ArrayList<RemoteViews> mFieldPresentations; - private ArrayList<Pattern> mFieldFilters; + private ArrayList<DatasetFieldFilter> mFieldFilters; private RemoteViews mPresentation; private IntentSender mAuthentication; private boolean mDestroyed; @@ -363,19 +362,21 @@ public final class Dataset implements Parcelable { * @param value the value to be autofilled. Pass {@code null} if you do not have the value * but the target view is a logical part of the dataset. For example, if * the dataset needs authentication and you have no access to the value. - * @param filter regex used to determine if the dataset should be shown in the autofill UI. + * @param filter regex used to determine if the dataset should be shown in the autofill UI; + * when {@code null}, it disables filtering on that dataset (this is the recommended + * approach when {@code value} is not {@code null} and field contains sensitive data + * such as passwords). * * @return this builder. * @throws IllegalStateException if the builder was constructed without a * {@link RemoteViews presentation}. */ public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value, - @NonNull Pattern filter) { + @Nullable Pattern filter) { throwIfDestroyed(); - Preconditions.checkNotNull(filter, "filter cannot be null"); Preconditions.checkState(mPresentation != null, "Dataset presentation not set on constructor"); - setLifeTheUniverseAndEverything(id, value, null, filter); + setLifeTheUniverseAndEverything(id, value, null, new DatasetFieldFilter(filter)); return this; } @@ -398,23 +399,26 @@ public final class Dataset implements Parcelable { * @param value the value to be autofilled. Pass {@code null} if you do not have the value * but the target view is a logical part of the dataset. For example, if * the dataset needs authentication and you have no access to the value. + * @param filter regex used to determine if the dataset should be shown in the autofill UI; + * when {@code null}, it disables filtering on that dataset (this is the recommended + * approach when {@code value} is not {@code null} and field contains sensitive data + * such as passwords). * @param presentation the presentation used to visualize this field. - * @param filter regex used to determine if the dataset should be shown in the autofill UI. * * @return this builder. */ public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value, - @NonNull Pattern filter, @NonNull RemoteViews presentation) { + @Nullable Pattern filter, @NonNull RemoteViews presentation) { throwIfDestroyed(); - Preconditions.checkNotNull(filter, "filter cannot be null"); Preconditions.checkNotNull(presentation, "presentation cannot be null"); - setLifeTheUniverseAndEverything(id, value, presentation, filter); + setLifeTheUniverseAndEverything(id, value, presentation, + new DatasetFieldFilter(filter)); return this; } private void setLifeTheUniverseAndEverything(@NonNull AutofillId id, @Nullable AutofillValue value, @Nullable RemoteViews presentation, - @Nullable Pattern filter) { + @Nullable DatasetFieldFilter filter) { Preconditions.checkNotNull(id, "id cannot be null"); if (mFieldIds != null) { final int existingIdx = mFieldIds.indexOf(id); @@ -477,8 +481,8 @@ public final class Dataset implements Parcelable { parcel.writeParcelable(mPresentation, flags); parcel.writeTypedList(mFieldIds, flags); parcel.writeTypedList(mFieldValues, flags); - parcel.writeParcelableList(mFieldPresentations, flags); - parcel.writeSerializable(mFieldFilters); + parcel.writeTypedList(mFieldPresentations, flags); + parcel.writeTypedList(mFieldFilters, flags); parcel.writeParcelable(mAuthentication, flags); parcel.writeString(mId); } @@ -493,22 +497,19 @@ public final class Dataset implements Parcelable { final Builder builder = (presentation == null) ? new Builder() : new Builder(presentation); - final ArrayList<AutofillId> ids = parcel.createTypedArrayList(AutofillId.CREATOR); + final ArrayList<AutofillId> ids = + parcel.createTypedArrayList(AutofillId.CREATOR); final ArrayList<AutofillValue> values = parcel.createTypedArrayList(AutofillValue.CREATOR); - final ArrayList<RemoteViews> presentations = new ArrayList<>(); - parcel.readParcelableList(presentations, null); - @SuppressWarnings("unchecked") - final ArrayList<Serializable> filters = - (ArrayList<Serializable>) parcel.readSerializable(); - final int idCount = (ids != null) ? ids.size() : 0; - final int valueCount = (values != null) ? values.size() : 0; - for (int i = 0; i < idCount; i++) { + final ArrayList<RemoteViews> presentations = + parcel.createTypedArrayList(RemoteViews.CREATOR); + final ArrayList<DatasetFieldFilter> filters = + parcel.createTypedArrayList(DatasetFieldFilter.CREATOR); + for (int i = 0; i < ids.size(); i++) { final AutofillId id = ids.get(i); - final AutofillValue value = (valueCount > i) ? values.get(i) : null; - final RemoteViews fieldPresentation = presentations.isEmpty() ? null - : presentations.get(i); - final Pattern filter = (Pattern) filters.get(i); + final AutofillValue value = values.get(i); + final RemoteViews fieldPresentation = presentations.get(i); + final DatasetFieldFilter filter = filters.get(i); builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, filter); } builder.setAuthentication(parcel.readParcelable(null)); @@ -521,4 +522,55 @@ public final class Dataset implements Parcelable { return new Dataset[size]; } }; + + /** + * Helper class used to indicate when the service explicitly set a {@link Pattern} filter for a + * dataset field‐ we cannot use a {@link Pattern} directly because then we wouldn't be + * able to differentiate whether the service explicitly passed a {@code null} filter to disable + * filter, or when it called the methods that does not take a filter {@link Pattern}. + * + * @hide + */ + public static final class DatasetFieldFilter implements Parcelable { + + @Nullable + public final Pattern pattern; + + private DatasetFieldFilter(@Nullable Pattern pattern) { + this.pattern = pattern; + } + + @Override + public String toString() { + if (!sDebug) return super.toString(); + + // Cannot log pattern because it could contain PII + return pattern == null ? "null" : pattern.pattern().length() + "_chars"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeSerializable(pattern); + } + + @SuppressWarnings("hiding") + public static final Creator<DatasetFieldFilter> CREATOR = + new Creator<DatasetFieldFilter>() { + + @Override + public DatasetFieldFilter createFromParcel(Parcel parcel) { + return new DatasetFieldFilter((Pattern) parcel.readSerializable()); + } + + @Override + public DatasetFieldFilter[] newArray(int size) { + return new DatasetFieldFilter[size]; + } + }; + } } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 23ae4b96810f..d66322c3aa89 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -17,6 +17,7 @@ package android.service.notification; import android.app.ActivityManager; +import android.app.AlarmManager; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.content.ComponentName; @@ -1445,4 +1446,99 @@ public class ZenModeConfig implements Parcelable { return !config.allowAlarms && !config.allowMediaSystemOther && areAllPriorityOnlyNotificationZenSoundsMuted(config); } + + /** + * Returns a description of the current do not disturb settings from config. + * - If turned on manually and end time is known, returns end time. + * - If turned on by an automatic rule, returns the automatic rule name. + * - If on due to an app, returns the app name. + * - If there's a combination of rules/apps that trigger, then shows the one that will + * last the longest if applicable. + * @return null if do not disturb is off. + */ + public static String getDescription(Context context, boolean zenOn, ZenModeConfig config) { + if (!zenOn) { + return null; + } + + String secondaryText = ""; + long latestEndTime = -1; + + // DND turned on by manual rule + if (config.manualRule != null) { + final Uri id = config.manualRule.conditionId; + if (config.manualRule.enabler != null) { + // app triggered manual rule + String appName = getOwnerCaption(context, config.manualRule.enabler); + if (!appName.isEmpty()) { + secondaryText = appName; + } + } else { + if (id == null) { + // Do not disturb manually triggered to remain on forever until turned off + // No subtext + return null; + } else { + latestEndTime = tryParseCountdownConditionId(id); + if (latestEndTime > 0) { + final CharSequence formattedTime = getFormattedTime(context, + latestEndTime, isToday(latestEndTime), + context.getUserId()); + secondaryText = context.getString(R.string.zen_mode_until, formattedTime); + } + } + } + } + + // DND turned on by an automatic rule + for (ZenRule automaticRule : config.automaticRules.values()) { + if (automaticRule.isAutomaticActive()) { + if (isValidEventConditionId(automaticRule.conditionId) + || isValidScheduleConditionId(automaticRule.conditionId)) { + // set text if automatic rule end time is the latest active rule end time + long endTime = parseAutomaticRuleEndTime(context, automaticRule.conditionId); + if (endTime > latestEndTime) { + latestEndTime = endTime; + secondaryText = automaticRule.name; + } + } else { + // set text if 3rd party rule + return automaticRule.name; + } + } + } + + return !secondaryText.equals("") ? secondaryText : null; + } + + private static long parseAutomaticRuleEndTime(Context context, Uri id) { + if (isValidEventConditionId(id)) { + // cannot look up end times for events + return Long.MAX_VALUE; + } + + if (isValidScheduleConditionId(id)) { + ScheduleCalendar schedule = toScheduleCalendar(id); + long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis()); + + // check if automatic rule will end on next alarm + if (schedule.exitAtAlarm()) { + long nextAlarm = getNextAlarm(context); + schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm); + if (schedule.shouldExitForAlarm(endTimeMs)) { + return nextAlarm; + } + } + + return endTimeMs; + } + + return -1; + } + + private static long getNextAlarm(Context context) { + final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + final AlarmManager.AlarmClockInfo info = alarms.getNextAlarmClock(context.getUserId()); + return info != null ? info.getTriggerTime() : 0; + } } diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java index 11e1e674a747..e97f963a0986 100644 --- a/core/java/android/service/settings/suggestions/Suggestion.java +++ b/core/java/android/service/settings/suggestions/Suggestion.java @@ -40,6 +40,7 @@ public final class Suggestion implements Parcelable { */ @IntDef(flag = true, prefix = { "FLAG_" }, value = { FLAG_HAS_BUTTON, + FLAG_ICON_TINTABLE, }) @Retention(RetentionPolicy.SOURCE) public @interface Flags { @@ -49,6 +50,10 @@ public final class Suggestion implements Parcelable { * Flag for suggestion type with a single button */ public static final int FLAG_HAS_BUTTON = 1 << 0; + /** + * @hide + */ + public static final int FLAG_ICON_TINTABLE = 1 << 1; private final String mId; private final CharSequence mTitle; diff --git a/core/java/android/text/style/DrawableMarginSpan.java b/core/java/android/text/style/DrawableMarginSpan.java index 35241796c3c3..cd199b3547c9 100644 --- a/core/java/android/text/style/DrawableMarginSpan.java +++ b/core/java/android/text/style/DrawableMarginSpan.java @@ -16,60 +16,100 @@ package android.text.style; +import android.annotation.NonNull; +import android.annotation.Px; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.text.Layout; import android.text.Spanned; -public class DrawableMarginSpan -implements LeadingMarginSpan, LineHeightSpan -{ - public DrawableMarginSpan(Drawable b) { - mDrawable = b; +/** + * A span which adds a drawable and a padding to the paragraph it's attached to. + * <p> + * If the height of the drawable is bigger than the height of the line it's attached to then the + * line height is increased to fit the drawable. <code>DrawableMarginSpan</code> allows setting a + * padding between the drawable and the text. The default value is 0. The span must be set from the + * beginning of the text, otherwise either the span won't be rendered or it will be rendered + * incorrectly. + * <p> + * For example, a drawable and a padding of 20px can be added like this: + * <pre>{@code SpannableString string = new SpannableString("Text with a drawable."); + * string.setSpan(new DrawableMarginSpan(drawable, 20), 0, string.length(), + * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre> + * <img src="{@docRoot}reference/android/images/text/style/drawablemarginspan.png" /> + * <figcaption>Text with a drawable and a padding.</figcaption> + * <p> + * + * @see IconMarginSpan for working with a {@link android.graphics.Bitmap} instead of + * a {@link Drawable}. + */ +public class DrawableMarginSpan implements LeadingMarginSpan, LineHeightSpan { + private static final int STANDARD_PAD_WIDTH = 0; + + @NonNull + private final Drawable mDrawable; + @Px + private final int mPad; + + /** + * Creates a {@link DrawableMarginSpan} from a {@link Drawable}. The pad width will be 0. + * + * @param drawable the drawable to be added + */ + public DrawableMarginSpan(@NonNull Drawable drawable) { + this(drawable, STANDARD_PAD_WIDTH); } - public DrawableMarginSpan(Drawable b, int pad) { - mDrawable = b; + /** + * Creates a {@link DrawableMarginSpan} from a {@link Drawable} and a padding, in pixels. + * + * @param drawable the drawable to be added + * @param pad the distance between the drawable and the text + */ + public DrawableMarginSpan(@NonNull Drawable drawable, int pad) { + mDrawable = drawable; mPad = pad; } + @Override public int getLeadingMargin(boolean first) { return mDrawable.getIntrinsicWidth() + mPad; } - public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, - int top, int baseline, int bottom, - CharSequence text, int start, int end, - boolean first, Layout layout) { + @Override + public void drawLeadingMargin(@NonNull Canvas c, @NonNull Paint p, int x, int dir, + int top, int baseline, int bottom, + @NonNull CharSequence text, int start, int end, + boolean first, @NonNull Layout layout) { int st = ((Spanned) text).getSpanStart(this); - int ix = (int)x; - int itop = (int)layout.getLineTop(layout.getLineForOffset(st)); + int ix = (int) x; + int itop = (int) layout.getLineTop(layout.getLineForOffset(st)); int dw = mDrawable.getIntrinsicWidth(); int dh = mDrawable.getIntrinsicHeight(); // XXX What to do about Paint? - mDrawable.setBounds(ix, itop, ix+dw, itop+dh); + mDrawable.setBounds(ix, itop, ix + dw, itop + dh); mDrawable.draw(c); } - public void chooseHeight(CharSequence text, int start, int end, - int istartv, int v, - Paint.FontMetricsInt fm) { + @Override + public void chooseHeight(@NonNull CharSequence text, int start, int end, + int istartv, int v, + @NonNull Paint.FontMetricsInt fm) { if (end == ((Spanned) text).getSpanEnd(this)) { int ht = mDrawable.getIntrinsicHeight(); int need = ht - (v + fm.descent - fm.ascent - istartv); - if (need > 0) + if (need > 0) { fm.descent += need; + } need = ht - (v + fm.bottom - fm.top - istartv); - if (need > 0) + if (need > 0) { fm.bottom += need; + } } } - - private Drawable mDrawable; - private int mPad; } diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java index 5b8a6dd3bf6f..1b16f3345bfa 100644 --- a/core/java/android/text/style/DynamicDrawableSpan.java +++ b/core/java/android/text/style/DynamicDrawableSpan.java @@ -16,6 +16,9 @@ package android.text.style; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; @@ -24,32 +27,71 @@ import android.graphics.drawable.Drawable; import java.lang.ref.WeakReference; /** + * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with + * the bottom or with the baseline of the surrounding text. + * <p> + * For an implementation that constructs the drawable from various sources (<code>Bitmap</code>, + * <code>Drawable</code>, resource id or <code>Uri</code>) use {@link ImageSpan}. + * <p> + * A simple implementation of <code>DynamicDrawableSpan</code> that uses drawables from resources + * looks like this: + * <pre> + * class MyDynamicDrawableSpan extends DynamicDrawableSpan { * + * private final Context mContext; + * private final int mResourceId; + * + * public MyDynamicDrawableSpan(Context context, @DrawableRes int resourceId) { + * mContext = context; + * mResourceId = resourceId; + * } + * + * {@literal @}Override + * public Drawable getDrawable() { + * Drawable drawable = mContext.getDrawable(mResourceId); + * drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + * return drawable; + * } + * }</pre> + * The class can be used like this: + * <pre> + * SpannableString string = new SpannableString("Text with a drawable span"); + * string.setSpan(new MyDynamicDrawableSpan(context, R.mipmap.ic_launcher), 12, 20, Spanned + * .SPAN_EXCLUSIVE_EXCLUSIVE);</pre> + * <img src="{@docRoot}reference/android/images/text/style/dynamicdrawablespan.png" /> + * <figcaption>Replacing text with a drawable.</figcaption> */ public abstract class DynamicDrawableSpan extends ReplacementSpan { - private static final String TAG = "DynamicDrawableSpan"; - + /** * A constant indicating that the bottom of this span should be aligned * with the bottom of the surrounding text, i.e., at the same level as the * lowest descender in the text. */ public static final int ALIGN_BOTTOM = 0; - + /** * A constant indicating that the bottom of this span should be aligned * with the baseline of the surrounding text. */ public static final int ALIGN_BASELINE = 1; - + protected final int mVerticalAlignment; - + + private WeakReference<Drawable> mDrawableRef; + + /** + * Creates a {@link DynamicDrawableSpan}. The default vertical alignment is + * {@link #ALIGN_BOTTOM} + */ public DynamicDrawableSpan() { mVerticalAlignment = ALIGN_BOTTOM; } /** - * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}. + * Creates a {@link DynamicDrawableSpan} based on a vertical alignment.\ + * + * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE} */ protected DynamicDrawableSpan(int verticalAlignment) { mVerticalAlignment = verticalAlignment; @@ -64,22 +106,22 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan { } /** - * Your subclass must implement this method to provide the bitmap + * Your subclass must implement this method to provide the bitmap * to be drawn. The dimensions of the bitmap must be the same * from each call to the next. */ public abstract Drawable getDrawable(); @Override - public int getSize(Paint paint, CharSequence text, - int start, int end, - Paint.FontMetricsInt fm) { + public int getSize(@NonNull Paint paint, CharSequence text, + @IntRange(from = 0) int start, @IntRange(from = 0) int end, + @Nullable Paint.FontMetricsInt fm) { Drawable d = getCachedDrawable(); Rect rect = d.getBounds(); if (fm != null) { - fm.ascent = -rect.bottom; - fm.descent = 0; + fm.ascent = -rect.bottom; + fm.descent = 0; fm.top = fm.ascent; fm.bottom = 0; @@ -89,12 +131,12 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan { } @Override - public void draw(Canvas canvas, CharSequence text, - int start, int end, float x, - int top, int y, int bottom, Paint paint) { + public void draw(@NonNull Canvas canvas, CharSequence text, + @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x, + int top, int y, int bottom, @NonNull Paint paint) { Drawable b = getCachedDrawable(); canvas.save(); - + int transY = bottom - b.getBounds().bottom; if (mVerticalAlignment == ALIGN_BASELINE) { transY -= paint.getFontMetricsInt().descent; @@ -109,8 +151,9 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan { WeakReference<Drawable> wr = mDrawableRef; Drawable d = null; - if (wr != null) + if (wr != null) { d = wr.get(); + } if (d == null) { d = getDrawable(); @@ -119,7 +162,5 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan { return d; } - - private WeakReference<Drawable> mDrawableRef; } diff --git a/core/java/android/text/style/IconMarginSpan.java b/core/java/android/text/style/IconMarginSpan.java index 304c83f19f02..ad78bd574696 100644 --- a/core/java/android/text/style/IconMarginSpan.java +++ b/core/java/android/text/style/IconMarginSpan.java @@ -16,57 +16,98 @@ package android.text.style; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Px; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.text.Layout; import android.text.Spanned; -public class IconMarginSpan -implements LeadingMarginSpan, LineHeightSpan -{ - public IconMarginSpan(Bitmap b) { - mBitmap = b; +/** + * Paragraph affecting span, that draws a bitmap at the beginning of a text. The span also allows + * setting a padding between the bitmap and the text. The default value of the padding is 0px. The + * span should be attached from the first character of the text. + * <p> + * For example, an <code>IconMarginSpan</code> with a bitmap and a padding of 30px can be set + * like this: + * <pre> + * SpannableString string = new SpannableString("Text with icon and padding"); + * string.setSpan(new IconMarginSpan(bitmap, 30), 0, string.length(), + * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + * </pre> + * <img src="{@docRoot}reference/android/images/text/style/iconmarginspan.png" /> + * <figcaption>Text with <code>IconMarginSpan</code></figcaption> + * <p> + * + * @see DrawableMarginSpan for working with a {@link android.graphics.drawable.Drawable} instead of + * a {@link Bitmap}. + */ +public class IconMarginSpan implements LeadingMarginSpan, LineHeightSpan { + + @NonNull + private final Bitmap mBitmap; + @Px + private final int mPad; + + /** + * Creates an {@link IconMarginSpan} from a {@link Bitmap}. + * + * @param bitmap bitmap to be rendered at the beginning of the text + */ + public IconMarginSpan(@NonNull Bitmap bitmap) { + this(bitmap, 0); } - public IconMarginSpan(Bitmap b, int pad) { - mBitmap = b; + /** + * Creates an {@link IconMarginSpan} from a {@link Bitmap}. + * + * @param bitmap bitmap to be rendered at the beginning of the text + * @param pad padding width, in pixels, between the bitmap and the text + */ + public IconMarginSpan(@NonNull Bitmap bitmap, @IntRange(from = 0) int pad) { + mBitmap = bitmap; mPad = pad; } + @Override public int getLeadingMargin(boolean first) { return mBitmap.getWidth() + mPad; } + @Override public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, - int top, int baseline, int bottom, - CharSequence text, int start, int end, - boolean first, Layout layout) { + int top, int baseline, int bottom, + CharSequence text, int start, int end, + boolean first, Layout layout) { int st = ((Spanned) text).getSpanStart(this); int itop = layout.getLineTop(layout.getLineForOffset(st)); - if (dir < 0) + if (dir < 0) { x -= mBitmap.getWidth(); + } c.drawBitmap(mBitmap, x, itop, p); } + @Override public void chooseHeight(CharSequence text, int start, int end, - int istartv, int v, - Paint.FontMetricsInt fm) { + int istartv, int v, + Paint.FontMetricsInt fm) { if (end == ((Spanned) text).getSpanEnd(this)) { int ht = mBitmap.getHeight(); int need = ht - (v + fm.descent - fm.ascent - istartv); - if (need > 0) + if (need > 0) { fm.descent += need; + } need = ht - (v + fm.bottom - fm.top - istartv); - if (need > 0) + if (need > 0) { fm.bottom += need; + } } } - private Bitmap mBitmap; - private int mPad; } diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java index b0bff680f390..95f0b43341a2 100644 --- a/core/java/android/text/style/ImageSpan.java +++ b/core/java/android/text/style/ImageSpan.java @@ -17,6 +17,8 @@ package android.text.style; import android.annotation.DrawableRes; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -27,18 +29,49 @@ import android.util.Log; import java.io.InputStream; +/** + * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with + * the bottom or with the baseline of the surrounding text. The drawable can be constructed from + * varied sources: + * <ul> + * <li>{@link Bitmap} - see {@link #ImageSpan(Context, Bitmap)} and + * {@link #ImageSpan(Context, Bitmap, int)} + * </li> + * <li>{@link Drawable} - see {@link #ImageSpan(Drawable, int)}</li> + * <li>resource id - see {@link #ImageSpan(Context, int, int)}</li> + * <li>{@link Uri} - see {@link #ImageSpan(Context, Uri, int)}</li> + * </ul> + * The default value for the vertical alignment is {@link DynamicDrawableSpan#ALIGN_BOTTOM} + * <p> + * For example, an <code>ImagedSpan</code> can be used like this: + * <pre> + * SpannableString string = SpannableString("Bottom: span.\nBaseline: span."); + * // using the default alignment: ALIGN_BOTTOM + * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher), 7, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher, DynamicDrawableSpan.ALIGN_BASELINE), + * 22, 23, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + * </pre> + * <img src="{@docRoot}reference/android/images/text/style/imagespan.png" /> + * <figcaption>Text with <code>ImageSpan</code>s aligned bottom and baseline.</figcaption> + */ public class ImageSpan extends DynamicDrawableSpan { + + @Nullable private Drawable mDrawable; + @Nullable private Uri mContentUri; + @DrawableRes private int mResourceId; + @Nullable private Context mContext; + @Nullable private String mSource; /** * @deprecated Use {@link #ImageSpan(Context, Bitmap)} instead. */ @Deprecated - public ImageSpan(Bitmap b) { + public ImageSpan(@NonNull Bitmap b) { this(null, b, ALIGN_BOTTOM); } @@ -46,80 +79,143 @@ public class ImageSpan extends DynamicDrawableSpan { * @deprecated Use {@link #ImageSpan(Context, Bitmap, int)} instead. */ @Deprecated - public ImageSpan(Bitmap b, int verticalAlignment) { + public ImageSpan(@NonNull Bitmap b, int verticalAlignment) { this(null, b, verticalAlignment); } - public ImageSpan(Context context, Bitmap b) { - this(context, b, ALIGN_BOTTOM); + /** + * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Bitmap} with the default + * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM} + * + * @param context context used to create a drawable from {@param bitmap} based on the display + * metrics of the resources + * @param bitmap bitmap to be rendered + */ + public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap) { + this(context, bitmap, ALIGN_BOTTOM); } /** + * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Bitmap} and a vertical + * alignment. + * + * @param context context used to create a drawable from {@param bitmap} based on + * the display metrics of the resources + * @param bitmap bitmap to be rendered * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or - * {@link DynamicDrawableSpan#ALIGN_BASELINE}. + * {@link DynamicDrawableSpan#ALIGN_BASELINE} */ - public ImageSpan(Context context, Bitmap b, int verticalAlignment) { + public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap, int verticalAlignment) { super(verticalAlignment); mContext = context; mDrawable = context != null - ? new BitmapDrawable(context.getResources(), b) - : new BitmapDrawable(b); + ? new BitmapDrawable(context.getResources(), bitmap) + : new BitmapDrawable(bitmap); int width = mDrawable.getIntrinsicWidth(); int height = mDrawable.getIntrinsicHeight(); - mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0); + mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0); } - public ImageSpan(Drawable d) { - this(d, ALIGN_BOTTOM); + /** + * Constructs an {@link ImageSpan} from a drawable with the default + * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}. + * + * @param drawable drawable to be rendered + */ + public ImageSpan(@NonNull Drawable drawable) { + this(drawable, ALIGN_BOTTOM); } /** + * Constructs an {@link ImageSpan} from a drawable and a vertical alignment. + * + * @param drawable drawable to be rendered * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or - * {@link DynamicDrawableSpan#ALIGN_BASELINE}. + * {@link DynamicDrawableSpan#ALIGN_BASELINE} */ - public ImageSpan(Drawable d, int verticalAlignment) { + public ImageSpan(@NonNull Drawable drawable, int verticalAlignment) { super(verticalAlignment); - mDrawable = d; + mDrawable = drawable; } - public ImageSpan(Drawable d, String source) { - this(d, source, ALIGN_BOTTOM); + /** + * Constructs an {@link ImageSpan} from a drawable and a source with the default + * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM} + * + * @param drawable drawable to be rendered + * @param source drawable's Uri source + */ + public ImageSpan(@NonNull Drawable drawable, @NonNull String source) { + this(drawable, source, ALIGN_BOTTOM); } /** + * Constructs an {@link ImageSpan} from a drawable, a source and a vertical alignment. + * + * @param drawable drawable to be rendered + * @param source drawable's uri source * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or - * {@link DynamicDrawableSpan#ALIGN_BASELINE}. + * {@link DynamicDrawableSpan#ALIGN_BASELINE} */ - public ImageSpan(Drawable d, String source, int verticalAlignment) { + public ImageSpan(@NonNull Drawable drawable, @NonNull String source, int verticalAlignment) { super(verticalAlignment); - mDrawable = d; + mDrawable = drawable; mSource = source; } - public ImageSpan(Context context, Uri uri) { + /** + * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Uri} with the default + * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}. The Uri source can be retrieved via + * {@link #getSource()} + * + * @param context context used to create a drawable from {@param bitmap} based on the display + * metrics of the resources + * @param uri {@link Uri} used to construct the drawable that will be rendered + */ + public ImageSpan(@NonNull Context context, @NonNull Uri uri) { this(context, uri, ALIGN_BOTTOM); } /** + * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Uri} and a vertical + * alignment. The Uri source can be retrieved via {@link #getSource()} + * + * @param context context used to create a drawable from {@param bitmap} based on + * the display + * metrics of the resources + * @param uri {@link Uri} used to construct the drawable that will be rendered. * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or - * {@link DynamicDrawableSpan#ALIGN_BASELINE}. + * {@link DynamicDrawableSpan#ALIGN_BASELINE} */ - public ImageSpan(Context context, Uri uri, int verticalAlignment) { + public ImageSpan(@NonNull Context context, @NonNull Uri uri, int verticalAlignment) { super(verticalAlignment); mContext = context; mContentUri = uri; mSource = uri.toString(); } - public ImageSpan(Context context, @DrawableRes int resourceId) { + /** + * Constructs an {@link ImageSpan} from a {@link Context} and a resource id with the default + * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM} + * + * @param context context used to retrieve the drawable from resources + * @param resourceId drawable resource id based on which the drawable is retrieved + */ + public ImageSpan(@NonNull Context context, @DrawableRes int resourceId) { this(context, resourceId, ALIGN_BOTTOM); } /** + * Constructs an {@link ImageSpan} from a {@link Context}, a resource id and a vertical + * alignment. + * + * @param context context used to retrieve the drawable from resources + * @param resourceId drawable resource id based on which the drawable is retrieved. * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or - * {@link DynamicDrawableSpan#ALIGN_BASELINE}. + * {@link DynamicDrawableSpan#ALIGN_BASELINE} */ - public ImageSpan(Context context, @DrawableRes int resourceId, int verticalAlignment) { + public ImageSpan(@NonNull Context context, @DrawableRes int resourceId, + int verticalAlignment) { super(verticalAlignment); mContext = context; mResourceId = resourceId; @@ -128,10 +224,10 @@ public class ImageSpan extends DynamicDrawableSpan { @Override public Drawable getDrawable() { Drawable drawable = null; - + if (mDrawable != null) { drawable = mDrawable; - } else if (mContentUri != null) { + } else if (mContentUri != null) { Bitmap bitmap = null; try { InputStream is = mContext.getContentResolver().openInputStream( @@ -142,7 +238,7 @@ public class ImageSpan extends DynamicDrawableSpan { drawable.getIntrinsicHeight()); is.close(); } catch (Exception e) { - Log.e("sms", "Failed to loaded content " + mContentUri, e); + Log.e("ImageSpan", "Failed to loaded content " + mContentUri, e); } } else { try { @@ -150,8 +246,8 @@ public class ImageSpan extends DynamicDrawableSpan { drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); } catch (Exception e) { - Log.e("sms", "Unable to find resource: " + mResourceId); - } + Log.e("ImageSpan", "Unable to find resource: " + mResourceId); + } } return drawable; @@ -159,9 +255,12 @@ public class ImageSpan extends DynamicDrawableSpan { /** * Returns the source string that was saved during construction. + * + * @return the source string that was saved during construction + * @see #ImageSpan(Drawable, String) and this{@link #ImageSpan(Context, Uri)} */ + @Nullable public String getSource() { return mSource; } - } diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java index 1ebee82c18a8..50ee5f387595 100644 --- a/core/java/android/text/style/LineHeightSpan.java +++ b/core/java/android/text/style/LineHeightSpan.java @@ -19,16 +19,42 @@ package android.text.style; import android.graphics.Paint; import android.text.TextPaint; -public interface LineHeightSpan -extends ParagraphStyle, WrapTogetherSpan -{ +/** + * The classes that affect the height of the line should implement this interface. + */ +public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan { + /** + * Classes that implement this should define how the height is being calculated. + * + * @param text the text + * @param start the start of the line + * @param end the end of the line + * @param spanstartv the start of the span + * @param lineHeight the line height + * @param fm font metrics of the paint, in integers + */ public void chooseHeight(CharSequence text, int start, int end, - int spanstartv, int v, - Paint.FontMetricsInt fm); + int spanstartv, int lineHeight, + Paint.FontMetricsInt fm); + /** + * The classes that affect the height of the line with respect to density, should implement this + * interface. + */ public interface WithDensity extends LineHeightSpan { + + /** + * Classes that implement this should define how the height is being calculated. + * + * @param text the text + * @param start the start of the line + * @param end the end of the line + * @param spanstartv the start of the span + * @param lineHeight the line height + * @param paint the paint + */ public void chooseHeight(CharSequence text, int start, int end, - int spanstartv, int v, - Paint.FontMetricsInt fm, TextPaint paint); + int spanstartv, int lineHeight, + Paint.FontMetricsInt fm, TextPaint paint); } } diff --git a/core/java/android/text/style/MaskFilterSpan.java b/core/java/android/text/style/MaskFilterSpan.java index 2ff52a8f70e8..d76ef941d42e 100644 --- a/core/java/android/text/style/MaskFilterSpan.java +++ b/core/java/android/text/style/MaskFilterSpan.java @@ -18,15 +18,36 @@ package android.text.style; import android.graphics.MaskFilter; import android.text.TextPaint; - +/** + * Span that allows setting a {@link MaskFilter} to the text it's attached to. + * <p> + * For example, to blur a text, a {@link android.graphics.BlurMaskFilter} can be used: + * <pre> + * MaskFilter blurMask = new BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL); + * SpannableString string = new SpannableString("Text with blur mask"); + * string.setSpan(new MaskFilterSpan(blurMask), 10, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + * </pre> + * <img src="{@docRoot}reference/android/images/text/style/maskfilterspan.png" /> + * <figcaption>Text blurred with the <code>MaskFilterSpan</code>.</figcaption> + */ public class MaskFilterSpan extends CharacterStyle implements UpdateAppearance { private MaskFilter mFilter; + /** + * Creates a {@link MaskFilterSpan} from a {@link MaskFilter}. + * + * @param filter the filter to be applied to the <code>TextPaint</code> + */ public MaskFilterSpan(MaskFilter filter) { mFilter = filter; } + /** + * Return the mask filter for this span. + * + * @return the mask filter for this span + */ public MaskFilter getMaskFilter() { return mFilter; } diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java index f900db502d0d..bdfa700215f8 100644 --- a/core/java/android/text/style/StyleSpan.java +++ b/core/java/android/text/style/StyleSpan.java @@ -16,6 +16,7 @@ package android.text.style; +import android.annotation.NonNull; import android.graphics.Paint; import android.graphics.Typeface; import android.os.Parcel; @@ -24,55 +25,76 @@ import android.text.TextPaint; import android.text.TextUtils; /** - * - * Describes a style in a span. + * Span that allows setting the style of the text it's attached to. + * Possible styles are: {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC} and + * {@link Typeface#BOLD_ITALIC}. + * <p> * Note that styles are cumulative -- if both bold and italic are set in * separate spans, or if the base style is bold and a span calls for italic, * you get bold italic. You can't turn off a style from the base style. - * + * <p> + * For example, the <code>StyleSpan</code> can be used like this: + * <pre> + * SpannableString string = new SpannableString("Bold and italic text"); + * string.setSpan(new StyleSpan(Typeface.BOLD), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + * string.setSpan(new StyleSpan(Typeface.ITALIC), 9, 15, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + * </pre> + * <img src="{@docRoot}reference/android/images/text/style/stylespan.png" /> + * <figcaption>Text styled bold and italic with the <code>StyleSpan</code>.</figcaption> */ public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan { private final int mStyle; /** - * + * Creates a {@link StyleSpan} from a style. + * * @param style An integer constant describing the style for this span. Examples - * include bold, italic, and normal. Values are constants defined - * in {@link android.graphics.Typeface}. + * include bold, italic, and normal. Values are constants defined + * in {@link Typeface}. */ public StyleSpan(int style) { mStyle = style; } - public StyleSpan(Parcel src) { + /** + * Creates a {@link StyleSpan} from a parcel. + * + * @param src the parcel + */ + public StyleSpan(@NonNull Parcel src) { mStyle = src.readInt(); } - + + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ + @Override public int getSpanTypeIdInternal() { return TextUtils.STYLE_SPAN; } - + + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + @Override + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeInt(mStyle); } /** - * Returns the style constant defined in {@link android.graphics.Typeface}. + * Returns the style constant defined in {@link Typeface}. */ public int getStyle() { return mStyle; diff --git a/core/java/android/text/style/TabStopSpan.java b/core/java/android/text/style/TabStopSpan.java index 056642841815..2cceb2c8ced9 100644 --- a/core/java/android/text/style/TabStopSpan.java +++ b/core/java/android/text/style/TabStopSpan.java @@ -16,39 +16,54 @@ package android.text.style; +import android.annotation.IntRange; +import android.annotation.Px; + /** - * Represents a single tab stop on a line. + * Paragraph affecting span that changes the position of the tab with respect to + * the leading margin of the line. <code>TabStopSpan</code> will only affect the first tab + * encountered on the first line of the text. */ -public interface TabStopSpan -extends ParagraphStyle -{ +public interface TabStopSpan extends ParagraphStyle { + /** - * Returns the offset of the tab stop from the leading margin of the - * line. - * @return the offset + * Returns the offset of the tab stop from the leading margin of the line, in pixels. + * + * @return the offset, in pixels */ - public int getTabStop(); + int getTabStop(); /** - * The default implementation of TabStopSpan. + * The default implementation of TabStopSpan that allows setting the offset of the tab stop + * from the leading margin of the first line of text. + * <p> + * Let's consider that we have the following text: <i>"\tParagraph text beginning with tab."</i> + * and we want to move the tab stop with 100px. This can be achieved like this: + * <pre> + * SpannableString string = new SpannableString("\tParagraph text beginning with tab."); + * string.setSpan(new TabStopSpan.Standard(100), 0, string.length(), + * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</pre> + * <img src="{@docRoot}reference/android/images/text/style/tabstopspan.png" /> + * <figcaption>Text with a tab stop and a <code>TabStopSpan</code></figcaption> */ - public static class Standard - implements TabStopSpan - { + class Standard implements TabStopSpan { + + @Px + private int mTabOffset; + /** - * Constructor. + * Constructs a {@link TabStopSpan.Standard} based on an offset. * - * @param where the offset of the tab stop from the leading margin of - * the line + * @param offset the offset of the tab stop from the leading margin of + * the line, in pixels */ - public Standard(int where) { - mTab = where; + public Standard(@IntRange(from = 0) int offset) { + mTabOffset = offset; } + @Override public int getTabStop() { - return mTab; + return mTabOffset; } - - private int mTab; } } diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java index aa622d87c74e..162281250208 100644 --- a/core/java/android/text/style/TypefaceSpan.java +++ b/core/java/android/text/style/TypefaceSpan.java @@ -16,6 +16,7 @@ package android.text.style; +import android.annotation.NonNull; import android.graphics.Paint; import android.graphics.Typeface; import android.os.Parcel; @@ -24,42 +25,59 @@ import android.text.TextPaint; import android.text.TextUtils; /** - * Changes the typeface family of the text to which the span is attached. + * Changes the typeface family of the text to which the span is attached. Examples of typeface + * family include "monospace", "serif", and "sans-serif". + * <p> + * For example, change the typeface of a text to "monospace" like this: + * <pre> + * SpannableString string = new SpannableString("Text with typeface span"); + * string.setSpan(new TypefaceSpan("monospace"), 10, 18, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + * </pre> + * <img src="{@docRoot}reference/android/images/text/style/typefacespan.png" /> + * <figcaption>Text with "monospace" typeface family.</figcaption> */ public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan { + private final String mFamily; /** - * @param family The font family for this typeface. Examples include - * "monospace", "serif", and "sans-serif". + * Constructs a {@link TypefaceSpan} based on a font family. + * + * @param family The font family for this typeface. Examples include + * "monospace", "serif", and "sans-serif". */ public TypefaceSpan(String family) { mFamily = family; } - public TypefaceSpan(Parcel src) { + public TypefaceSpan(@NonNull Parcel src) { mFamily = src.readString(); } - + + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ + @Override public int getSpanTypeIdInternal() { return TextUtils.TYPEFACE_SPAN; } - + + @Override public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + @Override + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeString(mFamily); } @@ -71,16 +89,16 @@ public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan } @Override - public void updateDrawState(TextPaint ds) { - apply(ds, mFamily); + public void updateDrawState(@NonNull TextPaint textPaint) { + apply(textPaint, mFamily); } @Override - public void updateMeasureState(TextPaint paint) { - apply(paint, mFamily); + public void updateMeasureState(@NonNull TextPaint textPaint) { + apply(textPaint, mFamily); } - private static void apply(Paint paint, String family) { + private static void apply(@NonNull Paint paint, String family) { int oldStyle; Typeface old = paint.getTypeface(); diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java index 58239efe5c5f..eab1ef4f6afd 100644 --- a/core/java/android/text/style/URLSpan.java +++ b/core/java/android/text/style/URLSpan.java @@ -16,6 +16,7 @@ package android.text.style; +import android.annotation.NonNull; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -27,40 +28,71 @@ import android.text.TextUtils; import android.util.Log; import android.view.View; +/** + * Implementation of the {@link ClickableSpan} that allows setting a url string. When + * selecting and clicking on the text to which the span is attached, the <code>URLSpan</code> + * will try to open the url, by launching an an Activity with an {@link Intent#ACTION_VIEW} intent. + * <p> + * For example, a <code>URLSpan</code> can be used like this: + * <pre> + * SpannableString string = new SpannableString("Text with a url span"); + * string.setSpan(new URLSpan("http://www.developer.android.com"), 12, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + * </pre> + * <img src="{@docRoot}reference/android/images/text/style/urlspan.png" /> + * <figcaption>Text with <code>URLSpan</code>.</figcaption> + */ public class URLSpan extends ClickableSpan implements ParcelableSpan { private final String mURL; + /** + * Constructs a {@link URLSpan} from a url string. + * + * @param url the url string + */ public URLSpan(String url) { mURL = url; } - public URLSpan(Parcel src) { + /** + * Constructs a {@link URLSpan} from a parcel. + */ + public URLSpan(@NonNull Parcel src) { mURL = src.readString(); } - + + @Override public int getSpanTypeId() { return getSpanTypeIdInternal(); } /** @hide */ + @Override public int getSpanTypeIdInternal() { return TextUtils.URL_SPAN; } - + + @Override public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + @Override + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeString(mURL); } + /** + * Get the url string for this span. + * + * @return the url string. + */ public String getURL() { return mURL; } diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 57d23ced7860..410cdc6a9bf1 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -42,7 +42,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_battery_v2", "true"); DEFAULT_FLAGS.put("settings_battery_display_app_list", "false"); DEFAULT_FLAGS.put("settings_zone_picker_v2", "true"); - DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false"); DEFAULT_FLAGS.put("settings_about_phone_v2", "false"); DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false"); } diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java index 687aa8375e01..51fb18a95c4c 100644 --- a/core/java/android/util/StatsManager.java +++ b/core/java/android/util/StatsManager.java @@ -60,9 +60,19 @@ public class StatsManager { */ @RequiresPermission(Manifest.permission.DUMP) public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) { - // To prevent breakages of dependencies on old API. - - return false; + synchronized (this) { + try { + IStatsManager service = getIStatsManagerLocked(); + if (service == null) { + Slog.d(TAG, "Failed to find statsd when adding configuration"); + return false; + } + return service.addConfiguration(Long.parseLong(configKey), config, pkg, cls); + } catch (RemoteException e) { + Slog.d(TAG, "Failed to connect to statsd when adding configuration"); + return false; + } + } } /** @@ -99,7 +109,19 @@ public class StatsManager { @RequiresPermission(Manifest.permission.DUMP) public boolean removeConfiguration(String configKey) { // To prevent breakages of old dependencies. - return false; + synchronized (this) { + try { + IStatsManager service = getIStatsManagerLocked(); + if (service == null) { + Slog.d(TAG, "Failed to find statsd when removing configuration"); + return false; + } + return service.removeConfiguration(Long.parseLong(configKey)); + } catch (RemoteException e) { + Slog.d(TAG, "Failed to connect to statsd when removing configuration"); + return false; + } + } } /** @@ -132,7 +154,19 @@ public class StatsManager { public byte[] getData(String configKey) { // TODO: remove this and all other methods with String-based config keys. // To prevent build breakages of dependencies. - return null; + synchronized (this) { + try { + IStatsManager service = getIStatsManagerLocked(); + if (service == null) { + Slog.d(TAG, "Failed to find statsd when getting data"); + return null; + } + return service.getData(Long.parseLong(configKey)); + } catch (RemoteException e) { + Slog.d(TAG, "Failed to connecto statsd when getting data"); + return null; + } + } } /** diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java index 4c6e511ede46..a3eeb275064a 100644 --- a/core/java/android/util/apk/ApkVerityBuilder.java +++ b/core/java/android/util/apk/ApkVerityBuilder.java @@ -106,18 +106,22 @@ abstract class ApkVerityBuilder { calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions); MessageDigest md = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM); - md.update(DEFAULT_SALT); - md.update(verityBlock); + md.update(header); + md.update(extensions); md.update(apkDigest); return md.digest(); } + /** + * Internal method to generate various parts of FSVerity constructs, including the header, + * extensions, Merkle tree, and the tree's root hash. The output buffer is flipped to the + * generated data size and is readey for consuming. + */ private static void calculateFsveritySignatureInternal( RandomAccessFile apk, SignatureInfo signatureInfo, ByteBuffer treeOutput, ByteBuffer rootHashOutput, ByteBuffer headerOutput, ByteBuffer extensionsOutput) throws IOException, NoSuchAlgorithmException, DigestException { assertSigningBlockAlignedAndHasFullPages(signatureInfo); - long signingBlockSize = signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset; long dataSize = apk.length() - signingBlockSize - ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE; @@ -128,6 +132,7 @@ abstract class ApkVerityBuilder { levelOffset, treeOutput); if (rootHashOutput != null) { rootHashOutput.put(apkRootHash); + rootHashOutput.flip(); } } @@ -333,9 +338,9 @@ abstract class ApkVerityBuilder { buffer.put((byte) 0); // auth block offset, disabled here buffer.put((byte) 2); // extension count buffer.put(salt); // salt (8 bytes) - // skip(buffer, 22); // reserved + skip(buffer, 22); // reserved - buffer.rewind(); + buffer.flip(); return buffer; } @@ -396,12 +401,10 @@ abstract class ApkVerityBuilder { buffer.put((byte) ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE); // length skip(buffer, 7); // reserved buffer.putInt(Math.toIntExact(signingBlockOffset)); // databytes - - // There are extra kPadding bytes of 0s here, included in the total size field of the - // extension header. The output ByteBuffer is assumed to be initialized to 0. + skip(buffer, kPadding); // padding } - buffer.rewind(); + buffer.flip(); return buffer; } diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 3fd469630db0..8cb46b704c18 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -23,6 +23,10 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; @@ -396,6 +400,33 @@ public final class PointerIcon implements Parcelable { return true; } + /** + * Get the Bitmap from the Drawable. + * + * If the Bitmap needed to be scaled up to account for density, BitmapDrawable + * handles this at draw time. But this class doesn't actually draw the Bitmap; + * it is just a holder for native code to access its SkBitmap. So this needs to + * get a version that is scaled to account for density. + */ + private Bitmap getBitmapFromDrawable(BitmapDrawable bitmapDrawable) { + Bitmap bitmap = bitmapDrawable.getBitmap(); + final int scaledWidth = bitmapDrawable.getIntrinsicWidth(); + final int scaledHeight = bitmapDrawable.getIntrinsicHeight(); + if (scaledWidth == bitmap.getWidth() && scaledHeight == bitmap.getHeight()) { + return bitmap; + } + + Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + RectF dst = new RectF(0, 0, scaledWidth, scaledHeight); + + Bitmap scaled = Bitmap.createBitmap(scaledWidth, scaledHeight, bitmap.getConfig()); + Canvas canvas = new Canvas(scaled); + Paint paint = new Paint(); + paint.setFilterBitmap(true); + canvas.drawBitmap(bitmap, src, dst, paint); + return scaled; + } + private void loadResource(Context context, Resources resources, @XmlRes int resourceId) { final XmlResourceParser parser = resources.getXml(resourceId); final int bitmapRes; @@ -452,7 +483,8 @@ public final class PointerIcon implements Parcelable { + "is different. All frames should have the exact same size and " + "share the same hotspot."); } - mBitmapFrames[i - 1] = ((BitmapDrawable)drawableFrame).getBitmap(); + BitmapDrawable bitmapDrawableFrame = (BitmapDrawable) drawableFrame; + mBitmapFrames[i - 1] = getBitmapFromDrawable(bitmapDrawableFrame); } } } @@ -461,7 +493,8 @@ public final class PointerIcon implements Parcelable { + "refer to a bitmap drawable."); } - final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + final Bitmap bitmap = getBitmapFromDrawable(bitmapDrawable); validateHotSpot(bitmap, hotSpotX, hotSpotY); // Set the properties now that we have successfully loaded the icon. mBitmap = bitmap; diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index 5070151815f5..ce7e8f3b55b5 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -353,9 +353,24 @@ public class RenderNode { return nHasShadow(mNativeRenderNode); } - /** setShadowColor */ - public boolean setShadowColor(int color) { - return nSetShadowColor(mNativeRenderNode, color); + /** setSpotShadowColor */ + public boolean setSpotShadowColor(int color) { + return nSetSpotShadowColor(mNativeRenderNode, color); + } + + /** setAmbientShadowColor */ + public boolean setAmbientShadowColor(int color) { + return nSetAmbientShadowColor(mNativeRenderNode, color); + } + + /** getSpotShadowColor */ + public int getSpotShadowColor() { + return nGetSpotShadowColor(mNativeRenderNode); + } + + /** getAmbientShadowColor */ + public int getAmbientShadowColor() { + return nGetAmbientShadowColor(mNativeRenderNode); } /** @@ -915,7 +930,13 @@ public class RenderNode { @CriticalNative private static native boolean nHasShadow(long renderNode); @CriticalNative - private static native boolean nSetShadowColor(long renderNode, int color); + private static native boolean nSetSpotShadowColor(long renderNode, int color); + @CriticalNative + private static native boolean nSetAmbientShadowColor(long renderNode, int color); + @CriticalNative + private static native int nGetSpotShadowColor(long renderNode); + @CriticalNative + private static native int nGetAmbientShadowColor(long renderNode); @CriticalNative private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline); @CriticalNative diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 5150c1fec8a3..c3e9e7387732 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -17,7 +17,6 @@ package android.view; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED; - import static java.lang.Math.max; import android.animation.AnimatorInflater; @@ -726,6 +725,8 @@ import java.util.function.Predicate; * @attr ref android.R.styleable#View_nextFocusRight * @attr ref android.R.styleable#View_nextFocusUp * @attr ref android.R.styleable#View_onClick + * @attr ref android.R.styleable#View_outlineSpotShadowColor + * @attr ref android.R.styleable#View_outlineAmbientShadowColor * @attr ref android.R.styleable#View_padding * @attr ref android.R.styleable#View_paddingHorizontal * @attr ref android.R.styleable#View_paddingVertical @@ -3975,6 +3976,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Current clip bounds. to which all drawing of this view are constrained. */ + @ViewDebug.ExportedProperty(category = "drawing") Rect mClipBounds = null; private boolean mLastIsOpaque; @@ -5446,6 +5448,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setAccessibilityPaneTitle(a.getString(attr)); } break; + case R.styleable.View_outlineSpotShadowColor: + setOutlineSpotShadowColor(a.getColor(attr, Color.BLACK)); + break; + case R.styleable.View_outlineAmbientShadowColor: + setOutlineAmbientShadowColor(a.getColor(attr, Color.BLACK)); + break; } } @@ -15481,14 +15489,61 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * @hide + * Sets the color of the spot shadow that is drawn when the view has a positive Z or + * elevation value. + * <p> + * By default the shadow color is black. Generally, this color will be opaque so the intensity + * of the shadow is consistent between different views with different colors. + * <p> + * The opacity of the final spot shadow is a function of the shadow caster height, the + * alpha channel of the outlineSpotShadowColor (typically opaque), and the + * {@link android.R.attr#spotShadowAlpha} theme attribute. + * + * @attr ref android.R.styleable#View_outlineSpotShadowColor + * @param color The color this View will cast for its elevation spot shadow. */ - public void setShadowColor(@ColorInt int color) { - if (mRenderNode.setShadowColor(color)) { + public void setOutlineSpotShadowColor(@ColorInt int color) { + if (mRenderNode.setSpotShadowColor(color)) { invalidateViewProperty(true, true); } } + /** + * @return The shadow color set by {@link #setOutlineSpotShadowColor(int)}, or black if nothing + * was set + */ + public @ColorInt int getOutlineSpotShadowColor() { + return mRenderNode.getSpotShadowColor(); + } + + /** + * Sets the color of the ambient shadow that is drawn when the view has a positive Z or + * elevation value. + * <p> + * By default the shadow color is black. Generally, this color will be opaque so the intensity + * of the shadow is consistent between different views with different colors. + * <p> + * The opacity of the final ambient shadow is a function of the shadow caster height, the + * alpha channel of the outlineAmbientShadowColor (typically opaque), and the + * {@link android.R.attr#ambientShadowAlpha} theme attribute. + * + * @attr ref android.R.styleable#View_outlineAmbientShadowColor + * @param color The color this View will cast for its elevation shadow. + */ + public void setOutlineAmbientShadowColor(@ColorInt int color) { + if (mRenderNode.setAmbientShadowColor(color)) { + invalidateViewProperty(true, true); + } + } + + /** + * @return The shadow color set by {@link #setOutlineAmbientShadowColor(int)}, or black if + * nothing was set + */ + public @ColorInt int getOutlineAmbientShadowColor() { + return mRenderNode.getAmbientShadowColor(); + } + /** @hide */ public void setRevealClip(boolean shouldClip, float x, float y, float radius) { @@ -23634,9 +23689,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Point shadowTouchPoint = new Point(); shadowBuilder.onProvideShadowMetrics(shadowSize, shadowTouchPoint); - if ((shadowSize.x <= 0) || (shadowSize.y <= 0) + if ((shadowSize.x < 0) || (shadowSize.y < 0) || (shadowTouchPoint.x < 0) || (shadowTouchPoint.y < 0)) { - throw new IllegalStateException("Drag shadow dimensions must be positive"); + throw new IllegalStateException("Drag shadow dimensions must not be negative"); + } + + // Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder + // does not accept zero size surface. + if (shadowSize.x == 0 || shadowSize.y == 0) { + shadowSize.x = 1; + shadowSize.y = 1; } if (ViewDebug.DEBUG_DRAG) { @@ -26979,6 +27041,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, stream.addProperty("drawing:scaleY", getScaleY()); stream.addProperty("drawing:pivotX", getPivotX()); stream.addProperty("drawing:pivotY", getPivotY()); + stream.addProperty("drawing:clipBounds", + mClipBounds == null ? null : mClipBounds.toString()); stream.addProperty("drawing:opaque", isOpaque()); stream.addProperty("drawing:alpha", getAlpha()); stream.addProperty("drawing:transitionAlpha", getTransitionAlpha()); @@ -26990,6 +27054,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, stream.addProperty("drawing:willNotCacheDrawing", willNotCacheDrawing()); stream.addProperty("drawing:drawingCacheEnabled", isDrawingCacheEnabled()); stream.addProperty("drawing:overlappingRendering", hasOverlappingRendering()); + stream.addProperty("drawing:outlineAmbientShadowColor", getOutlineAmbientShadowColor()); + stream.addProperty("drawing:outlineSpotShadowColor", getOutlineSpotShadowColor()); // focus stream.addProperty("focus:hasFocus", hasFocus()); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 7db5c3207296..33c9f7a2671e 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemService; +import android.annotation.TestApi; import android.content.Context; import android.graphics.Rect; import android.inputmethodservice.InputMethodService; @@ -2137,6 +2138,26 @@ public final class InputMethodManager { } /** + * A test API for CTS to make sure that {@link #showInputMethodPicker()} works as expected. + * + * <p>When customizing the implementation of {@link #showInputMethodPicker()} API, make sure + * that this test API returns when and only while and only while + * {@link #showInputMethodPicker()} is showing UI. Otherwise your OS implementation may not + * pass CTS.</p> + * + * @return {@code true} while and only while {@link #showInputMethodPicker()} is showing UI. + * @hide + */ + @TestApi + public boolean isInputMethodPickerShown() { + try { + return mService.isInputMethodPickerShownForTest(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Show the settings for enabling subtypes of the specified input method. * @param imiId An input method, whose subtypes settings will be shown. If imiId is null, * subtypes of all input methods will be shown. diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index b03c70d19199..9f389ba0c140 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -73,7 +73,7 @@ public final class TextClassifierImpl implements TextClassifier { private static final String LOG_TAG = DEFAULT_LOG_TAG; private static final String MODEL_DIR = "/etc/textclassifier/"; - private static final String MODEL_FILE_REGEX = "textclassifier\\.smartselection\\.(.*)\\.model"; + private static final String MODEL_FILE_REGEX = "textclassifier\\.(.*)\\.model"; private static final String UPDATED_MODEL_FILE_PATH = "/data/misc/textclassifier/textclassifier.model"; private static final List<String> ENTITY_TYPES_ALL = @@ -232,6 +232,7 @@ public final class TextClassifierImpl implements TextClassifier { } builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores); } + return builder.build(); } catch (Throwable t) { // Avoid throwing from this method. Log the error. Log.e(LOG_TAG, "Error getting links info.", t); diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index 670efdd286e9..ede52119390a 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -23,13 +23,13 @@ import android.annotation.Nullable; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; -import android.widget.TextView; import android.text.Spannable; import android.text.style.ClickableSpan; import android.text.util.Linkify; import android.text.util.Linkify.LinkifyMask; import android.view.View; import android.view.textclassifier.TextClassifier.EntityType; +import android.widget.TextView; import com.android.internal.util.Preconditions; @@ -351,7 +351,7 @@ public final class TextLinks implements Parcelable { * @throws IllegalArgumentException if applyStrategy is not valid * * @see #APPLY_STRATEGY_IGNORE - * @see #APPLY_STRAGETY_REPLACE + * @see #APPLY_STRATEGY_REPLACE */ public Options setApplyStrategy(@ApplyStrategy int applyStrategy) { checkValidApplyStrategy(applyStrategy); @@ -391,8 +391,8 @@ public final class TextLinks implements Parcelable { * @return the strategy for resolving conflictswhen applying generated links to text that * already have links. * - * @see #APPLY_STATEGY_IGNORE - * @see #APPLY_STRAGETY_REPLACE + * @see #APPLY_STRATEGY_IGNORE + * @see #APPLY_STRATEGY_REPLACE */ @ApplyStrategy public int getApplyStrategy() { diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 1dc5b44bed4f..4b951fa1824e 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -23,10 +23,12 @@ import android.annotation.TestApi; import android.content.ContentResolver; import android.content.Context; import android.content.res.ColorStateList; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Matrix; import android.graphics.PixelFormat; import android.graphics.PorterDuff; @@ -53,7 +55,6 @@ import android.widget.RemoteViews.RemoteView; import com.android.internal.R; import java.io.IOException; -import java.io.InputStream; /** * Displays image resources, for example {@link android.graphics.Bitmap} @@ -946,21 +947,15 @@ public class ImageView extends View { } } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) || ContentResolver.SCHEME_FILE.equals(scheme)) { - InputStream stream = null; try { - stream = mContext.getContentResolver().openInputStream(uri); - return Drawable.createFromResourceStream(sCompatUseCorrectStreamDensity - ? getResources() : null, null, stream, null); - } catch (Exception e) { + Resources res = sCompatUseCorrectStreamDensity ? getResources() : null; + ImageDecoder.Source src = ImageDecoder.createSource(mContext.getContentResolver(), + uri, res); + return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); + } catch (IOException e) { Log.w(LOG_TAG, "Unable to open content: " + uri, e); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (IOException e) { - Log.w(LOG_TAG, "Unable to close content: " + uri, e); - } - } } } else { return Drawable.createFromPath(uri.toString()); diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java index 2e4cccfc230f..e58e62f972c6 100644 --- a/core/java/android/widget/MediaControlView2.java +++ b/core/java/android/widget/MediaControlView2.java @@ -19,12 +19,11 @@ package android.widget; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.content.Context; import android.media.session.MediaController; import android.media.update.ApiLoader; -import android.media.update.FrameLayoutHelper; import android.media.update.MediaControlView2Provider; +import android.media.update.ViewGroupHelper; import android.util.AttributeSet; import java.lang.annotation.Retention; @@ -62,7 +61,7 @@ import java.lang.annotation.RetentionPolicy; * TODO PUBLIC API * @hide */ -public class MediaControlView2 extends FrameLayoutHelper<MediaControlView2Provider> { +public class MediaControlView2 extends ViewGroupHelper<MediaControlView2Provider> { /** @hide */ @IntDef({ BUTTON_PLAY_PAUSE, @@ -156,18 +155,12 @@ public class MediaControlView2 extends FrameLayoutHelper<MediaControlView2Provid public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super((instance, superProvider) -> + super((instance, superProvider, privateProvider) -> ApiLoader.getProvider(context).createMediaControlView2( - (MediaControlView2) instance, superProvider), + (MediaControlView2) instance, superProvider, privateProvider, + attrs, defStyleAttr, defStyleRes), context, attrs, defStyleAttr, defStyleRes); - } - - /** - * @hide - */ - @SystemApi - public MediaControlView2Provider getProvider() { - return mProvider; + mProvider.initialize(attrs, defStyleAttr, defStyleRes); } /** @@ -178,13 +171,6 @@ public class MediaControlView2 extends FrameLayoutHelper<MediaControlView2Provid } /** - * Returns whether the control view is currently shown or hidden. - */ - public boolean isShowing() { - return mProvider.isShowing_impl(); - } - - /** * Changes the visibility state of an individual button. Default value is View.Visible. * * @param button the {@code Button} assigned to individual buttons @@ -232,9 +218,7 @@ public class MediaControlView2 extends FrameLayoutHelper<MediaControlView2Provid } @Override - // TODO Move this method to ViewProvider - public void onVisibilityAggregated(boolean isVisible) { - - mProvider.onVisibilityAggregated_impl(isVisible); + protected void onLayout(boolean changed, int l, int t, int r, int b) { + mProvider.onLayout_impl(changed, l, t, r, b); } } diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java index 78ca0114b798..30811803bf9a 100644 --- a/core/java/android/widget/VideoView2.java +++ b/core/java/android/widget/VideoView2.java @@ -27,8 +27,8 @@ import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.media.update.ApiLoader; -import android.media.update.FrameLayoutHelper; import android.media.update.VideoView2Provider; +import android.media.update.ViewGroupHelper; import android.net.Uri; import android.os.Bundle; import android.util.AttributeSet; @@ -101,7 +101,7 @@ import java.util.concurrent.Executor; * * @hide */ -public class VideoView2 extends FrameLayoutHelper<VideoView2Provider> { +public class VideoView2 extends ViewGroupHelper<VideoView2Provider> { /** @hide */ @IntDef({ VIEW_TYPE_TEXTUREVIEW, @@ -139,10 +139,12 @@ public class VideoView2 extends FrameLayoutHelper<VideoView2Provider> { public VideoView2( @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super((instance, superProvider) -> + super((instance, superProvider, privateProvider) -> ApiLoader.getProvider(context).createVideoView2( - (VideoView2) instance, superProvider, attrs, defStyleAttr, defStyleRes), + (VideoView2) instance, superProvider, privateProvider, + attrs, defStyleAttr, defStyleRes), context, attrs, defStyleAttr, defStyleRes); + mProvider.initialize(attrs, defStyleAttr, defStyleRes); } /** @@ -487,4 +489,9 @@ public class VideoView2 extends FrameLayoutHelper<VideoView2Provider> { */ void onCustomAction(String action, Bundle extras); } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + mProvider.onLayout_impl(changed, l, t, r, b); + } } diff --git a/core/java/com/android/internal/alsa/AlsaCardsParser.java b/core/java/com/android/internal/alsa/AlsaCardsParser.java index bb75bf6e6fb8..fa8db96781ac 100644 --- a/core/java/com/android/internal/alsa/AlsaCardsParser.java +++ b/core/java/com/android/internal/alsa/AlsaCardsParser.java @@ -31,7 +31,9 @@ public class AlsaCardsParser { private static final String TAG = "AlsaCardsParser"; protected static final boolean DEBUG = false; - private static final String kCardsFilePath = "/proc/asound/cards"; + private static final String kAlsaFolderPath = "/proc/asound"; + private static final String kCardsFilePath = kAlsaFolderPath + "/cards"; + private static final String kDeviceAddressPrefix = "/dev/bus/usb/"; private static LineTokenizer mTokenizer = new LineTokenizer(" :[]"); @@ -47,14 +49,31 @@ public class AlsaCardsParser { private static final String TAG = "AlsaCardRecord"; private static final String kUsbCardKeyStr = "at usb-"; - public int mCardNum = -1; - public String mField1 = ""; - public String mCardName = ""; - public String mCardDescription = ""; - public boolean mIsUsb = false; + int mCardNum = -1; + String mField1 = ""; + String mCardName = ""; + String mCardDescription = ""; + + private String mUsbDeviceAddress = null; public AlsaCardRecord() {} + public int getCardNum() { + return mCardNum; + } + + public String getCardName() { + return mCardName; + } + + public String getCardDescription() { + return mCardDescription; + } + + public void setDeviceAddress(String usbDeviceAddress) { + mUsbDeviceAddress = usbDeviceAddress; + } + private boolean parse(String line, int lineIndex) { int tokenIndex = 0; int delimIndex = 0; @@ -87,8 +106,8 @@ public class AlsaCardsParser { tokenIndex = mTokenizer.nextToken(line, 0); if (tokenIndex != -1) { int keyIndex = line.indexOf(kUsbCardKeyStr); - mIsUsb = keyIndex != -1; - if (mIsUsb) { + boolean isUsb = keyIndex != -1; + if (isUsb) { mCardDescription = line.substring(tokenIndex, keyIndex - 1); } } @@ -97,14 +116,18 @@ public class AlsaCardsParser { return true; } + boolean isUsb() { + return mUsbDeviceAddress != null; + } + public String textFormat() { - return mCardName + " : " + mCardDescription; + return mCardName + " : " + mCardDescription + " [addr:" + mUsbDeviceAddress + "]"; } public void log(int listIndex) { Slog.d(TAG, "" + listIndex + " [" + mCardNum + " " + mCardName + " : " + mCardDescription + - " usb:" + mIsUsb); + " usb:" + isUsb()); } } @@ -112,7 +135,7 @@ public class AlsaCardsParser { public int scan() { if (DEBUG) { - Slog.i(TAG, "AlsaCardsParser.scan()...."); + Slog.d(TAG, "AlsaCardsParser.scan()...."); } mCardRecords = new ArrayList<AlsaCardRecord>(); @@ -125,7 +148,7 @@ public class AlsaCardsParser { while ((line = bufferedReader.readLine()) != null) { AlsaCardRecord cardRecord = new AlsaCardRecord(); if (DEBUG) { - Slog.i(TAG, " " + line); + Slog.d(TAG, " " + line); } cardRecord.parse(line, 0); @@ -134,10 +157,23 @@ public class AlsaCardsParser { break; } if (DEBUG) { - Slog.i(TAG, " " + line); + Slog.d(TAG, " " + line); } cardRecord.parse(line, 1); + // scan "usbbus" file + int cardNum = cardRecord.mCardNum; + String cardFolderPath = kAlsaFolderPath + "/card" + cardNum; + File usbbusFile = new File(cardFolderPath + "/usbbus"); + if (usbbusFile.exists()) { + // read it in + FileReader usbbusReader = new FileReader(usbbusFile); + String deviceAddress = (new BufferedReader(usbbusReader)).readLine(); + if (deviceAddress != null) { + cardRecord.setDeviceAddress(kDeviceAddressPrefix + deviceAddress); + } + usbbusReader.close(); + } mCardRecords.add(cardRecord); } reader.close(); @@ -147,14 +183,12 @@ public class AlsaCardsParser { mScanStatus = SCANSTATUS_EMPTY; } } catch (FileNotFoundException e) { - e.printStackTrace(); mScanStatus = SCANSTATUS_FAIL; } catch (IOException e) { - e.printStackTrace(); mScanStatus = SCANSTATUS_FAIL; } if (DEBUG) { - Slog.i(TAG, " status:" + mScanStatus); + Slog.d(TAG, " status:" + mScanStatus); } return mScanStatus; } @@ -163,142 +197,33 @@ public class AlsaCardsParser { return mScanStatus; } - public ArrayList<AlsaCardRecord> getScanRecords() { - return mCardRecords; - } - - public AlsaCardRecord getCardRecordAt(int index) { - return mCardRecords.get(index); - } - - public AlsaCardRecord getCardRecordFor(int cardNum) { - for (AlsaCardRecord rec : mCardRecords) { - if (rec.mCardNum == cardNum) { - return rec; + public AlsaCardRecord findCardNumFor(String deviceAddress) { + for(AlsaCardRecord cardRec : mCardRecords) { + if (cardRec.isUsb() && cardRec.mUsbDeviceAddress.equals(deviceAddress)) { + return cardRec; } } - return null; } - public int getNumCardRecords() { - return mCardRecords.size(); - } - - public boolean isCardUsb(int cardNum) { - for (AlsaCardRecord rec : mCardRecords) { - if (rec.mCardNum == cardNum) { - return rec.mIsUsb; - } - } - - return false; - } - - // return -1 if none found - public int getDefaultUsbCard() { - // save the current list of devices - ArrayList<AlsaCardsParser.AlsaCardRecord> prevRecs = mCardRecords; - if (DEBUG) { - LogDevices("Previous Devices:", prevRecs); - } - - // get the new list of devices - if (scan() != SCANSTATUS_SUCCESS) { - Slog.e(TAG, "Error scanning Alsa cards file."); - return -1; - } - - if (DEBUG) { - LogDevices("Current Devices:", mCardRecords); - } - - // Calculate the difference between the old and new device list - ArrayList<AlsaCardRecord> newRecs = getNewCardRecords(prevRecs); - if (DEBUG) { - LogDevices("New Devices:", newRecs); - } - - // Choose the most-recently added EXTERNAL card - // Check recently added devices - for (AlsaCardRecord rec : newRecs) { - if (DEBUG) { - Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb); - } - if (rec.mIsUsb) { - // Found it - return rec.mCardNum; - } - } - - // or return the first added EXTERNAL card? - for (AlsaCardRecord rec : prevRecs) { - if (DEBUG) { - Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb); - } - if (rec.mIsUsb) { - return rec.mCardNum; - } - } - - return -1; - } - - public int getDefaultCard() { - // return an external card if possible - int card = getDefaultUsbCard(); - if (DEBUG) { - Slog.d(TAG, "getDefaultCard() default usb card:" + card); - } - - if (card < 0 && getNumCardRecords() > 0) { - // otherwise return the (internal) card with the highest number - card = getCardRecordAt(getNumCardRecords() - 1).mCardNum; - } - if (DEBUG) { - Slog.d(TAG, " returns card:" + card); - } - return card; - } - - static public boolean hasCardNumber(ArrayList<AlsaCardRecord> recs, int cardNum) { - for (AlsaCardRecord cardRec : recs) { - if (cardRec.mCardNum == cardNum) { - return true; - } - } - return false; - } - - public ArrayList<AlsaCardRecord> getNewCardRecords(ArrayList<AlsaCardRecord> prevScanRecs) { - ArrayList<AlsaCardRecord> newRecs = new ArrayList<AlsaCardRecord>(); - for (AlsaCardRecord rec : mCardRecords) { - // now scan to see if this card number is in the previous scan list - if (!hasCardNumber(prevScanRecs, rec.mCardNum)) { - newRecs.add(rec); - } - } - return newRecs; - } - // // Logging // private void Log(String heading) { if (DEBUG) { - Slog.i(TAG, heading); + Slog.d(TAG, heading); for (AlsaCardRecord cardRec : mCardRecords) { - Slog.i(TAG, cardRec.textFormat()); + Slog.d(TAG, cardRec.textFormat()); } } } - static private void LogDevices(String caption, ArrayList<AlsaCardRecord> deviceList) { - Slog.d(TAG, caption + " ----------------"); - int listIndex = 0; - for (AlsaCardRecord device : deviceList) { - device.log(listIndex++); - } - Slog.d(TAG, "----------------"); - } +// static private void LogDevices(String caption, ArrayList<AlsaCardRecord> deviceList) { +// Slog.d(TAG, caption + " ----------------"); +// int listIndex = 0; +// for (AlsaCardRecord device : deviceList) { +// device.log(listIndex++); +// } +// Slog.d(TAG, caption + "----------------"); +// } } diff --git a/core/java/com/android/internal/alsa/AlsaDevicesParser.java b/core/java/com/android/internal/alsa/AlsaDevicesParser.java index 15261bafd299..8d92d016be24 100644 --- a/core/java/com/android/internal/alsa/AlsaDevicesParser.java +++ b/core/java/com/android/internal/alsa/AlsaDevicesParser.java @@ -27,6 +27,9 @@ import java.util.ArrayList; * @hide * Retrieves information from an ALSA "devices" file. */ +/* + * NOTE: This class is currently not being used, but may be needed in the future. + */ public class AlsaDevicesParser { private static final String TAG = "AlsaDevicesParser"; protected static final boolean DEBUG = false; @@ -207,12 +210,6 @@ public class AlsaDevicesParser { // // Predicates // -/* - public boolean hasPlaybackDevices() { - return mHasPlaybackDevices; - } -*/ - public boolean hasPlaybackDevices(int card) { for (AlsaDeviceRecord deviceRecord : mDeviceRecords) { if (deviceRecord.mCardNum == card && @@ -224,12 +221,6 @@ public class AlsaDevicesParser { return false; } -/* - public boolean hasCaptureDevices() { - return mHasCaptureDevices; - } -*/ - public boolean hasCaptureDevices(int card) { for (AlsaDeviceRecord deviceRecord : mDeviceRecords) { if (deviceRecord.mCardNum == card && @@ -241,12 +232,6 @@ public class AlsaDevicesParser { return false; } -/* - public boolean hasMIDIDevices() { - return mHasMIDIDevices; - } -*/ - public boolean hasMIDIDevices(int card) { for (AlsaDeviceRecord deviceRecord : mDeviceRecords) { if (deviceRecord.mCardNum == card && @@ -280,6 +265,7 @@ public class AlsaDevicesParser { if (isLineDeviceRecord(line)) { AlsaDeviceRecord deviceRecord = new AlsaDeviceRecord(); deviceRecord.parse(line); + Slog.i(TAG, deviceRecord.textFormat()); mDeviceRecords.add(deviceRecord); } } diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index 2ab2d20ed20d..1dfff5efcab5 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -103,11 +103,14 @@ public class ResolverListController { List<ResolverActivity.ResolvedComponentInfo> resolvedComponents = null; for (int i = 0, N = intents.size(); i < N; i++) { final Intent intent = intents.get(i); - final List<ResolveInfo> infos = mpm.queryIntentActivities(intent, - PackageManager.MATCH_DEFAULT_ONLY - | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0) - | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0) - | PackageManager.MATCH_INSTANT); + int flags = PackageManager.MATCH_DEFAULT_ONLY + | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0) + | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0); + if (intent.isBrowsableWebIntent() + || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { + flags |= PackageManager.MATCH_INSTANT; + } + final List<ResolveInfo> infos = mpm.queryIntentActivities(intent, flags); // Remove any activities that are not exported. int totalSize = infos.size(); for (int j = totalSize - 1; j >= 0 ; j--) { diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java index 902c8c1662ea..6f7695ce8c34 100644 --- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java +++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java @@ -21,24 +21,15 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import android.app.Activity; import android.app.AlertDialog; -import android.app.admin.DevicePolicyManager; import android.content.ComponentName; -import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentSender; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; -import android.text.TextUtils; import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; import android.view.Window; -import android.widget.TextView; import com.android.internal.R; @@ -85,14 +76,9 @@ public class UnlaunchableAppActivity extends Activity return; } - View rootView = LayoutInflater.from(this).inflate(R.layout.unlaunchable_app_activity, null); - TextView titleView = (TextView)rootView.findViewById(R.id.unlaunchable_app_title); - TextView messageView = (TextView)rootView.findViewById(R.id.unlaunchable_app_message); - titleView.setText(dialogTitle); - messageView.setText(dialogMessage); - AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setView(rootView) + .setTitle(dialogTitle) + .setMessage(dialogMessage) .setOnDismissListener(this); if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE) { builder.setPositiveButton(R.string.work_mode_turn_on, this) diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 7def87655ae3..40dcf25bbd10 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -3880,7 +3880,8 @@ public class BatteryStatsImpl extends BatteryStats { public void addIsolatedUidLocked(int isolatedUid, int appUid) { mIsolatedUids.put(isolatedUid, appUid); - StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid, 1); + StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid, + StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED); final Uid u = getUidStatsLocked(appUid); u.addIsolatedUid(isolatedUid); } @@ -3904,7 +3905,8 @@ public class BatteryStatsImpl extends BatteryStats { */ public void removeIsolatedUidLocked(int isolatedUid) { StatsLog.write( - StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1), isolatedUid, 0); + StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1), + isolatedUid, StatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED); final int idx = mIsolatedUids.indexOfKey(isolatedUid); if (idx >= 0) { final int ownerUid = mIsolatedUids.valueAt(idx); @@ -4255,10 +4257,12 @@ public class BatteryStatsImpl extends BatteryStats { if (wc != null) { StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), - getPowerManagerWakeLockLevel(type), name, 1); + getPowerManagerWakeLockLevel(type), name, + StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE); } else { StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, - getPowerManagerWakeLockLevel(type), name, 1); + getPowerManagerWakeLockLevel(type), name, + StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE); } } } @@ -4298,10 +4302,12 @@ public class BatteryStatsImpl extends BatteryStats { getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime); if (wc != null) { StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), - getPowerManagerWakeLockLevel(type), name, 0); + getPowerManagerWakeLockLevel(type), name, + StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE); } else { StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null, - getPowerManagerWakeLockLevel(type), name, 0); + getPowerManagerWakeLockLevel(type), name, + StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE); } } } @@ -4426,7 +4432,8 @@ public class BatteryStatsImpl extends BatteryStats { public void noteLongPartialWakelockStart(String name, String historyName, int uid) { StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, - uid, null, name, historyName, 1); + uid, null, name, historyName, + StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON); uid = mapUid(uid); noteLongPartialWakeLockStartInternal(name, historyName, uid); @@ -4439,7 +4446,8 @@ public class BatteryStatsImpl extends BatteryStats { final int uid = mapUid(workSource.get(i)); noteLongPartialWakeLockStartInternal(name, historyName, uid); StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, - workSource.get(i), workSource.getName(i), name, historyName, 1); + workSource.get(i), workSource.getName(i), name, historyName, + StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON); } final ArrayList<WorkChain> workChains = workSource.getWorkChains(); @@ -4450,7 +4458,8 @@ public class BatteryStatsImpl extends BatteryStats { noteLongPartialWakeLockStartInternal(name, historyName, uid); StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, - workChain.getUids(), workChain.getTags(), name, historyName, 1); + workChain.getUids(), workChain.getTags(), name, historyName, + StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON); } } } @@ -4470,8 +4479,8 @@ public class BatteryStatsImpl extends BatteryStats { } public void noteLongPartialWakelockFinish(String name, String historyName, int uid) { - StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, - uid, null, name, historyName, 0); + StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, null, + name, historyName, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF); uid = mapUid(uid); noteLongPartialWakeLockFinishInternal(name, historyName, uid); @@ -4484,7 +4493,8 @@ public class BatteryStatsImpl extends BatteryStats { final int uid = mapUid(workSource.get(i)); noteLongPartialWakeLockFinishInternal(name, historyName, uid); StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, - workSource.get(i), workSource.getName(i), name, historyName, 0); + workSource.get(i), workSource.getName(i), name, historyName, + StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF); } final ArrayList<WorkChain> workChains = workSource.getWorkChains(); @@ -4494,7 +4504,8 @@ public class BatteryStatsImpl extends BatteryStats { final int uid = workChain.getAttributionUid(); noteLongPartialWakeLockFinishInternal(name, historyName, uid); StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, - workChain.getUids(), workChain.getTags(), name, historyName, 0); + workChain.getUids(), workChain.getTags(), name, historyName, + StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF); } } } @@ -4518,7 +4529,8 @@ public class BatteryStatsImpl extends BatteryStats { long deltaUptime = uptimeMs - mLastWakeupUptimeMs; SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason); timer.add(deltaUptime * 1000, 1); // time in in microseconds - StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason, deltaUptime * 1000); + StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason, + /* duration_usec */ deltaUptime * 1000); mLastWakeupReason = null; } } @@ -4659,10 +4671,12 @@ public class BatteryStatsImpl extends BatteryStats { mGpsNesting++; if (workChain == null) { - StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, 1); + StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, + StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON); } else { StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, - workChain.getUids(), workChain.getTags(), 1); + workChain.getUids(), workChain.getTags(), + StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON); } getUidStatsLocked(uid).noteStartGps(elapsedRealtime); @@ -4683,10 +4697,11 @@ public class BatteryStatsImpl extends BatteryStats { } if (workChain == null) { - StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, 0); + StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, + StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF); } else { StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(), - workChain.getTags(), 0); + workChain.getTags(), StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF); } getUidStatsLocked(uid).noteStopGps(elapsedRealtime); @@ -4941,7 +4956,9 @@ public class BatteryStatsImpl extends BatteryStats { mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtime); } addHistoryRecordLocked(elapsedRealtime, uptime); - StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ? 1 : 0); + StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ? + StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON : + StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF); } } @@ -5545,16 +5562,19 @@ public class BatteryStatsImpl extends BatteryStats { if (workChain != null) { StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, - workChain.getUids(), workChain.getTags(), 1); + workChain.getUids(), workChain.getTags(), + StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON); if (isUnoptimized) { StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, - workChain.getUids(), workChain.getTags(), 1); + workChain.getUids(), workChain.getTags(), + StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON); } } else { - StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 1); + StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, + StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON); if (isUnoptimized) { StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null, - 1); + StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON); } } @@ -5594,16 +5614,19 @@ public class BatteryStatsImpl extends BatteryStats { if (workChain != null) { StatsLog.write( - StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), 0); + StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), + StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF); if (isUnoptimized) { StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, - workChain.getUids(), workChain.getTags(), 0); + workChain.getUids(), workChain.getTags(), + StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF); } } else { - StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 0); + StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, + StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF); if (isUnoptimized) { StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null, - 0); + StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF); } } @@ -5656,7 +5679,8 @@ public class BatteryStatsImpl extends BatteryStats { for (int j = 0; j < allWorkChains.size(); ++j) { StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED, allWorkChains.get(j).getUids(), - allWorkChains.get(j).getTags(), 0); + allWorkChains.get(j).getTags(), + StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF); } allWorkChains.clear(); } @@ -5666,7 +5690,8 @@ public class BatteryStatsImpl extends BatteryStats { for (int j = 0; j < unoptimizedWorkChains.size(); ++j) { StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, unoptimizedWorkChains.get(j).getUids(), - unoptimizedWorkChains.get(j).getTags(), 0); + unoptimizedWorkChains.get(j).getTags(), + StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF); } unoptimizedWorkChains.clear(); } @@ -6011,7 +6036,8 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<N; i++) { final int uid = mapUid(ws.get(i)); noteFullWifiLockAcquiredLocked(uid); - StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 1); + StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), + StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON); } final List<WorkChain> workChains = ws.getWorkChains(); @@ -6021,7 +6047,8 @@ public class BatteryStatsImpl extends BatteryStats { final int uid = mapUid(workChain.getAttributionUid()); noteFullWifiLockAcquiredLocked(uid); StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, - workChain.getUids(), workChain.getTags(), 1); + workChain.getUids(), workChain.getTags(), + StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON); } } } @@ -6031,7 +6058,8 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<N; i++) { final int uid = mapUid(ws.get(i)); noteFullWifiLockReleasedLocked(uid); - StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 0); + StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), + StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF); } final List<WorkChain> workChains = ws.getWorkChains(); @@ -6041,7 +6069,8 @@ public class BatteryStatsImpl extends BatteryStats { final int uid = mapUid(workChain.getAttributionUid()); noteFullWifiLockReleasedLocked(uid); StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED, - workChain.getUids(), workChain.getTags(), 0); + workChain.getUids(), workChain.getTags(), + StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF); } } } @@ -6052,7 +6081,7 @@ public class BatteryStatsImpl extends BatteryStats { final int uid = mapUid(ws.get(i)); noteWifiScanStartedLocked(uid); StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i), - 1); + StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON); } final List<WorkChain> workChains = ws.getWorkChains(); @@ -6062,7 +6091,7 @@ public class BatteryStatsImpl extends BatteryStats { final int uid = mapUid(workChain.getAttributionUid()); noteWifiScanStartedLocked(uid); StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, workChain.getUids(), - workChain.getTags(), 1); + workChain.getTags(), StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON); } } } @@ -6073,7 +6102,7 @@ public class BatteryStatsImpl extends BatteryStats { final int uid = mapUid(ws.get(i)); noteWifiScanStoppedLocked(uid); StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i), - 0); + StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF); } final List<WorkChain> workChains = ws.getWorkChains(); @@ -6083,7 +6112,8 @@ public class BatteryStatsImpl extends BatteryStats { final int uid = mapUid(workChain.getAttributionUid()); noteWifiScanStoppedLocked(uid); StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, - workChain.getUids(), workChain.getTags(), 0); + workChain.getUids(), workChain.getTags(), + StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF); } } } @@ -7035,7 +7065,8 @@ public class BatteryStatsImpl extends BatteryStats { } mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs); StatsLog.write_non_chained( - StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 1); + StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, + StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON); } } @@ -7045,7 +7076,8 @@ public class BatteryStatsImpl extends BatteryStats { mWifiMulticastEnabled = false; mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs); StatsLog.write_non_chained( - StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 0); + StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, + StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__OFF); } } @@ -7098,14 +7130,16 @@ public class BatteryStatsImpl extends BatteryStats { public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) { createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs); - StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 1); + StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, + StatsLog.AUDIO_STATE_CHANGED__STATE__ON); } public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) { if (mAudioTurnedOnTimer != null) { mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs); if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped - StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0); + StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, + StatsLog.AUDIO_STATE_CHANGED__STATE__OFF); } } } @@ -7113,7 +7147,8 @@ public class BatteryStatsImpl extends BatteryStats { public void noteResetAudioLocked(long elapsedRealtimeMs) { if (mAudioTurnedOnTimer != null) { mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs); - StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0); + StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, + StatsLog.AUDIO_STATE_CHANGED__STATE__OFF); } } @@ -7127,7 +7162,8 @@ public class BatteryStatsImpl extends BatteryStats { public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) { createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs); - StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null, 1); + StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null, + StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__ON); } public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) { @@ -7135,7 +7171,7 @@ public class BatteryStatsImpl extends BatteryStats { mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs); if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), - null, 0); + null, StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF); } } } @@ -7144,7 +7180,7 @@ public class BatteryStatsImpl extends BatteryStats { if (mVideoTurnedOnTimer != null) { mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs); StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null, - 0); + StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF); } } @@ -7158,7 +7194,8 @@ public class BatteryStatsImpl extends BatteryStats { public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) { createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs); - StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,1); + StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, + StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__ON); } public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) { @@ -7166,7 +7203,7 @@ public class BatteryStatsImpl extends BatteryStats { mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs); if (!mFlashlightTurnedOnTimer.isRunningLocked()) { StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, - 0); + StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF); } } } @@ -7174,7 +7211,8 @@ public class BatteryStatsImpl extends BatteryStats { public void noteResetFlashlightLocked(long elapsedRealtimeMs) { if (mFlashlightTurnedOnTimer != null) { mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs); - StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, 0); + StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, + StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF); } } @@ -7188,14 +7226,16 @@ public class BatteryStatsImpl extends BatteryStats { public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) { createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs); - StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 1); + StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, + StatsLog.CAMERA_STATE_CHANGED__STATE__ON); } public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) { if (mCameraTurnedOnTimer != null) { mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs); if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped - StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0); + StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, + StatsLog.CAMERA_STATE_CHANGED__STATE__OFF); } } } @@ -7203,7 +7243,8 @@ public class BatteryStatsImpl extends BatteryStats { public void noteResetCameraLocked(long elapsedRealtimeMs) { if (mCameraTurnedOnTimer != null) { mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs); - StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0); + StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, + StatsLog.CAMERA_STATE_CHANGED__STATE__OFF); } } @@ -9777,7 +9818,8 @@ public class BatteryStatsImpl extends BatteryStats { DualTimer t = mSyncStats.startObject(name); if (t != null) { t.startRunningLocked(elapsedRealtimeMs); - StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 1); + StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, + StatsLog.SYNC_STATE_CHANGED__STATE__ON); } } @@ -9786,7 +9828,8 @@ public class BatteryStatsImpl extends BatteryStats { if (t != null) { t.stopRunningLocked(elapsedRealtimeMs); if (!t.isRunningLocked()) { // only tell statsd if truly stopped - StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 0); + StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, + StatsLog.SYNC_STATE_CHANGED__STATE__OFF); } } } @@ -9796,7 +9839,7 @@ public class BatteryStatsImpl extends BatteryStats { if (t != null) { t.startRunningLocked(elapsedRealtimeMs); StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null, - name, 1); + name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED); } } @@ -9806,7 +9849,7 @@ public class BatteryStatsImpl extends BatteryStats { t.stopRunningLocked(elapsedRealtimeMs); if (!t.isRunningLocked()) { // only tell statsd if truly stopped StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null, - name, 0); + name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED); } } if (mBsi.mOnBatteryTimeBase.isRunning()) { @@ -9919,7 +9962,7 @@ public class BatteryStatsImpl extends BatteryStats { t.startRunningLocked(elapsedRealtimeMs); if (sensor != Sensor.GPS) { StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, sensor, - 1); + StatsLog.SENSOR_STATE_CHANGED__STATE__ON); } } @@ -9930,7 +9973,7 @@ public class BatteryStatsImpl extends BatteryStats { t.stopRunningLocked(elapsedRealtimeMs); if (sensor != Sensor.GPS) { StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, - sensor, 0); + sensor, StatsLog.SENSOR_STATE_CHANGED__STATE__OFF); } } } diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index f9a2341fc3e3..e69a36064693 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -55,6 +55,8 @@ public final class Zygote { public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10; /** Do not enfore hidden API access restrictions. */ public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11; + /** Force generation of native debugging information for backtraces. */ + public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12; /** No external storage should be mounted. */ public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE; diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 39279b5055b1..74802c8bdf79 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -583,7 +583,7 @@ public class ZygoteInit { installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName, instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter, uuid, classLoaderContext, seInfo, false /* downgrade */, - targetSdkVersion, /*profileName*/ null); + targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null); } catch (RemoteException | ServiceSpecificException e) { // Ignore (but log), we need this on the classpath for fallback mode. Log.w(TAG, "Failed compiling classpath element for system server: " diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 95bc352747d2..eadefc919934 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -86,6 +86,7 @@ import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.PopupWindow; +import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; @@ -2216,7 +2217,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind elevation = dipToPx(elevation); mElevationAdjustedForStack = true; } else if (windowingMode == WINDOWING_MODE_PINNED) { - elevation = dipToPx(DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP); + elevation = dipToPx(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP); mElevationAdjustedForStack = true; } else { mElevationAdjustedForStack = false; diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index ebb5f9f9e446..f70d3c2a27fd 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -132,6 +132,12 @@ oneway interface IStatusBar void clickQsTile(in ComponentName tile); void handleSystemKey(in int key); + /** + * Methods to show toast messages for screen pinning + */ + void showPinningEnterExitToast(boolean entering); + void showPinningEscapeToast(); + void showShutdownUi(boolean isReboot, String reason); // Used to show the dialog when FingerprintService starts authentication diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index cb0b53c495dc..adf42878ebb3 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -81,6 +81,12 @@ interface IStatusBarService void clickTile(in ComponentName tile); void handleSystemKey(in int key); + /** + * Methods to show toast messages for screen pinning + */ + void showPinningEnterExitToast(boolean entering); + void showPinningEscapeToast(); + // Used to show the dialog when FingerprintService starts authentication void showFingerprintDialog(in Bundle bundle, IFingerprintDialogReceiver receiver); // Used to hide the dialog when a finger is authenticated diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java index 71550be1c8d7..f8885a20970d 100644 --- a/core/java/com/android/internal/util/FileRotator.java +++ b/core/java/com/android/internal/util/FileRotator.java @@ -160,7 +160,7 @@ public class FileRotator { final File file = new File(mBasePath, name); final FileInputStream is = new FileInputStream(file); try { - Streams.copy(is, zos); + FileUtils.copy(is, zos); } finally { IoUtils.closeQuietly(is); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 5e0a986b432b..02822869f421 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -68,6 +68,7 @@ interface IInputMethodManager { void showInputMethodPickerFromClient(in IInputMethodClient client, int auxiliarySubtypeMode); void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId); + boolean isInputMethodPickerShownForTest(); void setInputMethod(in IBinder token, String id); void setInputMethodAndSubtype(in IBinder token, String id, in InputMethodSubtype subtype); void hideMySoftInput(in IBinder token, int flags); diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 5673814ca362..732534ccbcbe 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -66,6 +66,8 @@ interface ILockSettings { void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList); KeyChainSnapshot getKeyChainSnapshot(); byte[] generateAndStoreKey(String alias); + String generateKey(String alias, in byte[] account); + String getKey(String alias); void removeKey(String alias); void setSnapshotCreatedPendingIntent(in PendingIntent intent); Map getRecoverySnapshotVersions(); diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 32a7a2dadddb..7a248f264274 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -118,6 +118,7 @@ public class LockPatternView extends View { private float mInProgressY = -1; private long mAnimatingPeriodStart; + private long[] mLineFadeStart = new long[9]; private DisplayMode mPatternDisplayMode = DisplayMode.Correct; private boolean mInputEnabled = true; @@ -596,12 +597,14 @@ public class LockPatternView extends View { } /** - * Clear the pattern lookup table. + * Clear the pattern lookup table. Also reset the line fade start times for + * the next attempt. */ private void clearPatternDrawLookup() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { mPatternDrawLookup[i][j] = false; + mLineFadeStart[i+j] = 0; } } } @@ -1136,7 +1139,8 @@ public class LockPatternView extends View { boolean anyCircles = false; float lastX = 0f; float lastY = 0f; - for (int i = 0; i < count; i++) { + long elapsedRealtime = SystemClock.elapsedRealtime(); + for (int i = 0; i < count; i++) { Cell cell = pattern.get(i); // only draw the part of the pattern stored in @@ -1147,16 +1151,26 @@ public class LockPatternView extends View { } anyCircles = true; + if (mLineFadeStart[i] == 0) { + mLineFadeStart[i] = SystemClock.elapsedRealtime(); + } + float centerX = getCenterXForColumn(cell.column); float centerY = getCenterYForRow(cell.row); if (i != 0) { + // Set this line segment to slowly fade over the next second. + int lineFadeVal = (int) Math.min((elapsedRealtime - + mLineFadeStart[i])/2f, 255f); + CellState state = mCellStates[cell.row][cell.column]; currentPath.rewind(); currentPath.moveTo(lastX, lastY); if (state.lineEndX != Float.MIN_VALUE && state.lineEndY != Float.MIN_VALUE) { currentPath.lineTo(state.lineEndX, state.lineEndY); + mPathPaint.setAlpha((int) 255 - lineFadeVal ); } else { currentPath.lineTo(centerX, centerY); + mPathPaint.setAlpha((int) 255 - lineFadeVal ); } canvas.drawPath(currentPath, mPathPaint); } diff --git a/core/java/com/android/server/backup/SliceBackupHelper.java b/core/java/com/android/server/backup/SliceBackupHelper.java new file mode 100644 index 000000000000..8e5a5eecec89 --- /dev/null +++ b/core/java/com/android/server/backup/SliceBackupHelper.java @@ -0,0 +1,74 @@ +/* + * 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.backup; + +import android.app.backup.BlobBackupHelper; +import android.app.slice.ISliceManager; +import android.content.Context; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Log; +import android.util.Slog; + +public class SliceBackupHelper extends BlobBackupHelper { + static final String TAG = "SliceBackupHelper"; + static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + // Current version of the blob schema + static final int BLOB_VERSION = 1; + + // Key under which the payload blob is stored + static final String KEY_SLICES = "slices"; + + public SliceBackupHelper(Context context) { + super(BLOB_VERSION, KEY_SLICES); + // context is currently unused + } + + @Override + protected byte[] getBackupPayload(String key) { + byte[] newPayload = null; + if (KEY_SLICES.equals(key)) { + try { + ISliceManager sm = ISliceManager.Stub.asInterface( + ServiceManager.getService(Context.SLICE_SERVICE)); + // TODO: http://b/22388012 + newPayload = sm.getBackupPayload(UserHandle.USER_SYSTEM); + } catch (Exception e) { + // Treat as no data + Slog.e(TAG, "Couldn't communicate with slice manager"); + newPayload = null; + } + } + return newPayload; + } + + @Override + protected void applyRestoredPayload(String key, byte[] payload) { + if (DEBUG) Slog.v(TAG, "Got restore of " + key); + + if (KEY_SLICES.equals(key)) { + try { + ISliceManager sm = ISliceManager.Stub.asInterface( + ServiceManager.getService(Context.SLICE_SERVICE)); + // TODO: http://b/22388012 + sm.applyRestore(payload, UserHandle.USER_SYSTEM); + } catch (Exception e) { + Slog.e(TAG, "Couldn't communicate with slice manager"); + } + } + } + +} diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java index a96b5dd3ed70..47e7a0e74563 100644 --- a/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -51,6 +51,7 @@ public class SystemBackupAgent extends BackupAgentHelper { private static final String USAGE_STATS_HELPER = "usage_stats"; private static final String SHORTCUT_MANAGER_HELPER = "shortcut_manager"; private static final String ACCOUNT_MANAGER_HELPER = "account_manager"; + private static final String SLICES_HELPER = "slices"; // These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME // are also used in the full-backup file format, so must not change unless steps are @@ -88,6 +89,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this)); addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper()); addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper()); + addHelper(SLICES_HELPER, new SliceBackupHelper(this)); super.onBackup(oldState, data, newState); } @@ -116,6 +118,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this)); addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper()); addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper()); + addHelper(SLICES_HELPER, new SliceBackupHelper(this)); super.onRestore(data, appVersionCode, newState); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 93f95c634ae3..33f80ce8ffb0 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -164,6 +164,7 @@ cc_library_shared { "android_media_DeviceCallback.cpp", "android_media_JetPlayer.cpp", "android_media_MediaMetricsJNI.cpp", + "android_media_MicrophoneInfo.cpp", "android_media_RemoteDisplay.cpp", "android_media_ToneGenerator.cpp", "android_hardware_Camera.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index bf7a7794fcc6..d20217386b1e 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -104,6 +104,7 @@ extern int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv extern int register_android_media_AudioRecord(JNIEnv *env); extern int register_android_media_AudioSystem(JNIEnv *env); extern int register_android_media_AudioTrack(JNIEnv *env); +extern int register_android_media_MicrophoneInfo(JNIEnv *env); extern int register_android_media_JetPlayer(JNIEnv *env); extern int register_android_media_ToneGenerator(JNIEnv *env); @@ -765,18 +766,17 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) /* * Enable debugging only for apps forked from zygote. - * Set suspend=y to pause during VM init and use android ADB transport. */ if (zygote) { + // Set the JDWP provider and required arguments. By default let the runtime choose how JDWP is + // implemented. When this is not set the runtime defaults to not allowing JDWP. addOption("-XjdwpOptions:suspend=n,server=y"); + parseRuntimeOption("dalvik.vm.jdwp-provider", + jdwpProviderBuf, + "-XjdwpProvider:", + "default"); } - // Set the JDWP provider. By default let the runtime choose. - parseRuntimeOption("dalvik.vm.jdwp-provider", - jdwpProviderBuf, - "-XjdwpProvider:", - "default"); - parseRuntimeOption("dalvik.vm.lockprof.threshold", lockProfThresholdBuf, "-Xlockprofthreshold:"); @@ -1461,6 +1461,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_media_AudioSystem), REG_JNI(register_android_media_AudioTrack), REG_JNI(register_android_media_JetPlayer), + REG_JNI(register_android_media_MicrophoneInfo), REG_JNI(register_android_media_RemoteDisplay), REG_JNI(register_android_media_ToneGenerator), diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp index c88cf5cff04d..ba56d592acef 100644 --- a/core/jni/android/graphics/AnimatedImageDrawable.cpp +++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp @@ -26,10 +26,11 @@ #include <SkPictureRecorder.h> #include <hwui/AnimatedImageDrawable.h> #include <hwui/Canvas.h> +#include <utils/Looper.h> using namespace android; -static jmethodID gAnimatedImageDrawable_postOnAnimationEndMethodID; +static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID; // Note: jpostProcess holds a handle to the ImageDecoder. static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, @@ -123,9 +124,9 @@ static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlo return drawable->start(); } -static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { +static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); - drawable->stop(); + return drawable->stop(); } // Java's LOOP_INFINITE relies on this being the same. @@ -137,33 +138,63 @@ static void AnimatedImageDrawable_nSetLoopCount(JNIEnv* env, jobject /*clazz*/, drawable->setRepetitionCount(loopCount); } -class JniAnimationEndListener : public OnAnimationEndListener { +class InvokeListener : public MessageHandler { public: - JniAnimationEndListener(JNIEnv* env, jobject javaObject) { + InvokeListener(JNIEnv* env, jobject javaObject) { LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK); - mJavaObject = env->NewGlobalRef(javaObject); + // Hold a weak reference to break a cycle that would prevent GC. + mWeakRef = env->NewWeakGlobalRef(javaObject); } - ~JniAnimationEndListener() override { + ~InvokeListener() override { auto* env = get_env_or_die(mJvm); - env->DeleteGlobalRef(mJavaObject); + env->DeleteWeakGlobalRef(mWeakRef); } - void onAnimationEnd() override { + virtual void handleMessage(const Message&) override { auto* env = get_env_or_die(mJvm); - env->CallVoidMethod(mJavaObject, gAnimatedImageDrawable_postOnAnimationEndMethodID); + jobject localRef = env->NewLocalRef(mWeakRef); + if (localRef) { + env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID); + } } private: JavaVM* mJvm; - jobject mJavaObject; + jweak mWeakRef; +}; + +class JniAnimationEndListener : public OnAnimationEndListener { +public: + JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) { + mListener = new InvokeListener(env, javaObject); + mLooper = std::move(looper); + } + + void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); } + +private: + sp<InvokeListener> mListener; + sp<Looper> mLooper; }; static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jobject jdrawable) { auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); - drawable->setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener>( - new JniAnimationEndListener(env, jdrawable))); + if (!jdrawable) { + drawable->setOnAnimationEndListener(nullptr); + } else { + sp<Looper> looper = Looper::getForThread(); + if (!looper.get()) { + doThrowISE(env, + "Must set AnimatedImageDrawable's AnimationCallback on a thread with a " + "looper!"); + return; + } + + drawable->setOnAnimationEndListener( + std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable)); + } } static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { @@ -186,7 +217,7 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = { { "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter }, { "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning }, { "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart }, - { "nStop", "(J)V", (void*) AnimatedImageDrawable_nStop }, + { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop }, { "nSetLoopCount", "(JI)V", (void*) AnimatedImageDrawable_nSetLoopCount }, { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener }, { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize }, @@ -195,7 +226,7 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = { int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) { jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable"); - gAnimatedImageDrawable_postOnAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "postOnAnimationEnd", "()V"); + gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V"); return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable", gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods)); diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index dd3e6f02e9fe..937b3ffb9d60 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -44,11 +44,9 @@ namespace android { struct NativeFamilyBuilder { NativeFamilyBuilder(uint32_t langId, int variant) - : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)), - allowUnsupportedFont(false) {} + : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)) {} uint32_t langId; minikin::FontFamily::Variant variant; - bool allowUnsupportedFont; std::vector<minikin::Font> fonts; std::vector<minikin::FontVariation> axes; }; @@ -70,22 +68,17 @@ static jlong FontFamily_create(jlong builderPtr) { } std::unique_ptr<NativeFamilyBuilder> builder( reinterpret_cast<NativeFamilyBuilder*>(builderPtr)); + if (builder->fonts.empty()) { + return 0; + } std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>( builder->langId, builder->variant, std::move(builder->fonts)); - if (family->getCoverage().length() == 0 && !builder->allowUnsupportedFont) { + if (family->getCoverage().length() == 0) { return 0; } return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family))); } -static void FontFamily_allowUnsupportedFont(jlong builderPtr) { - if (builderPtr == 0) { - return; - } - NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); - builder->allowUnsupportedFont = true; -} - static void FontFamily_abort(jlong builderPtr) { NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); delete builder; @@ -270,7 +263,6 @@ static void FontFamily_addAxisValue(jlong builderPtr, jint tag, jfloat value) { static const JNINativeMethod gFontFamilyMethods[] = { { "nInitBuilder", "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder }, { "nCreateFamily", "(J)J", (void*)FontFamily_create }, - { "nAllowUnsupportedFont", "(J)V", (void*)FontFamily_allowUnsupportedFont }, { "nAbort", "(J)V", (void*)FontFamily_abort }, { "nUnrefFamily", "(J)V", (void*)FontFamily_unref }, { "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont }, diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index c79f5bd96e99..12da27307ade 100644 --- a/core/jni/android_media_AudioFormat.h +++ b/core/jni/android_media_AudioFormat.h @@ -36,6 +36,7 @@ #define ENCODING_AAC_ELD 15 #define ENCODING_AAC_XHE 16 #define ENCODING_AC4 17 +#define ENCODING_E_AC3_JOC 18 #define ENCODING_INVALID 0 #define ENCODING_DEFAULT 1 @@ -80,6 +81,8 @@ static inline audio_format_t audioFormatToNative(int audioFormat) return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC case ENCODING_AC4: return AUDIO_FORMAT_AC4; + // case ENCODING_E_AC3_JOC: // FIXME Not defined on the native side yet + // return AUDIO_FORMAT_E_AC3_JOC; case ENCODING_DEFAULT: return AUDIO_FORMAT_DEFAULT; default: @@ -130,6 +133,8 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat) // return ENCODING_AAC_XHE; case AUDIO_FORMAT_AC4: return ENCODING_AC4; + // case AUDIO_FORMAT_E_AC3_JOC: // FIXME Not defined on the native side yet + // return ENCODING_E_AC3_JOC; case AUDIO_FORMAT_DEFAULT: return ENCODING_DEFAULT; default: diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index ebd16c7084ac..375d68b85824 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -25,6 +25,8 @@ #include <utils/Log.h> #include <media/AudioRecord.h> +#include <media/MicrophoneInfo.h> +#include <vector> #include <nativehelper/ScopedUtfChars.h> @@ -32,6 +34,7 @@ #include "android_media_AudioErrors.h" #include "android_media_DeviceCallback.h" #include "android_media_MediaMetricsJNI.h" +#include "android_media_MicrophoneInfo.h" // ---------------------------------------------------------------------------- @@ -41,6 +44,11 @@ using namespace android; static const char* const kClassPathName = "android/media/AudioRecord"; static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes"; +static jclass gArrayListClass; +static struct { + jmethodID add; +} gArrayListMethods; + struct audio_record_fields_t { // these fields provide access from C++ to the... jmethodID postNativeEventInJava; //... event post callback method @@ -785,6 +793,46 @@ android_media_AudioRecord_native_getMetrics(JNIEnv *env, jobject thiz) } // ---------------------------------------------------------------------------- +static jint android_media_AudioRecord_get_active_microphones(JNIEnv *env, + jobject thiz, jobject jActiveMicrophones) { + if (jActiveMicrophones == NULL) { + ALOGE("jActiveMicrophones is null"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jActiveMicrophones, gArrayListClass)) { + ALOGE("getActiveMicrophones not an arraylist"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); + if (lpRecorder == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioRecord pointer for getActiveMicrophones()"); + return (jint)AUDIO_JAVA_ERROR; + } + + jint jStatus = AUDIO_JAVA_SUCCESS; + std::vector<media::MicrophoneInfo> activeMicrophones; + status_t status = lpRecorder->getActiveMicrophones(&activeMicrophones); + if (status != NO_ERROR) { + ALOGE_IF(status != NO_ERROR, "AudioRecord::getActiveMicrophones error %d", status); + jStatus = nativeToJavaStatus(status); + return jStatus; + } + + for (size_t i = 0; i < activeMicrophones.size(); i++) { + jobject jMicrophoneInfo; + jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &activeMicrophones[i]); + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; + } + env->CallBooleanMethod(jActiveMicrophones, gArrayListMethods.add, jMicrophoneInfo); + env->DeleteLocalRef(jMicrophoneInfo); + } + return jStatus; +} + +// ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { // name, signature, funcPtr @@ -824,6 +872,8 @@ static const JNINativeMethod gMethods[] = { (void *)android_media_AudioRecord_disableDeviceCallback}, {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I", (void *)android_media_AudioRecord_get_timestamp}, + {"native_get_active_microphones", "(Ljava/util/ArrayList;)I", + (void *)android_media_AudioRecord_get_active_microphones}, }; // field names found in android/media/AudioRecord.java @@ -873,6 +923,10 @@ int register_android_media_AudioRecord(JNIEnv *env) javaAudioTimestampFields.fieldNanoTime = GetFieldIDOrDie(env, audioTimestampClass, "nanoTime", "J"); + jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList"); + gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass); + gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z"); + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 376a79717677..adaff1fcfbcb 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -21,17 +21,20 @@ #include <utils/Log.h> #include <sstream> +#include <vector> #include <jni.h> #include <nativehelper/JNIHelp.h> #include "core_jni_helpers.h" #include <media/AudioSystem.h> #include <media/AudioPolicy.h> +#include <media/MicrophoneInfo.h> #include <nativehelper/ScopedLocalRef.h> #include <system/audio.h> #include <system/audio_policy.h> #include "android_media_AudioFormat.h" #include "android_media_AudioErrors.h" +#include "android_media_MicrophoneInfo.h" // ---------------------------------------------------------------------------- @@ -143,7 +146,6 @@ static struct { jfieldID mSource; } gAudioAttributesFields; - static const char* const kEventHandlerClassPathName = "android/media/AudioPortEventHandler"; static struct { @@ -1158,7 +1160,6 @@ exit: return jStatus; } - static jint android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz, jobject jPorts, jintArray jGeneration) @@ -1789,6 +1790,45 @@ android_media_AudioSystem_isOffloadSupported(JNIEnv *env, jobject thiz, return AudioSystem::isOffloadSupported(format); } +static jint +android_media_AudioSystem_getMicrophones(JNIEnv *env, jobject thiz, jobject jMicrophonesInfo) +{ + ALOGV("getMicrophones"); + + if (jMicrophonesInfo == NULL) { + ALOGE("jMicrophonesInfo NULL MicrophoneInfo ArrayList"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jMicrophonesInfo, gArrayListClass)) { + ALOGE("getMicrophones not an arraylist"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + jint jStatus; + std::vector<media::MicrophoneInfo> microphones; + status_t status = AudioSystem::getMicrophones(µphones); + if (status != NO_ERROR) { + ALOGE_IF(status != NO_ERROR, "AudioSystem::getMicrophones error %d", status); + jStatus = nativeToJavaStatus(status); + return jStatus; + } + if (microphones.size() == 0) { + jStatus = (jint)AUDIO_JAVA_SUCCESS; + return jStatus; + } + for (size_t i = 0; i < microphones.size(); i++) { + jobject jMicrophoneInfo; + jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, µphones[i]); + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; + } + env->CallBooleanMethod(jMicrophonesInfo, gArrayListMethods.add, jMicrophoneInfo); + env->DeleteLocalRef(jMicrophoneInfo); + } + + return jStatus; +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { @@ -1843,6 +1883,7 @@ static const JNINativeMethod gMethods[] = { {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady}, {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB}, {"native_is_offload_supported", "(IIII)Z", (void *)android_media_AudioSystem_isOffloadSupported}, + {"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones}, }; diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index afbc57913688..61a22c144f1b 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -1262,6 +1262,18 @@ static jobject android_media_AudioTrack_get_volume_shaper_state(JNIEnv *env, job return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state); } +static int android_media_AudioTrack_setPresentation( + JNIEnv *env, jobject thiz, jint presentationId, jint programId) { + sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); + if (lpTrack == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "AudioTrack not initialized"); + return (jint)AUDIO_JAVA_ERROR; + } + + return (jint)lpTrack->selectPresentation((int)presentationId, (int)programId); +} + // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { @@ -1333,6 +1345,7 @@ static const JNINativeMethod gMethods[] = { {"native_getVolumeShaperState", "(I)Landroid/media/VolumeShaper$State;", (void *)android_media_AudioTrack_get_volume_shaper_state}, + {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation}, }; diff --git a/core/jni/android_media_MicrophoneInfo.cpp b/core/jni/android_media_MicrophoneInfo.cpp new file mode 100644 index 000000000000..9198cbe648eb --- /dev/null +++ b/core/jni/android_media_MicrophoneInfo.cpp @@ -0,0 +1,187 @@ +/* + * 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 "android_media_MicrophoneInfo.h" +#include "android_media_AudioErrors.h" +#include "core_jni_helpers.h" + +using namespace android; + +static jclass gArrayListClass; +static jmethodID gArrayListCstor; +static struct { + jmethodID add; +} gArrayListMethods; + +static jclass gFloatClass; +static jmethodID gFloatCstor; + +static jclass gFloatArrayClass; + +static jclass gIntegerClass; +static jmethodID gIntegerCstor; + +static jclass gMicrophoneInfoClass; +static jmethodID gMicrophoneInfoCstor; + +static jclass gMicrophoneInfoCoordinateClass; +static jmethodID gMicrophoneInfoCoordinateCstor; + +static jclass gPairClass; +static jmethodID gPairCstor; + +namespace android { + +jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo, + const media::MicrophoneInfo *microphoneInfo) +{ + jint jStatus = (jint)AUDIO_JAVA_SUCCESS; + jstring jDeviceId = NULL; + jstring jAddress = NULL; + jobject jGeometricLocation = NULL; + jobject jOrientation = NULL; + jobject jFrequencyResponses = NULL; + jobject jChannelMappings = NULL; + + jDeviceId = env->NewStringUTF(String8(microphoneInfo->getDeviceId()).string()); + jAddress = env->NewStringUTF(String8(microphoneInfo->getAddress()).string()); + if (microphoneInfo->getGeometricLocation().size() != 3 || + microphoneInfo->getOrientation().size() != 3) { + jStatus = nativeToJavaStatus(BAD_VALUE); + goto exit; + } + jGeometricLocation = env->NewObject(gMicrophoneInfoCoordinateClass, + gMicrophoneInfoCoordinateCstor, + NULL, + microphoneInfo->getGeometricLocation()[0], + microphoneInfo->getGeometricLocation()[1], + microphoneInfo->getGeometricLocation()[2]); + jOrientation = env->NewObject(gMicrophoneInfoCoordinateClass, + gMicrophoneInfoCoordinateCstor, + NULL, + microphoneInfo->getOrientation()[0], + microphoneInfo->getOrientation()[1], + microphoneInfo->getOrientation()[2]); + // Create a list of Pair for frequency response. + if (microphoneInfo->getFrequencyResponses().size() != 2 || + microphoneInfo->getFrequencyResponses()[0].size() != + microphoneInfo->getFrequencyResponses()[1].size()) { + jStatus = nativeToJavaStatus(BAD_VALUE); + goto exit; + } + jFrequencyResponses = env->NewObject(gArrayListClass, gArrayListCstor); + for (size_t i = 0; i < microphoneInfo->getFrequencyResponses()[0].size(); i++) { + jobject jFrequency = env->NewObject(gFloatClass, gFloatCstor, + microphoneInfo->getFrequencyResponses()[0][i]); + jobject jResponse = env->NewObject(gFloatClass, gFloatCstor, + microphoneInfo->getFrequencyResponses()[1][i]); + jobject jFrequencyResponse = env->NewObject(gPairClass, gPairCstor, jFrequency, jResponse); + env->CallBooleanMethod(jFrequencyResponses, gArrayListMethods.add, jFrequencyResponse); + env->DeleteLocalRef(jFrequency); + env->DeleteLocalRef(jResponse); + env->DeleteLocalRef(jFrequencyResponse); + } + // Create a list of Pair for channel mapping. + if (microphoneInfo->getChannelMapping().size() != AUDIO_CHANNEL_COUNT_MAX) { + jStatus = nativeToJavaStatus(BAD_VALUE); + goto exit; + } + jChannelMappings = env->NewObject(gArrayListClass, gArrayListCstor); + for (size_t i = 0; i < microphoneInfo->getChannelMapping().size(); i++) { + int channelMappingType = microphoneInfo->getChannelMapping()[i]; + if (channelMappingType != AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED) { + jobject jChannelIndex = env->NewObject(gIntegerClass, gIntegerCstor, i); + jobject jChannelMappingType = env->NewObject(gIntegerClass, gIntegerCstor, + channelMappingType); + jobject jChannelMapping = env->NewObject(gPairClass, gPairCstor, + jChannelIndex, jChannelMappingType); + env->CallBooleanMethod(jChannelMappings, gArrayListMethods.add, jChannelMapping); + env->DeleteLocalRef(jChannelIndex); + env->DeleteLocalRef(jChannelMappingType); + env->DeleteLocalRef(jChannelMapping); + } + } + *jMicrophoneInfo = env->NewObject(gMicrophoneInfoClass, gMicrophoneInfoCstor, jDeviceId, + microphoneInfo->getType(), jAddress, + microphoneInfo->getDeviceLocation(), + microphoneInfo->getDeviceGroup(), + microphoneInfo->getIndexInTheGroup(), + jGeometricLocation, jOrientation, + jFrequencyResponses, jChannelMappings, + microphoneInfo->getSensitivity(), + microphoneInfo->getMaxSpl(), + microphoneInfo->getMinSpl(), + microphoneInfo->getDirectionality()); + +exit: + if (jDeviceId != NULL) { + env->DeleteLocalRef(jDeviceId); + } + if (jAddress != NULL) { + env->DeleteLocalRef(jAddress); + } + if (jFrequencyResponses != NULL) { + env->DeleteLocalRef(jFrequencyResponses); + } + if (jChannelMappings != NULL) { + env->DeleteLocalRef(jChannelMappings); + } + if (jGeometricLocation != NULL) { + env->DeleteLocalRef(jGeometricLocation); + } + if (jOrientation != NULL) { + env->DeleteLocalRef(jOrientation); + } + return jStatus; +} + +} + +int register_android_media_MicrophoneInfo(JNIEnv *env) +{ + jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList"); + gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass); + gArrayListCstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V"); + gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z"); + + jclass floatClass = FindClassOrDie(env, "java/lang/Float"); + gFloatClass = MakeGlobalRefOrDie(env, floatClass); + gFloatCstor = GetMethodIDOrDie(env, floatClass, "<init>", "(F)V"); + + jclass floatArrayClass = FindClassOrDie(env, "[F"); + gFloatArrayClass = MakeGlobalRefOrDie(env, floatArrayClass); + + jclass integerClass = FindClassOrDie(env, "java/lang/Integer"); + gIntegerClass = MakeGlobalRefOrDie(env, integerClass); + gIntegerCstor = GetMethodIDOrDie(env, integerClass, "<init>", "(I)V"); + + jclass microphoneInfoClass = FindClassOrDie(env, "android/media/MicrophoneInfo"); + gMicrophoneInfoClass = MakeGlobalRefOrDie(env, microphoneInfoClass); + gMicrophoneInfoCstor = GetMethodIDOrDie(env, microphoneInfoClass, "<init>", + "(Ljava/lang/String;ILjava/lang/String;IIILandroid/media/MicrophoneInfo$Coordinate3F;Landroid/media/MicrophoneInfo$Coordinate3F;Ljava/util/List;Ljava/util/List;FFFI)V"); + + jclass microphoneInfoCoordinateClass = FindClassOrDie( + env, "android/media/MicrophoneInfo$Coordinate3F"); + gMicrophoneInfoCoordinateClass = MakeGlobalRefOrDie(env, microphoneInfoCoordinateClass); + gMicrophoneInfoCoordinateCstor = GetMethodIDOrDie(env, microphoneInfoCoordinateClass, "<init>", + "(Landroid/media/MicrophoneInfo;FFF)V"); + + jclass pairClass = FindClassOrDie(env, "android/util/Pair"); + gPairClass = MakeGlobalRefOrDie(env, pairClass); + gPairCstor = GetMethodIDOrDie(env, pairClass, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V"); + + return 0; +} diff --git a/core/jni/android_media_MicrophoneInfo.h b/core/jni/android_media_MicrophoneInfo.h new file mode 100644 index 000000000000..241b6d05b724 --- /dev/null +++ b/core/jni/android_media_MicrophoneInfo.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef ANDROID_MEDIA_MICROPHONEINFO_H +#define ANDROID_MEDIA_MICROPHONEINFO_H + +#include <system/audio.h> +#include <media/MicrophoneInfo.h> + +#include "jni.h" + +namespace android { + +// Conversion from C++ MicrophoneInfo object to Java MicrophoneInfo object + +extern jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo, + const media::MicrophoneInfo *microphoneInfo); +} // namespace android + +#endif
\ No newline at end of file diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index 37ff8c8cefd2..8770d788fc37 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -174,8 +174,25 @@ static jboolean android_view_RenderNode_hasShadow(jlong renderNodePtr) { return renderNode->stagingProperties().hasShadow(); } -static jboolean android_view_RenderNode_setShadowColor(jlong renderNodePtr, jint shadowColor) { - return SET_AND_DIRTY(setShadowColor, static_cast<SkColor>(shadowColor), RenderNode::GENERIC); +static jboolean android_view_RenderNode_setSpotShadowColor(jlong renderNodePtr, jint shadowColor) { + return SET_AND_DIRTY(setSpotShadowColor, + static_cast<SkColor>(shadowColor), RenderNode::GENERIC); +} + +static jint android_view_RenderNode_getSpotShadowColor(jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getSpotShadowColor(); +} + +static jboolean android_view_RenderNode_setAmbientShadowColor(jlong renderNodePtr, + jint shadowColor) { + return SET_AND_DIRTY(setAmbientShadowColor, + static_cast<SkColor>(shadowColor), RenderNode::GENERIC); +} + +static jint android_view_RenderNode_getAmbientShadowColor(jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getAmbientShadowColor(); } static jboolean android_view_RenderNode_setClipToOutline(jlong renderNodePtr, @@ -575,7 +592,10 @@ static const JNINativeMethod gMethods[] = { { "nSetOutlineEmpty", "(J)Z", (void*) android_view_RenderNode_setOutlineEmpty }, { "nSetOutlineNone", "(J)Z", (void*) android_view_RenderNode_setOutlineNone }, { "nHasShadow", "(J)Z", (void*) android_view_RenderNode_hasShadow }, - { "nSetShadowColor", "(JI)Z", (void*) android_view_RenderNode_setShadowColor }, + { "nSetSpotShadowColor", "(JI)Z", (void*) android_view_RenderNode_setSpotShadowColor }, + { "nGetSpotShadowColor", "(J)I", (void*) android_view_RenderNode_getSpotShadowColor }, + { "nSetAmbientShadowColor","(JI)Z", (void*) android_view_RenderNode_setAmbientShadowColor }, + { "nGetAmbientShadowColor","(J)I", (void*) android_view_RenderNode_getAmbientShadowColor }, { "nSetClipToOutline", "(JZ)Z", (void*) android_view_RenderNode_setClipToOutline }, { "nSetRevealClip", "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip }, diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto index bfd575a5b18d..ff0f4fd4d3ac 100644 --- a/core/proto/android/providers/settings.proto +++ b/core/proto/android/providers/settings.proto @@ -208,7 +208,7 @@ message GlobalSettingsProto { optional SettingProto wifi_on = 136 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto wifi_scan_always_available = 137 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto wifi_wakeup_enabled = 138 [ (android.privacy).dest = DEST_AUTOMATIC ]; - optional SettingProto wifi_wakeup_available = 305 [ (android.privacy).dest = DEST_AUTOMATIC ]; + reserved 305; // Removed wifi_wakeup_available optional SettingProto network_scoring_ui_enabled = 306 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto speed_label_cache_eviction_age_millis = 307 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto recommended_network_evaluator_cache_expiry_ms = 308 [ (android.privacy).dest = DEST_AUTOMATIC ]; diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index c11058a22107..449e54672207 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -295,7 +295,6 @@ message WindowStateProto { optional bool animating_exit = 14; repeated WindowStateProto child_windows = 15; optional .android.graphics.RectProto surface_position = 16; - optional .android.graphics.RectProto shown_position = 17; optional int32 requested_width = 18; optional int32 requested_height = 19; optional int32 view_visibility = 20; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index e5ba6d76578d..d58b95a6697c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1785,13 +1785,22 @@ <!-- Must be required by an ImsService to ensure that only the system can bind to it. - <p>Protection level: signature|privileged + <p>Protection level: signature|privileged|vendorPrivileged @SystemApi @hide --> <permission android:name="android.permission.BIND_IMS_SERVICE" android:protectionLevel="signature|privileged|vendorPrivileged" /> + <!-- Must be required by a DataService to ensure that only the + system can bind to it. + <p>Protection level: signature|privileged|vendorPrivileged + @SystemApi + @hide + --> + <permission android:name="android.permission.BIND_DATA_SERVICE" + android:protectionLevel="signature|privileged|vendorPrivileged" /> + <!-- Allows an application to manage embedded subscriptions (those on a eUICC) through EuiccManager APIs. <p>Protection level: signature|privileged|development @@ -3029,6 +3038,13 @@ <permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE" android:protectionLevel="signature|privileged|development" /> + <!-- Allows an application to collect ambient light stats. + <p>Not for use by third party applications.</p> + TODO: Make a system API + @hide --> + <permission android:name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" + android:protectionLevel="signature|privileged|development" /> + <!-- Allows an application to modify the display brightness configuration @hide @SystemApi --> diff --git a/core/res/res/layout/popup_menu_item_layout.xml b/core/res/res/layout/popup_menu_item_layout.xml index 3b89f0d0cde7..1be915e809e6 100644 --- a/core/res/res/layout/popup_menu_item_layout.xml +++ b/core/res/res/layout/popup_menu_item_layout.xml @@ -28,18 +28,18 @@ android:layout_marginBottom="4dip" android:background="@drawable/list_divider_material" /> - <!-- Icon will be inserted here. --> - - <!-- The title and summary have some gap between them, - and this 'group' should be centered vertically. --> <LinearLayout android:id="@+id/content" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="?attr/dropdownListPreferredItemHeight" android:paddingEnd="16dip" android:duplicateParentState="true" > - <LinearLayout + <!-- Icon will be inserted here. --> + + <!-- The title and summary have some gap between them, + and this 'group' should be centered vertically. --> + <RelativeLayout android:layout_width="0dip" android:layout_weight="1" android:layout_height="wrap_content" @@ -71,7 +71,7 @@ android:duplicateParentState="true" android:textAlignment="viewStart" /> - </LinearLayout> + </RelativeLayout> <ImageView android:id="@+id/submenuarrow" @@ -81,8 +81,9 @@ android:layout_marginStart="8dp" android:scaleType="center" android:visibility="gone" /> - </LinearLayout> - <!-- Checkbox, and/or radio button will be inserted here. --> + <!-- Checkbox, and/or radio button will be inserted here. --> + + </LinearLayout> </com.android.internal.view.menu.ListMenuItemView> diff --git a/core/res/res/layout/unlaunchable_app_activity.xml b/core/res/res/layout/unlaunchable_app_activity.xml deleted file mode 100644 index 429d5edea8b7..000000000000 --- a/core/res/res/layout/unlaunchable_app_activity.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="@dimen/dialog_padding" - android:orientation="vertical"> - <TextView android:id="@+id/unlaunchable_app_title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="@dimen/dialog_padding" - android:paddingBottom="@dimen/dialog_padding" - android:textAppearance="@android:style/TextAppearance.Material.Title" /> - - <TextView android:id="@+id/unlaunchable_app_message" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="@dimen/dialog_padding" - android:textAppearance="@android:style/TextAppearance.Material.Subhead" - android:textColor="?android:attr/textColorSecondary" /> -</LinearLayout> - diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 0543305f9880..68dad87bd292 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3036,6 +3036,28 @@ <!-- The title this view should present to accessibility as a pane title. See {@link android.view.View#setAccessibilityPaneTitle(CharSequence)} --> <attr name="accessibilityPaneTitle" format="string" /> + + <!-- Sets the color of the spot shadow that is drawn when the view has a positive Z or + elevation value. + <p> + By default the shadow color is black. Generally, this color will be opaque so the + intensity of the shadow is consistent between different views with different colors. + <p> + The opacity of the final spot shadow is a function of the shadow caster height, the + alpha channel of the outlineSpotShadowColor (typically opaque), and the + {@link android.R.attr#spotShadowAlpha} theme attribute. --> + <attr name="outlineSpotShadowColor" format="color" /> + + <!-- Sets the color of the ambient shadow that is drawn when the view has a positive Z + or elevation value. + <p> + By default the shadow color is black. Generally, this color will be opaque so the + intensity of the shadow is consistent between different views with different colors. + <p> + The opacity of the final ambient shadow is a function of the shadow caster height, + the alpha channel of the outlineAmbientShadowColor (typically opaque), and the + {@link android.R.attr#ambientShadowAlpha} theme attribute. --> + <attr name="outlineAmbientShadowColor" format="color" /> </declare-styleable> <!-- Attributes that can be assigned to a tag for a particular View. --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 607414d666cc..05d51ec8e48e 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -432,12 +432,6 @@ <!-- Activity name to enable wifi tethering after provisioning app succeeds --> <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string> - <!-- Controls the WiFi wakeup feature. - 0 = Not available. - 1 = Available. - --> - <integer translatable="false" name="config_wifi_wakeup_available">1</integer> - <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering. Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or @@ -1132,7 +1126,7 @@ <bool name="config_hotswapCapable">false</bool> <!-- Component name of the ICC hotswap prompt for restart dialog --> - <string name="config_iccHotswapPromptForRestartDialogComponent" translateable="false">@null</string> + <string name="config_iccHotswapPromptForRestartDialogComponent" translatable="false">@null</string> <!-- Enable puk unlockscreen by default. If unlock screen is disabled, the puk should be unlocked through Emergency Dialer --> @@ -1431,7 +1425,7 @@ permission. [This is only used if config_enableUpdateableTimeZoneRules and config_timeZoneRulesUpdateTrackingEnabled are true.] --> - <string name="config_timeZoneRulesUpdaterPackage" translateable="false">com.android.timezone.updater</string> + <string name="config_timeZoneRulesUpdaterPackage" translatable="false">com.android.timezone.updater</string> <!-- The package of the time zone rules data application. Expected to be configured by OEMs to reference their own priv-app APK package. @@ -1440,7 +1434,7 @@ data app packages. [This is only used if config_enableUpdateableTimeZoneRules and config_timeZoneRulesUpdateTrackingEnabled are true.] --> - <string name="config_timeZoneRulesDataPackage" translateable="false"></string> + <string name="config_timeZoneRulesDataPackage" translatable="false"></string> <!-- The allowed time in milliseconds between an update check intent being broadcast and the response being considered overdue. Reliability triggers will not fire in this time. @@ -3258,7 +3252,7 @@ <string translatable="false" name="config_deviceSpecificAudioService"></string> <!-- Component name of media projection permission dialog --> - <string name="config_mediaProjectionPermissionDialogComponent" translateable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string> + <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string> <!-- Corner radius of system dialogs --> <dimen name="config_dialogCornerRadius">2dp</dimen> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 30586d17cf3c..dee880f0ea2d 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2866,6 +2866,8 @@ <public name="lastBaselineToBottomHeight" /> <public name="lineHeight" /> <public name="accessibilityHeading" /> + <public name="outlineSpotShadowColor" /> + <public name="outlineAmbientShadowColor" /> </public-group> <public-group type="style" first-id="0x010302e0"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 3cde765e04cb..ec81df7b89e2 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1351,6 +1351,10 @@ <string name="fingerprint_error_lockout_permanent">Too many attempts. Fingerprint sensor disabled.</string> <!-- Generic error message shown when the fingerprint hardware can't recognize the fingerprint --> <string name="fingerprint_error_unable_to_process">Try again.</string> + <!-- Generic error message shown when the user has no enrolled fingerprints --> + <string name="fingerprint_error_no_fingerprints">No fingerprints enrolled.</string> + <!-- Generic error message shown when the app requests fingerprint authentication on a device without a sensor --> + <string name="fingerprint_error_hw_not_present">This device does not have a fingerprint sensor</string> <!-- Template to be used to name enrolled fingerprints by default. --> <string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string> @@ -3222,19 +3226,23 @@ <string name="dlg_ok">OK</string> <!-- USB_PREFERENCES: Notification for when the user connected to the charger only. This is the title --> - <string name="usb_charging_notification_title">USB charging this device</string> + <string name="usb_charging_notification_title">Charging this device via USB</string> <!-- USB_PREFERENCES: Notification for when the user connects the phone to supply power to attached device. This is the title --> - <string name="usb_supplying_notification_title">USB supplying power to attached device</string> + <string name="usb_supplying_notification_title">Charging connected device via USB</string> <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in MTP mode. This is the title --> - <string name="usb_mtp_notification_title">USB for file transfer</string> + <string name="usb_mtp_notification_title">USB file transfer turned on</string> <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in PTP mode. This is the title --> - <string name="usb_ptp_notification_title">USB for photo transfer</string> + <string name="usb_ptp_notification_title">PTP via USB turned on</string> + <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in Tethering mode. This is the title --> + <string name="usb_tether_notification_title">USB tethering turned on</string> <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in MIDI mode. This is the title --> - <string name="usb_midi_notification_title">USB for MIDI</string> + <string name="usb_midi_notification_title">MIDI via USB turned on</string> <!-- USB_PREFERENCES: Notification for when a USB accessory is attached. This is the title --> - <string name="usb_accessory_notification_title">Connected to a USB accessory</string> + <string name="usb_accessory_notification_title">USB accessory mode turned on</string> <!-- See USB_PREFERENCES. This is the message. --> <string name="usb_notification_message">Tap for more options.</string> + <!-- See USB_PREFERENCES. This is the message when a data mode is turned on (mtp, ptp, midi) and the device is supplying power.. --> + <string name="usb_power_notification_message">Charging connected device. Tap for more options.</string> <!-- USB_PREFERENCES: Notification for when a type-c USB audio accessory is attached but not supported. This is the title --> <string name="usb_unsupported_audio_accessory_title">Analog audio accessory detected</string> <!-- Message of notification shown when a type-c USB audio accessory is attached but not supported. --> @@ -4419,15 +4427,6 @@ <!-- DO NOT TRANSLATE --> <string name="date_picker_day_typeface">sans-serif-medium</string> - <!-- Notify use that they are in Lock-to-app --> - <string name="lock_to_app_toast">To unpin this screen, touch & hold Back and Overview - buttons</string> - - <!-- Starting lock-to-app indication. --> - <string name="lock_to_app_start">Screen pinned</string> - <!-- Exting lock-to-app indication. --> - <string name="lock_to_app_exit">Screen unpinned</string> - <!-- Lock-to-app unlock pin string --> <string name="lock_to_app_unlock_pin">Ask for PIN before unpinning</string> <!-- Lock-to-app unlock pattern string --> @@ -4811,7 +4810,7 @@ A toast message shown when an app shortcut that was restored from a previous device is clicked, but it cannot be started because the shortcut was created by a newer version of the app. --> - <string name="shortcut_restored_on_lower_version">This shortcut requires latest app</string> + <string name="shortcut_restored_on_lower_version">App version downgraded, or isn\u2019t compatible with this shortcut</string> <!-- A toast message shown when an app shortcut that was restored from a previous device is clicked, diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a62d49ee7bfd..2ed94a0365b4 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -767,9 +767,6 @@ <java-symbol type="string" name="kilobyteShort" /> <java-symbol type="string" name="last_month" /> <java-symbol type="string" name="launchBrowserDefault" /> - <java-symbol type="string" name="lock_to_app_toast" /> - <java-symbol type="string" name="lock_to_app_start" /> - <java-symbol type="string" name="lock_to_app_exit" /> <java-symbol type="string" name="lock_to_app_unlock_pin" /> <java-symbol type="string" name="lock_to_app_unlock_pattern" /> <java-symbol type="string" name="lock_to_app_unlock_password" /> @@ -2026,8 +2023,10 @@ <java-symbol type="string" name="usb_mtp_notification_title" /> <java-symbol type="string" name="usb_charging_notification_title" /> <java-symbol type="string" name="usb_notification_message" /> + <java-symbol type="string" name="usb_power_notification_message" /> <java-symbol type="string" name="usb_ptp_notification_title" /> <java-symbol type="string" name="usb_midi_notification_title" /> + <java-symbol type="string" name="usb_tether_notification_title" /> <java-symbol type="string" name="usb_supplying_notification_title" /> <java-symbol type="string" name="usb_unsupported_audio_accessory_title" /> <java-symbol type="string" name="usb_unsupported_audio_accessory_message" /> @@ -2153,7 +2152,6 @@ <java-symbol type="string" name="config_mobile_hotspot_provision_response" /> <java-symbol type="integer" name="config_mobile_hotspot_provision_check_period" /> <java-symbol type="string" name="config_wifi_tether_enable" /> - <java-symbol type="integer" name="config_wifi_wakeup_available" /> <java-symbol type="bool" name="config_intrusiveNotificationLed" /> <java-symbol type="bool" name="config_notificationBadging" /> <java-symbol type="dimen" name="preference_fragment_padding_bottom" /> @@ -2352,6 +2350,8 @@ <java-symbol type="string" name="fingerprint_error_lockout_permanent" /> <java-symbol type="string" name="fingerprint_name_template" /> <java-symbol type="string" name="fingerprint_not_recognized" /> + <java-symbol type="string" name="fingerprint_error_no_fingerprints" /> + <java-symbol type="string" name="fingerprint_error_hw_not_present" /> <!-- Fingerprint config --> <java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/> @@ -2700,9 +2700,6 @@ <java-symbol type="string" name="language_selection_title" /> <java-symbol type="string" name="search_language_hint" /> - <java-symbol type="layout" name="unlaunchable_app_activity" /> - <java-symbol type="id" name="unlaunchable_app_title" /> - <java-symbol type="id" name="unlaunchable_app_message" /> <java-symbol type="string" name="work_mode_off_title" /> <java-symbol type="string" name="work_mode_off_message" /> <java-symbol type="string" name="work_mode_turn_on" /> @@ -3151,6 +3148,7 @@ <java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" /> <java-symbol type="string" name="config_headlineFontFamily" /> <java-symbol type="string" name="config_headlineFontFamilyLight" /> + <java-symbol type="string" name="config_headlineFontFamilyMedium" /> <java-symbol type="drawable" name="stat_sys_vitals" /> diff --git a/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java new file mode 100644 index 000000000000..5989da7af655 --- /dev/null +++ b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java @@ -0,0 +1,106 @@ +/* + * 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.os; + +import static android.os.FileUtils.copyInternalSendfile; +import static android.os.FileUtils.copyInternalSplice; +import static android.os.FileUtils.copyInternalUserspace; + +import android.os.FileUtils.MemoryPipe; + +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Param; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; + +public class FileUtilsBenchmark { + @Param({"32", "32000", "32000000"}) + private int mSize; + + private File mSrc; + private File mDest; + + private byte[] mData; + + @BeforeExperiment + protected void setUp() throws Exception { + mSrc = new File("/data/local/tmp/src"); + mDest = new File("/data/local/tmp/dest"); + + mData = new byte[mSize]; + + try (FileOutputStream os = new FileOutputStream(mSrc)) { + os.write(mData); + } + } + + public void timeRegularUserspace(int reps) throws Exception { + for (int i = 0; i < reps; i++) { + try (FileInputStream in = new FileInputStream(mSrc); + FileOutputStream out = new FileOutputStream(mDest)) { + copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE); + } + } + } + + public void timeRegularSendfile(int reps) throws Exception { + for (int i = 0; i < reps; i++) { + try (FileInputStream in = new FileInputStream(mSrc); + FileOutputStream out = new FileOutputStream(mDest)) { + copyInternalSendfile(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE); + } + } + } + + public void timePipeSourceUserspace(int reps) throws Exception { + for (int i = 0; i < reps; i++) { + try (MemoryPipe in = MemoryPipe.createSource(mData); + FileOutputStream out = new FileOutputStream(mDest)) { + copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE); + } + } + } + + public void timePipeSourceSplice(int reps) throws Exception { + for (int i = 0; i < reps; i++) { + try (MemoryPipe in = MemoryPipe.createSource(mData); + FileOutputStream out = new FileOutputStream(mDest)) { + copyInternalSplice(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE); + } + } + } + + public void timePipeSinkUserspace(int reps) throws Exception { + for (int i = 0; i < reps; i++) { + try (FileInputStream in = new FileInputStream(mSrc); + MemoryPipe out = MemoryPipe.createSink(mData)) { + copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE); + } + } + } + + public void timePipeSinkSplice(int reps) throws Exception { + for (int i = 0; i < reps; i++) { + try (FileInputStream in = new FileInputStream(mSrc); + MemoryPipe out = MemoryPipe.createSink(mData)) { + copyInternalSplice(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE); + } + } + } +} diff --git a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java new file mode 100644 index 000000000000..f90ae340b2e9 --- /dev/null +++ b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java @@ -0,0 +1,171 @@ +/* + * 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.hardware.display; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.os.Parcel; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.time.LocalDate; +import java.util.Arrays; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AmbientBrightnessDayStatsTest { + + private static final LocalDate LOCAL_DATE = LocalDate.now(); + private static final float[] BUCKET_BOUNDARIES = {0, 1, 10, 100}; + private static final float[] STATS = {1.3f, 2.6f, 5.8f, 10}; + + @Test + public void testParamsMustNotBeNull() { + assertThrows(NullPointerException.class, + () -> new AmbientBrightnessDayStats(null, BUCKET_BOUNDARIES)); + + assertThrows(NullPointerException.class, + () -> new AmbientBrightnessDayStats(LOCAL_DATE, null)); + + assertThrows(NullPointerException.class, + () -> new AmbientBrightnessDayStats(null, BUCKET_BOUNDARIES, STATS)); + + assertThrows(NullPointerException.class, + () -> new AmbientBrightnessDayStats(LOCAL_DATE, null, STATS)); + } + + @Test(expected = IllegalArgumentException.class) + public void testBucketBoundariesMustNotBeEmpty() { + new AmbientBrightnessDayStats(LocalDate.now(), new float[]{}); + } + + @Test(expected = IllegalArgumentException.class) + public void testStatsAndBoundariesMustHaveSameLength() { + float[] stats = Arrays.copyOf(STATS, STATS.length + 1); + stats[stats.length - 1] = 0; + new AmbientBrightnessDayStats(LOCAL_DATE, BUCKET_BOUNDARIES, stats); + } + + @Test + public void testAmbientBrightnessDayStatsAdd() { + AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE, + BUCKET_BOUNDARIES); + dayStats.log(0, 1); + dayStats.log(0.5f, 1.5f); + dayStats.log(50, 12.5f); + dayStats.log(2000, 1.24f); + dayStats.log(-10, 0.5f); + assertEquals(4, dayStats.getStats().length); + assertEquals(2.5f, dayStats.getStats()[0], 0); + assertEquals(0, dayStats.getStats()[1], 0); + assertEquals(12.5f, dayStats.getStats()[2], 0); + assertEquals(1.24f, dayStats.getStats()[3], 0); + } + + @Test + public void testGetters() { + AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE, + BUCKET_BOUNDARIES, STATS); + assertEquals(LOCAL_DATE, dayStats.getLocalDate()); + assertArrayEquals(BUCKET_BOUNDARIES, dayStats.getBucketBoundaries(), 0); + assertArrayEquals(STATS, dayStats.getStats(), 0); + } + + @Test + public void testParcelUnparcelAmbientBrightnessDayStats() { + LocalDate today = LocalDate.now(); + AmbientBrightnessDayStats stats = new AmbientBrightnessDayStats(today, + new float[]{0, 1, 10, 100}, new float[]{1.3f, 2.6f, 5.8f, 10}); + // Parcel the data + Parcel parcel = Parcel.obtain(); + stats.writeToParcel(parcel, 0); + byte[] parceled = parcel.marshall(); + parcel.recycle(); + // Unparcel and check that it has not changed + parcel = Parcel.obtain(); + parcel.unmarshall(parceled, 0, parceled.length); + parcel.setDataPosition(0); + AmbientBrightnessDayStats statsAgain = AmbientBrightnessDayStats.CREATOR.createFromParcel( + parcel); + assertEquals(stats, statsAgain); + } + + @Test + public void testAmbientBrightnessDayStatsEquals() { + AmbientBrightnessDayStats emptyDayStats = new AmbientBrightnessDayStats(LOCAL_DATE, + BUCKET_BOUNDARIES); + AmbientBrightnessDayStats identicalEmptyDayStats = new AmbientBrightnessDayStats(LOCAL_DATE, + BUCKET_BOUNDARIES, new float[BUCKET_BOUNDARIES.length]); + assertEquals(emptyDayStats, identicalEmptyDayStats); + assertEquals(emptyDayStats.hashCode(), identicalEmptyDayStats.hashCode()); + + AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE, + BUCKET_BOUNDARIES, STATS); + AmbientBrightnessDayStats identicalDayStats = new AmbientBrightnessDayStats(LOCAL_DATE, + BUCKET_BOUNDARIES, STATS); + assertEquals(dayStats, identicalDayStats); + assertEquals(dayStats.hashCode(), identicalDayStats.hashCode()); + + assertNotEquals(emptyDayStats, dayStats); + assertNotEquals(emptyDayStats.hashCode(), dayStats.hashCode()); + + AmbientBrightnessDayStats differentDateDayStats = new AmbientBrightnessDayStats( + LOCAL_DATE.plusDays(1), BUCKET_BOUNDARIES, STATS); + assertNotEquals(dayStats, differentDateDayStats); + assertNotEquals(dayStats.hashCode(), differentDateDayStats.hashCode()); + + float[] differentStats = Arrays.copyOf(STATS, STATS.length); + differentStats[differentStats.length - 1] += 5f; + AmbientBrightnessDayStats differentStatsDayStats = new AmbientBrightnessDayStats(LOCAL_DATE, + BUCKET_BOUNDARIES, differentStats); + assertNotEquals(dayStats, differentDateDayStats); + assertNotEquals(dayStats.hashCode(), differentStatsDayStats.hashCode()); + + float[] differentBucketBoundaries = Arrays.copyOf(BUCKET_BOUNDARIES, + BUCKET_BOUNDARIES.length); + differentBucketBoundaries[differentBucketBoundaries.length - 1] += 100f; + AmbientBrightnessDayStats differentBoundariesDayStats = new AmbientBrightnessDayStats( + LOCAL_DATE, differentBucketBoundaries, STATS); + assertNotEquals(dayStats, differentBoundariesDayStats); + assertNotEquals(dayStats.hashCode(), differentBoundariesDayStats.hashCode()); + } + + private interface ExceptionRunnable { + void run() throws Exception; + } + + private static void assertThrows(Class<? extends Throwable> exceptionClass, + ExceptionRunnable r) { + try { + r.run(); + } catch (Throwable e) { + assertTrue("Expected exception type " + exceptionClass.getName() + " but got " + + e.getClass().getName(), exceptionClass.isAssignableFrom(e.getClass())); + return; + } + fail("Expected exception type " + exceptionClass.getName() + + ", but no exception was thrown"); + } + +} diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java index 27b7f9e185bb..ea0347d67ad7 100644 --- a/core/tests/coretests/src/android/net/UriTest.java +++ b/core/tests/coretests/src/android/net/UriTest.java @@ -192,6 +192,12 @@ public class UriTest extends TestCase { assertEquals("a:a@example.com:a@example2.com", uri.getAuthority()); assertEquals("example2.com", uri.getHost()); assertEquals(-1, uri.getPort()); + assertEquals("/path", uri.getPath()); + + uri = Uri.parse("http://a.foo.com\\.example.com/path"); + assertEquals("a.foo.com", uri.getHost()); + assertEquals(-1, uri.getPort()); + assertEquals("\\.example.com/path", uri.getPath()); } @SmallTest diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java index cd20192edfd6..0bc3a2d879ab 100644 --- a/core/tests/coretests/src/android/os/FileUtilsTest.java +++ b/core/tests/coretests/src/android/os/FileUtilsTest.java @@ -21,16 +21,19 @@ import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.content.Context; +import android.os.FileUtils.MemoryPipe; import android.provider.DocumentsContract.Document; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import libcore.io.IoUtils; +import libcore.io.Streams; import com.google.android.collect.Sets; @@ -40,11 +43,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.FileWriter; import java.util.Arrays; import java.util.HashSet; +import java.util.Random; @RunWith(AndroidJUnit4.class) public class FileUtilsTest { @@ -56,6 +61,8 @@ public class FileUtilsTest { private File mCopyFile; private File mTarget; + private final int[] DATA_SIZES = { 32, 32_000, 32_000_000 }; + private Context getContext() { return InstrumentationRegistry.getContext(); } @@ -80,7 +87,7 @@ public class FileUtilsTest { @Test public void testCopyFile() throws Exception { - stageFile(mTestFile, TEST_DATA); + writeFile(mTestFile, TEST_DATA); assertFalse(mCopyFile.exists()); FileUtils.copyFile(mTestFile, mCopyFile); assertTrue(mCopyFile.exists()); @@ -97,6 +104,104 @@ public class FileUtilsTest { } @Test + public void testCopy_FileToFile() throws Exception { + for (int size : DATA_SIZES) { + final File src = new File(mTarget, "src"); + final File dest = new File(mTarget, "dest"); + + byte[] expected = new byte[size]; + byte[] actual = new byte[size]; + new Random().nextBytes(expected); + writeFile(src, expected); + + try (FileInputStream in = new FileInputStream(src); + FileOutputStream out = new FileOutputStream(dest)) { + FileUtils.copy(in, out); + } + + actual = readFile(dest); + assertArrayEquals(expected, actual); + } + } + + @Test + public void testCopy_FileToPipe() throws Exception { + for (int size : DATA_SIZES) { + final File src = new File(mTarget, "src"); + + byte[] expected = new byte[size]; + byte[] actual = new byte[size]; + new Random().nextBytes(expected); + writeFile(src, expected); + + try (FileInputStream in = new FileInputStream(src); + MemoryPipe out = MemoryPipe.createSink(actual)) { + FileUtils.copy(in.getFD(), out.getFD()); + out.join(); + } + + assertArrayEquals(expected, actual); + } + } + + @Test + public void testCopy_PipeToFile() throws Exception { + for (int size : DATA_SIZES) { + final File dest = new File(mTarget, "dest"); + + byte[] expected = new byte[size]; + byte[] actual = new byte[size]; + new Random().nextBytes(expected); + + try (MemoryPipe in = MemoryPipe.createSource(expected); + FileOutputStream out = new FileOutputStream(dest)) { + FileUtils.copy(in.getFD(), out.getFD()); + } + + actual = readFile(dest); + assertArrayEquals(expected, actual); + } + } + + @Test + public void testCopy_PipeToPipe() throws Exception { + for (int size : DATA_SIZES) { + byte[] expected = new byte[size]; + byte[] actual = new byte[size]; + new Random().nextBytes(expected); + + try (MemoryPipe in = MemoryPipe.createSource(expected); + MemoryPipe out = MemoryPipe.createSink(actual)) { + FileUtils.copy(in.getFD(), out.getFD()); + out.join(); + } + + assertArrayEquals(expected, actual); + } + } + + @Test + public void testCopy_ShortPipeToFile() throws Exception { + byte[] source = new byte[33_000_000]; + new Random().nextBytes(source); + + for (int size : DATA_SIZES) { + final File dest = new File(mTarget, "dest"); + + byte[] expected = Arrays.copyOf(source, size); + byte[] actual = new byte[size]; + + try (MemoryPipe in = MemoryPipe.createSource(source); + FileOutputStream out = new FileOutputStream(dest)) { + FileUtils.copy(in.getFD(), out.getFD(), null, null, size); + } + + actual = readFile(dest); + assertArrayEquals(expected, actual); + } + } + + @Test public void testIsFilenameSafe() throws Exception { assertTrue(FileUtils.isFilenameSafe(new File("foobar"))); assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23"))); @@ -106,7 +211,7 @@ public class FileUtilsTest { @Test public void testReadTextFile() throws Exception { - stageFile(mTestFile, TEST_DATA); + writeFile(mTestFile, TEST_DATA); assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 0, null)); @@ -127,7 +232,7 @@ public class FileUtilsTest { @Test public void testReadTextFileWithZeroLengthFile() throws Exception { - stageFile(mTestFile, TEST_DATA); + writeFile(mTestFile, TEST_DATA); new FileOutputStream(mTestFile).close(); // Zero out the file assertEquals("", FileUtils.readTextFile(mTestFile, 0, null)); assertEquals("", FileUtils.readTextFile(mTestFile, 1, "<>")); @@ -381,12 +486,21 @@ public class FileUtilsTest { file.setLastModified(System.currentTimeMillis() - age); } - private void stageFile(File file, String data) throws Exception { - FileWriter writer = new FileWriter(file); - try { - writer.write(data, 0, data.length()); - } finally { - writer.close(); + private void writeFile(File file, String data) throws Exception { + writeFile(file, data.getBytes()); + } + + private void writeFile(File file, byte[] data) throws Exception { + try (FileOutputStream out = new FileOutputStream(file)) { + out.write(data); + } + } + + private byte[] readFile(File file) throws Exception { + try (FileInputStream in = new FileInputStream(file); + ByteArrayOutputStream out = new ByteArrayOutputStream()) { + Streams.copy(in, out); + return out.toByteArray(); } } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 0083b017033b..83d0719c89d4 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -436,7 +436,6 @@ public class SettingsBackupTest { Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS, Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, - Settings.Global.WIFI_WAKEUP_AVAILABLE, Settings.Global.WIFI_WATCHDOG_ON, Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON, Settings.Global.WINDOW_ANIMATION_SCALE, diff --git a/core/tests/overlaytests/device/Android.mk b/core/tests/overlaytests/device/Android.mk new file mode 100644 index 000000000000..4ca3e4ce2389 --- /dev/null +++ b/core/tests/overlaytests/device/Android.mk @@ -0,0 +1,31 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-java-files-under,src) +LOCAL_MODULE_TAGS := tests +LOCAL_PACKAGE_NAME := OverlayDeviceTests +LOCAL_STATIC_JAVA_LIBRARIES := android-support-test +LOCAL_COMPATIBILITY_SUITE := device-tests +LOCAL_TARGET_REQUIRED_MODULES := \ + OverlayDeviceTests_AppOverlayOne \ + OverlayDeviceTests_AppOverlayTwo \ + OverlayDeviceTests_FrameworkOverlay +include $(BUILD_PACKAGE) + +# Include to build test-apps. +include $(call all-makefiles-under,$(LOCAL_PATH)) + diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml index e01caeedd862..d14fdf5ee819 100644 --- a/core/tests/overlaytests/device/AndroidManifest.xml +++ b/core/tests/overlaytests/device/AndroidManifest.xml @@ -15,15 +15,15 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.om.test"> + package="com.android.overlaytest"> <uses-sdk android:minSdkVersion="21" /> <application> - <uses-library android:name="android.test.runner" /> + <uses-library android:name="android.test.runner"/> </application> <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.server.om.test" /> - + android:targetPackage="com.android.overlaytest" + android:label="Runtime resource overlay tests" /> </manifest> diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml new file mode 100644 index 000000000000..f06983559830 --- /dev/null +++ b/core/tests/overlaytests/device/AndroidTest.xml @@ -0,0 +1,47 @@ +<?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. +--> + +<configuration description="Test module config for OverlayDeviceTests"> + <option name="test-tag" value="OverlayDeviceTests" /> + <option name="test-suite-tag" value="apct" /> + + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="OverlayDeviceTests.apk" /> + <option name="test-file-name" value="OverlayDeviceTests_AppOverlayOne.apk" /> + <option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" /> + <option name="test-file-name" value="OverlayDeviceTests_FrameworkOverlay.apk" /> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" + value="cmd overlay disable com.android.overlaytest.framework" /> + <option name="run-command" + value="cmd overlay disable com.android.overlaytest.app_overlay_one" /> + <option name="run-command" + value="cmd overlay disable com.android.overlaytest.app_overlay_two" /> + <option name="teardown-command" + value="cmd overlay disable com.android.overlaytest.framework" /> + <option name="teardown-command" + value="cmd overlay disable com.android.overlaytest.app_overlay_one" /> + <option name="teardown-command" + value="cmd overlay disable com.android.overlaytest.app_overlay_two" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.overlaytest" /> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" /> + </test> +</configuration> diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/Android.mk b/core/tests/overlaytests/device/OverlayAppFiltered/Android.mk deleted file mode 100644 index f76de7a93b2e..000000000000 --- a/core/tests/overlaytests/device/OverlayAppFiltered/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SDK_VERSION := system_current - -LOCAL_PACKAGE_NAME := com.android.overlaytest.filtered_app_overlay - -include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayAppFiltered/AndroidManifest.xml deleted file mode 100644 index 5b7950a25fbf..000000000000 --- a/core/tests/overlaytests/device/OverlayAppFiltered/AndroidManifest.xml +++ /dev/null @@ -1,9 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.overlaytest.filtered_app_overlay" - android:versionCode="1" - android:versionName="1.0"> - <overlay android:targetPackage="com.android.overlaytest" - android:requiredSystemPropertyName="persist.oem.overlay.test" - android:requiredSystemPropertyValue="foo" - android:priority="3"/> -</manifest> diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/device/OverlayAppFiltered/res/raw/lorem_ipsum.txt deleted file mode 100644 index 0954cedeb5d5..000000000000 --- a/core/tests/overlaytests/device/OverlayAppFiltered/res/raw/lorem_ipsum.txt +++ /dev/null @@ -1 +0,0 @@ -Lorem ipsum: filtered overlays. diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/res/values/config.xml b/core/tests/overlaytests/device/OverlayAppFiltered/res/values/config.xml deleted file mode 100644 index 60b94eec5994..000000000000 --- a/core/tests/overlaytests/device/OverlayAppFiltered/res/values/config.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="str">filtered</string> -</resources> diff --git a/core/tests/overlaytests/device/OverlayAppFiltered/res/xml/integer.xml b/core/tests/overlaytests/device/OverlayAppFiltered/res/xml/integer.xml deleted file mode 100644 index e2652b7e2915..000000000000 --- a/core/tests/overlaytests/device/OverlayAppFiltered/res/xml/integer.xml +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<integer value="3"/> diff --git a/core/tests/overlaytests/device/OverlayAppFirst/Android.mk b/core/tests/overlaytests/device/OverlayAppFirst/Android.mk deleted file mode 100644 index bf9416c279be..000000000000 --- a/core/tests/overlaytests/device/OverlayAppFirst/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SDK_VERSION := current - -LOCAL_PACKAGE_NAME := com.android.overlaytest.first_app_overlay - -include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/device/OverlayAppFirst/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayAppFirst/AndroidManifest.xml deleted file mode 100644 index ec10bbcf752e..000000000000 --- a/core/tests/overlaytests/device/OverlayAppFirst/AndroidManifest.xml +++ /dev/null @@ -1,6 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.overlaytest.first_app_overlay" - android:versionCode="1" - android:versionName="1.0"> - <overlay android:targetPackage="com.android.overlaytest" android:priority="1"/> -</manifest> diff --git a/core/tests/overlaytests/device/OverlayAppSecond/Android.mk b/core/tests/overlaytests/device/OverlayAppSecond/Android.mk deleted file mode 100644 index bb7d142d6809..000000000000 --- a/core/tests/overlaytests/device/OverlayAppSecond/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SDK_VERSION := current - -LOCAL_PACKAGE_NAME := com.android.overlaytest.second_app_overlay - -include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/device/OverlayAppSecond/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayAppSecond/AndroidManifest.xml deleted file mode 100644 index ed498637b454..000000000000 --- a/core/tests/overlaytests/device/OverlayAppSecond/AndroidManifest.xml +++ /dev/null @@ -1,6 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.overlaytest.second_app_overlay" - android:versionCode="1" - android:versionName="1.0"> - <overlay android:targetPackage="com.android.overlaytest" android:priority="2"/> -</manifest> diff --git a/core/tests/overlaytests/device/OverlayTest/Android.mk b/core/tests/overlaytests/device/OverlayTest/Android.mk deleted file mode 100644 index 5fe7b917102e..000000000000 --- a/core/tests/overlaytests/device/OverlayTest/Android.mk +++ /dev/null @@ -1,16 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_PACKAGE_NAME := OverlayTest - -LOCAL_DEX_PREOPT := false - -LOCAL_JAVA_LIBRARIES += android.test.base - -LOCAL_MODULE_PATH := $(TARGET_OUT)/app - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/device/OverlayTest/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayTest/AndroidManifest.xml deleted file mode 100644 index 9edba12ffa8f..000000000000 --- a/core/tests/overlaytests/device/OverlayTest/AndroidManifest.xml +++ /dev/null @@ -1,10 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.overlaytest"> - <uses-permission android:name="android.permission.RUN_INSTRUMENTATION"/> - <application> - <uses-library android:name="android.test.runner"/> - </application> - <instrumentation android:name="android.test.InstrumentationTestRunner" - android:targetPackage="com.android.overlaytest" - android:label="Runtime resource overlay tests"/> -</manifest> diff --git a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java deleted file mode 100644 index e104f5a670c1..000000000000 --- a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.android.overlaytest; - -public class WithMultipleOverlaysTest extends OverlayBaseTest { - public WithMultipleOverlaysTest() { - mMode = MODE_MULTIPLE_OVERLAYS; - } -} diff --git a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java deleted file mode 100644 index 816a476e28cf..000000000000 --- a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.android.overlaytest; - -public class WithOverlayTest extends OverlayBaseTest { - public WithOverlayTest() { - mMode = MODE_SINGLE_OVERLAY; - } -} diff --git a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java deleted file mode 100644 index 318cccc85461..000000000000 --- a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.android.overlaytest; - -public class WithoutOverlayTest extends OverlayBaseTest { - public WithoutOverlayTest() { - mMode = MODE_NO_OVERLAY; - } -} diff --git a/core/tests/overlaytests/device/OverlayTestOverlay/Android.mk b/core/tests/overlaytests/device/OverlayTestOverlay/Android.mk deleted file mode 100644 index ed330467f68a..000000000000 --- a/core/tests/overlaytests/device/OverlayTestOverlay/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SDK_VERSION := current - -LOCAL_PACKAGE_NAME := com.android.overlaytest.overlay - -include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/device/OverlayTestOverlay/AndroidManifest.xml b/core/tests/overlaytests/device/OverlayTestOverlay/AndroidManifest.xml deleted file mode 100644 index f8b6c7b888b5..000000000000 --- a/core/tests/overlaytests/device/OverlayTestOverlay/AndroidManifest.xml +++ /dev/null @@ -1,6 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.overlaytest.overlay" - android:versionCode="1" - android:versionName="1.0"> - <overlay android:targetPackage="android" android:priority="1"/> -</manifest> diff --git a/core/tests/overlaytests/device/OverlayTest/res/drawable-nodpi/drawable.jpg b/core/tests/overlaytests/device/res/drawable-nodpi/drawable.jpg Binary files differindex a3f14f325b85..a3f14f325b85 100644 --- a/core/tests/overlaytests/device/OverlayTest/res/drawable-nodpi/drawable.jpg +++ b/core/tests/overlaytests/device/res/drawable-nodpi/drawable.jpg diff --git a/core/tests/overlaytests/device/OverlayTest/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/device/res/raw/lorem_ipsum.txt index cee7a927c5fa..cee7a927c5fa 100644 --- a/core/tests/overlaytests/device/OverlayTest/res/raw/lorem_ipsum.txt +++ b/core/tests/overlaytests/device/res/raw/lorem_ipsum.txt diff --git a/core/tests/overlaytests/device/OverlayTest/res/values-sv/config.xml b/core/tests/overlaytests/device/res/values-sv/config.xml index 891853edb4c5..891853edb4c5 100644 --- a/core/tests/overlaytests/device/OverlayTest/res/values-sv/config.xml +++ b/core/tests/overlaytests/device/res/values-sv/config.xml diff --git a/core/tests/overlaytests/device/OverlayTest/res/values/config.xml b/core/tests/overlaytests/device/res/values/config.xml index c692a2625199..c692a2625199 100644 --- a/core/tests/overlaytests/device/OverlayTest/res/values/config.xml +++ b/core/tests/overlaytests/device/res/values/config.xml diff --git a/core/tests/overlaytests/device/OverlayTest/res/xml/integer.xml b/core/tests/overlaytests/device/res/xml/integer.xml index 9383daa20b6c..9383daa20b6c 100644 --- a/core/tests/overlaytests/device/OverlayTest/res/xml/integer.xml +++ b/core/tests/overlaytests/device/res/xml/integer.xml diff --git a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java index e57c55ced046..96ab977b799c 100644 --- a/core/tests/overlaytests/device/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java @@ -1,45 +1,80 @@ +/* + * 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.overlaytest; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import android.app.UiAutomation; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.XmlResourceParser; -import android.test.AndroidTestCase; +import android.os.LocaleList; +import android.os.ParcelFileDescriptor; +import android.support.test.InstrumentationRegistry; import android.util.AttributeSet; import android.util.Xml; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Locale; -public abstract class OverlayBaseTest extends AndroidTestCase { +@Ignore +public abstract class OverlayBaseTest { private Resources mResources; - protected int mMode; // will be set by subclasses - static final protected int MODE_NO_OVERLAY = 0; - static final protected int MODE_SINGLE_OVERLAY = 1; - static final protected int MODE_MULTIPLE_OVERLAYS = 2; + private final int mMode; + static final int MODE_NO_OVERLAY = 0; + static final int MODE_SINGLE_OVERLAY = 1; + static final int MODE_MULTIPLE_OVERLAYS = 2; + + static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one"; + static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two"; + static final String FRAMEWORK_OVERLAY_PKG = "com.android.overlaytest.framework"; + + protected OverlayBaseTest(int mode) { + mMode = mode; + } - protected void setUp() { - mResources = getContext().getResources(); + @Before + public void setUp() { + mResources = InstrumentationRegistry.getContext().getResources(); } private int calculateRawResourceChecksum(int resId) throws Throwable { - InputStream input = null; - try { - input = mResources.openRawResource(resId); + try (InputStream input = mResources.openRawResource(resId)) { int ch, checksum = 0; while ((ch = input.read()) != -1) { checksum = (checksum + ch) % 0xffddbb00; } return checksum; - } finally { - input.close(); } } private void setLocale(Locale locale) { - Locale.setDefault(locale); + final LocaleList locales = new LocaleList(locale); + LocaleList.setDefault(locales); Configuration config = new Configuration(); - config.locale = locale; + config.setLocales(locales); mResources.updateConfiguration(config, mResources.getDisplayMetrics()); } @@ -126,6 +161,7 @@ public abstract class OverlayBaseTest extends AndroidTestCase { } } + @Test public void testFrameworkBooleanOverlay() throws Throwable { // config_annoy_dianne has the value: // - true when no overlay exists (MODE_NO_OVERLAY) @@ -135,6 +171,7 @@ public abstract class OverlayBaseTest extends AndroidTestCase { assertResource(resId, true, false, false); } + @Test public void testBooleanOverlay() throws Throwable { // usually_false has the value: // - false when no overlay exists (MODE_NO_OVERLAY) @@ -144,12 +181,14 @@ public abstract class OverlayBaseTest extends AndroidTestCase { assertResource(resId, false, true, false); } + @Test public void testBoolean() throws Throwable { // always_true has no overlay final int resId = R.bool.always_true; assertResource(resId, true, true, true); } + @Test public void testIntegerArrayOverlay() throws Throwable { // fibonacci has values: // - eight first values of Fibonacci sequence, when no overlay exists (MODE_NO_OVERLAY) @@ -162,6 +201,7 @@ public abstract class OverlayBaseTest extends AndroidTestCase { new int[]{21, 13, 8, 5, 3, 2, 1, 1}); } + @Test public void testIntegerArray() throws Throwable { // prime_numbers has no overlay final int resId = R.array.prime_numbers; @@ -169,6 +209,7 @@ public abstract class OverlayBaseTest extends AndroidTestCase { assertResource(resId, expected, expected, expected); } + @Test public void testDrawable() throws Throwable { // drawable-nodpi/drawable has overlay (default config) final int resId = R.drawable.drawable; @@ -188,16 +229,19 @@ public abstract class OverlayBaseTest extends AndroidTestCase { assertEquals(expected, actual); } + @Test public void testAppString() throws Throwable { final int resId = R.string.str; assertResource(resId, "none", "single", "multiple"); } + @Test public void testApp2() throws Throwable { final int resId = R.string.str2; // only in base package and first app overlay assertResource(resId, "none", "single", "single"); } + @Test public void testAppXml() throws Throwable { int expected = getExpected(0, 1, 2); int actual = -1; @@ -214,6 +258,7 @@ public abstract class OverlayBaseTest extends AndroidTestCase { assertEquals(expected, actual); } + @Test public void testAppRaw() throws Throwable { final int resId = R.raw.lorem_ipsum; @@ -256,10 +301,10 @@ public abstract class OverlayBaseTest extends AndroidTestCase { * SLOT PACKAGE CONFIGURATION VALUE * A target package (default) 100 * B target package -sv 200 - * C OverlayAppFirst (default) 300 - * D OverlayAppFirst -sv 400 - * E OverlayAppSecond (default) 500 - * F OverlayAppSecond -sv 600 + * C AppOverlayOne (default) 300 + * D AppOverlayOne -sv 400 + * E AppOverlayTwo (default) 500 + * F AppOverlayTwo -sv 600 * * Example: in testMatrix101110, the base package defines the * R.integer.matrix101110 resource for the default configuration (value @@ -269,195 +314,283 @@ public abstract class OverlayBaseTest extends AndroidTestCase { * are loaded, the expected value after setting the language to Swedish is * 400. */ + @Test public void testMatrix100000() throws Throwable { final int resId = R.integer.matrix_100000; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 100, 100); } + @Test public void testMatrix100001() throws Throwable { final int resId = R.integer.matrix_100001; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 100, 600); } + @Test public void testMatrix100010() throws Throwable { final int resId = R.integer.matrix_100010; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 100, 500); } + @Test public void testMatrix100011() throws Throwable { final int resId = R.integer.matrix_100011; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 100, 600); } + @Test public void testMatrix100100() throws Throwable { final int resId = R.integer.matrix_100100; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 400, 400); } + @Test public void testMatrix100101() throws Throwable { final int resId = R.integer.matrix_100101; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 400, 600); } + @Test public void testMatrix100110() throws Throwable { final int resId = R.integer.matrix_100110; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 400, 400); } + @Test public void testMatrix100111() throws Throwable { final int resId = R.integer.matrix_100111; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 400, 600); } + @Test public void testMatrix101000() throws Throwable { final int resId = R.integer.matrix_101000; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 300, 300); } + @Test public void testMatrix101001() throws Throwable { final int resId = R.integer.matrix_101001; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 300, 600); } + @Test public void testMatrix101010() throws Throwable { final int resId = R.integer.matrix_101010; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 300, 500); } + @Test public void testMatrix101011() throws Throwable { final int resId = R.integer.matrix_101011; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 300, 600); } + @Test public void testMatrix101100() throws Throwable { final int resId = R.integer.matrix_101100; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 400, 400); } + @Test public void testMatrix101101() throws Throwable { final int resId = R.integer.matrix_101101; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 400, 600); } + @Test public void testMatrix101110() throws Throwable { final int resId = R.integer.matrix_101110; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 400, 400); } + @Test public void testMatrix101111() throws Throwable { final int resId = R.integer.matrix_101111; setLocale(new Locale("sv", "SE")); assertResource(resId, 100, 400, 600); } + @Test public void testMatrix110000() throws Throwable { final int resId = R.integer.matrix_110000; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 200, 200); } + @Test public void testMatrix110001() throws Throwable { final int resId = R.integer.matrix_110001; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 200, 600); } + @Test public void testMatrix110010() throws Throwable { final int resId = R.integer.matrix_110010; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 200, 200); } + @Test public void testMatrix110011() throws Throwable { final int resId = R.integer.matrix_110011; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 200, 600); } + @Test public void testMatrix110100() throws Throwable { final int resId = R.integer.matrix_110100; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 400, 400); } + @Test public void testMatrix110101() throws Throwable { final int resId = R.integer.matrix_110101; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 400, 600); } + @Test public void testMatrix110110() throws Throwable { final int resId = R.integer.matrix_110110; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 400, 400); } + @Test public void testMatrix110111() throws Throwable { final int resId = R.integer.matrix_110111; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 400, 600); } + @Test public void testMatrix111000() throws Throwable { final int resId = R.integer.matrix_111000; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 200, 200); } + @Test public void testMatrix111001() throws Throwable { final int resId = R.integer.matrix_111001; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 200, 600); } + @Test public void testMatrix111010() throws Throwable { final int resId = R.integer.matrix_111010; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 200, 200); } + @Test public void testMatrix111011() throws Throwable { final int resId = R.integer.matrix_111011; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 200, 600); } + @Test public void testMatrix111100() throws Throwable { final int resId = R.integer.matrix_111100; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 400, 400); } + @Test public void testMatrix111101() throws Throwable { final int resId = R.integer.matrix_111101; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 400, 600); } + @Test public void testMatrix111110() throws Throwable { final int resId = R.integer.matrix_111110; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 400, 400); } + @Test public void testMatrix111111() throws Throwable { final int resId = R.integer.matrix_111111; setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 400, 600); } + + /** + * Executes the shell command and reads all the output to ensure the command ran and didn't + * get stuck buffering on output. + */ + protected static String executeShellCommand(UiAutomation automation, String command) + throws Exception { + final ParcelFileDescriptor pfd = automation.executeShellCommand(command); + try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { + final BufferedReader reader = new BufferedReader( + new InputStreamReader(in, StandardCharsets.UTF_8)); + StringBuilder str = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + str.append(line); + } + return str.toString(); + } + } + + /** + * Enables overlay packages and waits for a configuration change event before + * returning, to guarantee that Resources are up-to-date. + * @param packages the list of package names to enable. + */ + protected static void enableOverlayPackages(String... packages) throws Exception { + enableOverlayPackages(true, packages); + } + + /** + * Disables overlay packages and waits for a configuration change event before + * returning, to guarantee that Resources are up-to-date. + * @param packages the list of package names to disable. + */ + protected static void disableOverlayPackages(String... packages) throws Exception { + enableOverlayPackages(false, packages); + } + + /** + * Enables/disables overlay packages and waits for a configuration change event before + * returning, to guarantee that Resources are up-to-date. + * @param enable enables the overlays when true, disables when false. + * @param packages the list of package names to enable/disable. + */ + private static void enableOverlayPackages(boolean enable, String[] packages) + throws Exception { + final UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation() + .getUiAutomation(); + for (final String pkg : packages) { + executeShellCommand(uiAutomation, + "cmd overlay " + (enable ? "enable " : "disable ") + pkg); + } + + // Wait for the overlay change to propagate. + Thread.sleep(1000); + } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java new file mode 100644 index 000000000000..f35e511cdcf4 --- /dev/null +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java @@ -0,0 +1,36 @@ +/* + * 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.overlaytest; + +import android.support.test.filters.MediumTest; + +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +@MediumTest +public class WithMultipleOverlaysTest extends OverlayBaseTest { + public WithMultipleOverlaysTest() { + super(MODE_MULTIPLE_OVERLAYS); + } + + @BeforeClass + public static void enableOverlay() throws Exception { + enableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG); + } +} diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java new file mode 100644 index 000000000000..037449fc2c05 --- /dev/null +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java @@ -0,0 +1,37 @@ +/* + * 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.overlaytest; + +import android.support.test.filters.MediumTest; + +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +@MediumTest +public class WithOverlayTest extends OverlayBaseTest { + public WithOverlayTest() { + super(MODE_SINGLE_OVERLAY); + } + + @BeforeClass + public static void enableOverlay() throws Exception { + disableOverlayPackages(APP_OVERLAY_TWO_PKG); + enableOverlayPackages(APP_OVERLAY_ONE_PKG, FRAMEWORK_OVERLAY_PKG); + } +} diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java new file mode 100644 index 000000000000..f657b5cef0e5 --- /dev/null +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java @@ -0,0 +1,36 @@ +/* + * 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.overlaytest; + +import android.support.test.filters.MediumTest; + +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +@MediumTest +public class WithoutOverlayTest extends OverlayBaseTest { + public WithoutOverlayTest() { + super(MODE_NO_OVERLAY); + } + + @BeforeClass + public static void disableOverlays() throws Exception { + disableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG); + } +} diff --git a/core/tests/overlaytests/device/test-apps/Android.mk b/core/tests/overlaytests/device/test-apps/Android.mk new file mode 100644 index 000000000000..9af9f444ca59 --- /dev/null +++ b/core/tests/overlaytests/device/test-apps/Android.mk @@ -0,0 +1,15 @@ +# 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 $(call all-subdir-makefiles) diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk new file mode 100644 index 000000000000..17e20eeeda85 --- /dev/null +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.mk @@ -0,0 +1,22 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := tests +LOCAL_PACKAGE_NAME := OverlayDeviceTests_AppOverlayOne +LOCAL_COMPATIBILITY_SUITE := device-tests +LOCAL_CERTIFICATE := platform +include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml new file mode 100644 index 000000000000..17191589e3f2 --- /dev/null +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml @@ -0,0 +1,22 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.overlaytest.app_overlay_one" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="com.android.overlaytest" android:priority="1" /> +</manifest> diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/drawable-nodpi/drawable.jpg b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/drawable-nodpi/drawable.jpg Binary files differindex 0d944d02d633..0d944d02d633 100644 --- a/core/tests/overlaytests/device/OverlayAppFirst/res/drawable-nodpi/drawable.jpg +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/drawable-nodpi/drawable.jpg diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/raw/lorem_ipsum.txt index 756b0a3fc532..756b0a3fc532 100644 --- a/core/tests/overlaytests/device/OverlayAppFirst/res/raw/lorem_ipsum.txt +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/raw/lorem_ipsum.txt diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/values-sv/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values-sv/config.xml index 9cdc73e27ade..9cdc73e27ade 100644 --- a/core/tests/overlaytests/device/OverlayAppFirst/res/values-sv/config.xml +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values-sv/config.xml diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/values/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml index 972137a3d1bf..972137a3d1bf 100644 --- a/core/tests/overlaytests/device/OverlayAppFirst/res/values/config.xml +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml diff --git a/core/tests/overlaytests/device/OverlayAppFirst/res/xml/integer.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/integer.xml index 7f628d9125f3..7f628d9125f3 100644 --- a/core/tests/overlaytests/device/OverlayAppFirst/res/xml/integer.xml +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/integer.xml diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk new file mode 100644 index 000000000000..c24bea9e06e9 --- /dev/null +++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.mk @@ -0,0 +1,22 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := tests +LOCAL_PACKAGE_NAME := OverlayDeviceTests_AppOverlayTwo +LOCAL_COMPATIBILITY_SUITE := device-tests +LOCAL_CERTIFICATE := platform +include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml new file mode 100644 index 000000000000..ae8307c446c1 --- /dev/null +++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/AndroidManifest.xml @@ -0,0 +1,22 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.overlaytest.app_overlay_two" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="com.android.overlaytest" android:priority="2" /> +</manifest> diff --git a/core/tests/overlaytests/device/OverlayAppSecond/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/raw/lorem_ipsum.txt index 613f5b63c66c..613f5b63c66c 100644 --- a/core/tests/overlaytests/device/OverlayAppSecond/res/raw/lorem_ipsum.txt +++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/raw/lorem_ipsum.txt diff --git a/core/tests/overlaytests/device/OverlayAppSecond/res/values-sv/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values-sv/config.xml index ec4b6c03e5ff..ec4b6c03e5ff 100644 --- a/core/tests/overlaytests/device/OverlayAppSecond/res/values-sv/config.xml +++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values-sv/config.xml diff --git a/core/tests/overlaytests/device/OverlayAppSecond/res/values/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values/config.xml index 8b072160ffba..8b072160ffba 100644 --- a/core/tests/overlaytests/device/OverlayAppSecond/res/values/config.xml +++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/values/config.xml diff --git a/core/tests/overlaytests/device/OverlayAppSecond/res/xml/integer.xml b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/xml/integer.xml index f3370a6b8bcf..f3370a6b8bcf 100644 --- a/core/tests/overlaytests/device/OverlayAppSecond/res/xml/integer.xml +++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/res/xml/integer.xml diff --git a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk new file mode 100644 index 000000000000..dc811c51e926 --- /dev/null +++ b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.mk @@ -0,0 +1,22 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := tests +LOCAL_PACKAGE_NAME := OverlayDeviceTests_FrameworkOverlay +LOCAL_COMPATIBILITY_SUITE := device-tests +LOCAL_CERTIFICATE := platform +include $(BUILD_PACKAGE) diff --git a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..77ea16afff83 --- /dev/null +++ b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/AndroidManifest.xml @@ -0,0 +1,22 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.overlaytest.framework" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="android" android:priority="1" /> +</manifest> diff --git a/core/tests/overlaytests/device/OverlayTestOverlay/res/values/config.xml b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/res/values/config.xml index c1e3de12059a..c1e3de12059a 100644 --- a/core/tests/overlaytests/device/OverlayTestOverlay/res/values/config.xml +++ b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/res/values/config.xml diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml index 2d6843948f29..b08ac96aca68 100644 --- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml +++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/AndroidManifest.xml @@ -15,6 +15,6 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.om.hosttest.signature_overlay"> + package="com.android.server.om.hosttest.signature_overlay"> <overlay android:targetPackage="android" /> </manifest> diff --git a/core/tests/overlaytests/testrunner.py b/core/tests/overlaytests/testrunner.py deleted file mode 100755 index e88805e8cbf1..000000000000 --- a/core/tests/overlaytests/testrunner.py +++ /dev/null @@ -1,732 +0,0 @@ -#!/usr/bin/python -import hashlib -import optparse -import os -import re -import shlex -import subprocess -import sys -import threading -import time - -TASK_COMPILATION = 'compile' -TASK_DISABLE_OVERLAYS = 'disable overlays' -TASK_ENABLE_MULTIPLE_OVERLAYS = 'enable multiple overlays' -TASK_ENABLE_SINGLE_OVERLAY = 'enable single overlay' -TASK_ENABLE_FILTERED_OVERLAYS = 'enable filtered overlays' -TASK_FILE_EXISTS_TEST = 'test (file exists)' -TASK_GREP_IDMAP_TEST = 'test (grep idmap)' -TASK_MD5_TEST = 'test (md5)' -TASK_IDMAP_PATH = 'idmap --path' -TASK_IDMAP_SCAN = 'idmap --scan' -TASK_INSTRUMENTATION = 'instrumentation' -TASK_INSTRUMENTATION_TEST = 'test (instrumentation)' -TASK_MKDIR = 'mkdir' -TASK_PUSH = 'push' -TASK_ROOT = 'root' -TASK_REMOUNT = 'remount' -TASK_RM = 'rm' -TASK_SETPROP = 'setprop' -TASK_SETUP_IDMAP_PATH = 'setup idmap --path' -TASK_SETUP_IDMAP_SCAN = 'setup idmap --scan' -TASK_START = 'start' -TASK_STOP = 'stop' - -adb = 'adb' - -def _adb_shell(cmd): - argv = shlex.split(adb + " shell '" + cmd + "; echo $?'") - proc = subprocess.Popen(argv, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (stdout, stderr) = proc.communicate() - (stdout, stderr) = (stdout.replace('\r', ''), stderr.replace('\r', '')) - tmp = stdout.rsplit('\n', 2) - if len(tmp) == 2: - stdout == '' - returncode = int(tmp[0]) - else: - stdout = tmp[0] + '\n' - returncode = int(tmp[1]) - return returncode, stdout, stderr - -class VerbosePrinter: - class Ticker(threading.Thread): - def _print(self): - s = '\r' + self.text + '[' + '.' * self.i + ' ' * (4 - self.i) + ']' - sys.stdout.write(s) - sys.stdout.flush() - self.i = (self.i + 1) % 5 - - def __init__(self, cond_var, text): - threading.Thread.__init__(self) - self.text = text - self.setDaemon(True) - self.cond_var = cond_var - self.running = False - self.i = 0 - self._print() - self.running = True - - def run(self): - self.cond_var.acquire() - while True: - self.cond_var.wait(0.25) - running = self.running - if not running: - break - self._print() - self.cond_var.release() - - def stop(self): - self.cond_var.acquire() - self.running = False - self.cond_var.notify_all() - self.cond_var.release() - - def _start_ticker(self): - self.ticker = VerbosePrinter.Ticker(self.cond_var, self.text) - self.ticker.start() - - def _stop_ticker(self): - self.ticker.stop() - self.ticker.join() - self.ticker = None - - def _format_begin(self, type, name): - N = self.width - len(type) - len(' [ ] ') - fmt = '%%s %%-%ds ' % N - return fmt % (type, name) - - def __init__(self, use_color): - self.cond_var = threading.Condition() - self.ticker = None - if use_color: - self.color_RED = '\033[1;31m' - self.color_red = '\033[0;31m' - self.color_reset = '\033[0;37m' - else: - self.color_RED = '' - self.color_red = '' - self.color_reset = '' - - argv = shlex.split('stty size') # get terminal width - proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (stdout, stderr) = proc.communicate() - if proc.returncode == 0: - (h, w) = stdout.split() - self.width = int(w) - else: - self.width = 72 # conservative guesstimate - - def begin(self, type, name): - self.text = self._format_begin(type, name) - sys.stdout.write(self.text + '[ ]') - sys.stdout.flush() - self._start_ticker() - - def end_pass(self, type, name): - self._stop_ticker() - sys.stdout.write('\r' + self.text + '[ OK ]\n') - sys.stdout.flush() - - def end_fail(self, type, name, msg): - self._stop_ticker() - sys.stdout.write('\r' + self.color_RED + self.text + '[FAIL]\n') - sys.stdout.write(self.color_red) - sys.stdout.write(msg) - sys.stdout.write(self.color_reset) - sys.stdout.flush() - -class QuietPrinter: - def begin(self, type, name): - pass - - def end_pass(self, type, name): - sys.stdout.write('PASS ' + type + ' ' + name + '\n') - sys.stdout.flush() - - def end_fail(self, type, name, msg): - sys.stdout.write('FAIL ' + type + ' ' + name + '\n') - sys.stdout.flush() - -class CompilationTask: - def __init__(self, makefile): - self.makefile = makefile - - def get_type(self): - return TASK_COMPILATION - - def get_name(self): - return self.makefile - - def execute(self): - os.putenv('ONE_SHOT_MAKEFILE', os.getcwd() + "/" + self.makefile) - argv = shlex.split('make -C "../../../../../" files') - proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (stdout, stderr) = proc.communicate() - return proc.returncode, stdout, stderr - -class InstrumentationTask: - def __init__(self, instrumentation_class): - self.instrumentation_class = instrumentation_class - - def get_type(self): - return TASK_INSTRUMENTATION - - def get_name(self): - return self.instrumentation_class - - def execute(self): - return _adb_shell('am instrument -r -w -e class %s com.android.overlaytest/android.test.InstrumentationTestRunner' % self.instrumentation_class) - -class PushTask: - def __init__(self, src, dest): - self.src = src - self.dest = dest - - def get_type(self): - return TASK_PUSH - - def get_name(self): - return "%s -> %s" % (self.src, self.dest) - - def execute(self): - src = os.getenv('OUT') - if (src is None): - return 1, "", "Unable to proceed - $OUT environment var not set\n" - src += "/" + self.src - argv = shlex.split(adb + ' push %s %s' % (src, self.dest)) - proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (stdout, stderr) = proc.communicate() - return proc.returncode, stdout, stderr - -class MkdirTask: - def __init__(self, path): - self.path = path - - def get_type(self): - return TASK_MKDIR - - def get_name(self): - return self.path - - def execute(self): - return _adb_shell('mkdir -p %s' % self.path) - -class RmTask: - def __init__(self, path): - self.path = path - - def get_type(self): - return TASK_RM - - def get_name(self): - return self.path - - def execute(self): - returncode, stdout, stderr = _adb_shell('ls %s' % self.path) - if returncode != 0 and stderr.endswith(': No such file or directory\n'): - return 0, "", "" - return _adb_shell('rm -r %s' % self.path) - -class SetPropTask: - def __init__(self, prop, value): - self.prop = prop - self.value = value - - def get_type(self): - return TASK_SETPROP - - def get_name(self): - return self.prop - - def execute(self): - return _adb_shell('setprop %s %s' % (self.prop, self.value)) - -class IdmapPathTask: - def __init__(self, path_target_apk, path_overlay_apk, path_idmap): - self.path_target_apk = path_target_apk - self.path_overlay_apk = path_overlay_apk - self.path_idmap = path_idmap - - def get_type(self): - return TASK_IDMAP_PATH - - def get_name(self): - return self.path_idmap - - def execute(self): - return _adb_shell('su system idmap --scan "%s" "%s" "%s" "%s"' % (self.target_pkg_name, self.target_pkg, self.idmap_dir, self.overlay_dir)) - -class IdmapScanTask: - def __init__(self, overlay_dir, target_pkg_name, target_pkg, idmap_dir, symlink_dir): - self.overlay_dir = overlay_dir - self.target_pkg_name = target_pkg_name - self.target_pkg = target_pkg - self.idmap_dir = idmap_dir - self.symlink_dir = symlink_dir - - def get_type(self): - return TASK_IDMAP_SCAN - - def get_name(self): - return self.target_pkg_name - - def execute(self): - return _adb_shell('su system idmap --scan "%s" "%s" "%s" "%s"' % (self.overlay_dir, self.target_pkg_name, self.target_pkg, self.idmap_dir)) - -class FileExistsTest: - def __init__(self, path): - self.path = path - - def get_type(self): - return TASK_FILE_EXISTS_TEST - - def get_name(self): - return self.path - - def execute(self): - return _adb_shell('ls %s' % self.path) - -class GrepIdmapTest: - def __init__(self, path_idmap, pattern, expected_n): - self.path_idmap = path_idmap - self.pattern = pattern - self.expected_n = expected_n - - def get_type(self): - return TASK_GREP_IDMAP_TEST - - def get_name(self): - return self.pattern - - def execute(self): - returncode, stdout, stderr = _adb_shell('idmap --inspect %s' % self.path_idmap) - if returncode != 0: - return returncode, stdout, stderr - all_matches = re.findall('\s' + self.pattern + '$', stdout, flags=re.MULTILINE) - if len(all_matches) != self.expected_n: - return 1, 'pattern=%s idmap=%s expected=%d found=%d\n' % (self.pattern, self.path_idmap, self.expected_n, len(all_matches)), '' - return 0, "", "" - -class Md5Test: - def __init__(self, path, expected_content): - self.path = path - self.expected_md5 = hashlib.md5(expected_content).hexdigest() - - def get_type(self): - return TASK_MD5_TEST - - def get_name(self): - return self.path - - def execute(self): - returncode, stdout, stderr = _adb_shell('md5sum %s' % self.path) - if returncode != 0: - return returncode, stdout, stderr - actual_md5 = stdout.split()[0] - if actual_md5 != self.expected_md5: - return 1, 'expected %s, got %s\n' % (self.expected_md5, actual_md5), '' - return 0, "", "" - -class StartTask: - def get_type(self): - return TASK_START - - def get_name(self): - return "" - - def execute(self): - (returncode, stdout, stderr) = _adb_shell('start') - if returncode != 0: - return returncode, stdout, stderr - - while True: - (returncode, stdout, stderr) = _adb_shell('getprop dev.bootcomplete') - if returncode != 0: - return returncode, stdout, stderr - if stdout.strip() == "1": - break - time.sleep(0.5) - - return 0, "", "" - -class StopTask: - def get_type(self): - return TASK_STOP - - def get_name(self): - return "" - - def execute(self): - (returncode, stdout, stderr) = _adb_shell('stop') - if returncode != 0: - return returncode, stdout, stderr - return _adb_shell('setprop dev.bootcomplete 0') - -class RootTask: - def get_type(self): - return TASK_ROOT - - def get_name(self): - return "" - - def execute(self): - (returncode, stdout, stderr) = _adb_shell('getprop service.adb.root 0') - if returncode != 0: - return returncode, stdout, stderr - if stdout.strip() == '1': # already root - return 0, "", "" - - argv = shlex.split(adb + ' root') - proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (stdout, stderr) = proc.communicate() - if proc.returncode != 0: - return proc.returncode, stdout, stderr - - argv = shlex.split(adb + ' wait-for-device') - proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (stdout, stderr) = proc.communicate() - return proc.returncode, stdout, stderr - -class RemountTask: - def get_type(self): - return TASK_REMOUNT - - def get_name(self): - return "" - - def execute(self): - argv = shlex.split(adb + ' remount') - proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (stdout, stderr) = proc.communicate() - # adb remount returns 0 even if the operation failed, so check stdout - if stdout.startswith('remount failed:'): - return 1, stdout, stderr - return proc.returncode, stdout, stderr - -class CompoundTask: - def __init__(self, type, tasks): - self.type = type - self.tasks = tasks - - def get_type(self): - return self.type - - def get_name(self): - return "" - - def execute(self): - for t in self.tasks: - (returncode, stdout, stderr) = t.execute() - if returncode != 0: - return returncode, stdout, stderr - return 0, "", "" - -def _create_disable_overlays_task(): - tasks = [ - RmTask("/vendor/overlay/framework_a.apk"), - RmTask("/vendor/overlay/framework_b.apk"), - RmTask("/data/resource-cache/vendor@overlay@framework_a.apk@idmap"), - RmTask("/data/resource-cache/vendor@overlay@framework_b.apk@idmap"), - RmTask("/vendor/overlay/app_a.apk"), - RmTask("/vendor/overlay/app_b.apk"), - RmTask("/vendor/overlay/app_c.apk"), - RmTask("/data/resource-cache/vendor@overlay@app_a.apk@idmap"), - RmTask("/data/resource-cache/vendor@overlay@app_b.apk@idmap"), - RmTask("/data/resource-cache/vendor@overlay@app_c.apk@idmap"), - SetPropTask('persist.oem.overlay.test', '""'), - RmTask("/data/property/persist.oem.overlay.test"), - ] - return CompoundTask(TASK_DISABLE_OVERLAYS, tasks) - -def _create_enable_single_overlay_task(): - tasks = [ - _create_disable_overlays_task(), - MkdirTask('/system/vendor'), - MkdirTask('/vendor/overlay'), - PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_a.apk'), - PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'), - ] - return CompoundTask(TASK_ENABLE_SINGLE_OVERLAY, tasks) - -def _create_enable_multiple_overlays_task(): - tasks = [ - _create_disable_overlays_task(), - MkdirTask('/system/vendor'), - MkdirTask('/vendor/overlay'), - - PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'), - PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'), - PushTask('/data/app/com.android.overlaytest.second_app_overlay/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'), - PushTask('/data/app/com.android.overlaytest.filtered_app_overlay/com.android.overlaytest.filtered_app_overlay.apk', '/vendor/overlay/app_c.apk'), - ] - return CompoundTask(TASK_ENABLE_MULTIPLE_OVERLAYS, tasks) - -def _create_enable_filtered_overlays_task(): - tasks = [ - _create_disable_overlays_task(), - SetPropTask('persist.oem.overlay.test', 'foo'), - MkdirTask('/system/vendor'), - MkdirTask('/vendor/overlay'), - PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'), - PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'), - PushTask('/data/app/com.android.overlaytest.second_app_overlay/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'), - PushTask('/data/app/com.android.overlaytest.filtered_app_overlay/com.android.overlaytest.filtered_app_overlay.apk', '/vendor/overlay/app_c.apk'), - ] - return CompoundTask(TASK_ENABLE_FILTERED_OVERLAYS, tasks) - -def _create_setup_idmap_path_task(idmaps, symlinks): - tasks = [ - _create_enable_single_overlay_task(), - RmTask(symlinks), - RmTask(idmaps), - MkdirTask(idmaps), - MkdirTask(symlinks), - ] - return CompoundTask(TASK_SETUP_IDMAP_PATH, tasks) - -def _create_setup_idmap_scan_task(idmaps, symlinks): - tasks = [ - _create_enable_filtered_overlays_task(), - RmTask(symlinks), - RmTask(idmaps), - MkdirTask(idmaps), - MkdirTask(symlinks), - ] - return CompoundTask(TASK_SETUP_IDMAP_SCAN, tasks) - -def _handle_instrumentation_task_output(stdout, printer): - regex_status_code = re.compile(r'^INSTRUMENTATION_STATUS_CODE: -?(\d+)') - regex_name = re.compile(r'^INSTRUMENTATION_STATUS: test=(.*)') - regex_begin_stack = re.compile(r'^INSTRUMENTATION_STATUS: stack=(.*)') - regex_end_stack = re.compile(r'^$') - - failed_tests = 0 - current_test = None - current_stack = [] - mode_stack = False - for line in stdout.split("\n"): - line = line.rstrip() # strip \r from adb output - m = regex_status_code.match(line) - if m: - c = int(m.group(1)) - if c == 1: - printer.begin(TASK_INSTRUMENTATION_TEST, current_test) - elif c == 0: - printer.end_pass(TASK_INSTRUMENTATION_TEST, current_test) - else: - failed_tests += 1 - current_stack.append("\n") - msg = "\n".join(current_stack) - printer.end_fail(TASK_INSTRUMENTATION_TEST, current_test, msg.rstrip() + '\n') - continue - - m = regex_name.match(line) - if m: - current_test = m.group(1) - continue - - m = regex_begin_stack.match(line) - if m: - mode_stack = True - current_stack = [] - current_stack.append(" " + m.group(1)) - continue - - m = regex_end_stack.match(line) - if m: - mode_stack = False - continue - - if mode_stack: - current_stack.append(" " + line.strip()) - - return failed_tests - -def _set_adb_device(option, opt, value, parser): - global adb - if opt == '-d' or opt == '--device': - adb = 'adb -d' - if opt == '-e' or opt == '--emulator': - adb = 'adb -e' - if opt == '-s' or opt == '--serial': - adb = 'adb -s ' + value - -def _create_opt_parser(): - parser = optparse.OptionParser() - parser.add_option('-d', '--device', action='callback', callback=_set_adb_device, - help='pass -d to adb') - parser.add_option('-e', '--emulator', action='callback', callback=_set_adb_device, - help='pass -e to adb') - parser.add_option('-s', '--serial', type="str", action='callback', callback=_set_adb_device, - help='pass -s <serical> to adb') - parser.add_option('-C', '--no-color', action='store_false', - dest='use_color', default=True, - help='disable color escape sequences in output') - parser.add_option('-q', '--quiet', action='store_true', - dest='quiet_mode', default=False, - help='quiet mode, output only results') - parser.add_option('-b', '--no-build', action='store_false', - dest='do_build', default=True, - help='do not rebuild test projects') - parser.add_option('-k', '--continue', action='store_true', - dest='do_continue', default=False, - help='do not rebuild test projects') - parser.add_option('-i', '--test-idmap', action='store_true', - dest='test_idmap', default=False, - help='run tests for idmap') - parser.add_option('-0', '--test-no-overlay', action='store_true', - dest='test_no_overlay', default=False, - help='run tests without any overlay') - parser.add_option('-1', '--test-single-overlay', action='store_true', - dest='test_single_overlay', default=False, - help='run tests for single overlay') - parser.add_option('-2', '--test-multiple-overlays', action='store_true', - dest='test_multiple_overlays', default=False, - help='run tests for multiple overlays') - parser.add_option('-3', '--test-filtered-overlays', action='store_true', - dest='test_filtered_overlays', default=False, - help='run tests for filtered (sys prop) overlays') - return parser - -if __name__ == '__main__': - opt_parser = _create_opt_parser() - opts, args = opt_parser.parse_args(sys.argv[1:]) - if not opts.test_idmap and not opts.test_no_overlay and not opts.test_single_overlay and not opts.test_multiple_overlays and not opts.test_filtered_overlays: - opts.test_idmap = True - opts.test_no_overlay = True - opts.test_single_overlay = True - opts.test_multiple_overlays = True - opts.test_filtered_overlays = True - - if len(args) > 0: - opt_parser.error("unexpected arguments: %s" % " ".join(args)) - # will never reach this: opt_parser.error will call sys.exit - - if opts.quiet_mode: - printer = QuietPrinter() - else: - printer = VerbosePrinter(opts.use_color) - tasks = [] - - # must be in the same directory as this script for compilation tasks to work - script = sys.argv[0] - dirname = os.path.dirname(script) - wd = os.path.realpath(dirname) - os.chdir(wd) - - # build test cases - if opts.do_build: - tasks.append(CompilationTask('OverlayTest/Android.mk')) - tasks.append(CompilationTask('OverlayTestOverlay/Android.mk')) - tasks.append(CompilationTask('OverlayAppFirst/Android.mk')) - tasks.append(CompilationTask('OverlayAppSecond/Android.mk')) - tasks.append(CompilationTask('OverlayAppFiltered/Android.mk')) - - # remount filesystem, install test project - tasks.append(RootTask()) - tasks.append(RemountTask()) - tasks.append(PushTask('/system/app/OverlayTest/OverlayTest.apk', '/system/app/OverlayTest.apk')) - - # test idmap - if opts.test_idmap: - idmaps='/data/local/tmp/idmaps' - symlinks='/data/local/tmp/symlinks' - - # idmap --path - tasks.append(StopTask()) - tasks.append(_create_setup_idmap_path_task(idmaps, symlinks)) - tasks.append(StartTask()) - tasks.append(IdmapPathTask('/vendor/overlay/framework_a.apk', '/system/framework/framework-res.apk', idmaps + '/a.idmap')) - tasks.append(FileExistsTest(idmaps + '/a.idmap')) - tasks.append(GrepIdmapTest(idmaps + '/a.idmap', 'bool/config_annoy_dianne', 1)) - - # idmap --scan - tasks.append(StopTask()) - tasks.append(_create_setup_idmap_scan_task(idmaps, symlinks)) - tasks.append(StartTask()) - tasks.append(IdmapScanTask('/vendor/overlay', 'android', '/system/framework/framework-res.apk', idmaps, symlinks)) - tasks.append(FileExistsTest(idmaps + '/vendor@overlay@framework_b.apk@idmap')) - tasks.append(GrepIdmapTest(idmaps + '/vendor@overlay@framework_b.apk@idmap', 'bool/config_annoy_dianne', 1)) - - - # overlays.list - overlays_list_path = idmaps + '/overlays.list' - expected_content = '''\ -/vendor/overlay/framework_b.apk /data/local/tmp/idmaps/vendor@overlay@framework_b.apk@idmap -''' - tasks.append(FileExistsTest(overlays_list_path)) - tasks.append(Md5Test(overlays_list_path, expected_content)) - - # idmap cleanup - tasks.append(RmTask(symlinks)) - tasks.append(RmTask(idmaps)) - - # test no overlay: all overlays cleared - if opts.test_no_overlay: - tasks.append(StopTask()) - tasks.append(_create_disable_overlays_task()) - tasks.append(StartTask()) - tasks.append(InstrumentationTask('com.android.overlaytest.WithoutOverlayTest')) - - # test single overlay: one overlay (a) - if opts.test_single_overlay: - tasks.append(StopTask()) - tasks.append(_create_enable_single_overlay_task()) - tasks.append(StartTask()) - tasks.append(InstrumentationTask('com.android.overlaytest.WithOverlayTest')) - - # test multiple overlays: all overlays - including 'disabled' filtered - # overlay (system property unset) so expect 'b[p=2]' overrides 'a[p=1]' but - # 'c[p=3]' should be ignored - if opts.test_multiple_overlays: - tasks.append(StopTask()) - tasks.append(_create_enable_multiple_overlays_task()) - tasks.append(StartTask()) - tasks.append(InstrumentationTask('com.android.overlaytest.WithMultipleOverlaysTest')) - - # test filtered overlays: all overlays - including 'enabled' filtered - # overlay (system property set/matched) so expect c[p=3] to override both a - # & b where applicable - if opts.test_filtered_overlays: - tasks.append(StopTask()) - tasks.append(_create_enable_filtered_overlays_task()) - tasks.append(StartTask()) - tasks.append(InstrumentationTask('com.android.overlaytest.WithFilteredOverlaysTest')) - - ignored_errors = 0 - for t in tasks: - type = t.get_type() - name = t.get_name() - if type == TASK_INSTRUMENTATION: - # InstrumentationTask will run several tests, but we want it - # to appear as if each test was run individually. Calling - # "am instrument" with a single test method is prohibitively - # expensive, so let's instead post-process the output to - # emulate individual calls. - retcode, stdout, stderr = t.execute() - if retcode != 0: - printer.begin(TASK_INSTRUMENTATION, name) - printer.end_fail(TASK_INSTRUMENTATION, name, stderr) - sys.exit(retcode) - retcode = _handle_instrumentation_task_output(stdout, printer) - if retcode != 0: - if not opts.do_continue: - sys.exit(retcode) - else: - ignored_errors += retcode - else: - printer.begin(type, name) - retcode, stdout, stderr = t.execute() - if retcode == 0: - printer.end_pass(type, name) - if retcode != 0: - if len(stderr) == 0: - # hope for output from stdout instead (true for eg adb shell rm) - stderr = stdout - printer.end_fail(type, name, stderr) - if not opts.do_continue: - sys.exit(retcode) - else: - ignored_errors += retcode - sys.exit(ignored_errors) diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 8addffbb02db..a678c4d49092 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -157,6 +157,7 @@ applications that come with the platform <permission name="android.permission.LOCAL_MAC_ADDRESS"/> <permission name="android.permission.MANAGE_USERS"/> <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.PACKAGE_USAGE_STATS"/> <permission name="android.permission.PERFORM_CDMA_PROVISIONING"/> <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> @@ -227,6 +228,7 @@ applications that come with the platform <permission name="android.permission.CALL_PRIVILEGED"/> <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MODIFY_AUDIO_ROUTING" /> <permission name="android.permission.MODIFY_PHONE_STATE"/> <permission name="android.permission.STOP_APP_SWITCHES"/> <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/> diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index dad24da6ad98..22867df76aea 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -333,6 +333,9 @@ <family lang="und-Cari"> <font weight="400" style="normal">NotoSansCarian-Regular.ttf</font> </family> + <family lang="und-Cakm"> + <font weight="400" style="normal">NotoSansChakma-Regular.ttf</font> + </family> <family lang="und-Cher"> <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font> </family> @@ -429,6 +432,9 @@ <family lang="und-Orkh"> <font weight="400" style="normal">NotoSansOldTurkic-Regular.ttf</font> </family> + <family lang="und-Osge"> + <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font> + </family> <family lang="und-Osma"> <font weight="400" style="normal">NotoSansOsmanya-Regular.ttf</font> </family> diff --git a/docs/html/reference/images/text/style/drawablemarginspan.png b/docs/html/reference/images/text/style/drawablemarginspan.png Binary files differnew file mode 100644 index 000000000000..edf926d80130 --- /dev/null +++ b/docs/html/reference/images/text/style/drawablemarginspan.png diff --git a/docs/html/reference/images/text/style/dynamicdrawablespan.png b/docs/html/reference/images/text/style/dynamicdrawablespan.png Binary files differnew file mode 100644 index 000000000000..8776b0387278 --- /dev/null +++ b/docs/html/reference/images/text/style/dynamicdrawablespan.png diff --git a/docs/html/reference/images/text/style/iconmarginspan.png b/docs/html/reference/images/text/style/iconmarginspan.png Binary files differnew file mode 100644 index 000000000000..8ec39be039f6 --- /dev/null +++ b/docs/html/reference/images/text/style/iconmarginspan.png diff --git a/docs/html/reference/images/text/style/imagespan.png b/docs/html/reference/images/text/style/imagespan.png Binary files differnew file mode 100644 index 000000000000..c03e6bb68bd5 --- /dev/null +++ b/docs/html/reference/images/text/style/imagespan.png diff --git a/docs/html/reference/images/text/style/maskfilterspan.png b/docs/html/reference/images/text/style/maskfilterspan.png Binary files differnew file mode 100644 index 000000000000..6e55dbc1509c --- /dev/null +++ b/docs/html/reference/images/text/style/maskfilterspan.png diff --git a/docs/html/reference/images/text/style/stylespan.png b/docs/html/reference/images/text/style/stylespan.png Binary files differnew file mode 100644 index 000000000000..9ffa05b5ac43 --- /dev/null +++ b/docs/html/reference/images/text/style/stylespan.png diff --git a/docs/html/reference/images/text/style/tabstopspan.png b/docs/html/reference/images/text/style/tabstopspan.png Binary files differnew file mode 100644 index 000000000000..89a1121d0745 --- /dev/null +++ b/docs/html/reference/images/text/style/tabstopspan.png diff --git a/docs/html/reference/images/text/style/typefacespan.png b/docs/html/reference/images/text/style/typefacespan.png Binary files differnew file mode 100644 index 000000000000..67e2cf9b0468 --- /dev/null +++ b/docs/html/reference/images/text/style/typefacespan.png diff --git a/docs/html/reference/images/text/style/urlspan.png b/docs/html/reference/images/text/style/urlspan.png Binary files differnew file mode 100644 index 000000000000..11345206e594 --- /dev/null +++ b/docs/html/reference/images/text/style/urlspan.png diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java index 627d5515b77a..69a5874e9381 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -541,10 +541,19 @@ public abstract class BaseCanvas { return mAllowHwBitmapsInSwMode; } + /** + * @hide + */ + protected void onHwBitmapInSwMode() { + if (!mAllowHwBitmapsInSwMode) { + throw new IllegalArgumentException( + "Software rendering doesn't support hardware bitmaps"); + } + } + private void throwIfHwBitmapInSwMode(Bitmap bitmap) { - if (!mAllowHwBitmapsInSwMode && !isHardwareAccelerated() - && bitmap.getConfig() == Bitmap.Config.HARDWARE) { - throw new IllegalStateException("Software rendering doesn't support hardware bitmaps"); + if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) { + onHwBitmapInSwMode(); } } diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 0072012f69ad..44e7066c5c66 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -29,6 +29,10 @@ import android.os.StrictMode; import android.os.Trace; import android.util.DisplayMetrics; import android.util.Log; +import android.view.DisplayListCanvas; +import android.view.RenderNode; +import android.view.ThreadedRenderer; + import libcore.util.NativeAllocationRegistry; import java.io.OutputStream; @@ -1171,6 +1175,82 @@ public final class Bitmap implements Parcelable { } /** + * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands. + * + * Equivalent to calling {@link #createBitmap(Picture, int, int, Config)} with + * width and height the same as the Picture's width and height and a Config.HARDWARE + * config. + * + * @param source The recorded {@link Picture} of drawing commands that will be + * drawn into the returned Bitmap. + * @return An immutable bitmap with a HARDWARE config whose contents are created + * from the recorded drawing commands in the Picture source. + */ + public static @NonNull Bitmap createBitmap(@NonNull Picture source) { + return createBitmap(source, source.getWidth(), source.getHeight(), Config.HARDWARE); + } + + /** + * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands. + * + * The bitmap will be immutable with the given width and height. If the width and height + * are not the same as the Picture's width & height, the Picture will be scaled to + * fit the given width and height. + * + * @param source The recorded {@link Picture} of drawing commands that will be + * drawn into the returned Bitmap. + * @param width The width of the bitmap to create. The picture's width will be + * scaled to match if necessary. + * @param height The height of the bitmap to create. The picture's height will be + * scaled to match if necessary. + * @param config The {@link Config} of the created bitmap. If this is null then + * the bitmap will be {@link Config#HARDWARE}. + * + * @return An immutable bitmap with a HARDWARE config whose contents are created + * from the recorded drawing commands in the Picture source. + */ + public static @NonNull Bitmap createBitmap(@NonNull Picture source, int width, int height, + @NonNull Config config) { + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException("width & height must be > 0"); + } + if (config == null) { + throw new IllegalArgumentException("Config must not be null"); + } + if (source.requiresHardwareAcceleration() && config != Config.HARDWARE) { + StrictMode.noteSlowCall("GPU readback"); + } + if (config == Config.HARDWARE || source.requiresHardwareAcceleration()) { + final RenderNode node = RenderNode.create("BitmapTemporary", null); + node.setLeftTopRightBottom(0, 0, width, height); + node.setClipToBounds(false); + final DisplayListCanvas canvas = node.start(width, height); + if (source.getWidth() != width || source.getHeight() != height) { + canvas.scale(width / (float) source.getWidth(), + height / (float) source.getHeight()); + } + canvas.drawPicture(source); + node.end(canvas); + Bitmap bitmap = ThreadedRenderer.createHardwareBitmap(node, width, height); + if (config != Config.HARDWARE) { + bitmap = bitmap.copy(config, false); + } + return bitmap; + } else { + Bitmap bitmap = Bitmap.createBitmap(width, height, config); + Canvas canvas = new Canvas(bitmap); + if (source.getWidth() != width || source.getHeight() != height) { + canvas.scale(width / (float) source.getWidth(), + height / (float) source.getHeight()); + } + canvas.drawPicture(source); + canvas.setBitmap(null); + bitmap.makeImmutable(); + return bitmap; + } + } + + /** * Returns an optional array of private data, used by the UI system for * some bitmaps. Not intended to be called by applications. */ @@ -1259,6 +1339,12 @@ public final class Bitmap implements Parcelable { return mIsMutable; } + /** @hide */ + public final void makeImmutable() { + // todo mIsMutable = false; + // todo nMakeImmutable(); + } + /** * <p>Indicates whether pixels stored in this bitmaps are stored pre-multiplied. * When a pixel is pre-multiplied, the RGB components have been multiplied by diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java index d77e6012fb46..fe2b52341063 100644 --- a/graphics/java/android/graphics/FontFamily.java +++ b/graphics/java/android/graphics/FontFamily.java @@ -160,25 +160,6 @@ public class FontFamily { isItalic); } - /** - * Allow creating unsupported FontFamily. - * - * For compatibility reasons, we still need to create a FontFamily object even if Minikin failed - * to find any usable 'cmap' table for some reasons, e.g. broken 'cmap' table, no 'cmap' table - * encoded with Unicode code points, etc. Without calling this method, the freeze() method will - * return null if Minikin fails to find any usable 'cmap' table. By calling this method, the - * freeze() won't fail and will create an empty FontFamily. This empty FontFamily is placed at - * the top of the fallback chain but is never used. if we don't create this empty FontFamily - * and put it at top, bad things (performance regressions, unexpected glyph selection) will - * happen. - */ - public void allowUnsupportedFont() { - if (mBuilderPtr == 0) { - throw new IllegalStateException("Unable to allow unsupported font."); - } - nAllowUnsupportedFont(mBuilderPtr); - } - // TODO: Remove once internal user stop using private API. private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) { return nAddFont(builderPtr, font, ttcIndex, -1, -1); @@ -190,9 +171,6 @@ public class FontFamily { private static native long nCreateFamily(long mBuilderPtr); @CriticalNative - private static native void nAllowUnsupportedFont(long builderPtr); - - @CriticalNative private static native void nAbort(long mBuilderPtr); @CriticalNative diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index bbf214559459..acefead785c4 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -74,7 +74,7 @@ public final class ImageDecoder implements AutoCloseable { int getDensity() { return Bitmap.DENSITY_NONE; } /* @hide */ - int computeDstDensity() { + final int computeDstDensity() { Resources res = getResources(); if (res == null) { return Bitmap.getDefaultDensity(); @@ -122,13 +122,19 @@ public final class ImageDecoder implements AutoCloseable { } private static class ContentResolverSource extends Source { - ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri) { + ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri, + @Nullable Resources res) { mResolver = resolver; mUri = uri; + mResources = res; } private final ContentResolver mResolver; private final Uri mUri; + private final Resources mResources; + + @Nullable + Resources getResources() { return mResources; } @Override public ImageDecoder createImageDecoder() throws IOException { @@ -438,6 +444,7 @@ public final class ImageDecoder implements AutoCloseable { private boolean mPreferRamOverQuality = false; private boolean mAsAlphaMask = false; private Rect mCropRect; + private Rect mOutPaddingRect; private Source mSource; private PostProcessor mPostProcessor; @@ -511,7 +518,18 @@ public final class ImageDecoder implements AutoCloseable { @NonNull public static Source createSource(@NonNull ContentResolver cr, @NonNull Uri uri) { - return new ContentResolverSource(cr, uri); + return new ContentResolverSource(cr, uri, null); + } + + /** + * Provide Resources for density scaling. + * + * @hide + */ + @NonNull + public static Source createSource(@NonNull ContentResolver cr, + @NonNull Uri uri, @Nullable Resources res) { + return new ContentResolverSource(cr, uri, res); } /** @@ -765,6 +783,18 @@ public final class ImageDecoder implements AutoCloseable { } /** + * Set a Rect for retrieving nine patch padding. + * + * If the image is a nine patch, this Rect will be set to the padding + * rectangle during decode. Otherwise it will not be modified. + * + * @hide + */ + public void setOutPaddingRect(@NonNull Rect outPadding) { + mOutPaddingRect = outPadding; + } + + /** * Specify whether the {@link Bitmap} should be mutable. * * <p>By default, a {@link Bitmap} created will be immutable, but that can @@ -875,7 +905,6 @@ public final class ImageDecoder implements AutoCloseable { postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect, mMutable, mAllocator, mRequireUnpremultiplied, mPreferRamOverQuality, mAsAlphaMask); - } private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener, @@ -948,7 +977,10 @@ public final class ImageDecoder implements AutoCloseable { if (np != null && NinePatch.isNinePatchChunk(np)) { Rect opticalInsets = new Rect(); bm.getOpticalInsets(opticalInsets); - Rect padding = new Rect(); + Rect padding = decoder.mOutPaddingRect; + if (padding == null) { + padding = new Rect(); + } nGetPadding(decoder.mNativePtr, padding); return new NinePatchDrawable(res, bm, np, padding, opticalInsets, null); @@ -991,6 +1023,15 @@ public final class ImageDecoder implements AutoCloseable { final int srcDensity = computeDensity(src, decoder); Bitmap bm = decoder.decodeBitmap(); bm.setDensity(srcDensity); + + Rect padding = decoder.mOutPaddingRect; + if (padding != null) { + byte[] np = bm.getNinePatchChunk(); + if (np != null && NinePatch.isNinePatchChunk(np)) { + nGetPadding(decoder.mNativePtr, padding); + } + } + return bm; } } diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java index 08eeaff69f9b..9ac94d895a37 100644 --- a/graphics/java/android/graphics/Picture.java +++ b/graphics/java/android/graphics/Picture.java @@ -31,8 +31,9 @@ import java.io.OutputStream; * be replayed on a hardware accelerated canvas.</p> */ public class Picture { - private Canvas mRecordingCanvas; + private PictureCanvas mRecordingCanvas; private long mNativePicture; + private boolean mRequiresHwAcceleration; private static final int WORKING_STREAM_STORAGE = 16 * 1024; @@ -78,8 +79,12 @@ public class Picture { * into it. */ public Canvas beginRecording(int width, int height) { + if (mRecordingCanvas != null) { + throw new IllegalStateException("Picture already recording, must call #endRecording()"); + } long ni = nativeBeginRecording(mNativePicture, width, height); - mRecordingCanvas = new RecordingCanvas(this, ni); + mRecordingCanvas = new PictureCanvas(this, ni); + mRequiresHwAcceleration = false; return mRecordingCanvas; } @@ -91,6 +96,7 @@ public class Picture { */ public void endRecording() { if (mRecordingCanvas != null) { + mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap; mRecordingCanvas = null; nativeEndRecording(mNativePicture); } @@ -113,6 +119,18 @@ public class Picture { } /** + * Indicates whether or not this Picture contains recorded commands that only work when + * drawn to a hardware-accelerated canvas. If this returns true then this Picture can only + * be drawn to another Picture or to a Canvas where canvas.isHardwareAccelerated() is true. + * + * @return true if the Picture can only be drawn to a hardware-accelerated canvas, + * false otherwise. + */ + public boolean requiresHardwareAcceleration() { + return mRequiresHwAcceleration; + } + + /** * Draw this picture on the canvas. * <p> * Prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this call could @@ -129,6 +147,9 @@ public class Picture { if (mRecordingCanvas != null) { endRecording(); } + if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) { + canvas.onHwBitmapInSwMode(); + } nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture); } @@ -164,8 +185,7 @@ public class Picture { if (stream == null) { throw new NullPointerException(); } - if (!nativeWriteToStream(mNativePicture, stream, - new byte[WORKING_STREAM_STORAGE])) { + if (!nativeWriteToStream(mNativePicture, stream, new byte[WORKING_STREAM_STORAGE])) { throw new RuntimeException(); } } @@ -182,10 +202,11 @@ public class Picture { OutputStream stream, byte[] storage); private static native void nativeDestructor(long nativePicture); - private static class RecordingCanvas extends Canvas { + private static class PictureCanvas extends Canvas { private final Picture mPicture; + boolean mHoldsHwBitmap; - public RecordingCanvas(Picture pict, long nativeCanvas) { + public PictureCanvas(Picture pict, long nativeCanvas) { super(nativeCanvas); mPicture = pict; } @@ -202,5 +223,10 @@ public class Picture { } super.drawPicture(picture); } + + @Override + protected void onHwBitmapInSwMode() { + mHoldsHwBitmap = true; + } } } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index ef4150763139..04c5295539ac 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -818,12 +818,9 @@ public class Typeface { if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */, 0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE, null /* axes */)) { - // Due to backward compatibility, even if the font is not supported by our font - // stack, we need to place the empty font at the first place. The typeface with - // empty font behaves different from default typeface especially in fallback - // font selection. - fontFamily.allowUnsupportedFont(); - fontFamily.freeze(); + if (!fontFamily.freeze()) { + return Typeface.DEFAULT; + } final FontFamily[] families = { fontFamily }; typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); @@ -870,12 +867,9 @@ public class Typeface { final FontFamily fontFamily = new FontFamily(); if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) { - // Due to backward compatibility, even if the font is not supported by our font - // stack, we need to place the empty font at the first place. The typeface with - // empty font behaves different from default typeface especially in fallback font - // selection. - fontFamily.allowUnsupportedFont(); - fontFamily.freeze(); + if (!fontFamily.freeze()) { + return Typeface.DEFAULT; + } FontFamily[] families = { fontFamily }; return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java index bd49b87ec200..27c8fda0d6e9 100644 --- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -348,7 +348,9 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { if (mState == null) { throw new IllegalStateException("called stop on empty AnimatedImageDrawable"); } - nStop(mState.mNativePtr); + if (nStop(mState.mNativePtr)) { + postOnAnimationEnd(); + } } // Animatable2 overrides @@ -365,21 +367,31 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { nSetOnAnimationEndListener(mState.mNativePtr, this); } - mAnimationCallbacks.add(callback); + if (!mAnimationCallbacks.contains(callback)) { + mAnimationCallbacks.add(callback); + } } @Override public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) { - if (callback == null || mAnimationCallbacks == null) { + if (callback == null || mAnimationCallbacks == null + || !mAnimationCallbacks.remove(callback)) { return false; } - return mAnimationCallbacks.remove(callback); + if (mAnimationCallbacks.isEmpty()) { + clearAnimationCallbacks(); + } + + return true; } @Override public void clearAnimationCallbacks() { - mAnimationCallbacks = null; + if (mAnimationCallbacks != null) { + mAnimationCallbacks = null; + nSetOnAnimationEndListener(mState.mNativePtr, null); + } } private void postOnAnimationStart() { @@ -413,6 +425,21 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { return mHandler; } + /** + * Called by JNI. + * + * The JNI code has already posted this to the thread that created the + * callback, so no need to post. + */ + @SuppressWarnings("unused") + private void onAnimationEnd() { + if (mAnimationCallbacks != null) { + for (Animatable2.AnimationCallback callback : mAnimationCallbacks) { + callback.onAnimationEnd(this); + } + } + } + private static native long nCreate(long nativeImageDecoder, @Nullable ImageDecoder decoder, int width, int height, Rect cropRect) @@ -432,7 +459,7 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { @FastNative private static native boolean nStart(long nativePtr); @FastNative - private static native void nStop(long nativePtr); + private static native boolean nStop(long nativePtr); @FastNative private static native void nSetLoopCount(long nativePtr, int loopCount); // Pass the drawable down to native so it can call onAnimationEnd. diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index 7ad062a6f6f9..44b783bb6e63 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -27,6 +27,7 @@ import android.graphics.BitmapFactory; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Outline; @@ -49,6 +50,7 @@ import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -111,7 +113,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable() { - mBitmapState = new BitmapState((Bitmap) null); + init(new BitmapState((Bitmap) null), null); } /** @@ -124,8 +126,7 @@ public class BitmapDrawable extends Drawable { @SuppressWarnings("unused") @Deprecated public BitmapDrawable(Resources res) { - mBitmapState = new BitmapState((Bitmap) null); - mBitmapState.mTargetDensity = mTargetDensity; + init(new BitmapState((Bitmap) null), res); } /** @@ -135,7 +136,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(Bitmap bitmap) { - this(new BitmapState(bitmap), null); + init(new BitmapState(bitmap), null); } /** @@ -143,8 +144,7 @@ public class BitmapDrawable extends Drawable { * the display metrics of the resources. */ public BitmapDrawable(Resources res, Bitmap bitmap) { - this(new BitmapState(bitmap), res); - mBitmapState.mTargetDensity = mTargetDensity; + init(new BitmapState(bitmap), res); } /** @@ -154,10 +154,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); - } + this(null, filepath); } /** @@ -165,10 +162,21 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" }) public BitmapDrawable(Resources res, String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); - mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + Bitmap bitmap = null; + try (FileInputStream stream = new FileInputStream(filepath)) { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream), + (decoder, info, src) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); + } catch (Exception e) { + /* do nothing. This matches the behavior of BitmapFactory.decodeFile() + If the exception happened on decode, mBitmapState.mBitmap will be null. + */ + } finally { + init(new BitmapState(bitmap), res); + if (mBitmapState.mBitmap == null) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + } } } @@ -179,10 +187,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null); - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); - } + this(null, is); } /** @@ -190,10 +195,21 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" }) public BitmapDrawable(Resources res, java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null); - mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + Bitmap bitmap = null; + try { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is), + (decoder, info, src) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); + } catch (Exception e) { + /* do nothing. This matches the behavior of BitmapFactory.decodeStream() + If the exception happened on decode, mBitmapState.mBitmap will be null. + */ + } finally { + init(new BitmapState(bitmap), res); + if (mBitmapState.mBitmap == null) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + } } } @@ -812,9 +828,19 @@ public class BitmapDrawable extends Drawable { } } + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + Bitmap bitmap = null; try (InputStream is = r.openRawResource(srcResId, value)) { - bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null); + ImageDecoder.Source source = ImageDecoder.createSource(r, is, density); + bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); } catch (Exception e) { // Do nothing and pick up the error below. } @@ -1013,14 +1039,21 @@ public class BitmapDrawable extends Drawable { } } + private BitmapDrawable(BitmapState state, Resources res) { + init(state, res); + } + /** - * The one constructor to rule them all. This is called by all public + * The one helper to rule them all. This is called by all public & private * constructors to set the state and initialize local properties. */ - private BitmapDrawable(BitmapState state, Resources res) { + private void init(BitmapState state, Resources res) { mBitmapState = state; - updateLocalState(res); + + if (mBitmapState != null && res != null) { + mBitmapState.mTargetDensity = mTargetDensity; + } } /** diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index f17cd768c386..36a4d26d62bb 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -37,6 +37,7 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.NinePatch; import android.graphics.Outline; @@ -50,11 +51,13 @@ import android.graphics.Xfermode; import android.os.Trace; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.Log; import android.util.StateSet; import android.util.TypedValue; import android.util.Xml; import android.view.View; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; @@ -1175,6 +1178,10 @@ public abstract class Drawable { return null; } + if (opts == null) { + return getBitmapDrawable(res, value, is); + } + /* ugh. The decodeStream contract is that we have already allocated the pad rect, but if the bitmap does not had a ninepatch chunk, then the pad will be ignored. If we could change this to lazily @@ -1207,6 +1214,33 @@ public abstract class Drawable { return null; } + private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) { + try { + ImageDecoder.Source source = null; + if (value != null) { + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + source = ImageDecoder.createSource(res, is, density); + } else { + source = ImageDecoder.createSource(res, is); + } + + return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); + } catch (IOException e) { + /* do nothing. + If the exception happened on decode, the drawable will be null. + */ + Log.e("Drawable", "Unable to decode stream: " + e); + } + return null; + } + /** * Create a drawable from an XML document. For more information on how to * create resources in XML, see @@ -1306,11 +1340,10 @@ public abstract class Drawable { } Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName); - try { - Bitmap bm = BitmapFactory.decodeFile(pathName); - if (bm != null) { - return drawableFromBitmap(null, bm, null, null, null, pathName); - } + try (FileInputStream stream = new FileInputStream(pathName)) { + return getBitmapDrawable(null, null, stream); + } catch(IOException e) { + // Do nothing; we will just return null if the FileInputStream had an error } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 17900204fa22..a56e8d1b25ed 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -24,9 +24,9 @@ import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.NinePatch; import android.graphics.Outline; @@ -211,7 +211,8 @@ public class NinePatchDrawable extends Drawable { restoreAlpha = -1; } - final boolean needsDensityScaling = canvas.getDensity() == 0; + final boolean needsDensityScaling = canvas.getDensity() == 0 + && Bitmap.DENSITY_NONE != state.mNinePatch.getDensity(); if (needsDensityScaling) { restoreToCount = restoreToCount >= 0 ? restoreToCount : canvas.save(); @@ -421,10 +422,6 @@ public class NinePatchDrawable extends Drawable { final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0); if (srcResId != 0) { - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inDither = !state.mDither; - options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi; - final Rect padding = new Rect(); final Rect opticalInsets = new Rect(); Bitmap bitmap = null; @@ -433,7 +430,17 @@ public class NinePatchDrawable extends Drawable { final TypedValue value = new TypedValue(); final InputStream is = r.openRawResource(srcResId, value); - bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options); + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + ImageDecoder.Source source = ImageDecoder.createSource(r, is, density); + bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> { + decoder.setOutPaddingRect(padding); + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); is.close(); } catch (IOException e) { diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java index f721ed3af7ba..09b3b9b523b4 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java @@ -248,7 +248,8 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { spec.getUserAuthenticationValidityDurationSeconds(), spec.isUserAuthenticationValidWhileOnBody(), spec.isInvalidatedByBiometricEnrollment(), - GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */); + GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */, + spec.isUserConfirmationRequired()); } catch (IllegalStateException | IllegalArgumentException e) { throw new InvalidAlgorithmParameterException(e); } @@ -289,7 +290,8 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { spec.getUserAuthenticationValidityDurationSeconds(), spec.isUserAuthenticationValidWhileOnBody(), spec.isInvalidatedByBiometricEnrollment(), - GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */); + GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */, + spec.isUserConfirmationRequired()); if (spec.isTrustedUserPresenceRequired()) { args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED); } diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index d1eb6888bbfd..e33e3cd4e92b 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -349,7 +349,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mSpec.getUserAuthenticationValidityDurationSeconds(), mSpec.isUserAuthenticationValidWhileOnBody(), mSpec.isInvalidatedByBiometricEnrollment(), - GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */); + GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */, + mSpec.isUserConfirmationRequired()); } catch (IllegalArgumentException | IllegalStateException e) { throw new InvalidAlgorithmParameterException(e); } @@ -545,7 +546,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mSpec.getUserAuthenticationValidityDurationSeconds(), mSpec.isUserAuthenticationValidWhileOnBody(), mSpec.isInvalidatedByBiometricEnrollment(), - GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */); + GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */, + mSpec.isUserConfirmationRequired()); args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart()); args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, mSpec.getKeyValidityForOriginationEnd()); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java index 9df37f58fd09..7bbc09964584 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java @@ -190,6 +190,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { && !keymasterSecureUserIds.contains(getGateKeeperSecureUserId()); } + boolean userConfirmationRequired = keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED); + return new KeyInfo(entryAlias, insideSecureHardware, origin, @@ -207,7 +209,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { userAuthenticationRequirementEnforcedBySecureHardware, userAuthenticationValidWhileOnBody, trustedUserPresenceRequred, - invalidatedByBiometricEnrollment); + invalidatedByBiometricEnrollment, + userConfirmationRequired); } private static BigInteger getGateKeeperSecureUserId() throws ProviderException { diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java index 440e0863fbb1..05cc74a0bec9 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java @@ -502,7 +502,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { spec.getUserAuthenticationValidityDurationSeconds(), spec.isUserAuthenticationValidWhileOnBody(), spec.isInvalidatedByBiometricEnrollment(), - spec.getBoundToSpecificSecureUserId()); + spec.getBoundToSpecificSecureUserId(), + spec.isUserConfirmationRequired()); importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()); importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, @@ -704,7 +705,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { params.getUserAuthenticationValidityDurationSeconds(), params.isUserAuthenticationValidWhileOnBody(), params.isInvalidatedByBiometricEnrollment(), - params.getBoundToSpecificSecureUserId()); + params.getBoundToSpecificSecureUserId(), + params.isUserConfirmationRequired()); KeymasterUtils.addMinMacLengthAuthorizationIfNecessary( args, keymasterAlgorithm, diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index a896c72463fb..da23c70f58bb 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -264,6 +264,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { private final boolean mUserAuthenticationValidWhileOnBody; private final boolean mInvalidatedByBiometricEnrollment; private final boolean mIsStrongBoxBacked; + private final boolean mUserConfirmationRequired; /** * @hide should be built with Builder @@ -293,7 +294,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { boolean uniqueIdIncluded, boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, - boolean isStrongBoxBacked) { + boolean isStrongBoxBacked, + boolean userConfirmationRequired) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } @@ -341,6 +343,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody; mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mIsStrongBoxBacked = isStrongBoxBacked; + mUserConfirmationRequired = userConfirmationRequired; } /** @@ -547,6 +550,26 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { } /** + * Returns {@code true} if the key is authorized to be used only for messages confirmed by the + * user. + * + * Confirmation is separate from user authentication (see + * {@link Builder#setUserAuthenticationRequired(boolean)}). Keys can be created that require + * confirmation but not user authentication, or user authentication but not confirmation, or + * both. Confirmation verifies that some user with physical possession of the device has + * approved a displayed message. User authentication verifies that the correct user is present + * and has authenticated. + * + * <p>This authorization applies only to secret key and private key operations. Public key + * operations are not restricted. + * + * @see Builder#setUserConfirmationRequired(boolean) + */ + public boolean isUserConfirmationRequired() { + return mUserConfirmationRequired; + } + + /** * Gets the duration of time (seconds) for which this key is authorized to be used after the * user is successfully authenticated. This has effect only if user authentication is required * (see {@link #isUserAuthenticationRequired()}). @@ -675,6 +698,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { private boolean mUserAuthenticationValidWhileOnBody; private boolean mInvalidatedByBiometricEnrollment = true; private boolean mIsStrongBoxBacked = false; + private boolean mUserConfirmationRequired; /** * Creates a new instance of the {@code Builder}. @@ -1063,6 +1087,29 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { } /** + * Sets whether this key is authorized to be used only for messages confirmed by the + * user. + * + * Confirmation is separate from user authentication (see + * {@link #setUserAuthenticationRequired(boolean)}). Keys can be created that require + * confirmation but not user authentication, or user authentication but not confirmation, + * or both. Confirmation verifies that some user with physical possession of the device has + * approved a displayed message. User authentication verifies that the correct user is + * present and has authenticated. + * + * <p>This authorization applies only to secret key and private key operations. Public key + * operations are not restricted. + * + * @see {@link android.security.ConfirmationPrompter ConfirmationPrompter} class for + * more details about user confirmations. + */ + @NonNull + public Builder setUserConfirmationRequired(boolean required) { + mUserConfirmationRequired = required; + return this; + } + + /** * Sets the duration of time (seconds) for which this key is authorized to be used after the * user is successfully authenticated. This has effect if the key requires user * authentication for its use (see {@link #setUserAuthenticationRequired(boolean)}). @@ -1249,7 +1296,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { mUniqueIdIncluded, mUserAuthenticationValidWhileOnBody, mInvalidatedByBiometricEnrollment, - mIsStrongBoxBacked); + mIsStrongBoxBacked, + mUserConfirmationRequired); } } } diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java index 864f62a6de04..0a75cd5268b7 100644 --- a/keystore/java/android/security/keystore/KeyInfo.java +++ b/keystore/java/android/security/keystore/KeyInfo.java @@ -82,6 +82,7 @@ public class KeyInfo implements KeySpec { private final boolean mUserAuthenticationValidWhileOnBody; private final boolean mTrustedUserPresenceRequired; private final boolean mInvalidatedByBiometricEnrollment; + private final boolean mUserConfirmationRequired; /** * @hide @@ -103,7 +104,8 @@ public class KeyInfo implements KeySpec { boolean userAuthenticationRequirementEnforcedBySecureHardware, boolean userAuthenticationValidWhileOnBody, boolean trustedUserPresenceRequired, - boolean invalidatedByBiometricEnrollment) { + boolean invalidatedByBiometricEnrollment, + boolean userConfirmationRequired) { mKeystoreAlias = keystoreKeyAlias; mInsideSecureHardware = insideSecureHardware; mOrigin = origin; @@ -125,6 +127,7 @@ public class KeyInfo implements KeySpec { mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody; mTrustedUserPresenceRequired = trustedUserPresenceRequired; mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; + mUserConfirmationRequired = userConfirmationRequired; } /** @@ -260,6 +263,27 @@ public class KeyInfo implements KeySpec { } /** + * Returns {@code true} if the key is authorized to be used only for messages confirmed by the + * user. + * + * Confirmation is separate from user authentication (see + * {@link #isUserAuthenticationRequired()}). Keys can be created that require confirmation but + * not user authentication, or user authentication but not confirmation, or both. Confirmation + * verifies that some user with physical possession of the device has approved a displayed + * message. User authentication verifies that the correct user is present and has + * authenticated. + * + * <p>This authorization applies only to secret key and private key operations. Public key + * operations are not restricted. + * + * @see KeyGenParameterSpec.Builder#setUserConfirmationRequired(boolean) + * @see KeyProtection.Builder#setUserConfirmationRequired(boolean) + */ + public boolean isUserConfirmationRequired() { + return mUserConfirmationRequired; + } + + /** * Gets the duration of time (seconds) for which this key is authorized to be used after the * user is successfully authenticated. This has effect only if user authentication is required * (see {@link #isUserAuthenticationRequired()}). diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index dbacb9c53dd6..b5b328192f21 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -228,6 +228,7 @@ public final class KeyProtection implements ProtectionParameter { private final boolean mInvalidatedByBiometricEnrollment; private final long mBoundToSecureUserId; private final boolean mCriticalToDeviceEncryption; + private final boolean mUserConfirmationRequired; private KeyProtection( Date keyValidityStart, @@ -244,7 +245,8 @@ public final class KeyProtection implements ProtectionParameter { boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, long boundToSecureUserId, - boolean criticalToDeviceEncryption) { + boolean criticalToDeviceEncryption, + boolean userConfirmationRequired) { mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart); mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd); mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd); @@ -262,6 +264,7 @@ public final class KeyProtection implements ProtectionParameter { mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mBoundToSecureUserId = boundToSecureUserId; mCriticalToDeviceEncryption = criticalToDeviceEncryption; + mUserConfirmationRequired = userConfirmationRequired; } /** @@ -396,6 +399,26 @@ public final class KeyProtection implements ProtectionParameter { } /** + * Returns {@code true} if the key is authorized to be used only for messages confirmed by the + * user. + * + * Confirmation is separate from user authentication (see + * {@link #isUserAuthenticationRequired()}). Keys can be created that require confirmation but + * not user authentication, or user authentication but not confirmation, or both. Confirmation + * verifies that some user with physical possession of the device has approved a displayed + * message. User authentication verifies that the correct user is present and has + * authenticated. + * + * <p>This authorization applies only to secret key and private key operations. Public key + * operations are not restricted. + * + * @see Builder#setUserConfirmationRequired(boolean) + */ + public boolean isUserConfirmationRequired() { + return mUserConfirmationRequired; + } + + /** * Gets the duration of time (seconds) for which this key is authorized to be used after the * user is successfully authenticated. This has effect only if user authentication is required * (see {@link #isUserAuthenticationRequired()}). @@ -488,6 +511,7 @@ public final class KeyProtection implements ProtectionParameter { private int mUserAuthenticationValidityDurationSeconds = -1; private boolean mUserAuthenticationValidWhileOnBody; private boolean mInvalidatedByBiometricEnrollment = true; + private boolean mUserConfirmationRequired; private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID; private boolean mCriticalToDeviceEncryption = false; @@ -719,6 +743,29 @@ public final class KeyProtection implements ProtectionParameter { } /** + * Sets whether this key is authorized to be used only for messages confirmed by the + * user. + * + * Confirmation is separate from user authentication (see + * {@link #setUserAuthenticationRequired(boolean)}). Keys can be created that require + * confirmation but not user authentication, or user authentication but not confirmation, + * or both. Confirmation verifies that some user with physical possession of the device has + * approved a displayed message. User authentication verifies that the correct user is + * present and has authenticated. + * + * <p>This authorization applies only to secret key and private key operations. Public key + * operations are not restricted. + * + * @see {@link android.security.ConfirmationPrompter ConfirmationPrompter} class for + * more details about user confirmations. + */ + @NonNull + public Builder setUserConfirmationRequired(boolean required) { + mUserConfirmationRequired = required; + return this; + } + + /** * Sets the duration of time (seconds) for which this key is authorized to be used after the * user is successfully authenticated. This has effect if the key requires user * authentication for its use (see {@link #setUserAuthenticationRequired(boolean)}). @@ -866,7 +913,8 @@ public final class KeyProtection implements ProtectionParameter { mUserAuthenticationValidWhileOnBody, mInvalidatedByBiometricEnrollment, mBoundToSecureUserId, - mCriticalToDeviceEncryption); + mCriticalToDeviceEncryption, + mUserConfirmationRequired); } } } diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java index 34c8d1f75f60..4e28601f17a1 100644 --- a/keystore/java/android/security/keystore/KeymasterUtils.java +++ b/keystore/java/android/security/keystore/KeymasterUtils.java @@ -16,6 +16,7 @@ package android.security.keystore; +import android.util.Log; import android.hardware.fingerprint.FingerprintManager; import android.security.GateKeeper; import android.security.KeyStore; @@ -93,6 +94,8 @@ public abstract class KeymasterUtils { * overriding the default logic in this method where the key is bound to either the root * SID of the current user, or the fingerprint SID if explicit fingerprint authorization * is requested. + * @param userConfirmationRequired whether user confirmation is required to authorize the use + * of the key. * @throws IllegalStateException if user authentication is required but the system is in a wrong * state (e.g., secure lock screen not set up) for generating or importing keys that * require user authentication. @@ -102,7 +105,12 @@ public abstract class KeymasterUtils { int userAuthenticationValidityDurationSeconds, boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, - long boundToSpecificSecureUserId) { + long boundToSpecificSecureUserId, + boolean userConfirmationRequired) { + if (userConfirmationRequired) { + args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED); + } + if (!userAuthenticationRequired) { args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); return; diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 3323bce8b5ad..24d819e93ff2 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -134,6 +134,8 @@ cc_defaults { name: "libhwui_defaults", defaults: ["hwui_defaults"], + shared_libs: ["libstatslog"], + whole_static_libs: ["libskia"], srcs: [ @@ -318,7 +320,10 @@ cc_test { "libgmock", "libhwui_static_debug", ], - shared_libs: ["libmemunreachable"], + shared_libs: [ + "libmemunreachable", + "libstatslog", + ], cflags: [ "-include debug/wrap_gles.h", "-DHWUI_NULL_GPU", @@ -383,7 +388,10 @@ cc_benchmark { // set to libhwui_static_debug to skip actual GL commands whole_static_libs: ["libhwui"], - shared_libs: ["libmemunreachable"], + shared_libs: [ + "libmemunreachable", + "libstatslog", + ], srcs: [ "tests/macrobench/TestSceneRunner.cpp", @@ -405,7 +413,10 @@ cc_benchmark { ], whole_static_libs: ["libhwui_static_debug"], - shared_libs: ["libmemunreachable"], + shared_libs: [ + "libmemunreachable", + "libstatslog", + ], srcs: [ "tests/microbench/main.cpp", diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index f41956cdebeb..ab27a0d00246 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -18,6 +18,7 @@ #include <errno.h> #include <inttypes.h> +#include <statslog.h> #include <sys/mman.h> #include <algorithm> @@ -164,6 +165,7 @@ void JankTracker::finishFrame(const FrameInfo& frame) { ALOGI("%s", ss.str().c_str()); // Just so we have something that counts up, the value is largely irrelevant ATRACE_INT(ss.str().c_str(), ++sDaveyCount); + android::util::stats_write(android::util::DAVEY_OCCURRED, ns2ms(totalDuration)); } } diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 3d2c2520624b..55f4d895265c 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -507,12 +507,20 @@ public: getOutline().getAlpha() != 0.0f; } - SkColor getShadowColor() const { - return mPrimitiveFields.mShadowColor; + SkColor getSpotShadowColor() const { + return mPrimitiveFields.mSpotShadowColor; } - bool setShadowColor(SkColor shadowColor) { - return RP_SET(mPrimitiveFields.mShadowColor, shadowColor); + bool setSpotShadowColor(SkColor shadowColor) { + return RP_SET(mPrimitiveFields.mSpotShadowColor, shadowColor); + } + + SkColor getAmbientShadowColor() const { + return mPrimitiveFields.mAmbientShadowColor; + } + + bool setAmbientShadowColor(SkColor shadowColor) { + return RP_SET(mPrimitiveFields.mAmbientShadowColor, shadowColor); } bool fitsOnLayer() const { @@ -538,7 +546,8 @@ private: int mLeft = 0, mTop = 0, mRight = 0, mBottom = 0; int mWidth = 0, mHeight = 0; int mClippingFlags = CLIP_TO_BOUNDS; - SkColor mShadowColor = SK_ColorBLACK; + SkColor mSpotShadowColor = SK_ColorBLACK; + SkColor mAmbientShadowColor = SK_ColorBLACK; float mAlpha = 1; float mTranslationX = 0, mTranslationY = 0, mTranslationZ = 0; float mElevation = 0; diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp index 5356d3bfc7c9..2bded9b3a2d8 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.cpp +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -48,8 +48,10 @@ bool AnimatedImageDrawable::start() { return true; } -void AnimatedImageDrawable::stop() { +bool AnimatedImageDrawable::stop() { + bool wasRunning = mRunning; mRunning = false; + return wasRunning; } bool AnimatedImageDrawable::isRunning() { @@ -180,7 +182,6 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { if (finalFrame) { if (mEndListener) { mEndListener->onAnimationEnd(); - mEndListener = nullptr; } } } diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h index 9d84ed5f4470..2fd6f40b71b5 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.h +++ b/libs/hwui/hwui/AnimatedImageDrawable.h @@ -68,7 +68,9 @@ public: // Returns true if the animation was started; false otherwise (e.g. it was // already running) bool start(); - void stop(); + // Returns true if the animation was stopped; false otherwise (e.g. it was + // already stopped) + bool stop(); bool isRunning(); void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); } diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index 43f46ef758ca..a0d000dd2179 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -44,7 +44,6 @@ minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint, minikinPaint.familyVariant = paint->getFamilyVariant(); minikinPaint.fontStyle = resolvedFace->fStyle; minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings(); - minikinPaint.hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit()); return minikinPaint; } @@ -55,18 +54,23 @@ minikin::Layout MinikinUtils::doLayout(const Paint* paint, minikin::Bidi bidiFla minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface); minikin::Layout layout; + const minikin::U16StringPiece textBuf(buf, bufSize); + const minikin::Range range(start, start + count); + const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit()); + const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit); + const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit); + if (mt == nullptr) { - layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint); + layout.doLayout(textBuf,range, bidiFlags, minikinPaint, startHyphen, endHyphen); return layout; } - if (mt->buildLayout(minikin::U16StringPiece(buf, bufSize), - minikin::Range(start, start + count), - minikinPaint, bidiFlags, mtOffset, &layout)) { + if (mt->buildLayout(textBuf, range, minikinPaint, bidiFlags, mtOffset, startHyphen, endHyphen, + &layout)) { return layout; } - layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint); + layout.doLayout(textBuf, range, bidiFlags, minikinPaint, startHyphen, endHyphen); return layout; } @@ -74,8 +78,14 @@ float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize, float* advances) { minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface); - return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinPaint, - advances, nullptr /* extent */); + const minikin::U16StringPiece textBuf(buf, bufSize); + const minikin::Range range(start, start + count); + const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit()); + const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit); + const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit); + + return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen, + endHyphen, advances, nullptr /* extent */); } bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) { diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index ebc14c8b675b..091b5267881d 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -182,10 +182,11 @@ void Typeface::setRobotoTypefaceForTest() { std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>( std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>()); - std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>( - std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())})); - std::shared_ptr<minikin::FontCollection> collection = - std::make_shared<minikin::FontCollection>(std::move(family)); + std::vector<minikin::Font> fonts; + fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle())); + + std::shared_ptr<minikin::FontCollection> collection = std::make_shared<minikin::FontCollection>( + std::make_shared<minikin::FontFamily>(std::move(fonts))); Typeface* hwTypeface = new Typeface(); hwTypeface->fFontCollection = collection; diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp index 7b59ccf3eec7..25c51f2716e6 100644 --- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp +++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp @@ -111,6 +111,10 @@ void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) { } } +static SkColor multiplyAlpha(SkColor color, float alpha) { + return SkColorSetA(color, alpha * SkColorGetA(color)); +} + // copied from FrameBuilder::deferShadow void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) { const RenderProperties& casterProperties = caster->getNodeProperties(); @@ -187,9 +191,11 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* } else { zParams = SkPoint3::Make(0, 0, casterProperties.getZ()); } + SkColor ambientColor = multiplyAlpha(casterProperties.getAmbientShadowColor(), ambientAlpha); + SkColor spotColor = multiplyAlpha(casterProperties.getSpotShadowColor(), spotAlpha); SkShadowUtils::DrawShadow( canvas, *casterPath, zParams, skiaLightPos, SkiaPipeline::getLightRadius(), - ambientAlpha, spotAlpha, casterProperties.getShadowColor(), + ambientColor, spotColor, casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0); } diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp index 66d6f527e604..2232c25de345 100644 --- a/libs/hwui/tests/unit/TypefaceTests.cpp +++ b/libs/hwui/tests/unit/TypefaceTests.cpp @@ -56,8 +56,9 @@ std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) { LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName); std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>( std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>()); - return std::make_shared<minikin::FontFamily>( - std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())})); + std::vector<minikin::Font> fonts; + fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle())); + return std::make_shared<minikin::FontFamily>(std::move(fonts)); } std::vector<std::shared_ptr<minikin::FontFamily>> makeSingleFamlyVector(const char* fileName) { diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 018db9a7fc9d..fa3f99a90cc8 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -88,11 +88,6 @@ interface ILocationManager boolean providerMeetsCriteria(String provider, in Criteria criteria); ProviderProperties getProviderProperties(String provider); String getNetworkProviderPackage(); - boolean isProviderEnabled(String provider); - boolean isProviderEnabledForUser(String provider, int userId); - boolean setProviderEnabledForUser(String provider, boolean enabled, int userId); - boolean isLocationEnabledForUser(int userId); - void setLocationEnabledForUser(boolean enabled, int userId); void addTestProvider(String name, in ProviderProperties properties, String opPackageName); void removeTestProvider(String provider, String opPackageName); diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index b07d04220046..f98480b28001 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -265,6 +265,12 @@ public final class AudioFormat implements Parcelable { public static final int ENCODING_AAC_XHE = 16; /** Audio data format: AC-4 sync frame transport format */ public static final int ENCODING_AC4 = 17; + /** Audio data format: E-AC-3-JOC compressed + * E-AC-3-JOC streams can be decoded by downstream devices supporting {@link #ENCODING_E_AC3}. + * Use {@link #ENCODING_E_AC3} as the AudioTrack encoding when the downstream device + * supports {@link #ENCODING_E_AC3} but not {@link #ENCODING_E_AC3_JOC}. + **/ + public static final int ENCODING_E_AC3_JOC = 18; /** @hide */ public static String toLogFriendlyEncoding(int enc) { @@ -512,6 +518,7 @@ public final class AudioFormat implements Parcelable { case ENCODING_PCM_FLOAT: case ENCODING_AC3: case ENCODING_E_AC3: + case ENCODING_E_AC3_JOC: case ENCODING_DTS: case ENCODING_DTS_HD: case ENCODING_MP3: @@ -537,6 +544,7 @@ public final class AudioFormat implements Parcelable { case ENCODING_PCM_FLOAT: case ENCODING_AC3: case ENCODING_E_AC3: + case ENCODING_E_AC3_JOC: case ENCODING_DTS: case ENCODING_DTS_HD: case ENCODING_IEC61937: @@ -564,6 +572,7 @@ public final class AudioFormat implements Parcelable { return true; case ENCODING_AC3: case ENCODING_E_AC3: + case ENCODING_E_AC3_JOC: case ENCODING_DTS: case ENCODING_DTS_HD: case ENCODING_MP3: @@ -593,6 +602,7 @@ public final class AudioFormat implements Parcelable { return true; case ENCODING_AC3: case ENCODING_E_AC3: + case ENCODING_E_AC3_JOC: case ENCODING_DTS: case ENCODING_DTS_HD: case ENCODING_MP3: @@ -829,6 +839,7 @@ public final class AudioFormat implements Parcelable { case ENCODING_PCM_FLOAT: case ENCODING_AC3: case ENCODING_E_AC3: + case ENCODING_E_AC3_JOC: case ENCODING_DTS: case ENCODING_DTS_HD: case ENCODING_IEC61937: @@ -1044,6 +1055,7 @@ public final class AudioFormat implements Parcelable { ENCODING_PCM_FLOAT, ENCODING_AC3, ENCODING_E_AC3, + ENCODING_E_AC3_JOC, ENCODING_DTS, ENCODING_DTS_HD, ENCODING_IEC61937, diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 22fa6202df5c..bf51d97f6b2f 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -48,11 +48,13 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.view.KeyEvent; +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -4578,6 +4580,51 @@ public class AudioManager { } } + /** + * Set port id for microphones by matching device type and address. + * @hide + */ + public static void setPortIdForMicrophones(ArrayList<MicrophoneInfo> microphones) { + AudioDeviceInfo[] devices = getDevicesStatic(AudioManager.GET_DEVICES_INPUTS); + for (int i = microphones.size() - 1; i >= 0; i--) { + boolean foundPortId = false; + for (AudioDeviceInfo device : devices) { + if (device.getPort().type() == microphones.get(i).getInternalDeviceType() + && TextUtils.equals(device.getAddress(), microphones.get(i).getAddress())) { + microphones.get(i).setId(device.getId()); + foundPortId = true; + break; + } + } + if (!foundPortId) { + Log.i(TAG, "Failed to find port id for device with type:" + + microphones.get(i).getType() + " address:" + + microphones.get(i).getAddress()); + microphones.remove(i); + } + } + } + + /** + * Returns a list of {@link MicrophoneInfo} that corresponds to the characteristics + * of all available microphones. The list is empty when no microphones are available + * on the device. An error during the query will result in an IOException being thrown. + * + * @return a list that contains all microphones' characteristics + * @throws IOException if an error occurs. + */ + public List<MicrophoneInfo> getMicrophones() throws IOException { + ArrayList<MicrophoneInfo> microphones = new ArrayList<MicrophoneInfo>(); + int status = AudioSystem.getMicrophones(microphones); + if (status != AudioManager.SUCCESS) { + // fail and bail! + Log.e(TAG, "getMicrophones failed:" + status); + return new ArrayList<MicrophoneInfo>(); // Always return a list. + } + setPortIdForMicrophones(microphones); + return microphones; + } + // Since we need to calculate the changes since THE LAST NOTIFICATION, and not since the // (unpredictable) last time updateAudioPortCache() was called by someone, keep a list // of the ports that exist at the time of the last notification. diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java new file mode 100644 index 000000000000..4652c180936c --- /dev/null +++ b/media/java/android/media/AudioPresentation.java @@ -0,0 +1,181 @@ +/* + * 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; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + + +/** + * The AudioPresentation class encapsulates the information that describes an audio presentation + * which is available in next generation audio content. + * + * Used by {@link MediaExtractor} {@link MediaExtractor#getAudioPresentations(int)} and + * {@link AudioTrack} {@link AudioTrack#setPresentation(AudioPresentation)} to query available + * presentations and to select one. + * + * A list of available audio presentations in a media source can be queried using + * {@link MediaExtractor#getAudioPresentations(int)}. This list can be presented to a user for + * selection. + * An AudioPresentation can be passed to an offloaded audio decoder via + * {@link AudioTrack#setPresentation(AudioPresentation)} to request decoding of the selected + * presentation. An audio stream may contain multiple presentations that differ by language, + * accessibility, end point mastering and dialogue enhancement. An audio presentation may also have + * a set of description labels in different languages to help the user to make an informed + * selection. + */ +public final class AudioPresentation { + private final int mPresentationId; + private final int mProgramId; + private final Map<String, String> mLabels; + private final String mLanguage; + + /** @hide */ + @IntDef( + value = { + MASTERING_NOT_INDICATED, + MASTERED_FOR_STEREO, + MASTERED_FOR_SURROUND, + MASTERED_FOR_3D, + MASTERED_FOR_HEADPHONE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MasteringIndicationType {} + + private final @MasteringIndicationType int mMasteringIndication; + private final boolean mAudioDescriptionAvailable; + private final boolean mSpokenSubtitlesAvailable; + private final boolean mDialogueEnhancementAvailable; + + /** + * No preferred reproduction channel layout. + */ + public static final int MASTERING_NOT_INDICATED = 0; + /** + * Stereo speaker layout. + */ + public static final int MASTERED_FOR_STEREO = 1; + /** + * Two-dimensional (e.g. 5.1) speaker layout. + */ + public static final int MASTERED_FOR_SURROUND = 2; + /** + * Three-dimensional (e.g. 5.1.2) speaker layout. + */ + public static final int MASTERED_FOR_3D = 3; + /** + * Prerendered for headphone playback. + */ + public static final int MASTERED_FOR_HEADPHONE = 4; + + AudioPresentation(int presentationId, + int programId, + Map<String, String> labels, + String language, + @MasteringIndicationType int masteringIndication, + boolean audioDescriptionAvailable, + boolean spokenSubtitlesAvailable, + boolean dialogueEnhancementAvailable) { + this.mPresentationId = presentationId; + this.mProgramId = programId; + this.mLanguage = language; + this.mMasteringIndication = masteringIndication; + this.mAudioDescriptionAvailable = audioDescriptionAvailable; + this.mSpokenSubtitlesAvailable = spokenSubtitlesAvailable; + this.mDialogueEnhancementAvailable = dialogueEnhancementAvailable; + + this.mLabels = new HashMap<String, String>(labels); + } + + /** + * The framework uses this presentation id to select an audio presentation rendered by a + * decoder. Presentation id is typically sequential, but does not have to be. + * @hide + */ + public int getPresentationId() { + return mPresentationId; + } + + /** + * The framework uses this program id to select an audio presentation rendered by a decoder. + * Program id can be used to further uniquely identify the presentation to a decoder. + * @hide + */ + public int getProgramId() { + return mProgramId; + } + + /** + * @return a map of available text labels for this presentation. Each label is indexed by its + * locale corresponding to the language code as specified by ISO 639-2 [42]. Either ISO 639-2/B + * or ISO 639-2/T could be used. + */ + public Map<Locale, String> getLabels() { + Map<Locale, String> localeLabels = new HashMap<>(); + for (Map.Entry<String, String> entry : mLabels.entrySet()) { + localeLabels.put(new Locale(entry.getKey()), entry.getValue()); + } + return localeLabels; + } + + /** + * @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code. + */ + public Locale getLocale() { + return new Locale(mLanguage); + } + + /** + * @return the mastering indication of the audio presentation. + * See {@link #MASTERING_NOT_INDICATED}, {@link #MASTERED_FOR_STEREO}, + * {@link #MASTERED_FOR_SURROUND}, {@link #MASTERED_FOR_3D}, {@link #MASTERED_FOR_HEADPHONE} + */ + @MasteringIndicationType + public int getMasteringIndication() { + return mMasteringIndication; + } + + /** + * Indicates whether an audio description for the visually impaired is available. + * @return {@code true} if audio description is available. + */ + public boolean hasAudioDescription() { + return mAudioDescriptionAvailable; + } + + /** + * Indicates whether spoken subtitles for the visually impaired are available. + * @return {@code true} if spoken subtitles are available. + */ + public boolean hasSpokenSubtitles() { + return mSpokenSubtitlesAvailable; + } + + /** + * Indicates whether dialogue enhancement is available. + * @return {@code true} if dialogue enhancement is available. + */ + public boolean hasDialogueEnhancement() { + return mDialogueEnhancementAvailable; + } +} diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index eb6e83065fe9..d0963cbcb87f 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -16,12 +16,15 @@ package android.media; +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.util.Collection; import java.util.Iterator; +import java.util.ArrayList; +import java.util.List; import android.annotation.IntDef; import android.annotation.NonNull; @@ -35,6 +38,7 @@ import android.os.Message; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ServiceManager; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; @@ -1601,6 +1605,32 @@ public class AudioRecord implements AudioRouting } } + //-------------------------------------------------------------------------- + // Microphone information + //-------------------- + /** + * Returns a lists of {@link MicrophoneInfo} representing the active microphones. + * By querying channel mapping for each active microphone, developer can know how + * the microphone is used by each channels or a capture stream. + * Note that the information about the active microphones may change during a recording. + * See {@link AudioManager#registerAudioDeviceCallback} to be notified of changes + * in the audio devices, querying the active microphones then will return the latest + * information. + * + * @return a lists of {@link MicrophoneInfo} representing the active microphones. + * @throws IOException if an error occurs + */ + public List<MicrophoneInfo> getActiveMicrophones() throws IOException { + ArrayList<MicrophoneInfo> activeMicrophones = new ArrayList<>(); + int status = native_get_active_microphones(activeMicrophones); + if (status != AudioManager.SUCCESS) { + Log.e(TAG, "getActiveMicrophones failed:" + status); + return new ArrayList<MicrophoneInfo>(); + } + AudioManager.setPortIdForMicrophones(activeMicrophones); + return activeMicrophones; + } + //--------------------------------------------------------- // Interface definitions //-------------------- @@ -1746,6 +1776,9 @@ public class AudioRecord implements AudioRouting private native final int native_get_timestamp(@NonNull AudioTimestamp outTimestamp, @AudioTimestamp.Timebase int timebase); + private native final int native_get_active_microphones( + ArrayList<MicrophoneInfo> activeMicrophones); + //--------------------------------------------------------- // Utility methods //------------------ diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index dcd37dafd495..be9fcb8fae83 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -827,6 +827,8 @@ public class AudioSystem private static native boolean native_is_offload_supported(int encoding, int sampleRate, int channelMask, int channelIndexMask); + public static native int getMicrophones(ArrayList<MicrophoneInfo> microphonesInfo); + // Items shared with audio service /** diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 4e9ce8e23e9c..8e822a5d7a37 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -2008,6 +2008,25 @@ public class AudioTrack extends PlayerBase } /** + * Sets the audio presentation. + * If the audio presentation is invalid then {@link #ERROR_BAD_VALUE} will be returned. + * If a multi-stream decoder (MSD) is not present, or the format does not support + * multiple presentations, then {@link #ERROR_INVALID_OPERATION} will be returned. + * @param presentation see {@link AudioPresentation}. In particular, id should be set. + * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE}, + * {@link #ERROR_INVALID_OPERATION} + * @throws IllegalArgumentException if the audio presentation is null. + * @throws IllegalStateException if track is not initialized. + */ + public int setPresentation(@NonNull AudioPresentation presentation) { + if (presentation == null) { + throw new IllegalArgumentException("audio presentation is null"); + } + return native_setPresentation(presentation.getPresentationId(), + presentation.getProgramId()); + } + + /** * Sets the initialization state of the instance. This method was originally intended to be used * in an AudioTrack subclass constructor to set a subclass-specific post-initialization state. * However, subclasses of AudioTrack are no longer recommended, so this method is obsolete. @@ -3245,6 +3264,7 @@ public class AudioTrack extends PlayerBase @NonNull VolumeShaper.Operation operation); private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id); + private native final int native_setPresentation(int presentationId, int programId); //--------------------------------------------------------- // Utility methods diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java index 5ad4313c0332..5cb831356472 100644 --- a/media/java/android/media/MediaBrowser2.java +++ b/media/java/android/media/MediaBrowser2.java @@ -19,7 +19,6 @@ package android.media; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.content.Context; import android.media.update.ApiLoader; import android.media.update.MediaBrowser2Provider; @@ -41,14 +40,14 @@ public class MediaBrowser2 extends MediaController2 { */ public static class BrowserCallback extends MediaController2.ControllerCallback { /** - * Called with the result of {@link #getBrowserRoot(Bundle)}. + * Called with the result of {@link #getLibraryRoot(Bundle)}. * <p> - * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the browser root isn't + * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the library root isn't * available. * * @param rootHints rootHints that you previously requested. - * @param rootMediaId media id of the browser root. Can be {@code null} - * @param rootExtra extra of the browser root. Can be {@code null} + * @param rootMediaId media id of the library root. Can be {@code null} + * @param rootExtra extra of the library root. Can be {@code null} */ public void onGetRootResult(Bundle rootHints, @Nullable String rootMediaId, @Nullable Bundle rootExtra) { } @@ -114,8 +113,15 @@ public class MediaBrowser2 extends MediaController2 { .createMediaBrowser2(context, this, token, executor, (BrowserCallback) callback); } - public void getBrowserRoot(Bundle rootHints) { - mProvider.getBrowserRoot_impl(rootHints); + /** + * Get the library root. Result would be sent back asynchronously with the + * {@link BrowserCallback#onGetRootResult(Bundle, String, Bundle)}. + * + * @param rootHints hint for the root + * @see BrowserCallback#onGetRootResult(Bundle, String, Bundle) + */ + public void getLibraryRoot(Bundle rootHints) { + mProvider.getLibraryRoot_impl(rootHints); } /** diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java index b32e5398d0de..fcdecbad908d 100644 --- a/media/java/android/media/MediaController2.java +++ b/media/java/android/media/MediaController2.java @@ -172,6 +172,14 @@ public class MediaController2 implements AutoCloseable { } /** + * @hide + */ + @SystemApi + public PlaybackInfoProvider getProvider() { + return mProvider; + } + + /** * Get the type of playback which affects volume handling. One of: * <ul> * <li>{@link #PLAYBACK_TYPE_LOCAL}</li> @@ -199,9 +207,9 @@ public class MediaController2 implements AutoCloseable { /** * Get the type of volume control that can be used. One of: * <ul> - * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li> - * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li> - * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li> + * <li>{@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}</li> + * <li>{@link VolumeProvider2#VOLUME_CONTROL_RELATIVE}</li> + * <li>{@link VolumeProvider2#VOLUME_CONTROL_FIXED}</li> * </ul> * * @return The type of volume control that may be used with this session. @@ -472,7 +480,7 @@ public class MediaController2 implements AutoCloseable { /** * Set the volume of the output this session is playing on. The command will be ignored if it - * does not support {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. + * does not support {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}. * <p> * If the session is local playback, this changes the device's volume with the stream that * session's player is using. Flags will be specified for the {@link AudioManager}. @@ -494,8 +502,8 @@ public class MediaController2 implements AutoCloseable { * must be one of {@link AudioManager#ADJUST_LOWER}, * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}. * The command will be ignored if the session does not support - * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or - * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. + * {@link VolumeProvider2#VOLUME_CONTROL_RELATIVE} or + * {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}. * <p> * If the session is local playback, this changes the device's volume with the stream that * session's player is using. Flags will be specified for the {@link AudioManager}. diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java index 174d6a3b879c..4919eeb4dacb 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; +import android.media.AudioPresentation; import android.media.MediaCodec; import android.media.MediaFormat; import android.media.MediaHTTPService; @@ -40,6 +41,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -396,6 +398,17 @@ final public class MediaExtractor { } /** + * Get the list of available audio presentations for the track. + * @param trackIndex index of the track. + * @return a list of available audio presentations for a given valid audio track index. + * The list will be empty if the source does not contain any audio presentations. + */ + @NonNull + public List<AudioPresentation> getAudioPresentations(int trackIndex) { + return new ArrayList<AudioPresentation>(); + } + + /** * Get the PSSH info if present. * @return a map of uuid-to-bytes, with the uuid specifying * the crypto scheme, and the bytes being the data specific to that scheme. diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java index a901c6895d9f..a11768e69b9d 100644 --- a/media/java/android/media/MediaLibraryService2.java +++ b/media/java/android/media/MediaLibraryService2.java @@ -25,8 +25,8 @@ import android.content.Context; import android.media.MediaSession2.BuilderBase; import android.media.MediaSession2.ControllerInfo; import android.media.update.ApiLoader; +import android.media.update.MediaLibraryService2Provider.LibraryRootProvider; import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider; -import android.media.update.MediaSession2Provider; import android.media.update.MediaSessionService2Provider; import android.os.Bundle; @@ -62,7 +62,8 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { public static final String SERVICE_INTERFACE = "android.media.MediaLibraryService2"; /** - * Session for the media library service. + * Session for the {@link MediaLibraryService2}. Build this object with + * {@link MediaLibrarySessionBuilder} and return in {@link #onCreateSession(String)}. */ public static class MediaLibrarySession extends MediaSession2 { private final MediaLibrarySessionProvider mProvider; @@ -100,6 +101,9 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { } } + /** + * Callback for the {@link MediaLibrarySession}. + */ public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback { public MediaLibrarySessionCallback(Context context) { @@ -116,15 +120,15 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { * * @param controllerInfo information of the controller requesting access to browse media. * @param rootHints An optional bundle of service-specific arguments to send - * to the media browser service when connecting and retrieving the + * to the media library service when connecting and retrieving the * root id for browsing, or null if none. The contents of this * bundle may affect the information returned when browsing. - * @return The {@link BrowserRoot} for accessing this app's content or null. - * @see BrowserRoot#EXTRA_RECENT - * @see BrowserRoot#EXTRA_OFFLINE - * @see BrowserRoot#EXTRA_SUGGESTED + * @return The {@link LibraryRoot} for accessing this app's content or null. + * @see LibraryRoot#EXTRA_RECENT + * @see LibraryRoot#EXTRA_OFFLINE + * @see LibraryRoot#EXTRA_SUGGESTED */ - public @Nullable BrowserRoot onGetRoot(@NonNull ControllerInfo controllerInfo, + public @Nullable LibraryRoot onGetRoot(@NonNull ControllerInfo controllerInfo, @Nullable Bundle rootHints) { return null; } @@ -199,6 +203,8 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { /** * Builder for {@link MediaLibrarySession}. */ + // Override all methods just to show them with the type instead of generics in Javadoc. + // This workarounds javadoc issue described in the MediaSession2.BuilderBase. public class MediaLibrarySessionBuilder extends BuilderBase<MediaLibrarySession, MediaLibrarySessionBuilder, MediaLibrarySessionCallback> { public MediaLibrarySessionBuilder( @@ -209,6 +215,38 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { context, (MediaLibrarySessionBuilder) instance, player, callbackExecutor, callback)); } + + @Override + public MediaLibrarySessionBuilder setVolumeProvider( + @Nullable VolumeProvider2 volumeProvider) { + return super.setVolumeProvider(volumeProvider); + } + + @Override + public MediaLibrarySessionBuilder setRatingType(int type) { + return super.setRatingType(type); + } + + @Override + public MediaLibrarySessionBuilder setSessionActivity(@Nullable PendingIntent pi) { + return super.setSessionActivity(pi); + } + + @Override + public MediaLibrarySessionBuilder setId(String id) { + return super.setId(id); + } + + @Override + public MediaLibrarySessionBuilder setSessionCallback( + @NonNull Executor executor, @NonNull MediaLibrarySessionCallback callback) { + return super.setSessionCallback(executor, callback); + } + + @Override + public MediaLibrarySession build() { + return super.build(); + } } @Override @@ -228,7 +266,7 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { * This method will be called on the main thread. * * @param sessionId session id written in the AndroidManifest.xml. - * @return a new browser session + * @return a new library session * @see MediaLibrarySessionBuilder * @see #getSession() * @throws RuntimeException if returned session is invalid @@ -237,17 +275,17 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { public @NonNull abstract MediaLibrarySession onCreateSession(String sessionId); /** - * Contains information that the browser service needs to send to the client - * when first connected. + * Contains information that the library service needs to send to the client when + * {@link MediaBrowser2#getLibraryRoot(Bundle)} is called. */ - public static final class BrowserRoot { + public static final class LibraryRoot { /** - * The lookup key for a boolean that indicates whether the browser service should return a - * browser root for recently played media items. + * The lookup key for a boolean that indicates whether the library service should return a + * librar root for recently played media items. * - * <p>When creating a media browser for a given media browser service, this key can be + * <p>When creating a media browser for a given media library service, this key can be * supplied as a root hint for retrieving media items that are recently played. - * If the media browser service can provide such media items, the implementation must return + * If the media library service can provide such media items, the implementation must return * the key in the root hint when * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back. * @@ -259,13 +297,13 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { public static final String EXTRA_RECENT = "android.media.extra.RECENT"; /** - * The lookup key for a boolean that indicates whether the browser service should return a - * browser root for offline media items. + * The lookup key for a boolean that indicates whether the library service should return a + * library root for offline media items. * - * <p>When creating a media browser for a given media browser service, this key can be + * <p>When creating a media browser for a given media library service, this key can be * supplied as a root hint for retrieving media items that are can be played without an * internet connection. - * If the media browser service can provide such media items, the implementation must return + * If the media library service can provide such media items, the implementation must return * the key in the root hint when * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back. * @@ -277,14 +315,14 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { public static final String EXTRA_OFFLINE = "android.media.extra.OFFLINE"; /** - * The lookup key for a boolean that indicates whether the browser service should return a - * browser root for suggested media items. + * The lookup key for a boolean that indicates whether the library service should return a + * library root for suggested media items. * - * <p>When creating a media browser for a given media browser service, this key can be - * supplied as a root hint for retrieving the media items suggested by the media browser + * <p>When creating a media browser for a given media library service, this key can be + * supplied as a root hint for retrieving the media items suggested by the media library * service. The list of media items is considered ordered by relevance, first being the top * suggestion. - * If the media browser service can provide such media items, the implementation must return + * If the media library service can provide such media items, the implementation must return * the key in the root hint when * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back. * @@ -295,35 +333,31 @@ public abstract class MediaLibraryService2 extends MediaSessionService2 { */ public static final String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED"; - final private String mRootId; - final private Bundle mExtras; + private final LibraryRootProvider mProvider; /** - * Constructs a browser root. + * Constructs a library root. * @param rootId The root id for browsing. - * @param extras Any extras about the browser service. + * @param extras Any extras about the library service. */ - public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) { - if (rootId == null) { - throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " + - "Use null for BrowserRoot instead."); - } - mRootId = rootId; - mExtras = extras; + public LibraryRoot(@NonNull Context context, + @NonNull String rootId, @Nullable Bundle extras) { + mProvider = ApiLoader.getProvider(context).createMediaLibraryService2LibraryRoot( + context, this, rootId, extras); } /** * Gets the root id for browsing. */ public String getRootId() { - return mRootId; + return mProvider.getRootId_impl(); } /** - * Gets any extras about the browser service. + * Gets any extras about the library service. */ public Bundle getExtras() { - return mExtras; + return mProvider.getExtras_impl(); } } } diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index d84eedf94820..e331b2cbf645 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -1671,35 +1671,6 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener public abstract void deselectTrack(int index); /** - * Sets the target UDP re-transmit endpoint for the low level player. - * Generally, the address portion of the endpoint is an IP multicast - * address, although a unicast address would be equally valid. When a valid - * retransmit endpoint has been set, the media player will not decode and - * render the media presentation locally. Instead, the player will attempt - * to re-multiplex its media data using the Android@Home RTP profile and - * re-transmit to the target endpoint. Receiver devices (which may be - * either the same as the transmitting device or different devices) may - * instantiate, prepare, and start a receiver player using a setDataSource - * URL of the form... - * - * aahRX://<multicastIP>:<port> - * - * to receive, decode and render the re-transmitted content. - * - * setRetransmitEndpoint may only be called before setDataSource has been - * called; while the player is in the Idle state. - * - * @param endpoint the address and UDP port of the re-transmission target or - * null if no re-transmission is to be performed. - * @throws IllegalStateException if it is called in an invalid state - * @throws IllegalArgumentException if the retransmit endpoint is supplied, - * but invalid. - * - * {@hide} pending API council - */ - public void setRetransmitEndpoint(InetSocketAddress endpoint) { } - - /** * Releases the resources held by this {@code MediaPlayer2} object. * * It is considered good practice to call this method when you're diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java index 222c66ea4551..e3d5ac07665e 100644 --- a/media/java/android/media/MediaPlayer2Impl.java +++ b/media/java/android/media/MediaPlayer2Impl.java @@ -2964,53 +2964,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } /** - * Sets the target UDP re-transmit endpoint for the low level player. - * Generally, the address portion of the endpoint is an IP multicast - * address, although a unicast address would be equally valid. When a valid - * retransmit endpoint has been set, the media player will not decode and - * render the media presentation locally. Instead, the player will attempt - * to re-multiplex its media data using the Android@Home RTP profile and - * re-transmit to the target endpoint. Receiver devices (which may be - * either the same as the transmitting device or different devices) may - * instantiate, prepare, and start a receiver player using a setDataSource - * URL of the form... - * - * aahRX://<multicastIP>:<port> - * - * to receive, decode and render the re-transmitted content. - * - * setRetransmitEndpoint may only be called before setDataSource has been - * called; while the player is in the Idle state. - * - * @param endpoint the address and UDP port of the re-transmission target or - * null if no re-transmission is to be performed. - * @throws IllegalStateException if it is called in an invalid state - * @throws IllegalArgumentException if the retransmit endpoint is supplied, - * but invalid. - * - * {@hide} pending API council - */ - @Override - public void setRetransmitEndpoint(InetSocketAddress endpoint) - throws IllegalStateException, IllegalArgumentException - { - String addrString = null; - int port = 0; - - if (null != endpoint) { - addrString = endpoint.getAddress().getHostAddress(); - port = endpoint.getPort(); - } - - int ret = native_setRetransmitEndpoint(addrString, port); - if (ret != 0) { - throw new IllegalArgumentException("Illegal re-transmit endpoint; native ret " + ret); - } - } - - private native final int native_setRetransmitEndpoint(String addrString, int port); - - /** * Releases the resources held by this {@code MediaPlayer2} object. * * It is considered good practice to call this method when you're diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 78477f757e2a..62240cedf0be 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.view.Surface; @@ -34,6 +35,8 @@ import java.io.FileDescriptor; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; import com.android.internal.annotations.GuardedBy; @@ -1406,6 +1409,31 @@ public class MediaRecorder implements AudioRouting private native final int native_getRoutedDeviceId(); private native final void native_enableDeviceCallback(boolean enabled); + //-------------------------------------------------------------------------- + // Microphone information + //-------------------- + /** + * Return A lists of {@link MicrophoneInfo} representing the active microphones. + * By querying channel mapping for each active microphone, developer can know how + * the microphone is used by each channels or a capture stream. + * + * @return a lists of {@link MicrophoneInfo} representing the active microphones + * @throws IOException if an error occurs + */ + public List<MicrophoneInfo> getActiveMicrophones() throws IOException { + ArrayList<MicrophoneInfo> activeMicrophones = new ArrayList<>(); + int status = native_getActiveMicrophones(activeMicrophones); + if (status != AudioManager.SUCCESS) { + Log.e(TAG, "getActiveMicrophones failed:" + status); + return new ArrayList<MicrophoneInfo>(); + } + AudioManager.setPortIdForMicrophones(activeMicrophones); + return activeMicrophones; + } + + private native final int native_getActiveMicrophones( + ArrayList<MicrophoneInfo> activeMicrophones); + /** * Called from native code when an interesting event happens. This method * just uses the EventHandler system to post the event back to the main app thread. diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java index acf9615404d7..ae2649a70a55 100644 --- a/media/java/android/media/MediaSession2.java +++ b/media/java/android/media/MediaSession2.java @@ -31,6 +31,7 @@ import android.media.session.PlaybackState; import android.media.update.ApiLoader; import android.media.update.MediaSession2Provider; import android.media.update.MediaSession2Provider.BuilderBaseProvider; +import android.media.update.MediaSession2Provider.CommandButtonProvider; import android.media.update.MediaSession2Provider.CommandGroupProvider; import android.media.update.MediaSession2Provider.CommandProvider; import android.media.update.MediaSession2Provider.ControllerInfoProvider; @@ -579,8 +580,20 @@ public class MediaSession2 implements AutoCloseable { }; /** - * Base builder class for MediaSession2 and its subclass. - * + * Base builder class for MediaSession2 and its subclass. Any change in this class should be + * also applied to the subclasses {@link MediaSession2.Builder} and + * {@link MediaLibraryService2.MediaLibrarySessionBuilder}. + * <p> + * APIs here should be package private, but should have documentations for developers. + * Otherwise, javadoc will generate documentation with the generic types such as follows. + * <pre>U extends BuilderBase<T, U, C> setSessionCallback(Executor executor, C callback)</pre> + * <p> + * This class is hidden to prevent from generating test stub, which fails with + * 'unexpected bound' because it tries to auto generate stub class as follows. + * <pre>abstract static class BuilderBase< + * T extends android.media.MediaSession2, + * U extends android.media.MediaSession2.BuilderBase< + * T, U, C extends android.media.MediaSession2.SessionCallback>, C></pre> * @hide */ static abstract class BuilderBase @@ -598,9 +611,9 @@ public class MediaSession2 implements AutoCloseable { * <p> * Set {@code null} to reset. * - * @param volumeProvider The provider that will handle volume changes. Can be {@code null} + * @param volumeProvider The provider that will handle volume changes. Can be {@code null}. */ - public U setVolumeProvider(@Nullable VolumeProvider volumeProvider) { + U setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) { mProvider.setVolumeProvider_impl(volumeProvider); return (U) this; } @@ -618,7 +631,7 @@ public class MediaSession2 implements AutoCloseable { * <li>{@link Rating2#RATING_THUMB_UP_DOWN}</li> * </ul> */ - public U setRatingType(@Rating2.Style int type) { + U setRatingType(@Rating2.Style int type) { mProvider.setRatingType_impl(type); return (U) this; } @@ -630,7 +643,7 @@ public class MediaSession2 implements AutoCloseable { * * @param pi The intent to launch to show UI for this session. */ - public U setSessionActivity(@Nullable PendingIntent pi) { + U setSessionActivity(@Nullable PendingIntent pi) { mProvider.setSessionActivity_impl(pi); return (U) this; } @@ -645,7 +658,7 @@ public class MediaSession2 implements AutoCloseable { * @throws IllegalArgumentException if id is {@code null} * @return */ - public U setId(@NonNull String id) { + U setId(@NonNull String id) { mProvider.setId_impl(id); return (U) this; } @@ -657,7 +670,7 @@ public class MediaSession2 implements AutoCloseable { * @param callback session callback. * @return */ - public U setSessionCallback(@NonNull @CallbackExecutor Executor executor, + U setSessionCallback(@NonNull @CallbackExecutor Executor executor, @NonNull C callback) { mProvider.setSessionCallback_impl(executor, callback); return (U) this; @@ -670,7 +683,7 @@ public class MediaSession2 implements AutoCloseable { * @throws IllegalStateException if the session with the same id is already exists for the * package. */ - public T build() { + T build() { return mProvider.build_impl(); } } @@ -681,13 +694,44 @@ public class MediaSession2 implements AutoCloseable { * Any incoming event from the {@link MediaController2} will be handled on the thread * that created session with the {@link Builder#build()}. */ - // TODO(jaewan): Add setRatingType() - // TODO(jaewan): Add setSessionActivity() + // Override all methods just to show them with the type instead of generics in Javadoc. + // This workarounds javadoc issue described in the MediaSession2.BuilderBase. public static final class Builder extends BuilderBase<MediaSession2, Builder, SessionCallback> { public Builder(Context context, @NonNull MediaPlayerInterface player) { super((instance) -> ApiLoader.getProvider(context).createMediaSession2Builder( context, (Builder) instance, player)); } + + @Override + public Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) { + return super.setVolumeProvider(volumeProvider); + } + + @Override + public Builder setRatingType(@Rating2.Style int type) { + return super.setRatingType(type); + } + + @Override + public Builder setSessionActivity(@Nullable PendingIntent pi) { + return super.setSessionActivity(pi); + } + + @Override + public Builder setId(@NonNull String id) { + return super.setId(id); + } + + @Override + public Builder setSessionCallback(@NonNull Executor executor, + @Nullable SessionCallback callback) { + return super.setSessionCallback(executor, callback); + } + + @Override + public MediaSession2 build() { + return super.build(); + } } /** @@ -768,32 +812,15 @@ public class MediaSession2 implements AutoCloseable { * <p> * It's up to the controller's decision to respect or ignore this customization request. */ - // TODO(jaewan): Move this to updatable. public static class CommandButton { - private static final String KEY_COMMAND - = "android.media.media_session2.command_button.command"; - private static final String KEY_ICON_RES_ID - = "android.media.media_session2.command_button.icon_res_id"; - private static final String KEY_DISPLAY_NAME - = "android.media.media_session2.command_button.display_name"; - private static final String KEY_EXTRA - = "android.media.media_session2.command_button.extra"; - private static final String KEY_ENABLED - = "android.media.media_session2.command_button.enabled"; - - private Command mCommand; - private int mIconResId; - private String mDisplayName; - private Bundle mExtra; - private boolean mEnabled; - - private CommandButton(@Nullable Command command, int iconResId, - @Nullable String displayName, Bundle extra, boolean enabled) { - mCommand = command; - mIconResId = iconResId; - mDisplayName = displayName; - mExtra = extra; - mEnabled = enabled; + private final CommandButtonProvider mProvider; + + /** + * @hide + */ + @SystemApi + public CommandButton(CommandButtonProvider provider) { + mProvider = provider; } /** @@ -803,7 +830,7 @@ public class MediaSession2 implements AutoCloseable { * @return command or {@code null} */ public @Nullable Command getCommand() { - return mCommand; + return mProvider.getCommand_impl(); } /** @@ -813,7 +840,7 @@ public class MediaSession2 implements AutoCloseable { * @return resource id of the icon. Can be {@code 0}. */ public int getIconResId() { - return mIconResId; + return mProvider.getIconResId_impl(); } /** @@ -823,7 +850,7 @@ public class MediaSession2 implements AutoCloseable { * @return custom display name. Can be {@code null} or empty. */ public @Nullable String getDisplayName() { - return mDisplayName; + return mProvider.getDisplayName_impl(); } /** @@ -832,7 +859,7 @@ public class MediaSession2 implements AutoCloseable { * @return */ public @Nullable Bundle getExtra() { - return mExtra; + return mProvider.getExtra_impl(); } /** @@ -841,92 +868,50 @@ public class MediaSession2 implements AutoCloseable { * @return {@code true} if enabled. {@code false} otherwise. */ public boolean isEnabled() { - return mEnabled; - } - - /** - * @hide - */ - // TODO(jaewan): @SystemApi - public @NonNull Bundle toBundle() { - Bundle bundle = new Bundle(); - bundle.putBundle(KEY_COMMAND, mCommand.toBundle()); - bundle.putInt(KEY_ICON_RES_ID, mIconResId); - bundle.putString(KEY_DISPLAY_NAME, mDisplayName); - bundle.putBundle(KEY_EXTRA, mExtra); - bundle.putBoolean(KEY_ENABLED, mEnabled); - return bundle; + return mProvider.isEnabled_impl(); } /** * @hide */ - // TODO(jaewan): @SystemApi - public static @Nullable CommandButton fromBundle(Context context, Bundle bundle) { - Builder builder = new Builder(); - builder.setCommand(Command.fromBundle(context, bundle.getBundle(KEY_COMMAND))); - builder.setIconResId(bundle.getInt(KEY_ICON_RES_ID, 0)); - builder.setDisplayName(bundle.getString(KEY_DISPLAY_NAME)); - builder.setExtra(bundle.getBundle(KEY_EXTRA)); - builder.setEnabled(bundle.getBoolean(KEY_ENABLED)); - try { - return builder.build(); - } catch (IllegalStateException e) { - // Malformed or version mismatch. Return null for now. - return null; - } + @SystemApi + public CommandButtonProvider getProvider() { + return mProvider; } /** * Builder for {@link CommandButton}. */ public static class Builder { - private Command mCommand; - private int mIconResId; - private String mDisplayName; - private Bundle mExtra; - private boolean mEnabled; - - public Builder() { - mEnabled = true; + private final CommandButtonProvider.BuilderProvider mProvider; + + public Builder(@NonNull Context context) { + mProvider = ApiLoader.getProvider(context) + .createMediaSession2CommandButtonBuilder(context, this); } public Builder setCommand(Command command) { - mCommand = command; - return this; + return mProvider.setCommand_impl(command); } public Builder setIconResId(int resId) { - mIconResId = resId; - return this; + return mProvider.setIconResId_impl(resId); } public Builder setDisplayName(String displayName) { - mDisplayName = displayName; - return this; + return mProvider.setDisplayName_impl(displayName); } public Builder setEnabled(boolean enabled) { - mEnabled = enabled; - return this; + return mProvider.setEnabled_impl(enabled); } public Builder setExtra(Bundle extra) { - mExtra = extra; - return this; + return mProvider.setExtra_impl(extra); } public CommandButton build() { - if (mEnabled && mCommand == null) { - throw new IllegalStateException("Enabled button needs Command" - + " for controller to invoke the command"); - } - if (mCommand != null && mCommand.getCommandCode() == COMMAND_CODE_CUSTOM - && (mIconResId == 0 || TextUtils.isEmpty(mDisplayName))) { - throw new IllegalStateException("Custom commands needs icon and" - + " and name to display"); - } - return new CommandButton(mCommand, mIconResId, mDisplayName, mExtra, mEnabled); + return mProvider.build_impl(); } } } @@ -1093,8 +1078,8 @@ public class MediaSession2 implements AutoCloseable { * If the new player is successfully set, {@link PlaybackListener} * will be called to tell the current playback state of the new player. * <p> - * You can also specify a volume provider. If so, playback in the player is considered as - * remote playback. + * For the remote playback case which you want to handle volume by yourself, use + * {@link #setPlayer(MediaPlayerInterface, VolumeProvider2)}. * * @param player a {@link MediaPlayerInterface} that handles actual media playback in your app. * @throws IllegalArgumentException if the player is {@code null}. @@ -1109,10 +1094,10 @@ public class MediaSession2 implements AutoCloseable { * @param player a {@link MediaPlayerInterface} that handles actual media playback in your app. * @param volumeProvider a volume provider * @see #setPlayer(MediaPlayerInterface) - * @see Builder#setVolumeProvider(VolumeProvider) + * @see Builder#setVolumeProvider(VolumeProvider2) */ public void setPlayer(@NonNull MediaPlayerInterface player, - @NonNull VolumeProvider volumeProvider) { + @NonNull VolumeProvider2 volumeProvider) { mProvider.setPlayer_impl(player, volumeProvider); } diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java index 6b2de06942db..0b5dddf92af5 100644 --- a/media/java/android/media/MediaSessionService2.java +++ b/media/java/android/media/MediaSessionService2.java @@ -21,10 +21,12 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.Service; +import android.content.Context; import android.content.Intent; import android.media.MediaSession2.ControllerInfo; import android.media.update.ApiLoader; import android.media.update.MediaSessionService2Provider; +import android.media.update.MediaSessionService2Provider.MediaNotificationProvider; import android.os.IBinder; /** @@ -165,7 +167,6 @@ public abstract class MediaSessionService2 extends Service { * @param state playback state * @return a {@link MediaNotification}. If it's {@code null}, notification wouldn't be shown. */ - // TODO(jaewan): Also add metadata public MediaNotification onUpdateNotification(PlaybackState2 state) { return mProvider.onUpdateNotification_impl(state); } @@ -204,31 +205,31 @@ public abstract class MediaSessionService2 extends Service { * foreground service to keep playback running in the background. It's highly recommended to * show media style notification here. */ - // TODO(jaewan): Should we also move this to updatable? public static class MediaNotification { - public final int id; - public final Notification notification; - - private MediaNotification(int id, @NonNull Notification notification) { - this.id = id; - this.notification = notification; - } + private final MediaNotificationProvider mProvider; /** - * Create a {@link MediaNotification}. + * Default constructor * + * @param context context * @param notificationId notification id to be used for * {@link android.app.NotificationManager#notify(int, Notification)}. * @param notification a notification to make session service foreground service. Media * style notification is recommended here. - * @return */ - public static MediaNotification create(int notificationId, - @NonNull Notification notification) { - if (notification == null) { - throw new IllegalArgumentException("Notification cannot be null"); - } - return new MediaNotification(notificationId, notification); + public MediaNotification(@NonNull Context context, + int notificationId, @NonNull Notification notification) { + mProvider = ApiLoader.getProvider(context) + .createMediaSessionService2MediaNotification( + context, this, notificationId, notification); + } + + public int getNotificationId() { + return mProvider.getNotificationId_impl(); + } + + public Notification getNotification() { + return mProvider.getNotification_impl(); } } } diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java new file mode 100644 index 000000000000..131e37bd6646 --- /dev/null +++ b/media/java/android/media/MicrophoneInfo.java @@ -0,0 +1,342 @@ +/* + * 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; + +import android.annotation.IntDef; +import android.util.Pair; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; + +/** + * Class providing information on a microphone. It indicates the location and orientation of the + * microphone on the device as well as useful information like frequency response and sensitivity. + * It can be used by applications implementing special pre processing effects like noise suppression + * of beam forming that need to know about precise microphone characteristics in order to adapt + * their algorithms. + */ +public final class MicrophoneInfo { + + /** + * A microphone that the location is unknown. + */ + public static final int LOCATION_UNKNOWN = 0; + + /** + * A microphone that locate on main body of the device. + */ + public static final int LOCATION_MAINBODY = 1; + + /** + * A microphone that locate on a movable main body of the device. + */ + public static final int LOCATION_MAINBODY_MOVABLE = 2; + + /** + * A microphone that locate on a peripheral. + */ + public static final int LOCATION_PERIPHERAL = 3; + + /** + * Unknown microphone directionality. + */ + public static final int DIRECTIONALITY_UNKNOWN = 0; + + /** + * Microphone directionality type: omni. + */ + public static final int DIRECTIONALITY_OMNI = 1; + + /** + * Microphone directionality type: bi-directional. + */ + public static final int DIRECTIONALITY_BI_DIRECTIONAL = 2; + + /** + * Microphone directionality type: cardioid. + */ + public static final int DIRECTIONALITY_CARDIOID = 3; + + /** + * Microphone directionality type: hyper cardioid. + */ + public static final int DIRECTIONALITY_HYPER_CARDIOID = 4; + + /** + * Microphone directionality type: super cardioid. + */ + public static final int DIRECTIONALITY_SUPER_CARDIOID = 5; + + /** + * The channel contains raw audio from this microphone. + */ + public static final int CHANNEL_MAPPING_DIRECT = 1; + + /** + * The channel contains processed audio from this microphone and possibly another microphone. + */ + public static final int CHANNEL_MAPPING_PROCESSED = 2; + + /** @hide */ + @IntDef(flag = true, prefix = { "LOCATION_" }, value = { + LOCATION_UNKNOWN, + LOCATION_MAINBODY, + LOCATION_MAINBODY_MOVABLE, + LOCATION_PERIPHERAL, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MicrophoneLocation {} + + /** @hide */ + @IntDef(flag = true, prefix = { "DIRECTIONALITY_" }, value = { + DIRECTIONALITY_UNKNOWN, + DIRECTIONALITY_OMNI, + DIRECTIONALITY_BI_DIRECTIONAL, + DIRECTIONALITY_CARDIOID, + DIRECTIONALITY_HYPER_CARDIOID, + DIRECTIONALITY_SUPER_CARDIOID, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MicrophoneDirectionality {} + + private Coordinate3F mPosition; + private Coordinate3F mOrientation; + private String mDeviceId; + private String mAddress; + private List<Pair<Float, Float>> mFrequencyResponse; + private List<Pair<Integer, Integer>> mChannelMapping; + private float mMaxSpl; + private float mMinSpl; + private float mSensitivity; + private int mLocation; + private int mGroup; /* Usually 0 will be used for main body. */ + private int mIndexInTheGroup; + private int mPortId; /* mPortId will correspond to the id in AudioPort */ + private int mType; + private int mDirectionality; + + MicrophoneInfo(String deviceId, int type, String address, int location, + int group, int indexInTheGroup, Coordinate3F position, + Coordinate3F orientation, List<Pair<Float, Float>> frequencyResponse, + List<Pair<Integer, Integer>> channelMapping, float sensitivity, float maxSpl, + float minSpl, int directionality) { + mDeviceId = deviceId; + mType = type; + mAddress = address; + mLocation = location; + mGroup = group; + mIndexInTheGroup = indexInTheGroup; + mPosition = position; + mOrientation = orientation; + mFrequencyResponse = frequencyResponse; + mChannelMapping = channelMapping; + mSensitivity = sensitivity; + mMaxSpl = maxSpl; + mMinSpl = minSpl; + mDirectionality = directionality; + } + + /** + * Returns alphanumeric code that uniquely identifies the device. + * + * @return the description of the microphone + */ + public String getDescription() { + return mDeviceId; + } + + /** + * Returns The system unique device ID that corresponds to the id + * returned by {@link AudioDeviceInfo#getId()}. + * + * @return the microphone's id + */ + public int getId() { + return mPortId; + } + + /** + * @hide + * Returns the internal device type (e.g AudioSystem.DEVICE_IN_BUILTIN_MIC). + * The internal device type could be used when getting microphone's port id + * by matching type and address. + * + * @return the internal device type + */ + public int getInternalDeviceType() { + return mType; + } + + /** + * Returns the device type identifier of the microphone (e.g AudioDeviceInfo.TYPE_BUILTIN_MIC). + * + * @return the device type of the microphone + */ + public int getType() { + return AudioDeviceInfo.convertInternalDeviceToDeviceType(mType); + } + + /** + * @hide + * Returns The "address" string of the microphone that corresponds to the + * address returned by {@link AudioDeviceInfo#getAddress()} + * @return the address of the microphone + */ + public String getAddress() { + return mAddress; + } + + /** + * Returns the location of the microphone. The return value is + * one of {@link #LOCATION_UNKNOWN}, {@link #LOCATION_MAINBODY}, + * {@link #LOCATION_MAINBODY_MOVABLE}, or {@link #LOCATION_PERIPHERAL}. + * + * @return the location of the microphone + */ + public @MicrophoneLocation int getLocation() { + return mLocation; + } + + /** + * Returns A device group id that can be used to group together microphones on the same + * peripheral, attachments or logical groups. Main body is usually group 0. + * + * @return the group of the microphone + */ + public int getGroup() { + return mGroup; + } + + /** + * Returns unique index for device within its group. + * + * @return the microphone's index in its group + */ + public int getIndexInTheGroup() { + return mIndexInTheGroup; + } + + /** + * Returns A {@link Coordinate3F} object that represents the geometric location of microphone + * in meters, from botton-left-back corner of appliance. X-axis, Y-axis and Z-axis show + * as the x, y, z values. + * + * @return the geometric location of the microphone + */ + public Coordinate3F getPosition() { + return mPosition; + } + + /** + * Returns A {@link Coordinate3F} object that represents the orientation of microphone. + * X-axis, Y-axis and Z-axis show as the x, y, z value. The orientation will be normalized + * such as sqrt(x^2 + y^2 + z^2) equals 1. + * + * @return the orientation of the microphone + */ + public Coordinate3F getOrientation() { + return mOrientation; + } + + /** + * Returns a {@link android.util.Pair} list of frequency responses. + * For every {@link android.util.Pair} in the list, the first value represents frequency in Hz, + * and the second value represents response in dB. + * + * @return the frequency response of the microphone + */ + public List<Pair<Float, Float>> getFrequencyResponse() { + return mFrequencyResponse; + } + + /** + * Returns a {@link android.util.Pair} list for channel mapping, which indicating how this + * microphone is used by each channels or a capture stream. For each {@link android.util.Pair}, + * the first value is channel index, the second value is channel mapping type, which could be + * either {@link #CHANNEL_MAPPING_DIRECT} or {@link #CHANNEL_MAPPING_PROCESSED}. + * If a channel has contributions from more than one microphone, it is likely the HAL + * did some extra processing to combine the sources, but this is to be inferred by the user. + * Empty list when the MicrophoneInfo is returned by AudioManager.getMicrophones(). + * At least one entry when the MicrophoneInfo is returned by AudioRecord.getActiveMicrophones(). + * + * @return a {@link android.util.Pair} list for channel mapping + */ + public List<Pair<Integer, Integer>> getChannelMapping() { + return mChannelMapping; + } + + /** + * Returns the level in dBFS produced by a 1000Hz tone at 94 dB SPL. + * + * @return the sensitivity of the microphone + */ + public float getSensitivity() { + return mSensitivity; + } + + /** + * Returns the level in dB of the maximum SPL supported by the device at 1000Hz. + * + * @return the maximum level in dB + */ + public float getMaxSpl() { + return mMaxSpl; + } + + /** + * Returns the level in dB of the minimum SPL that can be registered by the device at 1000Hz. + * + * @return the minimum level in dB + */ + public float getMinSpl() { + return mMinSpl; + } + + /** + * Returns the directionality of microphone. The return value is one of + * {@link #DIRECTIONALITY_UNKNOWN}, {@link #DIRECTIONALITY_OMNI}, + * {@link #DIRECTIONALITY_BI_DIRECTIONAL}, {@link #DIRECTIONALITY_CARDIOID}, + * {@link #DIRECTIONALITY_HYPER_CARDIOID}, or {@link #DIRECTIONALITY_SUPER_CARDIOID}. + * + * @return the directionality of microphone + */ + public @MicrophoneDirectionality int getDirectionality() { + return mDirectionality; + } + + /** + * Set the port id for the device. + * @hide + */ + public void setId(int portId) { + mPortId = portId; + } + + /* A class containing three float value to represent a 3D coordinate */ + public class Coordinate3F { + public final float x; + public final float y; + public final float z; + + Coordinate3F(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + } +} diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 3eb9d529b756..fefa1ede849e 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -28,11 +28,13 @@ import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.database.Cursor; import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.net.Uri; import android.os.Environment; +import android.os.FileUtils; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.Process; @@ -47,22 +49,17 @@ import android.util.Log; import com.android.internal.database.SortCursor; -import libcore.io.Streams; - import java.io.Closeable; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.util.ArrayList; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; -import static android.content.ContentProvider.maybeAddUserId; -import static android.content.pm.PackageManager.NameNotFoundException; - /** * RingtoneManager provides access to ringtones, notification, and other types * of sounds. It manages querying the different media providers and combines the @@ -855,7 +852,7 @@ public class RingtoneManager { final Uri cacheUri = getCacheForType(type, context.getUserId()); try (InputStream in = openRingtone(context, ringtoneUri); OutputStream out = resolver.openOutputStream(cacheUri)) { - Streams.copy(in, out); + FileUtils.copy(in, out); } catch (IOException e) { Log.w(TAG, "Failed to cache ringtone: " + e); } @@ -960,7 +957,7 @@ public class RingtoneManager { // Copy contents to external ringtone storage. Throws IOException if the copy fails. try (final InputStream input = mContext.getContentResolver().openInputStream(fileUri); final OutputStream output = new FileOutputStream(outFile)) { - Streams.copy(input, output); + FileUtils.copy(input, output); } // Tell MediaScanner about the new file. Wait for it to assign a {@link Uri}. diff --git a/media/java/android/media/VolumeProvider2.java b/media/java/android/media/VolumeProvider2.java index 00746e25c247..53ba4663aaf6 100644 --- a/media/java/android/media/VolumeProvider2.java +++ b/media/java/android/media/VolumeProvider2.java @@ -32,7 +32,7 @@ import java.lang.annotation.RetentionPolicy; * {@link #setCurrentVolume(int)} each time the volume being provided changes. * <p> * You can set a volume provider on a session by calling - * {@link MediaSession2#setPlayer(MediaPlayerInterface, VolumeProvider)}. + * {@link MediaSession2#setPlayer(MediaPlayerInterface, VolumeProvider2)}. * * @hide */ diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java index 0fe7246e85c4..f2b4fe093248 100644 --- a/media/java/android/media/audiofx/Visualizer.java +++ b/media/java/android/media/audiofx/Visualizer.java @@ -546,22 +546,39 @@ public class Visualizer { /** * Method called when a new waveform capture is available. * <p>Data in the waveform buffer is valid only within the scope of the callback. - * Applications which needs access to the waveform data after returning from the callback + * Applications which need access to the waveform data after returning from the callback * should make a copy of the data instead of holding a reference. * @param visualizer Visualizer object on which the listener is registered. * @param waveform array of bytes containing the waveform representation. - * @param samplingRate sampling rate of the audio visualized. + * @param samplingRate sampling rate of the visualized audio. */ void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate); /** * Method called when a new frequency capture is available. * <p>Data in the fft buffer is valid only within the scope of the callback. - * Applications which needs access to the fft data after returning from the callback + * Applications which need access to the fft data after returning from the callback * should make a copy of the data instead of holding a reference. + * + * <p>In order to obtain magnitude and phase values the following formulas can + * be used: + * <pre class="prettyprint"> + * for (int i = 0; i < fft.size(); i += 2) { + * float magnitude = (float)Math.hypot(fft[i], fft[i + 1]); + * float phase = (float)Math.atan2(fft[i + 1], fft[i]); + * }</pre> * @param visualizer Visualizer object on which the listener is registered. * @param fft array of bytes containing the frequency representation. - * @param samplingRate sampling rate of the audio visualized. + * The fft array only contains the first half of the actual + * FFT spectrum (frequencies up to Nyquist frequency), exploiting + * the symmetry of the spectrum. For each frequencies bin <code>i</code>: + * <ul> + * <li>the element at index <code>2*i</code> in the array contains + * the real part of a complex number,</li> + * <li>the element at index <code>2*i+1</code> contains the imaginary + * part of the complex number.</li> + * </ul> + * @param samplingRate sampling rate of the visualized audio. */ void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate); } diff --git a/media/java/android/media/update/FrameLayoutHelper.java b/media/java/android/media/update/FrameLayoutHelper.java deleted file mode 100644 index 983dc703a9b5..000000000000 --- a/media/java/android/media/update/FrameLayoutHelper.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 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.update; - -import android.content.Context; -import android.graphics.Canvas; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.widget.FrameLayout; - -/** - * Helper class for connecting the public API to an updatable implementation. - * - * @see ViewProvider - * - * @hide - */ -public abstract class FrameLayoutHelper<T extends ViewProvider> extends FrameLayout { - /** @hide */ - final public T mProvider; - - /** @hide */ - public FrameLayoutHelper(ProviderCreator<T> creator, - Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - - mProvider = creator.createProvider(this, new SuperProvider()); - } - - /** @hide */ - // TODO @SystemApi - public T getProvider() { - return mProvider; - } - - @Override - public CharSequence getAccessibilityClassName() { - return mProvider.getAccessibilityClassName_impl(); - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - return mProvider.onTouchEvent_impl(ev); - } - - @Override - public boolean onTrackballEvent(MotionEvent ev) { - return mProvider.onTrackballEvent_impl(ev); - } - - @Override - public void onFinishInflate() { - mProvider.onFinishInflate_impl(); - } - - @Override - public void setEnabled(boolean enabled) { - mProvider.setEnabled_impl(enabled); - } - - @Override - protected void onAttachedToWindow() { - mProvider.onAttachedToWindow_impl(); - } - - @Override - protected void onDetachedFromWindow() { - mProvider.onDetachedFromWindow_impl(); - } - - /** @hide */ - public class SuperProvider implements ViewProvider { - @Override - public CharSequence getAccessibilityClassName_impl() { - return FrameLayoutHelper.super.getAccessibilityClassName(); - } - - @Override - public boolean onTouchEvent_impl(MotionEvent ev) { - return FrameLayoutHelper.super.onTouchEvent(ev); - } - - @Override - public boolean onTrackballEvent_impl(MotionEvent ev) { - return FrameLayoutHelper.super.onTrackballEvent(ev); - } - - @Override - public void onFinishInflate_impl() { - FrameLayoutHelper.super.onFinishInflate(); - } - - @Override - public void setEnabled_impl(boolean enabled) { - FrameLayoutHelper.super.setEnabled(enabled); - } - - @Override - public void onAttachedToWindow_impl() { - FrameLayoutHelper.super.onAttachedToWindow(); - } - - @Override - public void onDetachedFromWindow_impl() { - FrameLayoutHelper.super.onDetachedFromWindow(); - } - } - - /** @hide */ - @FunctionalInterface - public interface ProviderCreator<U extends ViewProvider> { - U createProvider(FrameLayoutHelper<U> instance, ViewProvider superProvider); - } -} diff --git a/media/java/android/media/update/MediaBrowser2Provider.java b/media/java/android/media/update/MediaBrowser2Provider.java index 67680c75c2b2..17256a891277 100644 --- a/media/java/android/media/update/MediaBrowser2Provider.java +++ b/media/java/android/media/update/MediaBrowser2Provider.java @@ -23,7 +23,7 @@ import android.os.Bundle; * @hide */ public interface MediaBrowser2Provider extends MediaController2Provider { - void getBrowserRoot_impl(Bundle rootHints); + void getLibraryRoot_impl(Bundle rootHints); void subscribe_impl(String parentId, Bundle options); void unsubscribe_impl(String parentId, Bundle options); diff --git a/media/java/android/media/update/MediaControlView2Provider.java b/media/java/android/media/update/MediaControlView2Provider.java index 95fe36317164..e155e5f0d8b9 100644 --- a/media/java/android/media/update/MediaControlView2Provider.java +++ b/media/java/android/media/update/MediaControlView2Provider.java @@ -18,6 +18,7 @@ package android.media.update; import android.annotation.SystemApi; import android.media.session.MediaController; +import android.util.AttributeSet; import android.view.View; /** @@ -34,12 +35,12 @@ import android.view.View; * @hide */ // TODO @SystemApi -public interface MediaControlView2Provider extends ViewProvider { +public interface MediaControlView2Provider extends ViewGroupProvider { + void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes); + void setController_impl(MediaController controller); - boolean isShowing_impl(); void setButtonVisibility_impl(int button, int visibility); void requestPlayButtonFocus_impl(); - void onVisibilityAggregated_impl(boolean isVisible); void setTimeout_impl(long timeout); long getTimeout_impl(); } diff --git a/media/java/android/media/update/MediaLibraryService2Provider.java b/media/java/android/media/update/MediaLibraryService2Provider.java index 87f509a932c4..923551a226b6 100644 --- a/media/java/android/media/update/MediaLibraryService2Provider.java +++ b/media/java/android/media/update/MediaLibraryService2Provider.java @@ -33,4 +33,9 @@ public interface MediaLibraryService2Provider extends MediaSessionService2Provid void notifyChildrenChanged_impl(ControllerInfo controller, String parentId, Bundle options); void notifyChildrenChanged_impl(String parentId, Bundle options); } + + interface LibraryRootProvider { + String getRootId_impl(); + Bundle getExtras_impl(); + } } diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java index da4d0c74b30a..41162e0a15ea 100644 --- a/media/java/android/media/update/MediaSession2Provider.java +++ b/media/java/android/media/update/MediaSession2Provider.java @@ -24,12 +24,13 @@ import android.media.MediaPlayerInterface.PlaybackListener; import android.media.MediaSession2; import android.media.MediaSession2.Command; import android.media.MediaSession2.CommandButton; +import android.media.MediaSession2.CommandButton.Builder; import android.media.MediaSession2.CommandGroup; import android.media.MediaSession2.ControllerInfo; import android.media.MediaSession2.PlaylistParams; import android.media.MediaSession2.SessionCallback; import android.media.SessionToken2; -import android.media.VolumeProvider; +import android.media.VolumeProvider2; import android.os.Bundle; import android.os.ResultReceiver; @@ -43,7 +44,7 @@ import java.util.concurrent.Executor; public interface MediaSession2Provider extends TransportControlProvider { void close_impl(); void setPlayer_impl(MediaPlayerInterface player); - void setPlayer_impl(MediaPlayerInterface player, VolumeProvider volumeProvider); + void setPlayer_impl(MediaPlayerInterface player, VolumeProvider2 volumeProvider); MediaPlayerInterface getPlayer_impl(); SessionToken2 getToken_impl(); List<ControllerInfo> getConnectedControllers_impl(); @@ -82,6 +83,23 @@ public interface MediaSession2Provider extends TransportControlProvider { Bundle toBundle_impl(); } + interface CommandButtonProvider { + Command getCommand_impl(); + int getIconResId_impl(); + String getDisplayName_impl(); + Bundle getExtra_impl(); + boolean isEnabled_impl(); + + interface BuilderProvider { + Builder setCommand_impl(Command command); + Builder setIconResId_impl(int resId); + Builder setDisplayName_impl(String displayName); + Builder setEnabled_impl(boolean enabled); + Builder setExtra_impl(Bundle extra); + CommandButton build_impl(); + } + } + interface ControllerInfoProvider { String getPackageName_impl(); int getUid_impl(); @@ -98,7 +116,7 @@ public interface MediaSession2Provider extends TransportControlProvider { } interface BuilderBaseProvider<T extends MediaSession2, C extends SessionCallback> { - void setVolumeProvider_impl(VolumeProvider volumeProvider); + void setVolumeProvider_impl(VolumeProvider2 volumeProvider); void setRatingType_impl(int type); void setSessionActivity_impl(PendingIntent pi); void setId_impl(String id); diff --git a/media/java/android/media/update/MediaSessionService2Provider.java b/media/java/android/media/update/MediaSessionService2Provider.java index 9455da7d0bc7..42e75871e12f 100644 --- a/media/java/android/media/update/MediaSessionService2Provider.java +++ b/media/java/android/media/update/MediaSessionService2Provider.java @@ -17,6 +17,7 @@ package android.media.update; import android.annotation.SystemApi; +import android.app.Notification; import android.content.Intent; import android.media.MediaSession2; import android.media.MediaSessionService2.MediaNotification; @@ -33,4 +34,9 @@ public interface MediaSessionService2Provider { // Service void onCreate_impl(); IBinder onBind_impl(Intent intent); + + interface MediaNotificationProvider { + int getNotificationId_impl(); + Notification getNotification_impl(); + } } diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java index 922b452a2edb..57f04cc88ff5 100644 --- a/media/java/android/media/update/StaticProvider.java +++ b/media/java/android/media/update/StaticProvider.java @@ -17,6 +17,7 @@ package android.media.update; import android.annotation.Nullable; +import android.app.Notification; import android.content.Context; import android.media.DataSourceDesc; import android.media.MediaBrowser2; @@ -25,25 +26,31 @@ import android.media.MediaController2; import android.media.MediaController2.ControllerCallback; import android.media.MediaItem2; import android.media.MediaLibraryService2; +import android.media.MediaLibraryService2.LibraryRoot; import android.media.MediaLibraryService2.MediaLibrarySession; import android.media.MediaLibraryService2.MediaLibrarySessionBuilder; import android.media.MediaLibraryService2.MediaLibrarySessionCallback; import android.media.MediaMetadata2; import android.media.MediaPlayerInterface; import android.media.MediaSession2; +import android.media.MediaSession2.CommandButton.Builder; import android.media.MediaSession2.PlaylistParams; import android.media.MediaSession2.SessionCallback; import android.media.MediaSessionService2; +import android.media.MediaSessionService2.MediaNotification; import android.media.PlaybackState2; import android.media.Rating2; import android.media.SessionPlayer2; import android.media.SessionToken2; import android.media.VolumeProvider2; +import android.media.update.MediaLibraryService2Provider.LibraryRootProvider; import android.media.update.MediaSession2Provider.BuilderBaseProvider; +import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider; import android.media.update.MediaSession2Provider.CommandGroupProvider; import android.media.update.MediaSession2Provider.CommandProvider; import android.media.update.MediaSession2Provider.ControllerInfoProvider; import android.media.update.MediaSession2Provider.PlaylistParamsProvider; +import android.media.update.MediaSessionService2Provider.MediaNotificationProvider; import android.os.Bundle; import android.os.IInterface; import android.util.AttributeSet; @@ -60,10 +67,11 @@ import java.util.concurrent.Executor; * @hide */ public interface StaticProvider { - MediaControlView2Provider createMediaControlView2( - MediaControlView2 instance, ViewProvider superProvider); - VideoView2Provider createVideoView2( - VideoView2 instance, ViewProvider superProvider, + MediaControlView2Provider createMediaControlView2(MediaControlView2 instance, + ViewGroupProvider superProvider, ViewGroupProvider privateProvider, + @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes); + VideoView2Provider createVideoView2(VideoView2 instance, + ViewGroupProvider superProvider, ViewGroupProvider privateProvider, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes); CommandProvider createMediaSession2Command(MediaSession2.Command instance, @@ -79,6 +87,7 @@ public interface StaticProvider { PlaylistParams playlistParams, int repeatMode, int shuffleMode, MediaMetadata2 playlistMetadata); PlaylistParams fromBundle_PlaylistParams(Context context, Bundle bundle); + BuilderProvider createMediaSession2CommandButtonBuilder(Context context, Builder builder); BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder( Context context, MediaSession2.Builder instance, MediaPlayerInterface player); @@ -89,12 +98,16 @@ public interface StaticProvider { SessionToken2 token, Executor executor, BrowserCallback callback); MediaSessionService2Provider createMediaSessionService2(MediaSessionService2 instance); + MediaNotificationProvider createMediaSessionService2MediaNotification(Context context, + MediaNotification mediaNotification, int notificationId, Notification notification); MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance); BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback> createMediaLibraryService2Builder( Context context, MediaLibrarySessionBuilder instance, MediaPlayerInterface player, Executor callbackExecutor, MediaLibrarySessionCallback callback); + LibraryRootProvider createMediaLibraryService2LibraryRoot(Context context, LibraryRoot instance, + String rootId, Bundle extras); SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance, String packageName, String serviceName, int uid); diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java index 10f03d223d9f..7251180cb6a9 100644 --- a/media/java/android/media/update/VideoView2Provider.java +++ b/media/java/android/media/update/VideoView2Provider.java @@ -16,12 +16,14 @@ package android.media.update; +import android.annotation.SystemApi; import android.media.AudioAttributes; import android.media.MediaPlayerInterface; import android.media.session.MediaController; import android.media.session.PlaybackState; import android.media.session.MediaSession; import android.net.Uri; +import android.util.AttributeSet; import android.widget.MediaControlView2; import android.widget.VideoView2; @@ -43,7 +45,9 @@ import java.util.concurrent.Executor; * @hide */ // TODO @SystemApi -public interface VideoView2Provider extends ViewProvider { +public interface VideoView2Provider extends ViewGroupProvider { + void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes); + void setMediaControlView2_impl(MediaControlView2 mediaControlView); MediaController getMediaController_impl(); MediaControlView2 getMediaControlView2_impl(); @@ -52,6 +56,9 @@ public interface VideoView2Provider extends ViewProvider { void setSpeed_impl(float speed); void setAudioFocusRequest_impl(int focusGain); void setAudioAttributes_impl(AudioAttributes attributes); + /** + * @hide + */ void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerInterface player); // TODO: remove setRouteAttributes_impl with MediaSession.Callback once MediaSession2 is ready. void setRouteAttributes_impl(List<String> routeCategories, MediaSession.Callback sessionPlayer); diff --git a/media/java/android/media/update/ViewGroupHelper.java b/media/java/android/media/update/ViewGroupHelper.java new file mode 100644 index 000000000000..2c4f9b92bdac --- /dev/null +++ b/media/java/android/media/update/ViewGroupHelper.java @@ -0,0 +1,354 @@ +/* + * Copyright 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.update; + +import android.content.Context; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +/** + * Helper class for connecting the public API to an updatable implementation. + * + * @see ViewGroupProvider + * + * @hide + */ +public abstract class ViewGroupHelper<T extends ViewGroupProvider> extends ViewGroup { + /** @hide */ + final public T mProvider; + + /** @hide */ + public ViewGroupHelper(ProviderCreator<T> creator, + Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + mProvider = creator.createProvider(this, new SuperProvider(), + new PrivateProvider()); + } + + /** @hide */ + // TODO @SystemApi + public T getProvider() { + return mProvider; + } + + @Override + protected void onAttachedToWindow() { + mProvider.onAttachedToWindow_impl(); + } + + @Override + protected void onDetachedFromWindow() { + mProvider.onDetachedFromWindow_impl(); + } + + @Override + public CharSequence getAccessibilityClassName() { + return mProvider.getAccessibilityClassName_impl(); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + return mProvider.onTouchEvent_impl(ev); + } + + @Override + public boolean onTrackballEvent(MotionEvent ev) { + return mProvider.onTrackballEvent_impl(ev); + } + + @Override + public void onFinishInflate() { + mProvider.onFinishInflate_impl(); + } + + @Override + public void setEnabled(boolean enabled) { + mProvider.setEnabled_impl(enabled); + } + + @Override + public void onVisibilityAggregated(boolean isVisible) { + mProvider.onVisibilityAggregated_impl(isVisible); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + mProvider.onLayout_impl(changed, left, top, right, bottom); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + mProvider.onMeasure_impl(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected int getSuggestedMinimumWidth() { + return mProvider.getSuggestedMinimumWidth_impl(); + } + + @Override + protected int getSuggestedMinimumHeight() { + return mProvider.getSuggestedMinimumHeight_impl(); + } + + // setMeasuredDimension is final + + @Override + protected boolean checkLayoutParams(LayoutParams p) { + return mProvider.checkLayoutParams_impl(p); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return mProvider.generateDefaultLayoutParams_impl(); + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return mProvider.generateLayoutParams_impl(attrs); + } + + @Override + protected LayoutParams generateLayoutParams(LayoutParams lp) { + return mProvider.generateLayoutParams_impl(lp); + } + + @Override + public boolean shouldDelayChildPressedState() { + return mProvider.shouldDelayChildPressedState_impl(); + } + + @Override + protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, + int parentHeightMeasureSpec, int heightUsed) { + mProvider.measureChildWithMargins_impl(child, + parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); + } + + /** @hide */ + public class SuperProvider implements ViewGroupProvider { + @Override + public CharSequence getAccessibilityClassName_impl() { + return ViewGroupHelper.super.getAccessibilityClassName(); + } + + @Override + public boolean onTouchEvent_impl(MotionEvent ev) { + return ViewGroupHelper.super.onTouchEvent(ev); + } + + @Override + public boolean onTrackballEvent_impl(MotionEvent ev) { + return ViewGroupHelper.super.onTrackballEvent(ev); + } + + @Override + public void onFinishInflate_impl() { + ViewGroupHelper.super.onFinishInflate(); + } + + @Override + public void setEnabled_impl(boolean enabled) { + ViewGroupHelper.super.setEnabled(enabled); + } + + @Override + public void onAttachedToWindow_impl() { + ViewGroupHelper.super.onAttachedToWindow(); + } + + @Override + public void onDetachedFromWindow_impl() { + ViewGroupHelper.super.onDetachedFromWindow(); + } + + @Override + public void onVisibilityAggregated_impl(boolean isVisible) { + ViewGroupHelper.super.onVisibilityAggregated(isVisible); + } + + @Override + public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) { + // abstract method; no super + } + + @Override + public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) { + ViewGroupHelper.super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + public int getSuggestedMinimumWidth_impl() { + return ViewGroupHelper.super.getSuggestedMinimumWidth(); + } + + @Override + public int getSuggestedMinimumHeight_impl() { + return ViewGroupHelper.super.getSuggestedMinimumHeight(); + } + + @Override + public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) { + ViewGroupHelper.super.setMeasuredDimension(measuredWidth, measuredHeight); + } + + @Override + public boolean checkLayoutParams_impl(LayoutParams p) { + return ViewGroupHelper.super.checkLayoutParams(p); + } + + @Override + public LayoutParams generateDefaultLayoutParams_impl() { + return ViewGroupHelper.super.generateDefaultLayoutParams(); + } + + @Override + public LayoutParams generateLayoutParams_impl(AttributeSet attrs) { + return ViewGroupHelper.super.generateLayoutParams(attrs); + } + + @Override + public LayoutParams generateLayoutParams_impl(LayoutParams lp) { + return ViewGroupHelper.super.generateLayoutParams(lp); + } + + @Override + public boolean shouldDelayChildPressedState_impl() { + return ViewGroupHelper.super.shouldDelayChildPressedState(); + } + + @Override + public void measureChildWithMargins_impl(View child, + int parentWidthMeasureSpec, int widthUsed, + int parentHeightMeasureSpec, int heightUsed) { + ViewGroupHelper.super.measureChildWithMargins(child, + parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); + } + } + + /** @hide */ + public class PrivateProvider implements ViewGroupProvider { + @Override + public CharSequence getAccessibilityClassName_impl() { + return ViewGroupHelper.this.getAccessibilityClassName(); + } + + @Override + public boolean onTouchEvent_impl(MotionEvent ev) { + return ViewGroupHelper.this.onTouchEvent(ev); + } + + @Override + public boolean onTrackballEvent_impl(MotionEvent ev) { + return ViewGroupHelper.this.onTrackballEvent(ev); + } + + @Override + public void onFinishInflate_impl() { + ViewGroupHelper.this.onFinishInflate(); + } + + @Override + public void setEnabled_impl(boolean enabled) { + ViewGroupHelper.this.setEnabled(enabled); + } + + @Override + public void onAttachedToWindow_impl() { + ViewGroupHelper.this.onAttachedToWindow(); + } + + @Override + public void onDetachedFromWindow_impl() { + ViewGroupHelper.this.onDetachedFromWindow(); + } + + @Override + public void onVisibilityAggregated_impl(boolean isVisible) { + ViewGroupHelper.this.onVisibilityAggregated(isVisible); + } + + @Override + public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) { + ViewGroupHelper.this.onLayout(changed, left, top, right, bottom); + } + + @Override + public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) { + ViewGroupHelper.this.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + public int getSuggestedMinimumWidth_impl() { + return ViewGroupHelper.this.getSuggestedMinimumWidth(); + } + + @Override + public int getSuggestedMinimumHeight_impl() { + return ViewGroupHelper.this.getSuggestedMinimumHeight(); + } + + @Override + public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) { + ViewGroupHelper.this.setMeasuredDimension(measuredWidth, measuredHeight); + } + + @Override + public boolean checkLayoutParams_impl(LayoutParams p) { + return ViewGroupHelper.this.checkLayoutParams(p); + } + + @Override + public LayoutParams generateDefaultLayoutParams_impl() { + return ViewGroupHelper.this.generateDefaultLayoutParams(); + } + + @Override + public LayoutParams generateLayoutParams_impl(AttributeSet attrs) { + return ViewGroupHelper.this.generateLayoutParams(attrs); + } + + @Override + public LayoutParams generateLayoutParams_impl(LayoutParams lp) { + return ViewGroupHelper.this.generateLayoutParams(lp); + } + + @Override + public boolean shouldDelayChildPressedState_impl() { + return ViewGroupHelper.this.shouldDelayChildPressedState(); + } + + @Override + public void measureChildWithMargins_impl(View child, + int parentWidthMeasureSpec, int widthUsed, + int parentHeightMeasureSpec, int heightUsed) { + ViewGroupHelper.this.measureChildWithMargins(child, + parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); + } + } + + /** @hide */ + @FunctionalInterface + public interface ProviderCreator<T extends ViewGroupProvider> { + T createProvider(ViewGroupHelper<T> instance, ViewGroupProvider superProvider, + ViewGroupProvider privateProvider); + } +} diff --git a/media/java/android/media/update/ViewProvider.java b/media/java/android/media/update/ViewGroupProvider.java index 0dd8f388a8fe..5f125298cd49 100644 --- a/media/java/android/media/update/ViewProvider.java +++ b/media/java/android/media/update/ViewGroupProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright 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. @@ -17,7 +17,10 @@ package android.media.update; import android.annotation.SystemApi; +import android.util.AttributeSet; import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup.LayoutParams; /** * Interface for connecting the public API to an updatable implementation. @@ -33,8 +36,8 @@ import android.view.MotionEvent; * @hide */ // TODO @SystemApi -public interface ViewProvider { - // TODO Add more (all?) methods from View +public interface ViewGroupProvider { + // View methods void onAttachedToWindow_impl(); void onDetachedFromWindow_impl(); CharSequence getAccessibilityClassName_impl(); @@ -42,4 +45,22 @@ public interface ViewProvider { boolean onTrackballEvent_impl(MotionEvent ev); void onFinishInflate_impl(); void setEnabled_impl(boolean enabled); + void onVisibilityAggregated_impl(boolean isVisible); + void onLayout_impl(boolean changed, int left, int top, int right, int bottom); + void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec); + int getSuggestedMinimumWidth_impl(); + int getSuggestedMinimumHeight_impl(); + void setMeasuredDimension_impl(int measuredWidth, int measuredHeight); + + // ViewGroup methods + boolean checkLayoutParams_impl(LayoutParams p); + LayoutParams generateDefaultLayoutParams_impl(); + LayoutParams generateLayoutParams_impl(AttributeSet attrs); + LayoutParams generateLayoutParams_impl(LayoutParams lp); + boolean shouldDelayChildPressedState_impl(); + void measureChildWithMargins_impl(View child, int parentWidthMeasureSpec, int widthUsed, + int parentHeightMeasureSpec, int heightUsed); + + // ViewManager methods + // ViewParent methods } diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h new file mode 100644 index 000000000000..71b8dacfbdfa --- /dev/null +++ b/media/jni/android_media_AudioPresentation.h @@ -0,0 +1,173 @@ +/* + * Copyright 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. + */ + +#ifndef _ANDROID_MEDIA_AUDIO_PRESENTATION_H_ +#define _ANDROID_MEDIA_AUDIO_PRESENTATION_H_ + +#include "jni.h" + +#include <media/AudioPresentationInfo.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> + +#include <nativehelper/ScopedLocalRef.h> + +namespace android { + +struct JAudioPresentationInfo { + struct fields_t { + jclass clazz; + jmethodID constructID; + + // list parameters + jclass listclazz; + jmethodID listConstructId; + jmethodID listAddId; + + void init(JNIEnv *env) { + jclass lclazz = env->FindClass("android/media/AudioPresentation"); + if (lclazz == NULL) { + return; + } + + clazz = (jclass)env->NewGlobalRef(lclazz); + if (clazz == NULL) { + return; + } + + constructID = env->GetMethodID(clazz, "<init>", + "(IILjava/util/Map;Ljava/lang/String;IZZZ)V"); + env->DeleteLocalRef(lclazz); + + // list objects + jclass llistclazz = env->FindClass("java/util/ArrayList"); + CHECK(llistclazz != NULL); + listclazz = static_cast<jclass>(env->NewGlobalRef(llistclazz)); + CHECK(listclazz != NULL); + listConstructId = env->GetMethodID(listclazz, "<init>", "()V"); + CHECK(listConstructId != NULL); + listAddId = env->GetMethodID(listclazz, "add", "(Ljava/lang/Object;)Z"); + CHECK(listAddId != NULL); + env->DeleteLocalRef(llistclazz); + } + + void exit(JNIEnv *env) { + env->DeleteGlobalRef(clazz); + clazz = NULL; + env->DeleteGlobalRef(listclazz); + listclazz = NULL; + } + }; + + static status_t ConvertMessageToMap(JNIEnv *env, const sp<AMessage> &msg, jobject *map) { + ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap")); + + if (hashMapClazz.get() == NULL) { + return -EINVAL; + } + jmethodID hashMapConstructID = + env->GetMethodID(hashMapClazz.get(), "<init>", "()V"); + + if (hashMapConstructID == NULL) { + return -EINVAL; + } + jmethodID hashMapPutID = + env->GetMethodID( + hashMapClazz.get(), + "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + + if (hashMapPutID == NULL) { + return -EINVAL; + } + + jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID); + + for (size_t i = 0; i < msg->countEntries(); ++i) { + AMessage::Type valueType; + const char *key = msg->getEntryNameAt(i, &valueType); + + if (!strncmp(key, "android._", 9)) { + // don't expose private keys (starting with android._) + continue; + } + + jobject valueObj = NULL; + + AString val; + CHECK(msg->findString(key, &val)); + + valueObj = env->NewStringUTF(val.c_str()); + + if (valueObj != NULL) { + jstring keyObj = env->NewStringUTF(key); + + env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj); + + env->DeleteLocalRef(keyObj); keyObj = NULL; + env->DeleteLocalRef(valueObj); valueObj = NULL; + } + } + + *map = hashMap; + + return OK; + } + + jobject asJobject(JNIEnv *env, const fields_t& fields, const AudioPresentationInfo &info) { + jobject list = env->NewObject(fields.listclazz, fields.listConstructId); + + for (size_t i = 0; i < info.countPresentations(); ++i) { + const sp<AudioPresentation> &ap = info.getPresentation(i); + jobject jLabelObject; + + sp<AMessage> labelMessage = new AMessage(); + for (size_t i = 0; i < ap->mLabels.size(); ++i) { + labelMessage->setString(ap->mLabels.keyAt(i).string(), + ap->mLabels.valueAt(i).string()); + } + if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) { + return NULL; + } + jstring jLanguage = env->NewStringUTF(ap->mLanguage.string()); + + jobject jValueObj = env->NewObject(fields.clazz, fields.constructID, + static_cast<jint>(ap->mPresentationId), + static_cast<jint>(ap->mProgramId), + jLabelObject, + jLanguage, + static_cast<jint>(ap->mMasteringIndication), + static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ? + 1 : 0), + static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ? + 1 : 0), + static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ? + 1 : 0)); + if (jValueObj == NULL) { + env->DeleteLocalRef(jLanguage); jLanguage = NULL; + return NULL; + } + + env->CallBooleanMethod(list, fields.listAddId, jValueObj); + env->DeleteLocalRef(jValueObj); jValueObj = NULL; + env->DeleteLocalRef(jLanguage); jLanguage = NULL; + } + return list; + } +}; +} // namespace android + +#endif // _ANDROID_MEDIA_AUDIO_PRESENTATION_H_ diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp index 90ee8a6814d5..27eaed05b04d 100644 --- a/media/jni/android_media_MediaPlayer2.cpp +++ b/media/jni/android_media_MediaPlayer2.cpp @@ -19,12 +19,16 @@ #define LOG_TAG "MediaPlayer2-JNI" #include "utils/Log.h" +#include <sys/stat.h> + #include <media/mediaplayer2.h> #include <media/AudioResamplerPublic.h> +#include <media/DataSourceDesc.h> #include <media/MediaHTTPService.h> #include <media/MediaPlayer2Interface.h> #include <media/MediaAnalyticsItem.h> #include <media/NdkWrapper.h> +#include <media/stagefright/Utils.h> #include <media/stagefright/foundation/ByteUtils.h> // for FOURCC definition #include <stdio.h> #include <assert.h> @@ -234,7 +238,8 @@ static sp<MediaPlayer2> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<Media // event to the client application; otherwise, if exception is not NULL and // opStatus is not OK, this method throws the given exception to the client // application. -static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message) +static void process_media_player_call( + JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message) { if (exception == NULL) { // Don't throw exception. Instead, send an event. if (opStatus != (status_t) OK) { @@ -268,7 +273,7 @@ android_media_MediaPlayer2_setDataSourceAndHeaders( jobjectArray keys, jobjectArray values) { sp<MediaPlayer2> mp = getMediaPlayer(env, thiz); - if (mp == NULL ) { + if (mp == NULL) { jniThrowException(env, "java/lang/IllegalStateException", NULL); return; } @@ -282,16 +287,25 @@ android_media_MediaPlayer2_setDataSourceAndHeaders( if (tmp == NULL) { // Out of memory return; } - ALOGV("setDataSource: path %s", tmp); + ALOGV("setDataSourceAndHeaders: path %s", tmp); + + if (strncmp(tmp, "content://", 10) == 0) { + ALOGE("setDataSourceAndHeaders: content scheme is not supported in native code"); + jniThrowException(env, "java/io/IOException", + "content scheme is not supported in native code"); + return; + } + + sp<DataSourceDesc> dsd = new DataSourceDesc(); + dsd->mType = DataSourceDesc::TYPE_URL; + dsd->mUrl = tmp; - String8 pathStr(tmp); env->ReleaseStringUTFChars(path, tmp); tmp = NULL; // We build a KeyedVector out of the key and val arrays - KeyedVector<String8, String8> headersVector; if (!ConvertKeyValueArraysToKeyedVector( - env, keys, values, &headersVector)) { + env, keys, values, &dsd->mHeaders)) { return; } @@ -299,20 +313,16 @@ android_media_MediaPlayer2_setDataSourceAndHeaders( if (httpServiceObj != NULL) { httpService = new JMedia2HTTPService(env, httpServiceObj); } - - status_t opStatus = - mp->setDataSource( - httpService, - pathStr, - headersVector.size() > 0? &headersVector : NULL); + dsd->mHttpService = httpService; process_media_player_call( - env, thiz, opStatus, "java/io/IOException", - "setDataSource failed." ); + env, thiz, mp->setDataSource(dsd), "java/io/IOException", + "setDataSourceAndHeaders failed." ); } static void -android_media_MediaPlayer2_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) +android_media_MediaPlayer2_setDataSourceFD( + JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) { sp<MediaPlayer2> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { @@ -325,12 +335,46 @@ android_media_MediaPlayer2_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fi return; } int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - ALOGV("setDataSourceFD: fd %d", fd); - process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ); + ALOGV("setDataSourceFD: fd=%d (%s), offset=%lld, length=%lld", + fd, nameForFd(fd).c_str(), (long long)offset, (long long)length); + + struct stat sb; + int ret = fstat(fd, &sb); + if (ret != 0) { + ALOGE("setDataSourceFD: fstat(%d) failed: %d, %s", fd, ret, strerror(errno)); + jniThrowException(env, "java/io/IOException", "setDataSourceFD failed fstat"); + return; + } + + ALOGV("st_dev = %llu", static_cast<unsigned long long>(sb.st_dev)); + ALOGV("st_mode = %u", sb.st_mode); + ALOGV("st_uid = %lu", static_cast<unsigned long>(sb.st_uid)); + ALOGV("st_gid = %lu", static_cast<unsigned long>(sb.st_gid)); + ALOGV("st_size = %llu", static_cast<unsigned long long>(sb.st_size)); + + if (offset >= sb.st_size) { + ALOGE("setDataSourceFD: offset is out of range"); + jniThrowException(env, "java/lang/IllegalArgumentException", + "setDataSourceFD failed, offset is out of range."); + return; + } + if (offset + length > sb.st_size) { + length = sb.st_size - offset; + ALOGV("setDataSourceFD: adjusted length = %lld", (long long)length); + } + + sp<DataSourceDesc> dsd = new DataSourceDesc(); + dsd->mType = DataSourceDesc::TYPE_FD; + dsd->mFD = fd; + dsd->mFDOffset = offset; + dsd->mFDLength = length; + process_media_player_call(env, thiz, mp->setDataSource(dsd), + "java/io/IOException", "setDataSourceFD failed." ); } static void -android_media_MediaPlayer2_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject dataSource) +android_media_MediaPlayer2_setDataSourceCallback( + JNIEnv *env, jobject thiz, jobject dataSource) { sp<MediaPlayer2> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { @@ -343,7 +387,11 @@ android_media_MediaPlayer2_setDataSourceCallback(JNIEnv *env, jobject thiz, jobj return; } sp<DataSource> callbackDataSource = new JMedia2DataSource(env, dataSource); - process_media_player_call(env, thiz, mp->setDataSource(callbackDataSource), "java/lang/RuntimeException", "setDataSourceCallback failed." ); + sp<DataSourceDesc> dsd = new DataSourceDesc(); + dsd->mType = DataSourceDesc::TYPE_CALLBACK; + dsd->mCallbackSource = callbackDataSource; + process_media_player_call(env, thiz, mp->setDataSource(dsd), + "java/lang/RuntimeException", "setDataSourceCallback failed." ); } static sp<ANativeWindowWrapper> @@ -1099,45 +1147,6 @@ static void android_media_MediaPlayer2_attachAuxEffect(JNIEnv *env, jobject thi process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL ); } -static jint -android_media_MediaPlayer2_setRetransmitEndpoint(JNIEnv *env, jobject thiz, - jstring addrString, jint port) { - sp<MediaPlayer2> mp = getMediaPlayer(env, thiz); - if (mp == NULL ) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return INVALID_OPERATION; - } - - const char *cAddrString = NULL; - - if (NULL != addrString) { - cAddrString = env->GetStringUTFChars(addrString, NULL); - if (cAddrString == NULL) { // Out of memory - return NO_MEMORY; - } - } - ALOGV("setRetransmitEndpoint: %s:%d", - cAddrString ? cAddrString : "(null)", port); - - status_t ret; - if (cAddrString && (port > 0xFFFF)) { - ret = BAD_VALUE; - } else { - ret = mp->setRetransmitEndpoint(cAddrString, - static_cast<uint16_t>(port)); - } - - if (NULL != addrString) { - env->ReleaseStringUTFChars(addrString, cAddrString); - } - - if (ret == INVALID_OPERATION ) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - } - - return (jint) ret; -} - static void android_media_MediaPlayer2_setNextMediaPlayer(JNIEnv *env, jobject thiz, jobject java_player) { @@ -1418,7 +1427,6 @@ static const JNINativeMethod gMethods[] = { {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer2_set_audio_session_id}, {"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer2_setAuxEffectSendLevel}, {"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer2_attachAuxEffect}, - {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer2_setRetransmitEndpoint}, {"setNextMediaPlayer", "(Landroid/media/MediaPlayer2;)V", (void *)android_media_MediaPlayer2_setNextMediaPlayer}, // Modular DRM { "_prepareDrm", "([B[B)V", (void *)android_media_MediaPlayer2_prepareDrm }, diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index d2bc1743654f..b3a8b21147c1 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -20,6 +20,7 @@ #include <limits.h> #include <stdio.h> #include <unistd.h> +#include <vector> //#define LOG_NDEBUG 0 #define LOG_TAG "MediaRecorderJNI" @@ -29,6 +30,7 @@ #include <camera/Camera.h> #include <media/mediarecorder.h> #include <media/MediaAnalyticsItem.h> +#include <media/MicrophoneInfo.h> #include <media/stagefright/PersistentSurface.h> #include <utils/threads.h> @@ -36,7 +38,9 @@ #include "jni.h" #include <nativehelper/JNIHelp.h> +#include "android_media_AudioErrors.h" #include "android_media_MediaMetricsJNI.h" +#include "android_media_MicrophoneInfo.h" #include "android_runtime/AndroidRuntime.h" #include <system/audio.h> @@ -61,6 +65,12 @@ struct fields_t { }; static fields_t fields; +struct ArrayListFields { + jmethodID add; + jclass classId; +}; +static ArrayListFields gArrayListFields; + static Mutex sLock; // ---------------------------------------------------------------------------- @@ -565,6 +575,13 @@ android_media_MediaRecorder_native_init(JNIEnv *env) if (fields.post_event == NULL) { return; } + + clazz = env->FindClass("java/util/ArrayList"); + if (clazz == NULL) { + return; + } + gArrayListFields.add = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z"); + gArrayListFields.classId = static_cast<jclass>(env->NewGlobalRef(clazz)); } @@ -707,6 +724,45 @@ android_media_MediaRecorder_enableDeviceCallback(JNIEnv *env, jobject thiz, jboo process_media_recorder_call(env, mr->enableAudioDeviceCallback(enabled), "java/lang/RuntimeException", "enableDeviceCallback failed."); } + +static jint +android_media_MediaRecord_getActiveMicrophones(JNIEnv *env, + jobject thiz, jobject jActiveMicrophones) { + if (jActiveMicrophones == NULL) { + ALOGE("jActiveMicrophones is null"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jActiveMicrophones, gArrayListFields.classId)) { + ALOGE("getActiveMicrophones not an arraylist"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); + if (mr == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return (jint)AUDIO_JAVA_NO_INIT; + } + + jint jStatus = AUDIO_JAVA_SUCCESS; + std::vector<media::MicrophoneInfo> activeMicrophones; + status_t status = mr->getActiveMicrophones(&activeMicrophones); + if (status != NO_ERROR) { + ALOGE_IF(status != NO_ERROR, "MediaRecorder::getActiveMicrophones error %d", status); + jStatus = nativeToJavaStatus(status); + return jStatus; + } + + for (size_t i = 0; i < activeMicrophones.size(); i++) { + jobject jMicrophoneInfo; + jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &activeMicrophones[i]); + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; + } + env->CallBooleanMethod(jActiveMicrophones, gArrayListFields.add, jMicrophoneInfo); + env->DeleteLocalRef(jMicrophoneInfo); + } + return jStatus; +} // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { @@ -742,7 +798,9 @@ static const JNINativeMethod gMethods[] = { {"native_setInputDevice", "(I)Z", (void *)android_media_MediaRecorder_setInputDevice}, {"native_getRoutedDeviceId", "()I", (void *)android_media_MediaRecorder_getRoutedDeviceId}, - {"native_enableDeviceCallback", "(Z)V", (void *)android_media_MediaRecorder_enableDeviceCallback}, + {"native_enableDeviceCallback", "(Z)V", (void *)android_media_MediaRecorder_enableDeviceCallback}, + + {"native_getActiveMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_MediaRecord_getActiveMicrophones}, }; // This function only registers the native methods, and is called from diff --git a/native/android/Android.bp b/native/android/Android.bp index 00fe6382fc17..4fb5e748aaac 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -48,6 +48,7 @@ cc_library_shared { "sensor.cpp", "sharedmem.cpp", "storage_manager.cpp", + "surface_texture.cpp", "trace.cpp", ], diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 3d5ee39e37d3..d6dcd723e721 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -205,6 +205,14 @@ LIBANDROID { AStorageManager_mountObb; AStorageManager_new; AStorageManager_unmountObb; + ASurfaceTexture_acquireANativeWindow; # introduced=28 + ASurfaceTexture_attachToGLContext; # introduced=28 + ASurfaceTexture_detachFromGLContext; # introduced=28 + ASurfaceTexture_fromSurfaceTexture; # introduced=28 + ASurfaceTexture_getTimestamp; # introduced=28 + ASurfaceTexture_getTransformMatrix; # introduced=28 + ASurfaceTexture_release; # introduced=28 + ASurfaceTexture_updateTexImage; # introduced=28 ATrace_beginSection; # introduced=23 ATrace_endSection; # introduced=23 ATrace_isEnabled; # introduced=23 diff --git a/native/android/surface_texture.cpp b/native/android/surface_texture.cpp new file mode 100644 index 000000000000..b26688190ccd --- /dev/null +++ b/native/android/surface_texture.cpp @@ -0,0 +1,75 @@ +/* + * 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 <android/surface_texture.h> +#include <android/surface_texture_jni.h> + +#define LOG_TAG "ASurfaceTexture" + +#include <utils/Log.h> + +#include <gui/GLConsumer.h> +#include <gui/Surface.h> + +#include <android_runtime/android_graphics_SurfaceTexture.h> + +using namespace android; + +struct ASurfaceTexture { + sp<GLConsumer> consumer; + sp<IGraphicBufferProducer> producer; +}; + +ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture) { + if (!surfacetexture || !android_SurfaceTexture_isInstanceOf(env, surfacetexture)) { + return nullptr; + } + ASurfaceTexture* ast = new ASurfaceTexture; + ast->consumer = SurfaceTexture_getSurfaceTexture(env, surfacetexture); + ast->producer = SurfaceTexture_getProducer(env, surfacetexture); + return ast; +} + +ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st) { + sp<Surface> surface = new Surface(st->producer); + ANativeWindow* win(surface.get()); + ANativeWindow_acquire(win); + return win; +} + +void ASurfaceTexture_release(ASurfaceTexture* st) { + delete st; +} + +int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t tex) { + return st->consumer->attachToContext(tex); +} + +int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st) { + return st->consumer->detachFromContext(); +} + +int ASurfaceTexture_updateTexImage(ASurfaceTexture* st) { + return st->consumer->updateTexImage(); +} + +void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]) { + st->consumer->getTransformMatrix(mtx); +} + +int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) { + return st->consumer->getTimestamp(); +} diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 8b01aef81c67..9f165bc97768 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -30,6 +30,7 @@ import android.content.res.ObbInfo; import android.content.res.ObbScanner; import android.os.Binder; import android.os.Environment.UserEnvironment; +import android.os.FileUtils; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.Process; @@ -43,7 +44,6 @@ import com.android.internal.os.IParcelFileDescriptorFactory; import com.android.internal.util.ArrayUtils; import libcore.io.IoUtils; -import libcore.io.Streams; import java.io.File; import java.io.FileInputStream; @@ -260,7 +260,7 @@ public class DefaultContainerService extends IntentService { in = new FileInputStream(sourcePath); out = new ParcelFileDescriptor.AutoCloseOutputStream( target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE)); - Streams.copy(in, out); + FileUtils.copy(in, out); } finally { IoUtils.closeQuietly(out); IoUtils.closeQuietly(in); diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java index 6fe8975577a4..9a66b07fb74f 100644 --- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java +++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java @@ -94,7 +94,7 @@ public class Assistant extends NotificationAssistantService { infile = mFile.openRead(); readXml(infile); } catch (FileNotFoundException e) { - // No data yet + Log.d(TAG, "File doesn't exist or isn't readable yet"); } catch (IOException e) { Log.e(TAG, "Unable to read channel impressions", e); } catch (NumberFormatException | XmlPullParserException e) { diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java index 7c35b48310f2..db48f610471d 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java @@ -325,7 +325,8 @@ public class AssistantTest extends ServiceTestCase<Assistant> { int dismiss2 = 777; String key2 = mAssistant.getKey("pkg2", 2, "channel2"); - String xml = "<assistant version=\"1\">\n" + String xml = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + + "<assistant version=\"1\">\n" + "<impression-set key=\"" + key1 + "\" " + "dismisses=\"" + dismiss1 + "\" views=\"" + views1 + "\" streak=\"" + streak1 + "\"/>\n" @@ -377,7 +378,6 @@ public class AssistantTest extends ServiceTestCase<Assistant> { mAssistant.insertImpressions(key2, ci2); mAssistant.insertImpressions(key3, ci3); - XmlSerializer serializer = new FastXmlSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java index 79838962ef1e..c23f22648764 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java @@ -17,21 +17,20 @@ package com.android.settingslib.core.instrumentation; import android.app.Activity; -import android.content.Context; +import android.arch.lifecycle.Lifecycle.Event; +import android.arch.lifecycle.LifecycleObserver; +import android.arch.lifecycle.OnLifecycleEvent; import android.content.Intent; import android.os.SystemClock; import com.android.internal.logging.nano.MetricsProto; -import com.android.settingslib.core.lifecycle.LifecycleObserver; -import com.android.settingslib.core.lifecycle.events.OnPause; -import com.android.settingslib.core.lifecycle.events.OnResume; import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN; /** * Logs visibility change of a fragment. */ -public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPause { +public class VisibilityLoggerMixin implements LifecycleObserver { private static final String TAG = "VisibilityLoggerMixin"; @@ -55,7 +54,7 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPau mMetricsFeature = metricsFeature; } - @Override + @OnLifecycleEvent(Event.ON_RESUME) public void onResume() { mVisibleTimestamp = SystemClock.elapsedRealtime(); if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) { @@ -63,7 +62,7 @@ public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPau } } - @Override + @OnLifecycleEvent(Event.ON_PAUSE) public void onPause() { mVisibleTimestamp = 0; if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index e11017ce3449..f69944006a87 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -622,6 +622,19 @@ public class AccessPoint implements Comparable<AccessPoint> { return builder.toString(); } + public static String getKey(WifiConfiguration config) { + StringBuilder builder = new StringBuilder(); + + if (TextUtils.isEmpty(config.SSID)) { + builder.append(config.BSSID); + } else { + builder.append(removeDoubleQuotes(config.SSID)); + } + + builder.append(',').append(getSecurity(config)); + return builder.toString(); + } + public String getKey() { return mKey; } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 1ac56a9de98f..fac585e06306 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -92,8 +92,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro * and used so as to assist with in-the-field WiFi connectivity debugging */ public static boolean sVerboseLogging; - // TODO(b/36733768): Remove flag includeSaved - // TODO: Allow control of this? // Combo scans can take 5-6s to complete - set to 10s. private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000; @@ -106,8 +104,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private final NetworkRequest mNetworkRequest; private final AtomicBoolean mConnected = new AtomicBoolean(false); private final WifiListener mListener; - private final boolean mIncludeSaved; - private final boolean mIncludeScans; @VisibleForTesting MainHandler mMainHandler; @VisibleForTesting WorkHandler mWorkHandler; private HandlerThread mWorkThread; @@ -150,7 +146,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro // TODO(sghuman): Change this to be keyed on AccessPoint.getKey private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>(); - private Integer mScanId = 0; private NetworkInfo mLastNetworkInfo; private WifiInfo mLastInfo; @@ -189,16 +184,18 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro @Deprecated public WifiTracker(Context context, WifiListener wifiListener, boolean includeSaved, boolean includeScans) { - this(context, wifiListener, includeSaved, includeScans, + 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 + // calling apps once IC window is complete public WifiTracker(Context context, WifiListener wifiListener, @NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) { - this(context, wifiListener, includeSaved, includeScans, + this(context, wifiListener, context.getSystemService(WifiManager.class), context.getSystemService(ConnectivityManager.class), context.getSystemService(NetworkScoreManager.class), @@ -208,19 +205,13 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro @VisibleForTesting WifiTracker(Context context, WifiListener wifiListener, - boolean includeSaved, boolean includeScans, WifiManager wifiManager, ConnectivityManager connectivityManager, NetworkScoreManager networkScoreManager, IntentFilter filter) { - if (!includeSaved && !includeScans) { - throw new IllegalArgumentException("Must include either saved or scans"); - } mContext = context; mMainHandler = new MainHandler(Looper.getMainLooper()); mWifiManager = wifiManager; - mIncludeSaved = includeSaved; - mIncludeScans = includeScans; - mListener = wifiListener; + mListener = new WifiListenerWrapper(wifiListener); mConnectivityManager = connectivityManager; // check if verbose logging has been turned on or off @@ -458,7 +449,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private void handleResume() { mScanResultCache.clear(); mSeenBssids.clear(); - mScanId = 0; } private Collection<ScanResult> updateScanResultCache(final List<ScanResult> newResults) { @@ -533,7 +523,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro /** * Update the internal list of access points. * - * <p>Do not called directly (except for forceUpdate), use {@link #updateAccessPoints()} which + * <p>Do not call directly (except for forceUpdate), use {@link #updateAccessPoints()} which * respects {@link #mStaleScanResults}. */ @GuardedBy("mLock") @@ -542,7 +532,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro WifiConfiguration connectionConfig = null; if (mLastInfo != null) { connectionConfig = getWifiConfigurationForNetworkId( - mLastInfo.getNetworkId(), mWifiManager.getConfiguredNetworks()); + mLastInfo.getNetworkId(), configs); } // Swap the current access points into a cached list. @@ -554,43 +544,12 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro accessPoint.clearConfig(); } - /* Lookup table to more quickly update AccessPoints by only considering objects with the - * correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */ - Multimap<String, AccessPoint> existingApMap = new Multimap<String, AccessPoint>(); - final Collection<ScanResult> results = updateScanResultCache(newScanResults); - // TODO(sghuman): This entire block only exists to populate the WifiConfiguration for - // APs, remove and refactor + final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size()); if (configs != null) { for (WifiConfiguration config : configs) { - if (config.selfAdded && config.numAssociation == 0) { - continue; - } - AccessPoint accessPoint = getCachedOrCreate(config, cachedAccessPoints); - if (mLastInfo != null && mLastNetworkInfo != null) { - accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); - } - if (mIncludeSaved) { - // If saved network not present in scan result then set its Rssi to - // UNREACHABLE_RSSI - boolean apFound = false; - for (ScanResult result : results) { - if (result.SSID.equals(accessPoint.getSsidStr())) { - apFound = true; - break; - } - } - if (!apFound) { - accessPoint.setUnreachable(); - } - accessPoints.add(accessPoint); - existingApMap.put(accessPoint.getSsidStr(), accessPoint); - } else { - // If we aren't using saved networks, drop them into the cache so that - // we have access to their saved info. - cachedAccessPoints.add(accessPoint); - } + configsByKey.put(AccessPoint.getKey(config), config); } } @@ -626,40 +585,20 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro for (Map.Entry<String, List<ScanResult>> entry : scanResultsByApKey.entrySet()) { // List can not be empty as it is dynamically constructed on each iteration ScanResult firstResult = entry.getValue().get(0); - boolean found = false; - for (AccessPoint accessPoint : existingApMap.getAll(firstResult.SSID)) { - accessPoint.setScanResults(entry.getValue()); - found = true; - break; - } - // Only create a new AP / add to the list if it wasn't already in the saved configs - if (!found) { - AccessPoint accessPoint = - getCachedOrCreate(entry.getValue(), cachedAccessPoints); - if (mLastInfo != null && mLastNetworkInfo != null) { - accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); - } - - // TODO(sghuman): Move isPasspointNetwork logic into AccessPoint.java - if (firstResult.isPasspointNetwork()) { - // Retrieve a WifiConfiguration for a Passpoint provider that matches - // the given ScanResult. This is used for showing that a given AP - // (ScanResult) is available via a Passpoint provider (provider friendly - // name). - try { - WifiConfiguration config = - mWifiManager.getMatchingWifiConfig(firstResult); - if (config != null) { - accessPoint.update(config); - } - } catch (UnsupportedOperationException e) { - // Passpoint not supported on the device. - } - } + AccessPoint accessPoint = + getCachedOrCreate(entry.getValue(), cachedAccessPoints); + if (mLastInfo != null && mLastNetworkInfo != null) { + accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); + } - accessPoints.add(accessPoint); + // Update the matching config if there is one, to populate saved network info + WifiConfiguration config = configsByKey.get(entry.getKey()); + if (config != null) { + accessPoint.update(config); } + + accessPoints.add(accessPoint); } } @@ -1052,6 +991,39 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } } + /** + * Wraps the given {@link WifiListener} instance and executes it's methods on the Main Thread. + * + * <p>This mechanism allows us to no longer need a separate MainHandler and WorkHandler, which + * were previously both performing work, while avoiding errors which occur from executing + * callbacks which manipulate UI elements from a different thread than the MainThread. + */ + private static class WifiListenerWrapper implements WifiListener { + + private final Handler mHandler; + private final WifiListener mDelegatee; + + public WifiListenerWrapper(WifiListener listener) { + mHandler = new Handler(Looper.getMainLooper()); + mDelegatee = listener; + } + + @Override + public void onWifiStateChanged(int state) { + mHandler.post(() -> mDelegatee.onWifiStateChanged(state)); + } + + @Override + public void onConnectedChanged() { + mHandler.post(() -> mDelegatee.onConnectedChanged()); + } + + @Override + public void onAccessPointsChanged() { + mHandler.post(() -> mDelegatee.onAccessPointsChanged()); + } + } + public interface WifiListener { /** * Called when the state of Wifi has changed, the state will be one of 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 b36dda9deecf..6be4936413b7 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 @@ -281,8 +281,6 @@ public class WifiTrackerTest { final WifiTracker wifiTracker = new WifiTracker( mContext, mockWifiListener, - true, - true, mockWifiManager, mockConnectivityManager, mockNetworkScoreManager, @@ -687,6 +685,7 @@ public class WifiTrackerTest { */ @Test public void trackPasspointApWithPasspointDisabled() throws Exception { + // TODO(sghuman): Delete this test and replace with a passpoint test WifiTracker tracker = createMockedWifiTracker(); // Add a Passpoint AP to the scan results. @@ -709,10 +708,7 @@ public class WifiTrackerTest { when(mockWifiManager.getConfiguredNetworks()) .thenReturn(new ArrayList<WifiConfiguration>()); when(mockWifiManager.getScanResults()).thenReturn(results); - doThrow(new UnsupportedOperationException()) - .when(mockWifiManager).getMatchingWifiConfig(any(ScanResult.class)); tracker.forceUpdate(); - verify(mockWifiManager).getMatchingWifiConfig(any(ScanResult.class)); } @Test @@ -758,7 +754,7 @@ public class WifiTrackerTest { tracker.forceUpdate(); verify(mockWifiManager).getConnectionInfo(); - verify(mockWifiManager, times(2)).getConfiguredNetworks(); + verify(mockWifiManager, times(1)).getConfiguredNetworks(); verify(mockConnectivityManager).getNetworkInfo(any(Network.class)); verify(mockWifiListener, never()).onAccessPointsChanged(); // mStaleAccessPoints is true diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java index a2648861d1d8..1ab6afe5b99d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java @@ -18,6 +18,7 @@ package com.android.settingslib.core.instrumentation; import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN; import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -30,6 +31,8 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; import com.android.internal.logging.nano.MetricsProto; import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.TestConfig; @@ -39,6 +42,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; @@ -110,6 +115,30 @@ public class VisibilityLoggerMixinTest { .hidden(nullable(Context.class), anyInt()); } + @Test + public void activityShouldBecomeVisibleAndHide() { + ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class); + TestActivity testActivity = ac.get(); + MockitoAnnotations.initMocks(testActivity); + ac.create().start().resume(); + verify(testActivity.mMetricsFeatureProvider, times(1)).visible(any(), anyInt(), anyInt()); + ac.pause().stop().destroy(); + verify(testActivity.mMetricsFeatureProvider, times(1)).hidden(any(), anyInt()); + } + + public static class TestActivity extends FragmentActivity { + @Mock + MetricsFeatureProvider mMetricsFeatureProvider; + + @Override + public void onCreate(Bundle savedInstanceState) { + VisibilityLoggerMixin mixin = new VisibilityLoggerMixin( + TestInstrumentable.TEST_METRIC, mMetricsFeatureProvider); + getLifecycle().addObserver(mixin); + super.onCreate(savedInstanceState); + } + } + private final class TestInstrumentable implements Instrumentable { public static final int TEST_METRIC = 12345; diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 742814924137..1dc8e46a694a 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -87,7 +87,6 @@ class DatabaseHelper extends SQLiteOpenHelper { private static final HashSet<String> mValidTables = new HashSet<String>(); - private static final String DATABASE_JOURNAL_SUFFIX = "-journal"; private static final String DATABASE_BACKUP_SUFFIX = "-backup"; private static final String TABLE_SYSTEM = "system"; @@ -148,12 +147,7 @@ class DatabaseHelper extends SQLiteOpenHelper { } File databaseFile = mContext.getDatabasePath(getDatabaseName()); if (databaseFile.exists()) { - databaseFile.delete(); - } - File databaseJournalFile = mContext.getDatabasePath(getDatabaseName() - + DATABASE_JOURNAL_SUFFIX); - if (databaseJournalFile.exists()) { - databaseJournalFile.delete(); + SQLiteDatabase.deleteDatabase(databaseFile); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index b286f89d0049..537e8dca39c8 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -523,9 +523,6 @@ class SettingsProtoDumpUtil { Settings.Global.WIFI_WAKEUP_ENABLED, GlobalSettingsProto.WIFI_WAKEUP_ENABLED); dumpSetting(s, p, - Settings.Global.WIFI_WAKEUP_AVAILABLE, - GlobalSettingsProto.WIFI_WAKEUP_AVAILABLE); - dumpSetting(s, p, Settings.Global.NETWORK_SCORING_UI_ENABLED, GlobalSettingsProto.NETWORK_SCORING_UI_ENABLED); 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 d1459bba90c1..adb4dbf98dd0 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2328,7 +2328,9 @@ public class SettingsProvider extends ContentProvider { // Get all uids for the user's packages. final List<PackageInfo> packages; try { - packages = mPackageManager.getInstalledPackages(0, user.id).getList(); + packages = mPackageManager.getInstalledPackages( + PackageManager.MATCH_UNINSTALLED_PACKAGES, + user.id).getList(); } catch (RemoteException e) { throw new IllegalStateException("Package manager not available"); } @@ -3015,7 +3017,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 152; + private static final int SETTINGS_VERSION = 153; private final int mUserId; @@ -3401,7 +3403,9 @@ public class SettingsProvider extends ContentProvider { // Fill each uid with the legacy ssaid to be backwards compatible. final List<PackageInfo> packages; try { - packages = mPackageManager.getInstalledPackages(0, userId).getList(); + packages = mPackageManager.getInstalledPackages( + PackageManager.MATCH_UNINSTALLED_PACKAGES, + userId).getList(); } catch (RemoteException e) { throw new IllegalStateException("Package manager not available"); } @@ -3416,6 +3420,9 @@ public class SettingsProvider extends ContentProvider { // Android Id doesn't exist for this package so create it. ssaidSettings.insertSettingLocked(uid, legacySsaid, null, true, info.packageName); + if (DEBUG) { + Slog.d(LOG_TAG, "Keep the legacy ssaid for uid=" + uid); + } } } } @@ -3540,21 +3547,9 @@ public class SettingsProvider extends ContentProvider { } if (currentVersion == 146) { - // Version 147: Set the default value for WIFI_WAKEUP_AVAILABLE. - if (userId == UserHandle.USER_SYSTEM) { - final SettingsState globalSettings = getGlobalSettingsLocked(); - final Setting currentSetting = globalSettings.getSettingLocked( - Settings.Global.WIFI_WAKEUP_AVAILABLE); - if (currentSetting.getValue() == null) { - final int defaultValue = getContext().getResources().getInteger( - com.android.internal.R.integer.config_wifi_wakeup_available); - globalSettings.insertSettingLocked( - Settings.Global.WIFI_WAKEUP_AVAILABLE, - String.valueOf(defaultValue), - null, true, SettingsState.SYSTEM_PACKAGE_NAME); - } - } - + // Version 147: Removed. (This version previously allowed showing the + // "wifi_wakeup_available" setting). + // The setting that was added here is deleted in 153. currentVersion = 147; } @@ -3621,18 +3616,17 @@ public class SettingsProvider extends ContentProvider { } if (currentVersion == 151) { - // Version 152: Reset wifi wake available for upgrading users - final SettingsState globalSettings = getGlobalSettingsLocked(); - final int defaultValue = getContext().getResources().getInteger( - com.android.internal.R.integer.config_wifi_wakeup_available); - globalSettings.insertSettingLocked( - Settings.Global.WIFI_WAKEUP_AVAILABLE, - String.valueOf(defaultValue), - null, true, SettingsState.SYSTEM_PACKAGE_NAME); - + // Version 152: Removed. (This version made the setting for wifi_wakeup enabled + // by default but it is now no longer configurable). + // The setting updated here is deleted in 153. currentVersion = 152; } + if (currentVersion == 152) { + getGlobalSettingsLocked().deleteSettingLocked("wifi_wakeup_available"); + currentVersion = 153; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml new file mode 100644 index 000000000000..509cd1fb5db7 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml @@ -0,0 +1,22 @@ +<?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. +--> +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle" > + <solid + android:color="#e5e5e5" /> + <corners android:radius="2dp" /> +</shape> diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index 9f6a946dea2e..100c2aa51b4d 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -25,87 +25,121 @@ android:baselineAligned="false" android:clickable="false" android:clipChildren="false" - android:clipToPadding="false" - android:paddingTop="0dp" - android:gravity="center_vertical" - android:orientation="horizontal"> + android:clipToPadding="false"> + + <View + android:id="@+id/qs_footer_divider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:layout_gravity="top" + android:background="?android:attr/dividerHorizontal"/> <LinearLayout android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="16dp" + android:layout_height="match_parent" + android:layout_marginTop="1dp" + android:layout_marginStart="8dp" android:layout_marginEnd="8dp" - android:gravity="end"> + android:layout_gravity="center_vertical" + android:gravity="end" > - <com.android.keyguard.CarrierText - android:id="@+id/qs_carrier_text" + <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" - android:layout_weight="1" - android:gravity="center_vertical|start" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textColor="?android:attr/textColorPrimary" - android:textDirection="locale" - android:singleLine="true" /> + android:layout_weight="1" > + <!-- Add an extra 8dp margin before carrier text without shifting it right --> + <android.widget.Space + android:layout_width="8dp" + android:layout_height="match_parent" /> - <com.android.systemui.statusbar.phone.MultiUserSwitch - android:id="@+id/multi_user_switch" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_alignParentEnd="true" - android:background="@drawable/ripple_drawable" - android:focusable="true"> + <com.android.keyguard.CarrierText + android:id="@+id/qs_carrier_text" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:gravity="center_vertical|start" + android:ellipsize="marquee" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:attr/textColorPrimary" + android:textDirection="locale" + android:singleLine="true" /> + </LinearLayout> - <ImageView - android:id="@+id/multi_user_avatar" - android:layout_width="@dimen/multi_user_avatar_expanded_size" - android:layout_height="@dimen/multi_user_avatar_expanded_size" - android:layout_gravity="center" - android:scaleType="centerInside"/> - </com.android.systemui.statusbar.phone.MultiUserSwitch> + <FrameLayout + android:layout_width="24dp" + android:layout_height="match_parent" > + <View + android:id="@+id/qs_drag_handle_view" + android:layout_width="match_parent" + android:layout_height="4dp" + android:layout_marginTop="28dp" + android:background="@drawable/qs_footer_drag_handle" /> + </FrameLayout> - <com.android.systemui.statusbar.AlphaOptimizedImageView - android:id="@android:id/edit" - android:layout_width="48dp" - android:layout_height="48dp" - android:background="?android:attr/selectableItemBackgroundBorderless" - android:clickable="true" - android:clipToPadding="false" - android:contentDescription="@string/accessibility_quick_settings_edit" - android:focusable="true" - android:padding="16dp" - android:src="@drawable/ic_mode_edit" - android:tint="?android:attr/colorForeground"/> + <LinearLayout + android:id="@+id/qs_footer_actions_container" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:gravity="end" > + <com.android.systemui.statusbar.phone.MultiUserSwitch + android:id="@+id/multi_user_switch" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_alignParentEnd="true" + android:background="@drawable/ripple_drawable" + android:focusable="true"> - <com.android.systemui.statusbar.AlphaOptimizedFrameLayout - android:id="@+id/settings_button_container" - android:layout_width="48dp" - android:layout_height="48dp" - android:clipChildren="false" - android:clipToPadding="false"> + <ImageView + android:id="@+id/multi_user_avatar" + android:layout_width="@dimen/multi_user_avatar_expanded_size" + android:layout_height="@dimen/multi_user_avatar_expanded_size" + android:layout_gravity="center" + android:scaleType="centerInside"/> + </com.android.systemui.statusbar.phone.MultiUserSwitch> - <com.android.systemui.statusbar.phone.SettingsButton - android:id="@+id/settings_button" - style="@android:style/Widget.Material.Button.Borderless" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@drawable/ripple_drawable" - android:contentDescription="@string/accessibility_quick_settings_settings" - android:src="@drawable/ic_settings_16dp" + <com.android.systemui.statusbar.AlphaOptimizedImageView + android:id="@android:id/edit" + android:layout_width="48dp" + android:layout_height="48dp" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:clickable="true" + android:clipToPadding="false" + android:contentDescription="@string/accessibility_quick_settings_edit" + android:focusable="true" + android:padding="16dp" + android:src="@drawable/ic_mode_edit" android:tint="?android:attr/colorForeground"/> - <com.android.systemui.statusbar.AlphaOptimizedImageView - android:id="@+id/tuner_icon" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingStart="36dp" - android:paddingEnd="4dp" - android:src="@drawable/tuner" - android:tint="?android:attr/textColorTertiary" - android:visibility="invisible"/> + <com.android.systemui.statusbar.AlphaOptimizedFrameLayout + android:id="@+id/settings_button_container" + android:layout_width="48dp" + android:layout_height="48dp" + android:clipChildren="false" + android:clipToPadding="false"> + + <com.android.systemui.statusbar.phone.SettingsButton + android:id="@+id/settings_button" + style="@android:style/Widget.Material.Button.Borderless" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/ripple_drawable" + android:contentDescription="@string/accessibility_quick_settings_settings" + android:src="@drawable/ic_settings_16dp" + android:tint="?android:attr/colorForeground"/> + + <com.android.systemui.statusbar.AlphaOptimizedImageView + android:id="@+id/tuner_icon" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingStart="36dp" + android:paddingEnd="4dp" + android:src="@drawable/tuner" + android:tint="?android:attr/textColorTertiary" + android:visibility="invisible"/> - </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> + </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> + </LinearLayout> </LinearLayout> </com.android.systemui.qs.QSFooterImpl> diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml index c2b1009c3137..a3118b0a5d91 100644 --- a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml +++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml @@ -84,7 +84,25 @@ android:layout_height="@dimen/screen_pinning_request_button_height" android:layout_weight="0" android:paddingStart="@dimen/screen_pinning_request_frame_padding" - android:paddingEnd="@dimen/screen_pinning_request_frame_padding" > + android:paddingEnd="@dimen/screen_pinning_request_frame_padding" + android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent"> + + <ImageView + android:id="@+id/screen_pinning_home_bg_light" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="matrix" + android:src="@drawable/screen_pinning_light_bg_circ" /> + + <ImageView + android:id="@+id/screen_pinning_home_bg" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingEnd="@dimen/screen_pinning_request_inner_padding" + android:paddingStart="@dimen/screen_pinning_request_inner_padding" + android:paddingTop="@dimen/screen_pinning_request_inner_padding" + android:scaleType="matrix" + android:src="@drawable/screen_pinning_bg_circ" /> <ImageView android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml index b5ef1d72fd8f..61fe906d65fc 100644 --- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml +++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml @@ -78,7 +78,25 @@ android:id="@+id/screen_pinning_home_group" android:layout_height="@dimen/screen_pinning_request_button_width" android:layout_width="@dimen/screen_pinning_request_button_height" - android:layout_weight="0" > + android:layout_weight="0" + android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent"> + + <ImageView + android:id="@+id/screen_pinning_home_bg_light" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:scaleType="matrix" + android:src="@drawable/screen_pinning_light_bg_circ" /> + + <ImageView + android:id="@+id/screen_pinning_home_bg" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:scaleType="matrix" + android:paddingLeft="@dimen/screen_pinning_request_inner_padding" + android:paddingTop="@dimen/screen_pinning_request_inner_padding" + android:paddingBottom="@dimen/screen_pinning_request_inner_padding" + android:src="@drawable/screen_pinning_bg_circ" /> <ImageView android:layout_height="match_parent" diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml index f3a6d44f5ec7..d1ca2ce5935c 100644 --- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml +++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml @@ -80,7 +80,27 @@ android:id="@+id/screen_pinning_home_group" android:layout_height="@dimen/screen_pinning_request_button_width" android:layout_width="@dimen/screen_pinning_request_button_height" - android:layout_weight="0" > + android:layout_weight="0" + android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent" > + + <ImageView + android:id="@+id/screen_pinning_home_bg_light" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:scaleType="matrix" + android:layout_marginLeft="@dimen/screen_pinning_request_seascape_padding_negative" + android:src="@drawable/screen_pinning_light_bg_circ" /> + + <ImageView + android:id="@+id/screen_pinning_home_bg" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:scaleType="matrix" + android:layout_marginLeft="@dimen/screen_pinning_request_seascape_button_offset" + android:paddingRight="@dimen/screen_pinning_request_inner_padding" + android:paddingTop="@dimen/screen_pinning_request_inner_padding" + android:paddingBottom="@dimen/screen_pinning_request_inner_padding" + android:src="@drawable/screen_pinning_bg_circ" /> <ImageView android:layout_height="match_parent" diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index 117cd14f4463..36298ca0c095 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -20,14 +20,15 @@ android:background="@android:color/transparent" android:theme="@style/qs_theme" android:clipChildren="false" > + <!-- right-aligned to be physically near volume button --> <LinearLayout android:id="@+id/volume_dialog" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical|end" + android:layout_gravity="center_vertical|right" android:minWidth="@dimen/volume_dialog_panel_width" android:background="@android:color/transparent" - android:layout_margin="12dp" + android:layout_margin="@dimen/volume_dialog_base_margin" android:translationZ="8dp" android:orientation="vertical" android:clipChildren="false" > @@ -38,8 +39,8 @@ android:layout_height="wrap_content" android:clipChildren="false" android:clipToPadding="false" - android:paddingTop="12dp" - android:paddingBottom="12dp" + android:paddingTop="10dp" + android:paddingBottom="10dp" android:background="@drawable/rounded_bg_full" android:translationZ="8dp" android:orientation="horizontal" > @@ -59,6 +60,7 @@ android:gravity="center" android:layout_gravity="end" android:translationZ="8dp" + android:clickable="true" android:orientation="vertical" > <TextView @@ -70,13 +72,13 @@ android:maxLines="1" android:layout_centerVertical="true" android:textColor="?android:attr/colorControlNormal" - android:textAppearance="?android:attr/textAppearanceSmall" /> + android:textAppearance="@style/TextAppearance.Volume.Header" /> <com.android.keyguard.AlphaOptimizedImageButton android:id="@+id/ringer_icon" style="@style/VolumeButtons" android:background="?android:selectableItemBackgroundBorderless" - android:layout_width="@dimen/volume_button_size" + android:layout_width="@dimen/volume_dialog_panel_width" android:layout_height="@dimen/volume_button_size" android:tint="?android:attr/colorAccent" android:soundEffectsEnabled="false" /> @@ -88,7 +90,7 @@ android:ellipsize="end" android:maxLines="1" android:textColor="?android:attr/colorControlNormal" - android:textAppearance="?android:attr/textAppearanceSmall" /> + android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" /> </LinearLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml index 3e80085225e2..70654a803b44 100644 --- a/packages/SystemUI/res/layout/volume_dialog_row.xml +++ b/packages/SystemUI/res/layout/volume_dialog_row.xml @@ -29,15 +29,16 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" - android:padding="10dp"> + android:padding="5dp"> <TextView android:id="@+id/volume_row_header" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" + android:maxLength="10" android:maxLines="1" android:textColor="?android:attr/colorControlNormal" - android:textAppearance="?android:attr/textAppearanceSmall" /> + android:textAppearance="@style/TextAppearance.Volume.Header" /> <LinearLayout android:id="@+id/output_chooser" android:orientation="vertical" @@ -53,9 +54,10 @@ android:visibility="gone" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:maxLength="10" android:ellipsize="end" android:maxLines="1" - android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" /> + android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" /> <com.android.keyguard.AlphaOptimizedImageButton android:id="@+id/output_chooser_button" android:layout_width="24dp" @@ -63,6 +65,7 @@ android:background="?android:selectableItemBackgroundBorderless" android:contentDescription="@string/accessibility_output_chooser" style="@style/VolumeButtons" + android:clickable="false" android:layout_centerVertical="true" android:src="@drawable/ic_swap" android:soundEffectsEnabled="false" /> @@ -70,19 +73,20 @@ </LinearLayout> <FrameLayout android:id="@+id/volume_row_slider_frame" - android:padding="10dp" + android:padding="0dp" android:layout_width="@dimen/volume_dialog_panel_width" - android:layout_height="150dp"> + android:layoutDirection="rtl" + android:layout_height="@dimen/volume_dialog_panel_width"> <SeekBar android:id="@+id/volume_row_slider" + android:clickable="true" android:padding="0dp" android:layout_margin="0dp" - android:layout_width="150dp" + android:layout_width="@dimen/volume_dialog_panel_width" android:layout_height="@dimen/volume_dialog_panel_width" + android:layoutDirection="rtl" android:layout_gravity="center" - android:focusable="true" - android:focusableInTouchMode="true" - android:rotation="270" /> + android:rotation="90" /> </FrameLayout> <com.android.keyguard.AlphaOptimizedImageButton @@ -91,6 +95,7 @@ android:padding="10dp" android:layout_width="@dimen/volume_button_size" android:layout_height="@dimen/volume_button_size" + android:background="?android:selectableItemBackgroundBorderless" android:soundEffectsEnabled="false" /> </LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-af/config.xml b/packages/SystemUI/res/values-af/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-af/config.xml +++ b/packages/SystemUI/res/values-af/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-am/config.xml b/packages/SystemUI/res/values-am/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-am/config.xml +++ b/packages/SystemUI/res/values-am/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ar/config.xml b/packages/SystemUI/res/values-ar/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ar/config.xml +++ b/packages/SystemUI/res/values-ar/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-az/config.xml b/packages/SystemUI/res/values-az/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-az/config.xml +++ b/packages/SystemUI/res/values-az/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-b+sr+Latn/config.xml b/packages/SystemUI/res/values-b+sr+Latn/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/config.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-be/config.xml b/packages/SystemUI/res/values-be/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-be/config.xml +++ b/packages/SystemUI/res/values-be/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-bg/config.xml b/packages/SystemUI/res/values-bg/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-bg/config.xml +++ b/packages/SystemUI/res/values-bg/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-bn/config.xml b/packages/SystemUI/res/values-bn/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-bn/config.xml +++ b/packages/SystemUI/res/values-bn/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-bs/config.xml b/packages/SystemUI/res/values-bs/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-bs/config.xml +++ b/packages/SystemUI/res/values-bs/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ca/config.xml b/packages/SystemUI/res/values-ca/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ca/config.xml +++ b/packages/SystemUI/res/values-ca/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-cs/config.xml b/packages/SystemUI/res/values-cs/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-cs/config.xml +++ b/packages/SystemUI/res/values-cs/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-da/config.xml b/packages/SystemUI/res/values-da/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-da/config.xml +++ b/packages/SystemUI/res/values-da/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-de/config.xml b/packages/SystemUI/res/values-de/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-de/config.xml +++ b/packages/SystemUI/res/values-de/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-el/config.xml b/packages/SystemUI/res/values-el/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-el/config.xml +++ b/packages/SystemUI/res/values-el/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-en-rAU/config.xml b/packages/SystemUI/res/values-en-rAU/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-en-rAU/config.xml +++ b/packages/SystemUI/res/values-en-rAU/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-en-rCA/config.xml b/packages/SystemUI/res/values-en-rCA/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-en-rCA/config.xml +++ b/packages/SystemUI/res/values-en-rCA/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/config.xml b/packages/SystemUI/res/values-en-rGB/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-en-rGB/config.xml +++ b/packages/SystemUI/res/values-en-rGB/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/config.xml b/packages/SystemUI/res/values-en-rIN/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-en-rIN/config.xml +++ b/packages/SystemUI/res/values-en-rIN/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-en-rXC/config.xml b/packages/SystemUI/res/values-en-rXC/config.xml index f015d9b378d4..5309563e3986 100644 --- a/packages/SystemUI/res/values-en-rXC/config.xml +++ b/packages/SystemUI/res/values-en-rXC/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/config.xml b/packages/SystemUI/res/values-es-rUS/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-es-rUS/config.xml +++ b/packages/SystemUI/res/values-es-rUS/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-es/config.xml b/packages/SystemUI/res/values-es/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-es/config.xml +++ b/packages/SystemUI/res/values-es/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-et/config.xml b/packages/SystemUI/res/values-et/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-et/config.xml +++ b/packages/SystemUI/res/values-et/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-eu/config.xml b/packages/SystemUI/res/values-eu/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-eu/config.xml +++ b/packages/SystemUI/res/values-eu/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-fa/config.xml b/packages/SystemUI/res/values-fa/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-fa/config.xml +++ b/packages/SystemUI/res/values-fa/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-fi/config.xml b/packages/SystemUI/res/values-fi/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-fi/config.xml +++ b/packages/SystemUI/res/values-fi/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-fr-rCA/config.xml b/packages/SystemUI/res/values-fr-rCA/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-fr-rCA/config.xml +++ b/packages/SystemUI/res/values-fr-rCA/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-fr/config.xml b/packages/SystemUI/res/values-fr/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-fr/config.xml +++ b/packages/SystemUI/res/values-fr/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-gl/config.xml b/packages/SystemUI/res/values-gl/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-gl/config.xml +++ b/packages/SystemUI/res/values-gl/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-gu/config.xml b/packages/SystemUI/res/values-gu/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-gu/config.xml +++ b/packages/SystemUI/res/values-gu/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-hi/config.xml b/packages/SystemUI/res/values-hi/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-hi/config.xml +++ b/packages/SystemUI/res/values-hi/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-hr/config.xml b/packages/SystemUI/res/values-hr/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-hr/config.xml +++ b/packages/SystemUI/res/values-hr/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-hu/config.xml b/packages/SystemUI/res/values-hu/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-hu/config.xml +++ b/packages/SystemUI/res/values-hu/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-hy/config.xml b/packages/SystemUI/res/values-hy/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-hy/config.xml +++ b/packages/SystemUI/res/values-hy/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-in/config.xml b/packages/SystemUI/res/values-in/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-in/config.xml +++ b/packages/SystemUI/res/values-in/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-is/config.xml b/packages/SystemUI/res/values-is/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-is/config.xml +++ b/packages/SystemUI/res/values-is/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-it/config.xml b/packages/SystemUI/res/values-it/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-it/config.xml +++ b/packages/SystemUI/res/values-it/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-iw/config.xml b/packages/SystemUI/res/values-iw/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-iw/config.xml +++ b/packages/SystemUI/res/values-iw/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ja/config.xml b/packages/SystemUI/res/values-ja/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ja/config.xml +++ b/packages/SystemUI/res/values-ja/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ka/config.xml b/packages/SystemUI/res/values-ka/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ka/config.xml +++ b/packages/SystemUI/res/values-ka/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-kk/config.xml b/packages/SystemUI/res/values-kk/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-kk/config.xml +++ b/packages/SystemUI/res/values-kk/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-km/config.xml b/packages/SystemUI/res/values-km/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-km/config.xml +++ b/packages/SystemUI/res/values-km/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-kn/config.xml b/packages/SystemUI/res/values-kn/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-kn/config.xml +++ b/packages/SystemUI/res/values-kn/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ko/config.xml b/packages/SystemUI/res/values-ko/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ko/config.xml +++ b/packages/SystemUI/res/values-ko/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ky/config.xml b/packages/SystemUI/res/values-ky/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ky/config.xml +++ b/packages/SystemUI/res/values-ky/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-lo/config.xml b/packages/SystemUI/res/values-lo/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-lo/config.xml +++ b/packages/SystemUI/res/values-lo/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-lt/config.xml b/packages/SystemUI/res/values-lt/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-lt/config.xml +++ b/packages/SystemUI/res/values-lt/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-lv/config.xml b/packages/SystemUI/res/values-lv/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-lv/config.xml +++ b/packages/SystemUI/res/values-lv/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-mk/config.xml b/packages/SystemUI/res/values-mk/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-mk/config.xml +++ b/packages/SystemUI/res/values-mk/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ml/config.xml b/packages/SystemUI/res/values-ml/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ml/config.xml +++ b/packages/SystemUI/res/values-ml/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-mn/config.xml b/packages/SystemUI/res/values-mn/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-mn/config.xml +++ b/packages/SystemUI/res/values-mn/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-mr/config.xml b/packages/SystemUI/res/values-mr/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-mr/config.xml +++ b/packages/SystemUI/res/values-mr/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ms/config.xml b/packages/SystemUI/res/values-ms/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ms/config.xml +++ b/packages/SystemUI/res/values-ms/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-my/config.xml b/packages/SystemUI/res/values-my/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-my/config.xml +++ b/packages/SystemUI/res/values-my/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-nb/config.xml b/packages/SystemUI/res/values-nb/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-nb/config.xml +++ b/packages/SystemUI/res/values-nb/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ne/config.xml b/packages/SystemUI/res/values-ne/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ne/config.xml +++ b/packages/SystemUI/res/values-ne/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-nl/config.xml b/packages/SystemUI/res/values-nl/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-nl/config.xml +++ b/packages/SystemUI/res/values-nl/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-pa/config.xml b/packages/SystemUI/res/values-pa/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-pa/config.xml +++ b/packages/SystemUI/res/values-pa/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-pl/config.xml b/packages/SystemUI/res/values-pl/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-pl/config.xml +++ b/packages/SystemUI/res/values-pl/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-pt-rBR/config.xml b/packages/SystemUI/res/values-pt-rBR/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-pt-rBR/config.xml +++ b/packages/SystemUI/res/values-pt-rBR/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/config.xml b/packages/SystemUI/res/values-pt-rPT/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-pt-rPT/config.xml +++ b/packages/SystemUI/res/values-pt-rPT/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-pt/config.xml b/packages/SystemUI/res/values-pt/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-pt/config.xml +++ b/packages/SystemUI/res/values-pt/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ro/config.xml b/packages/SystemUI/res/values-ro/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ro/config.xml +++ b/packages/SystemUI/res/values-ro/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ru/config.xml b/packages/SystemUI/res/values-ru/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ru/config.xml +++ b/packages/SystemUI/res/values-ru/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-si/config.xml b/packages/SystemUI/res/values-si/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-si/config.xml +++ b/packages/SystemUI/res/values-si/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-sk/config.xml b/packages/SystemUI/res/values-sk/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-sk/config.xml +++ b/packages/SystemUI/res/values-sk/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-sl/config.xml b/packages/SystemUI/res/values-sl/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-sl/config.xml +++ b/packages/SystemUI/res/values-sl/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-sq/config.xml b/packages/SystemUI/res/values-sq/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-sq/config.xml +++ b/packages/SystemUI/res/values-sq/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-sr/config.xml b/packages/SystemUI/res/values-sr/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-sr/config.xml +++ b/packages/SystemUI/res/values-sr/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-sv/config.xml b/packages/SystemUI/res/values-sv/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-sv/config.xml +++ b/packages/SystemUI/res/values-sv/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-sw/config.xml b/packages/SystemUI/res/values-sw/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-sw/config.xml +++ b/packages/SystemUI/res/values-sw/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ta/config.xml b/packages/SystemUI/res/values-ta/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ta/config.xml +++ b/packages/SystemUI/res/values-ta/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-te/config.xml b/packages/SystemUI/res/values-te/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-te/config.xml +++ b/packages/SystemUI/res/values-te/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-th/config.xml b/packages/SystemUI/res/values-th/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-th/config.xml +++ b/packages/SystemUI/res/values-th/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-tl/config.xml b/packages/SystemUI/res/values-tl/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-tl/config.xml +++ b/packages/SystemUI/res/values-tl/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-tr/config.xml b/packages/SystemUI/res/values-tr/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-tr/config.xml +++ b/packages/SystemUI/res/values-tr/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-uk/config.xml b/packages/SystemUI/res/values-uk/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-uk/config.xml +++ b/packages/SystemUI/res/values-uk/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-ur/config.xml b/packages/SystemUI/res/values-ur/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-ur/config.xml +++ b/packages/SystemUI/res/values-ur/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-uz/config.xml b/packages/SystemUI/res/values-uz/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-uz/config.xml +++ b/packages/SystemUI/res/values-uz/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-vi/config.xml b/packages/SystemUI/res/values-vi/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-vi/config.xml +++ b/packages/SystemUI/res/values-vi/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/config.xml b/packages/SystemUI/res/values-zh-rCN/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-zh-rCN/config.xml +++ b/packages/SystemUI/res/values-zh-rCN/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-zh-rHK/config.xml b/packages/SystemUI/res/values-zh-rHK/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-zh-rHK/config.xml +++ b/packages/SystemUI/res/values-zh-rHK/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/config.xml b/packages/SystemUI/res/values-zh-rTW/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-zh-rTW/config.xml +++ b/packages/SystemUI/res/values-zh-rTW/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values-zu/config.xml b/packages/SystemUI/res/values-zu/config.xml index 477f2198e4a6..5309563e3986 100644 --- a/packages/SystemUI/res/values-zu/config.xml +++ b/packages/SystemUI/res/values-zu/config.xml @@ -22,6 +22,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string> <string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 1cc1cc883268..f2e5d3b2cc3f 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -46,7 +46,7 @@ <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string> <!-- Component name of launcher service for overview to connect to --> - <string name="config_overviewServiceComponent" translateable="false">com.android.launcher3/com.android.quickstep.TouchInteractionService</string> + <string name="config_overviewServiceComponent" translatable="false">com.android.launcher3/com.android.quickstep.TouchInteractionService</string> <!-- Whether or not we show the number in the bar. --> <bool name="config_statusBarShowNumber">false</bool> @@ -124,7 +124,7 @@ <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" --> <string name="quick_settings_tiles_stock" translatable="false"> - wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night + wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night,alarm </string> <!-- The tiles to display in QuickSettings --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 3db79d7556d5..e55c65a47f7b 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -262,7 +262,11 @@ <!-- The width of the panel that holds the quick settings. --> <dimen name="qs_panel_width">@dimen/notification_panel_width</dimen> - <dimen name="volume_dialog_panel_width">120dp</dimen> + <!-- the amount the volume panel should be offset at the end from the view next to it (or + the scren edge, in portrait--> + <dimen name="volume_dialog_base_margin">12dp</dimen> + + <dimen name="volume_dialog_panel_width">100dp</dimen> <dimen name="output_chooser_panel_width">320dp</dimen> @@ -332,6 +336,8 @@ <dimen name="qs_footer_padding_start">16dp</dimen> <dimen name="qs_footer_padding_end">24dp</dimen> <dimen name="qs_footer_icon_size">16dp</dimen> + <!-- Difference between drag handle margin in QQS and expanded QS --> + <dimen name="qs_footer_drag_handle_offset">6dp</dimen> <dimen name="qs_notif_collapsed_space">64dp</dimen> @@ -902,4 +908,11 @@ <dimen name="config_batteryLevelTextSizeEnd" format="float">32.0</dimen> <!-- Wireless Charging battery level text animation duration --> <integer name="config_batteryLevelTextAnimationDuration">400</integer> + + <!-- Wired charging on AOD, text animation duration --> + <integer name="wired_charging_aod_text_animation_duration_down">500</integer> + <!-- Wired charging on AOD, text animation duration --> + <integer name="wired_charging_aod_text_animation_duration_up">300</integer> + <!-- Wired charging on AOD, text animation distance --> + <integer name="wired_charging_aod_text_animation_distance">-30</integer> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 72615ce690e5..fadcbcd4f4bc 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -739,6 +739,8 @@ <string name="quick_settings_wifi_on_label">Wi-Fi On</string> <!-- QuickSettings: Wifi detail panel, text when there are no items [CHAR LIMIT=NONE] --> <string name="quick_settings_wifi_detail_empty_text">No Wi-Fi networks available</string> + <!-- QuickSettings: Alarm title [CHAR LIMIT=NONE] --> + <string name="quick_settings_alarm_title">Alarm</string> <!-- QuickSettings: Cast title [CHAR LIMIT=NONE] --> <string name="quick_settings_cast_title">Cast</string> <!-- QuickSettings: Cast detail panel, status text when casting [CHAR LIMIT=NONE] --> @@ -1279,12 +1281,23 @@ <string name="screen_pinning_title">Screen is pinned</string> <!-- Screen pinning dialog description. --> <string name="screen_pinning_description">This keeps it in view until you unpin. Touch & hold Back and Overview to unpin.</string> + <string name="screen_pinning_description_recents_invisible">This keeps it in view until you unpin. Touch & hold Back and Home to unpin.</string> <!-- Screen pinning dialog description. --> <string name="screen_pinning_description_accessible">This keeps it in view until you unpin. Touch & hold Overview to unpin.</string> + <string name="screen_pinning_description_recents_invisible_accessible">This keeps it in view until you unpin. Touch & hold Home to unpin.</string> + <!-- Notify use that they are in Lock-to-app --> + <string name="screen_pinning_toast">To unpin this screen, touch & hold Back and Overview + buttons</string> + <string name="screen_pinning_toast_recents_invisible">To unpin this screen, touch & hold Back + and Home buttons</string> <!-- Screen pinning positive response. --> <string name="screen_pinning_positive">Got it</string> <!-- Screen pinning negative response. --> <string name="screen_pinning_negative">No thanks</string> + <!-- Enter/Exiting screen pinning indication. --> + <string name="screen_pinning_start">Screen pinned</string> + <string name="screen_pinning_exit">Screen unpinned</string> + <!-- Hide quick settings tile confirmation title --> <string name="quick_settings_reset_confirmation_title">Hide <xliff:g id="tile_label" example="Hotspot">%1$s</xliff:g>?</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 2497d2080592..d2ed4d117cd8 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -410,16 +410,11 @@ <item name="android:textColor">?android:attr/textColorSecondary</item> </style> - <style name="TextAppearance.Volume.ZenSummary"> - <item name="android:textSize">14sp</item> - <item name="android:fontFamily">sans-serif-medium</item> + <style name="TextAppearance.Volume.Header.Secondary"> + <item name="android:textSize">12sp</item> + <item name="android:textColor">?android:attr/textColorTertiary</item> </style> - <style name="TextAppearance.Volume.ZenDetail"> - <item name="android:textSize">14sp</item> - <item name="android:fontFamily">sans-serif</item> - <item name="android:textColor">?android:attr/textColorSecondary</item> - </style> <style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless"> <item name="android:background">@drawable/btn_borderless_rect</item> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index f9e1069cfe95..90e3b1e73454 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -16,6 +16,8 @@ package com.android.systemui.shared.system; +import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; +import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; @@ -31,6 +33,7 @@ import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IAssistDataReceiver; import android.app.WindowConfiguration.ActivityType; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -47,6 +50,7 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.Settings; import android.util.IconDrawableFactory; import android.util.Log; import android.view.IRecentsAnimationController; @@ -436,4 +440,23 @@ public class ActivityManagerWrapper { Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e); } } + + /** + * @return whether there is currently a locked task (ie. in screen pinning). + */ + public boolean isLockToAppActive() { + try { + return ActivityManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE; + } catch (RemoteException e) { + return false; + } + } + + /** + * @return whether screen pinning is enabled. + */ + public boolean isLockToAppEnabled() { + final ContentResolver cr = AppGlobals.getInitialApplication().getContentResolver(); + return Settings.System.getInt(cr, Settings.System.LOCK_TO_APP_ENABLED, 0) != 0; + } } diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java index b8cfa3e48c20..aeef49689517 100644 --- a/packages/SystemUI/src/com/android/systemui/Interpolators.java +++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java @@ -18,6 +18,7 @@ package com.android.systemui; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; +import android.view.animation.BounceInterpolator; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; @@ -43,6 +44,7 @@ public class Interpolators { public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f); public static final Interpolator PANEL_CLOSE_ACCELERATED = new PathInterpolator(0.3f, 0, 0.5f, 1); + public static final Interpolator BOUNCE = new BounceInterpolator(); /** * Interpolator to be used when animating a move based on a click. Pair with enough duration. diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index 8d8b726f8fa8..b7e1d67a5b3a 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -199,7 +199,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis launcherServiceIntent.setComponent(mLauncherComponentName); boolean bound = mContext.bindServiceAsUser(launcherServiceIntent, mOverviewServiceConnection, Context.BIND_AUTO_CREATE, - UserHandle.getUserHandleForUid(mDeviceProvisionedController.getCurrentUser())); + UserHandle.of(mDeviceProvisionedController.getCurrentUser())); if (!bound) { // Retry after exponential backoff timeout final long timeoutMs = (long) Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts); diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java index 89bc82f87930..43b918dbea73 100644 --- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java @@ -15,13 +15,19 @@ */ package com.android.systemui; +import android.annotation.StringRes; import android.content.Context; import android.view.WindowManager; import android.widget.Toast; +import static android.widget.Toast.Duration; public class SysUIToast { - public static Toast makeText(Context context, CharSequence text, int duration) { + public static Toast makeText(Context context, @StringRes int resId, @Duration int duration) { + return makeText(context, context.getString(resId), duration); + } + + public static Toast makeText(Context context, CharSequence text, @Duration int duration) { Toast toast = Toast.makeText(context, text, duration); toast.getWindowParams().privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 8501519d26aa..eedc50f23d60 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -709,8 +709,7 @@ public class KeyguardViewMediator extends SystemUI { mSecondaryDisplayShowing, true /* forceCallbacks */); } else { // The system's keyguard is disabled or missing. - setShowingLocked(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()), - mSecondaryDisplayShowing, true); + setShowingLocked(false, mSecondaryDisplayShowing, true); } mStatusBarKeyguardViewManager = diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index b43e99be828e..ea2a432ea6ca 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -44,6 +44,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; +import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUI; @@ -141,8 +142,12 @@ public class PowerUI extends SystemUI { final int lowPowerModeTriggerLevel = Settings.Global.getInt(resolver, Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel); - // NOTE: Keep the logic in sync with BatteryService. - // TODO: Propagate this value from BatteryService to system UI, really. + // Note LOW_POWER_MODE_TRIGGER_LEVEL can take any value between 0 and 100, but + // for the UI purposes, let's cap it at 15% -- i.e. even if the trigger level is higher + // like 50%, let's not show the "low battery" notification until it hits + // config_lowBatteryWarningLevel, which is 15% by default. + // LOW_POWER_MODE_TRIGGER_LEVEL is still used in other places as-is. For example, if it's + // 50, then battery saver kicks in when the battery level hits 50%. int warnLevel = Math.min(defWarnLevel, lowPowerModeTriggerLevel); if (warnLevel == 0) { @@ -242,7 +247,9 @@ public class PowerUI extends SystemUI { } // Show the correct version of low battery warning if needed - maybeShowBatteryWarning(plugged, oldPlugged, oldBucket, bucket); + ThreadUtils.postOnBackgroundThread(() -> { + maybeShowBatteryWarning(plugged, oldPlugged, oldBucket, bucket); + }); } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { mScreenOffTime = SystemClock.elapsedRealtime(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java index f960dc5b4a47..2a2bc0923e1b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java +++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java @@ -51,10 +51,11 @@ public class AutoAddTracker { public AutoAddTracker(Context context) { mContext = context; mAutoAdded = new ArraySet<>(getAdded()); + // TODO: remove migration code and shared preferences keys after P release for (String[] convertPref : CONVERT_PREFS) { if (Prefs.getBoolean(context, convertPref[0], false)) { setTileAdded(convertPref[1]); - Prefs.putBoolean(context, convertPref[0], false); + Prefs.remove(context, convertPref[0]); } } mContext.getContentResolver().registerContentObserver( diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index 6b0d592a4d01..6ccb81772db9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -36,7 +36,6 @@ import com.android.systemui.statusbar.ExpandableOutlineView; public class QSContainerImpl extends FrameLayout { private final Point mSizePoint = new Point(); - private final Path mClipPath = new Path(); private int mHeightOverride = -1; protected View mQSPanel; @@ -46,7 +45,6 @@ public class QSContainerImpl extends FrameLayout { private QSCustomizer mQSCustomizer; private View mQSFooter; private View mBackground; - private float mRadius; private int mSideMargins; public QSContainerImpl(Context context, AttributeSet attrs) { @@ -62,8 +60,6 @@ public class QSContainerImpl extends FrameLayout { mQSCustomizer = findViewById(R.id.qs_customize); mQSFooter = findViewById(R.id.qs_footer); mBackground = findViewById(R.id.quick_settings_background); - mRadius = getResources().getDimensionPixelSize( - Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius)); mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings); setClickable(true); @@ -115,18 +111,6 @@ public class QSContainerImpl extends FrameLayout { updateExpansion(); } - @Override - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - boolean ret; - canvas.save(); - if (child != mQSCustomizer) { - canvas.clipPath(mClipPath); - } - ret = super.drawChild(canvas, child, drawingTime); - canvas.restore(); - return ret; - } - /** * Overrides the height of this view (post-layout), so that the content is clipped to that * height and the background is set to that height. @@ -146,10 +130,6 @@ public class QSContainerImpl extends FrameLayout { mQSFooter.setTranslationY(height - mQSFooter.getHeight()); mBackground.setTop(mQSPanel.getTop()); mBackground.setBottom(height); - - ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius, - mRadius, - mClipPath); } protected int calculateContainerHeight() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index 92475da697bd..7f34acb2ef94 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -21,7 +21,6 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; @@ -47,7 +46,6 @@ import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.TouchAnimator.Builder; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.phone.ExpandableIndicator; import com.android.systemui.statusbar.phone.MultiUserSwitch; import com.android.systemui.statusbar.phone.SettingsButton; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -61,6 +59,7 @@ import com.android.systemui.tuner.TunerService; public class QSFooterImpl extends FrameLayout implements QSFooter, OnClickListener, OnUserInfoChangedListener, EmergencyListener, SignalCallback, CommandQueue.Callbacks { + private ActivityStarter mActivityStarter; private UserInfoController mUserInfoController; private SettingsButton mSettingsButton; @@ -75,24 +74,32 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, private boolean mListening; private boolean mShowEmergencyCallsOnly; + private View mDivider; protected MultiUserSwitch mMultiUserSwitch; private ImageView mMultiUserAvatar; - protected TouchAnimator mSettingsAlpha; + protected TouchAnimator mFooterAnimator; private float mExpansionAmount; protected View mEdit; - private TouchAnimator mAnimator; + private TouchAnimator mSettingsCogAnimator; + + private View mActionsContainer; + private View mDragHandle; + private final int mDragHandleExpandOffset; public QSFooterImpl(Context context, AttributeSet attrs) { super(context, attrs); + + mDragHandleExpandOffset = getResources(). + getDimensionPixelSize(R.dimen.qs_footer_drag_handle_offset); + } @Override protected void onFinishInflate() { super.onFinishInflate(); - Resources res = getResources(); - + mDivider = findViewById(R.id.qs_footer_divider); mEdit = findViewById(android.R.id.edit); mEdit.setOnClickListener(view -> Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> @@ -107,6 +114,9 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mMultiUserSwitch = findViewById(R.id.multi_user_switch); mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar); + mDragHandle = findViewById(R.id.qs_drag_handle_view); + mActionsContainer = findViewById(R.id.qs_footer_actions_container); + // RenderThread is doing more harm than good when touching the header (to expand quick // settings), so disable it for this view ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true); @@ -126,7 +136,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, int remaining = (width - numTiles * size) / (numTiles - 1); int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space); - mAnimator = new Builder() + mSettingsCogAnimator = new Builder() .addFloat(mSettingsContainer, "translationX", isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0) .addFloat(mSettingsButton, "rotation", -120, 0) @@ -148,20 +158,20 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, } private void updateResources() { - updateSettingsAnimator(); + updateFooterAnimator(); } - private void updateSettingsAnimator() { - mSettingsAlpha = createSettingsAlphaAnimator(); + private void updateFooterAnimator() { + mFooterAnimator = createFooterAnimator(); } @Nullable - private TouchAnimator createSettingsAlphaAnimator() { + private TouchAnimator createFooterAnimator() { return new TouchAnimator.Builder() - .addFloat(mEdit, "alpha", 0, 1) - .addFloat(mMultiUserSwitch, "alpha", 0, 1) + .addFloat(mDivider, "alpha", 0, 1) .addFloat(mCarrierText, "alpha", 0, 1) - .addFloat(mSettingsButton, "alpha", 0, 1) + .addFloat(mActionsContainer, "alpha", 0, 1) + .addFloat(mDragHandle, "translationY", 0, -mDragHandleExpandOffset) .build(); } @@ -180,10 +190,10 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, @Override public void setExpansion(float headerExpansionFraction) { mExpansionAmount = headerExpansionFraction; - if (mAnimator != null) mAnimator.setPosition(headerExpansionFraction); + if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction); - if (mSettingsAlpha != null) { - mSettingsAlpha.setPosition(headerExpansionFraction); + if (mFooterAnimator != null) { + mFooterAnimator.setPosition(headerExpansionFraction); } } @@ -269,6 +279,11 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, @Override public void onClick(View v) { + // Don't do anything until view are unhidden + if (!mExpanded) { + return; + } + if (v == mSettingsButton) { if (!Dependency.get(DeviceProvisionedController.class).isCurrentUserSetup()) { // If user isn't setup just unlock the device and dump them back at SUW. diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 669439d7f6ca..d8e10516fe69 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -69,7 +69,7 @@ public class QSFragment extends Fragment implements QS { @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { - inflater =inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme)); + inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme)); return inflater.inflate(R.layout.qs_panel, container, false); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 77c3bfab8de9..bf9746eafaf0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -23,6 +23,7 @@ import com.android.systemui.plugins.qs.*; import com.android.systemui.plugins.qs.QSTileView; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.tiles.AirplaneModeTile; +import com.android.systemui.qs.tiles.AlarmTile; import com.android.systemui.qs.tiles.BatterySaverTile; import com.android.systemui.qs.tiles.BluetoothTile; import com.android.systemui.qs.tiles.CastTile; @@ -69,6 +70,7 @@ public class QSFactoryImpl implements QSFactory { else if (tileSpec.equals("saver")) return new DataSaverTile(mHost); else if (tileSpec.equals("night")) return new NightDisplayTile(mHost); else if (tileSpec.equals("nfc")) return new NfcTile(mHost); + else if (tileSpec.equals("alarm")) return new AlarmTile(mHost); // Intent tiles. else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(mHost, tileSpec); else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(mHost, tileSpec); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java new file mode 100644 index 000000000000..ff3fe731ad4f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.java @@ -0,0 +1,102 @@ +/* + * 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.qs.tiles; + +import static android.service.quicksettings.Tile.STATE_ACTIVE; +import static android.service.quicksettings.Tile.STATE_UNAVAILABLE; + +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.QS_ALARM; +import static com.android.systemui.keyguard.KeyguardSliceProvider.formatNextAlarm; + +import android.app.AlarmManager.AlarmClockInfo; +import android.app.PendingIntent; +import android.content.Intent; +import android.provider.AlarmClock; + +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.qs.QSTileHost; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.statusbar.policy.NextAlarmController; +import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback; + +public class AlarmTile extends QSTileImpl implements NextAlarmChangeCallback { + private final NextAlarmController mController; + private String mNextAlarm; + private PendingIntent mIntent; + + public AlarmTile(QSTileHost host) { + super(host); + mController = Dependency.get(NextAlarmController.class); + } + + @Override + public State newTileState() { + return new BooleanState(); + } + + @Override + protected void handleClick() { + if (mIntent != null) { + Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(mIntent); + } + } + + @Override + protected void handleUpdateState(State state, Object arg) { + state.state = mNextAlarm != null ? STATE_ACTIVE : STATE_UNAVAILABLE; + state.label = getTileLabel(); + state.secondaryLabel = mNextAlarm; + state.icon = ResourceIcon.get(R.drawable.stat_sys_alarm); + ((BooleanState) state).value = mNextAlarm != null; + } + + @Override + public void onNextAlarmChanged(AlarmClockInfo nextAlarm) { + if (nextAlarm != null) { + mNextAlarm = formatNextAlarm(mContext, nextAlarm); + mIntent = nextAlarm.getShowIntent(); + } else { + mNextAlarm = null; + mIntent = null; + } + refreshState(); + } + + @Override + public int getMetricsCategory() { + return QS_ALARM; + } + + @Override + public Intent getLongClickIntent() { + return new Intent(AlarmClock.ACTION_SET_ALARM); + } + + @Override + protected void handleSetListening(boolean listening) { + if (listening) { + mController.addCallback(this); + } else { + mController.removeCallback(this); + } + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.status_bar_alarm); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index aaf6ef52d761..6205e9afcb03 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -19,8 +19,6 @@ package com.android.systemui.qs.tiles; import static android.provider.Settings.Global.ZEN_MODE_ALARMS; import static android.provider.Settings.Global.ZEN_MODE_OFF; -import android.app.AlarmManager; -import android.app.AlarmManager.AlarmClockInfo; import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.Context; @@ -30,11 +28,9 @@ import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.net.Uri; import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.Global; -import android.service.notification.ScheduleCalendar; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.ZenRule; import android.service.quicksettings.Tile; @@ -56,7 +52,6 @@ import com.android.systemui.R; import com.android.systemui.SysUIToast; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.DetailAdapter; -import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.tileimpl.QSTileImpl; @@ -197,7 +192,8 @@ public class DndTile extends QSTileImpl<BooleanState> { state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; state.slash.isSlashed = !state.value; state.label = getTileLabel(); - state.secondaryLabel = getSecondaryLabel(zen != Global.ZEN_MODE_OFF); + state.secondaryLabel = ZenModeConfig.getDescription(mContext,zen != Global.ZEN_MODE_OFF, + mController.getConfig()); state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on); checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME); switch (zen) { @@ -226,102 +222,6 @@ public class DndTile extends QSTileImpl<BooleanState> { state.expandedAccessibilityClassName = Switch.class.getName(); } - /** - * Returns the secondary label to use for the given instance of do not disturb. - * - If turned on manually and end time is known, returns end time. - * - If turned on by an automatic rule, returns the automatic rule name. - * - If on due to an app, returns the app name. - * - If there's a combination of rules/apps that trigger, then shows the one that will - * last the longest if applicable. - * @return null if do not disturb is off. - */ - private String getSecondaryLabel(boolean zenOn) { - if (!zenOn) { - return null; - } - - ZenModeConfig config = mController.getConfig(); - String secondaryText = ""; - long latestEndTime = -1; - - // DND turned on by manual rule - if (config.manualRule != null) { - final Uri id = config.manualRule.conditionId; - if (config.manualRule.enabler != null) { - // app triggered manual rule - String appName = ZenModeConfig.getOwnerCaption(mContext, config.manualRule.enabler); - if (!appName.isEmpty()) { - secondaryText = appName; - } - } else { - if (id == null) { - // Do not disturb manually triggered to remain on forever until turned off - // No subtext - return null; - } else { - latestEndTime = ZenModeConfig.tryParseCountdownConditionId(id); - if (latestEndTime > 0) { - final CharSequence formattedTime = ZenModeConfig.getFormattedTime(mContext, - latestEndTime, ZenModeConfig.isToday(latestEndTime), - mContext.getUserId()); - secondaryText = mContext.getString(R.string.qs_dnd_until, formattedTime); - } - } - } - } - - // DND turned on by an automatic rule - for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) { - if (automaticRule.isAutomaticActive()) { - if (ZenModeConfig.isValidEventConditionId(automaticRule.conditionId) || - ZenModeConfig.isValidScheduleConditionId(automaticRule.conditionId)) { - // set text if automatic rule end time is the latest active rule end time - long endTime = parseAutomaticRuleEndTime(automaticRule.conditionId); - if (endTime > latestEndTime) { - latestEndTime = endTime; - secondaryText = automaticRule.name; - } - } else { - // set text if 3rd party rule - return automaticRule.name; - } - } - } - - return !secondaryText.equals("") ? secondaryText : null; - } - - private long parseAutomaticRuleEndTime(Uri id) { - if (ZenModeConfig.isValidEventConditionId(id)) { - // cannot look up end times for events - return Long.MAX_VALUE; - } - - if (ZenModeConfig.isValidScheduleConditionId(id)) { - ScheduleCalendar schedule = ZenModeConfig.toScheduleCalendar(id); - long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis()); - - // check if automatic rule will end on next alarm - if (schedule.exitAtAlarm()) { - long nextAlarm = getNextAlarm(mContext); - schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm); - if (schedule.shouldExitForAlarm(endTimeMs)) { - return nextAlarm; - } - } - - return endTimeMs; - } - - return -1; - } - - private long getNextAlarm(Context context) { - final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - final AlarmClockInfo info = alarms.getNextAlarmClock(mContext.getUserId()); - return info != null ? info.getTriggerTime() : 0; - } - @Override public int getMetricsCategory() { return MetricsEvent.QS_DND; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index 8bdbf28b5cf1..d7f2a2642180 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -102,7 +102,7 @@ public class LocationTile extends QSTileImpl<BooleanState> { state.value = locationEnabled; checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_SHARE_LOCATION); if (state.disabledByPolicy == false) { - checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION_MODE); + checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION); } state.icon = mIcon; state.slash.isSlashed = !state.value; diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 1da4deb61176..409c753c147c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -415,7 +415,7 @@ public class Recents extends SystemUI final int activityType = runningTask != null ? runningTask.configuration.windowConfiguration.getActivityType() : ACTIVITY_TYPE_UNDEFINED; - boolean screenPinningActive = sSystemServicesProxy.isScreenPinningActive(); + boolean screenPinningActive = ActivityManagerWrapper.getInstance().isLockToAppActive(); boolean isRunningTaskInHomeOrRecentsStack = activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS; if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index ee1b09109d38..3f6f30bba8c4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -386,8 +386,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener public void toggleRecents(int growTarget) { // Skip preloading if the task is locked - SystemServicesProxy ssp = Recents.getSystemServices(); - if (ssp.isScreenPinningActive()) { + if (ActivityManagerWrapper.getInstance().isLockToAppActive()) { return; } @@ -409,8 +408,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener MutableBoolean isHomeStackVisible = new MutableBoolean(true); long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime; + SystemServicesProxy ssp = Recents.getSystemServices(); if (ssp.isRecentsActivityVisible(isHomeStackVisible)) { - RecentsDebugFlags debugFlags = Recents.getDebugFlags(); RecentsConfiguration config = Recents.getConfiguration(); RecentsActivityLaunchState launchState = config.getLaunchState(); if (!launchState.launchedWithAltTab) { @@ -466,8 +465,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener public void preloadRecents() { // Skip preloading if the task is locked - SystemServicesProxy ssp = Recents.getSystemServices(); - if (ssp.isScreenPinningActive()) { + if (ActivityManagerWrapper.getInstance().isLockToAppActive()) { return; } @@ -481,6 +479,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // RecentsActivity) only if there is a task to animate to. Post this to ensure that we // don't block the touch feedback on the nav bar button which triggers this. mHandler.post(() -> { + SystemServicesProxy ssp = Recents.getSystemServices(); if (!ssp.isRecentsActivityVisible(null)) { ActivityManager.RunningTaskInfo runningTask = ActivityManagerWrapper.getInstance().getRunningTask(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index 316ad1661898..57f7818eae58 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -23,15 +23,12 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Configuration; import android.graphics.PixelFormat; -import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.os.Binder; import android.os.RemoteException; import android.util.DisplayMetrics; import android.view.Gravity; -import android.view.Surface; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -43,6 +40,9 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.systemui.R; +import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.statusbar.phone.NavigationBarView; +import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.leak.RotationUtils; import java.util.ArrayList; @@ -233,11 +233,30 @@ public class ScreenPinningRequest implements View.OnClickListener { .setVisibility(View.INVISIBLE); } + StatusBar statusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class); + NavigationBarView navigationBarView = statusBar.getNavigationBarView(); + final boolean recentsVisible = navigationBarView != null + && navigationBarView.isRecentsButtonVisible(); boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled(); + int descriptionStringResId; + if (recentsVisible) { + mLayout.findViewById(R.id.screen_pinning_recents_group).setVisibility(VISIBLE); + mLayout.findViewById(R.id.screen_pinning_home_bg_light).setVisibility(INVISIBLE); + mLayout.findViewById(R.id.screen_pinning_home_bg).setVisibility(INVISIBLE); + descriptionStringResId = touchExplorationEnabled + ? R.string.screen_pinning_description_accessible + : R.string.screen_pinning_description; + } else { + mLayout.findViewById(R.id.screen_pinning_recents_group).setVisibility(INVISIBLE); + mLayout.findViewById(R.id.screen_pinning_home_bg_light).setVisibility(VISIBLE); + mLayout.findViewById(R.id.screen_pinning_home_bg).setVisibility(VISIBLE); + descriptionStringResId = touchExplorationEnabled + ? R.string.screen_pinning_description_recents_invisible_accessible + : R.string.screen_pinning_description_recents_invisible; + } + ((TextView) mLayout.findViewById(R.id.screen_pinning_description)) - .setText(touchExplorationEnabled - ? R.string.screen_pinning_description_accessible - : R.string.screen_pinning_description); + .setText(descriptionStringResId); final int backBgVisibility = touchExplorationEnabled ? View.INVISIBLE : View.VISIBLE; mLayout.findViewById(R.id.screen_pinning_back_bg).setVisibility(backBgVisibility); mLayout.findViewById(R.id.screen_pinning_back_bg_light).setVisibility(backBgVisibility); diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 613d9fbb985c..93fd34aa0519 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -379,29 +379,6 @@ public class SystemServicesProxy { } /** - * Returns a global setting. - */ - public int getGlobalSetting(Context context, String setting) { - ContentResolver cr = context.getContentResolver(); - return Settings.Global.getInt(cr, setting, 0); - } - - /** - * Returns a system setting. - */ - public int getSystemSetting(Context context, String setting) { - ContentResolver cr = context.getContentResolver(); - return Settings.System.getInt(cr, setting, 0); - } - - /** - * Returns a system property. - */ - public String getSystemProperty(String key) { - return SystemProperties.get(key); - } - - /** * Returns the smallest width/height. */ public int getDeviceSmallestWidth() { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 5be2900831b3..3cc3273c0db4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -95,6 +95,7 @@ import com.android.systemui.recents.views.grid.GridTaskView; import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm; import com.android.systemui.recents.views.grid.TaskViewFocusFrame; +import com.android.systemui.shared.system.ActivityManagerWrapper; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -2187,8 +2188,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal private void readSystemFlags() { SystemServicesProxy ssp = Recents.getSystemServices(); mTouchExplorationEnabled = ssp.isTouchExplorationEnabled(); - mScreenPinningEnabled = ssp.getSystemSetting(getContext(), - Settings.System.LOCK_TO_APP_ENABLED) != 0; + mScreenPinningEnabled = ActivityManagerWrapper.getInstance().isLockToAppEnabled(); } private void updateStackActionButtonVisibility() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 79e9f7b45d8d..11bdf6b3c72e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -24,6 +24,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.RemoteException; import android.support.annotation.VisibleForTesting; import android.util.Pair; @@ -90,6 +91,8 @@ public class CommandQueue extends IStatusBar.Stub { private static final int MSG_FINGERPRINT_ERROR = 42 << MSG_SHIFT; private static final int MSG_FINGERPRINT_HIDE = 43 << MSG_SHIFT; private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT; + private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT; + private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -148,6 +151,8 @@ public class CommandQueue extends IStatusBar.Stub { default void clickTile(ComponentName tile) { } default void handleSystemKey(int arg1) { } + default void showPinningEnterExitToast(boolean entering) { } + default void showPinningEscapeToast() { } default void handleShowGlobalActionsMenu() { } default void handleShowShutdownUi(boolean isReboot, String reason) { } @@ -453,6 +458,21 @@ public class CommandQueue extends IStatusBar.Stub { } @Override + public void showPinningEnterExitToast(boolean entering) { + synchronized (mLock) { + mHandler.obtainMessage(MSG_SHOW_PINNING_TOAST_ENTER_EXIT, entering).sendToTarget(); + } + } + + @Override + public void showPinningEscapeToast() { + synchronized (mLock) { + mHandler.obtainMessage(MSG_SHOW_PINNING_TOAST_ESCAPE).sendToTarget(); + } + } + + + @Override public void showGlobalActionsMenu() { synchronized (mLock) { mHandler.removeMessages(MSG_SHOW_GLOBAL_ACTIONS); @@ -767,6 +787,16 @@ public class CommandQueue extends IStatusBar.Stub { mCallbacks.get(i).showChargingAnimation(msg.arg1); } break; + case MSG_SHOW_PINNING_TOAST_ENTER_EXIT: + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).showPinningEnterExitToast((Boolean) msg.obj); + } + break; + case MSG_SHOW_PINNING_TOAST_ESCAPE: + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).showPinningEscapeToast(); + } + break; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 0a12be4eac7f..b7a15005b170 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -44,6 +46,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; import com.android.systemui.Dependency; +import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; import com.android.systemui.statusbar.phone.LockIcon; @@ -192,7 +195,7 @@ public class KeyguardIndicationController { if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) { hideTransientIndication(); } - updateIndication(); + updateIndication(false); } else if (!visible) { // If we unlock and return to keyguard quickly, previous error should not be shown hideTransientIndication(); @@ -204,7 +207,7 @@ public class KeyguardIndicationController { */ public void setRestingIndication(String restingIndication) { mRestingIndication = restingIndication; - updateIndication(); + updateIndication(false); } /** @@ -265,7 +268,8 @@ public class KeyguardIndicationController { mWakeLock.setAcquired(true); hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS); } - updateIndication(); + + updateIndication(false); } /** @@ -275,11 +279,11 @@ public class KeyguardIndicationController { if (mTransientIndication != null) { mTransientIndication = null; mHandler.removeMessages(MSG_HIDE_TRANSIENT); - updateIndication(); + updateIndication(false); } } - protected final void updateIndication() { + protected final void updateIndication(boolean animate) { if (TextUtils.isEmpty(mTransientIndication)) { mWakeLock.setAcquired(false); } @@ -295,7 +299,35 @@ public class KeyguardIndicationController { mTextView.switchIndication(mTransientIndication); } else if (mPowerPluggedIn) { String indication = computePowerIndication(); - mTextView.switchIndication(indication); + if (animate) { + int yTranslation = mContext.getResources().getInteger( + R.integer.wired_charging_aod_text_animation_distance); + int animateUpDuration = mContext.getResources().getInteger( + R.integer.wired_charging_aod_text_animation_duration_up); + int animateDownDuration = mContext.getResources().getInteger( + R.integer.wired_charging_aod_text_animation_duration_down); + mTextView.animate() + .translationYBy(yTranslation) + .setInterpolator(Interpolators.LINEAR) + .setDuration(animateUpDuration) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mTextView.switchIndication(indication); + } + @Override + public void onAnimationEnd(Animator animation) { + mTextView.animate() + .setDuration(animateDownDuration) + .setInterpolator(Interpolators.BOUNCE) + .translationYBy(-1 * yTranslation) + .setListener(null); + } + }); + } else { + mTextView.switchIndication(indication); + } + } else { String percentage = NumberFormat.getPercentInstance() .format(mBatteryLevel / 100f); @@ -390,7 +422,7 @@ public class KeyguardIndicationController { public void onReceive(Context context, Intent intent) { mHandler.post(() -> { if (mVisible) { - updateIndication(); + updateIndication(false); } }); } @@ -412,7 +444,7 @@ public class KeyguardIndicationController { return; } mDozing = dozing; - updateIndication(); + updateIndication(false); updateDisclosure(); } @@ -445,7 +477,7 @@ public class KeyguardIndicationController { mChargingWattage = status.maxChargingWattage; mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold); mBatteryLevel = status.level; - updateIndication(); + updateIndication(!wasPluggedIn && mPowerPluggedIn); if (mDozing) { if (!wasPluggedIn && mPowerPluggedIn) { showTransientIndication(computePowerIndication()); @@ -551,7 +583,7 @@ public class KeyguardIndicationController { @Override public void onUserUnlocked() { if (mVisible) { - updateIndication(); + updateIndication(false); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 8325df7faddb..cad956cd602a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -802,10 +802,6 @@ public class NotificationShelf extends ActivatableNotificationView implements updateRelativeOffset(); } - public void setDarkOffsetX(int offsetX) { - mShelfIcons.setDarkOffsetX(offsetX); - } - private class ShelfState extends ExpandableViewState { private float openedAmount; private boolean hasItemsInStableShelf; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 3dfb9130af2e..3ebeb4d45c26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -379,7 +379,7 @@ public class CarStatusBar extends StatusBar implements // Because space is usually constrained in the auto use-case, there should not be a // pinned notification when the shade has been expanded. Ensure this by removing all heads- // up notifications. - mHeadsUpManager.releaseAllImmediately(); + mHeadsUpManager.removeAllHeadsUpEntries(); super.animateExpandNotificationsPanel(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java index 3491f8121545..c2141714b391 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification; import android.content.Context; import android.support.v4.view.AsyncLayoutInflater; +import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -30,15 +31,23 @@ import com.android.systemui.statusbar.NotificationData; * An inflater task that asynchronously inflates a ExpandableNotificationRow */ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInflateFinishedListener { + + private static final String TAG = "RowInflaterTask"; + private static final boolean TRACE_ORIGIN = true; + private RowInflationFinishedListener mListener; private NotificationData.Entry mEntry; private boolean mCancelled; + private Throwable mInflateOrigin; /** * Inflates a new notificationView. This should not be called twice on this object */ public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry, RowInflationFinishedListener listener) { + if (TRACE_ORIGIN) { + mInflateOrigin = new Throwable("inflate requested here"); + } mListener = listener; AsyncLayoutInflater inflater = new AsyncLayoutInflater(context); mEntry = entry; @@ -54,8 +63,16 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf @Override public void onInflateFinished(View view, int resid, ViewGroup parent) { if (!mCancelled) { - mEntry.onInflationTaskFinished(); - mListener.onInflationFinished((ExpandableNotificationRow) view); + try { + mEntry.onInflationTaskFinished(); + mListener.onInflationFinished((ExpandableNotificationRow) view); + } catch (Throwable t) { + if (mInflateOrigin != null) { + Log.e(TAG, "Error in inflation finished listener: " + t, mInflateOrigin); + t.addSuppressed(mInflateOrigin); + } + throw t; + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java index 36f9f6b79417..b220686b3056 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java @@ -14,16 +14,13 @@ package com.android.systemui.statusbar.phone; +import android.app.AlarmManager.AlarmClockInfo; import android.content.Context; import android.os.Handler; -import android.os.Looper; import android.provider.Settings.Secure; - import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ColorDisplayController; import com.android.systemui.Dependency; -import com.android.systemui.Prefs; -import com.android.systemui.Prefs.Key; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.SecureSetting; @@ -31,27 +28,37 @@ import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.DataSaverController.Listener; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.HotspotController.Callback; +import com.android.systemui.statusbar.policy.NextAlarmController; +import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback; /** * Manages which tiles should be automatically added to QS. */ public class AutoTileManager { - public static final String HOTSPOT = "hotspot"; public static final String SAVER = "saver"; public static final String INVERSION = "inversion"; public static final String WORK = "work"; public static final String NIGHT = "night"; + public static final String ALARM = "alarm"; + private final Context mContext; private final QSTileHost mHost; private final Handler mHandler; private final AutoAddTracker mAutoTracker; public AutoTileManager(Context context, QSTileHost host) { - mAutoTracker = new AutoAddTracker(context); + this(context, new AutoAddTracker(context), host, + new Handler(Dependency.get(Dependency.BG_LOOPER))); + } + + @VisibleForTesting + AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host, + Handler handler) { + mAutoTracker = autoAddTracker; mContext = context; mHost = host; - mHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER)); + mHandler = handler; if (!mAutoTracker.isAdded(HOTSPOT)) { Dependency.get(HotspotController.class).addCallback(mHotspotCallback); } @@ -76,20 +83,25 @@ public class AutoTileManager { if (!mAutoTracker.isAdded(WORK)) { Dependency.get(ManagedProfileController.class).addCallback(mProfileCallback); } - if (!mAutoTracker.isAdded(NIGHT) - && ColorDisplayController.isAvailable(mContext)) { + && ColorDisplayController.isAvailable(mContext)) { Dependency.get(ColorDisplayController.class).setListener(mColorDisplayCallback); } + if (!mAutoTracker.isAdded(ALARM)) { + Dependency.get(NextAlarmController.class).addCallback(mNextAlarmChangeCallback); + } } public void destroy() { - mColorsSetting.setListening(false); + if (mColorsSetting != null) { + mColorsSetting.setListening(false); + } mAutoTracker.destroy(); Dependency.get(HotspotController.class).removeCallback(mHotspotCallback); Dependency.get(DataSaverController.class).removeCallback(mDataSaverListener); Dependency.get(ManagedProfileController.class).removeCallback(mProfileCallback); Dependency.get(ColorDisplayController.class).setListener(null); + Dependency.get(NextAlarmController.class).removeCallback(mNextAlarmChangeCallback); } private final ManagedProfileController.Callback mProfileCallback = @@ -138,6 +150,19 @@ public class AutoTileManager { } }; + private final NextAlarmChangeCallback mNextAlarmChangeCallback = new NextAlarmChangeCallback() { + @Override + public void onNextAlarmChanged(AlarmClockInfo nextAlarm) { + if (mAutoTracker.isAdded(ALARM)) return; + if (nextAlarm != null) { + mHost.addTile(ALARM); + mAutoTracker.setTileAdded(ALARM); + mHandler.post(() -> Dependency.get(NextAlarmController.class) + .removeCallback(mNextAlarmChangeCallback)); + } + } + }; + @VisibleForTesting final ColorDisplayController.Callback mColorDisplayCallback = new ColorDisplayController.Callback() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java deleted file mode 100644 index c45c5386eda4..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ /dev/null @@ -1,455 +0,0 @@ -/* - * 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.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Resources; -import android.support.v4.util.ArraySet; -import android.util.Log; -import android.util.Pools; -import android.view.View; -import android.view.ViewTreeObserver; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dumpable; -import com.android.systemui.statusbar.ExpandableNotificationRow; -import com.android.systemui.statusbar.NotificationData; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.VisualStabilityManager; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.HashSet; -import java.util.Stack; - -/** - * A implementation of HeadsUpManager for phone and car. - */ -public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, - ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback, - OnHeadsUpChangedListener { - private static final String TAG = "HeadsUpManagerPhone"; - private static final boolean DEBUG = false; - - private final View mStatusBarWindowView; - private final int mStatusBarHeight; - private final NotificationGroupManager mGroupManager; - private final StatusBar mBar; - private final VisualStabilityManager mVisualStabilityManager; - - private boolean mReleaseOnExpandFinish; - private boolean mTrackingHeadsUp; - private HashSet<String> mSwipedOutKeys = new HashSet<>(); - private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>(); - private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed - = new ArraySet<>(); - private boolean mIsExpanded; - private int[] mTmpTwoArray = new int[2]; - private boolean mHeadsUpGoingAway; - private boolean mWaitingOnCollapseWhenGoingAway; - private boolean mIsObserving; - private int mStatusBarState; - - private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() { - private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>(); - - @Override - public HeadsUpEntryPhone acquire() { - if (!mPoolObjects.isEmpty()) { - return mPoolObjects.pop(); - } - return new HeadsUpEntryPhone(); - } - - @Override - public boolean release(@NonNull HeadsUpEntryPhone instance) { - instance.reset(); - mPoolObjects.push(instance); - return true; - } - }; - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Constructor: - - public HeadsUpManagerPhone(@NonNull final Context context, @NonNull View statusBarWindowView, - @NonNull NotificationGroupManager groupManager, @NonNull StatusBar bar, - @NonNull VisualStabilityManager visualStabilityManager) { - super(context); - - mStatusBarWindowView = statusBarWindowView; - mGroupManager = groupManager; - mBar = bar; - mVisualStabilityManager = visualStabilityManager; - - Resources resources = mContext.getResources(); - mStatusBarHeight = resources.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); - - addListener(new OnHeadsUpChangedListener() { - @Override - public void onHeadsUpPinnedModeChanged(boolean hasPinnedNotification) { - if (DEBUG) Log.w(TAG, "onHeadsUpPinnedModeChanged"); - updateTouchableRegionListener(); - } - }); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Public methods: - - /** - * Decides whether a click is invalid for a notification, i.e it has not been shown long enough - * that a user might have consciously clicked on it. - * - * @param key the key of the touched notification - * @return whether the touch is invalid and should be discarded - */ - public boolean shouldSwallowClick(@NonNull String key) { - HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key); - if (entry != null && mClock.currentTimeMillis() < entry.postTime) { - return true; - } - return false; - } - - public void onExpandingFinished() { - if (mReleaseOnExpandFinish) { - releaseAllImmediately(); - mReleaseOnExpandFinish = false; - } else { - for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) { - if (isHeadsUp(entry.key)) { - // Maybe the heads-up was removed already - removeHeadsUpEntry(entry); - } - } - } - mEntriesToRemoveAfterExpand.clear(); - } - - /** - * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry - * from the list even after a Heads Up Notification is gone. - */ - public void setTrackingHeadsUp(boolean trackingHeadsUp) { - mTrackingHeadsUp = trackingHeadsUp; - } - - /** - * Notify that the status bar panel gets expanded or collapsed. - * - * @param isExpanded True to notify expanded, false to notify collapsed. - */ - public void setIsPanelExpanded(boolean isExpanded) { - if (isExpanded != mIsExpanded) { - mIsExpanded = isExpanded; - if (isExpanded) { - // make sure our state is sane - mWaitingOnCollapseWhenGoingAway = false; - mHeadsUpGoingAway = false; - updateTouchableRegionListener(); - } - } - } - - /** - * Set the current state of the statusbar. - */ - public void setStatusBarState(int statusBarState) { - mStatusBarState = statusBarState; - } - - /** - * Set that we are exiting the headsUp pinned mode, but some notifications might still be - * animating out. This is used to keep the touchable regions in a sane state. - */ - public void setHeadsUpGoingAway(boolean headsUpGoingAway) { - if (headsUpGoingAway != mHeadsUpGoingAway) { - mHeadsUpGoingAway = headsUpGoingAway; - if (!headsUpGoingAway) { - waitForStatusBarLayout(); - } - updateTouchableRegionListener(); - } - } - - /** - * Notifies that a remote input textbox in notification gets active or inactive. - * @param entry The entry of the target notification. - * @param remoteInputActive True to notify active, False to notify inactive. - */ - public void setRemoteInputActive( - @NonNull NotificationData.Entry entry, boolean remoteInputActive) { - HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key); - if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) { - headsUpEntry.remoteInputActive = remoteInputActive; - if (remoteInputActive) { - headsUpEntry.removeAutoRemovalCallbacks(); - } else { - headsUpEntry.updateEntry(false /* updatePostTime */); - } - } - } - - @VisibleForTesting - public void removeMinimumDisplayTimeForTesting() { - mMinimumDisplayTime = 1; - mHeadsUpNotificationDecay = 1; - mTouchAcceptanceDelay = 1; - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // HeadsUpManager public methods overrides: - - @Override - public boolean isTrackingHeadsUp() { - return mTrackingHeadsUp; - } - - @Override - public void snooze() { - super.snooze(); - mReleaseOnExpandFinish = true; - } - - /** - * React to the removal of the notification in the heads up. - * - * @return true if the notification was removed and false if it still needs to be kept around - * for a bit since it wasn't shown long enough - */ - @Override - public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) { - if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) { - return super.removeNotification(key, ignoreEarliestRemovalTime); - } else { - HeadsUpEntryPhone entry = getHeadsUpEntryPhone(key); - entry.removeAsSoonAsPossible(); - return false; - } - } - - public void addSwipedOutNotification(@NonNull String key) { - mSwipedOutKeys.add(key); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Dumpable overrides: - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("HeadsUpManagerPhone state:"); - dumpInternal(fd, pw, args); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // ViewTreeObserver.OnComputeInternalInsetsListener overrides: - - /** - * Overridden from TreeObserver. - */ - @Override - public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { - if (mIsExpanded || mBar.isBouncerShowing()) { - // The touchable region is always the full area when expanded - return; - } - if (hasPinnedHeadsUp()) { - ExpandableNotificationRow topEntry = getTopEntry().row; - if (topEntry.isChildInGroup()) { - final ExpandableNotificationRow groupSummary - = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification()); - if (groupSummary != null) { - topEntry = groupSummary; - } - } - topEntry.getLocationOnScreen(mTmpTwoArray); - int minX = mTmpTwoArray[0]; - int maxX = mTmpTwoArray[0] + topEntry.getWidth(); - int maxY = topEntry.getIntrinsicHeight(); - - info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); - info.touchableRegion.set(minX, 0, maxX, maxY); - } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) { - info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); - info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight); - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // VisualStabilityManager.Callback overrides: - - @Override - public void onReorderingAllowed() { - mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false); - for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) { - if (isHeadsUp(entry.key)) { - // Maybe the heads-up was removed already - removeHeadsUpEntry(entry); - } - } - mEntriesToRemoveWhenReorderingAllowed.clear(); - mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // HeadsUpManager utility (protected) methods overrides: - - @Override - protected HeadsUpEntry createHeadsUpEntry() { - return mEntryPool.acquire(); - } - - @Override - protected void releaseHeadsUpEntry(HeadsUpEntry entry) { - mEntryPool.release((HeadsUpEntryPhone) entry); - } - - @Override - protected boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) { - return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded - || super.shouldHeadsUpBecomePinned(entry); - } - - @Override - protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) { - super.dumpInternal(fd, pw, args); - pw.print(" mStatusBarState="); pw.println(mStatusBarState); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Protected utility methods: - - @Nullable - protected HeadsUpEntryPhone getHeadsUpEntryPhone(@NonNull String key) { - return (HeadsUpEntryPhone) getHeadsUpEntry(key); - } - - @Nullable - protected HeadsUpEntryPhone getTopHeadsUpEntryPhone() { - return (HeadsUpEntryPhone) getTopHeadsUpEntry(); - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Private utility methods: - - private boolean wasShownLongEnough(@NonNull String key) { - if (mSwipedOutKeys.contains(key)) { - // We always instantly dismiss views being manually swiped out. - mSwipedOutKeys.remove(key); - return true; - } - - HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(key); - HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone(); - if (headsUpEntry != topEntry) { - return true; - } - return headsUpEntry.wasShownLongEnough(); - } - - /** - * We need to wait on the whole panel to collapse, before we can remove the touchable region - * listener. - */ - private void waitForStatusBarLayout() { - mWaitingOnCollapseWhenGoingAway = true; - mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, - int oldTop, int oldRight, int oldBottom) { - if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) { - mStatusBarWindowView.removeOnLayoutChangeListener(this); - mWaitingOnCollapseWhenGoingAway = false; - updateTouchableRegionListener(); - } - } - }); - } - - private void updateTouchableRegionListener() { - boolean shouldObserve = hasPinnedHeadsUp() || mHeadsUpGoingAway - || mWaitingOnCollapseWhenGoingAway; - if (shouldObserve == mIsObserving) { - return; - } - if (shouldObserve) { - mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this); - mStatusBarWindowView.requestLayout(); - } else { - mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this); - } - mIsObserving = shouldObserve; - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // HeadsUpEntryPhone: - - protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry { - public void setEntry(@NonNull final NotificationData.Entry entry) { - Runnable removeHeadsUpRunnable = () -> { - if (!mVisualStabilityManager.isReorderingAllowed()) { - mEntriesToRemoveWhenReorderingAllowed.add(entry); - mVisualStabilityManager.addReorderingAllowedCallback( - HeadsUpManagerPhone.this); - } else if (!mTrackingHeadsUp) { - removeHeadsUpEntry(entry); - } else { - mEntriesToRemoveAfterExpand.add(entry); - } - }; - - super.setEntry(entry, removeHeadsUpRunnable); - } - - public boolean wasShownLongEnough() { - return earliestRemovaltime < mClock.currentTimeMillis(); - } - - @Override - public void updateEntry(boolean updatePostTime) { - super.updateEntry(updatePostTime); - - if (mEntriesToRemoveAfterExpand.contains(entry)) { - mEntriesToRemoveAfterExpand.remove(entry); - } - if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) { - mEntriesToRemoveWhenReorderingAllowed.remove(entry); - } - } - - @Override - public void expanded(boolean expanded) { - if (this.expanded == expanded) { - return; - } - - this.expanded = expanded; - if (expanded) { - removeAutoRemovalCallbacks(); - } else { - updateEntry(false /* updatePostTime */); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java index 2bfdefe39017..c85571c1895d 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,7 @@ import android.view.ViewConfiguration; import com.android.systemui.Gefingerpoken; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; /** @@ -31,7 +31,7 @@ import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; */ public class HeadsUpTouchHelper implements Gefingerpoken { - private HeadsUpManagerPhone mHeadsUpManager; + private HeadsUpManager mHeadsUpManager; private NotificationStackScrollLayout mStackScroller; private int mTrackingPointer; private float mTouchSlop; @@ -43,7 +43,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken { private NotificationPanelView mPanel; private ExpandableNotificationRow mPickedChild; - public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager, + public HeadsUpTouchHelper(HeadsUpManager headsUpManager, NotificationStackScrollLayout stackScroller, NotificationPanelView notificationPanelView) { mHeadsUpManager = headsUpManager; 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 65c45a3120c1..1239a9ea0240 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -22,11 +22,13 @@ import static android.app.StatusBarManager.windowStateToString; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE; import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions; +import static com.android.systemui.OverviewProxyService.OverviewProxyListener; import android.accessibilityservice.AccessibilityServiceInfo; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; +import android.annotation.IdRes; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerNative; @@ -69,6 +71,7 @@ import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; +import android.widget.Button; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -153,6 +156,18 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private Animator mRotateShowAnimator; private Animator mRotateHideAnimator; + private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() { + @Override + public void onConnectionChanged(boolean isConnected) { + mNavigationBarView.onOverviewProxyConnectionChanged(isConnected); + updateScreenPinningGestures(); + } + + @Override + public void onRecentsAnimationStarted() { + mNavigationBarView.setRecentsAnimationStarted(true); + } + }; // ----- Fragment Lifecycle Callbacks ----- @@ -239,12 +254,14 @@ public class NavigationBarFragment extends Fragment implements Callbacks { filter.addAction(Intent.ACTION_SCREEN_ON); getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); notifyNavigationBarScreenOn(); + mOverviewProxyService.addCallback(mOverviewProxyListener); } @Override public void onDestroyView() { super.onDestroyView(); mNavigationBarView.getLightTransitionsController().destroy(getContext()); + mOverviewProxyService.removeCallback(mOverviewProxyListener); getContext().unregisterReceiver(mBroadcastReceiver); } @@ -514,6 +531,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { if (masked != mDisabledFlags1) { mDisabledFlags1 = masked; if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1); + updateScreenPinningGestures(); } } @@ -528,7 +546,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private boolean shouldDisableNavbarGestures() { return !mStatusBar.isDeviceProvisioned() || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0 - || mOverviewProxyService.getProxy() != null; + || mNavigationBarView.getRecentsButton().getVisibility() != View.VISIBLE; } private void repositionNavigationBar() { @@ -540,6 +558,24 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ((View) mNavigationBarView.getParent()).getLayoutParams()); } + private void updateScreenPinningGestures() { + if (mNavigationBarView == null) { + return; + } + + // Change the cancel pin gesture to home and back if recents button is invisible + boolean recentsVisible = mNavigationBarView.isRecentsButtonVisible(); + ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); + ButtonDispatcher backButton = mNavigationBarView.getBackButton(); + if (recentsVisible) { + homeButton.setOnLongClickListener(this::onHomeLongClick); + backButton.setOnLongClickListener(this::onLongPressBackRecents); + } else { + homeButton.setOnLongClickListener(this::onLongPressBackHome); + backButton.setOnLongClickListener(this::onLongPressBackHome); + } + } + private void notifyNavigationBarScreenOn() { mNavigationBarView.notifyScreenOn(); } @@ -555,11 +591,9 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ButtonDispatcher backButton = mNavigationBarView.getBackButton(); backButton.setLongClickable(true); - backButton.setOnLongClickListener(this::onLongPressBackRecents); ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); homeButton.setOnTouchListener(this::onHomeTouch); - homeButton.setOnLongClickListener(this::onHomeLongClick); ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); accessibilityButton.setOnClickListener(this::onAccessibilityClick); @@ -569,6 +603,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton(); rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick); rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover); + updateScreenPinningGestures(); } private boolean onHomeTouch(View v, MotionEvent event) { @@ -649,20 +684,29 @@ public class NavigationBarFragment extends Fragment implements Callbacks { mCommandQueue.toggleRecentApps(); } + private boolean onLongPressBackHome(View v) { + return onLongPressNavigationButtons(v, R.id.back, R.id.home); + } + + private boolean onLongPressBackRecents(View v) { + return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps); + } + /** - * This handles long-press of both back and recents. They are - * handled together to capture them both being long-pressed + * This handles long-press of both back and recents/home. Back is the common button with + * combination of recents if it is visible or home if recents is invisible. + * They are handled together to capture them both being long-pressed * at the same time to exit screen pinning (lock task). * - * When accessibility mode is on, only a long-press from recents + * When accessibility mode is on, only a long-press from recents/home * is required to exit. * * In all other circumstances we try to pass through long-press events * for Back, so that apps can still use it. Which can be from two things. * 1) Not currently in screen pinning (lock task). - * 2) Back is long-pressed without recents. + * 2) Back is long-pressed without recents/home. */ - private boolean onLongPressBackRecents(View v) { + private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) { try { boolean sendBackLongPress = false; IActivityManager activityManager = ActivityManagerNative.getDefault(); @@ -670,6 +714,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { boolean inLockTaskMode = activityManager.isInLockTaskMode(); if (inLockTaskMode && !touchExplorationEnabled) { long time = System.currentTimeMillis(); + // If we recently long-pressed the other button then they were // long-pressed 'together' if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) { @@ -677,26 +722,32 @@ public class NavigationBarFragment extends Fragment implements Callbacks { // When exiting refresh disabled flags. mNavigationBarView.setDisabledFlags(mDisabledFlags1, true); return true; - } else if ((v.getId() == R.id.back) - && !mNavigationBarView.getRecentsButton().getCurrentView().isPressed()) { - // If we aren't pressing recents right now then they presses - // won't be together, so send the standard long-press action. - sendBackLongPress = true; + } else if (v.getId() == btnId1) { + ButtonDispatcher button = btnId2 == R.id.recent_apps + ? mNavigationBarView.getRecentsButton() + : mNavigationBarView.getHomeButton(); + if (!button.getCurrentView().isPressed()) { + // If we aren't pressing recents/home right now then they presses + // won't be together, so send the standard long-press action. + sendBackLongPress = true; + } } mLastLockToAppLongPress = time; } else { // If this is back still need to handle sending the long-press event. - if (v.getId() == R.id.back) { + if (v.getId() == btnId1) { sendBackLongPress = true; } else if (touchExplorationEnabled && inLockTaskMode) { - // When in accessibility mode a long press that is recents (not back) + // When in accessibility mode a long press that is recents/home (not back) // should stop lock task. activityManager.stopSystemLockTaskMode(); // When exiting refresh disabled flags. mNavigationBarView.setDisabledFlags(mDisabledFlags1, true); return true; - } else if (v.getId() == R.id.recent_apps) { - return onLongPressRecents(); + } else if (v.getId() == btnId2) { + return btnId2 == R.id.recent_apps + ? onLongPressRecents() + : onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView()); } } if (sendBackLongPress) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index de6ecac5b00e..c37dd55cc6ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -207,23 +207,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } } - private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() { - @Override - public void onConnectionChanged(boolean isConnected) { - updateSlippery(); - setDisabledFlags(mDisabledFlags, true); - setUpSwipeUpOnboarding(isConnected); - } - - @Override - public void onRecentsAnimationStarted() { - mRecentsAnimationStarted = true; - if (mSwipeUpOnboarding != null) { - mSwipeUpOnboarding.onRecentsAnimationStarted(); - } - } - }; - public NavigationBarView(Context context, AttributeSet attrs) { super(context, attrs); @@ -280,6 +263,19 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav notifyVerticalChangedListener(mVertical); } + public void setRecentsAnimationStarted(boolean started) { + mRecentsAnimationStarted = started; + if (mSwipeUpOnboarding != null) { + mSwipeUpOnboarding.onRecentsAnimationStarted(); + } + } + + public void onConnectionChanged(boolean isConnected) { + updateSlippery(); + setDisabledFlags(mDisabledFlags, true); + setUpSwipeUpOnboarding(isConnected); + } + @Override public boolean onTouchEvent(MotionEvent event) { if (mGestureHelper.onTouchEvent(event)) { @@ -304,7 +300,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } } } - return mRecentsAnimationStarted || mGestureHelper.onInterceptTouchEvent(event); + return mGestureHelper.onInterceptTouchEvent(event) || mRecentsAnimationStarted; } public void abortCurrentGesture() { @@ -353,6 +349,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav return mButtonDispatchers; } + public boolean isRecentsButtonVisible() { + return getRecentsButton().getVisibility() == View.VISIBLE; + } + private void updateCarModeIcons(Context ctx) { mBackCarModeIcon = getDrawable(ctx, R.drawable.ic_sysbar_back_carmode, R.drawable.ic_sysbar_back_carmode); @@ -613,6 +613,9 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav final ViewGroup navbarView = ((ViewGroup) getParent()); final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView .getLayoutParams(); + if (lp == null) { + return; + } if (slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) == 0) { lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY; changed = true; @@ -676,6 +679,12 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } } + public void onOverviewProxyConnectionChanged(boolean isConnected) { + setSlippery(!isConnected); + setDisabledFlags(mDisabledFlags, true); + setUpSwipeUpOnboarding(isConnected); + } + @Override protected void onDraw(Canvas canvas) { mGestureHelper.onDraw(canvas); @@ -873,7 +882,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav onPluginDisconnected(null); // Create default gesture helper Dependency.get(PluginManager.class).addPluginListener(this, NavGesture.class, false /* Only one */); - mOverviewProxyService.addCallback(mOverviewProxyListener); setUpSwipeUpOnboarding(mOverviewProxyService.getProxy() != null); } @@ -884,7 +892,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav if (mGestureHelper != null) { mGestureHelper.destroy(); } - mOverviewProxyService.removeCallback(mOverviewProxyListener); setUpSwipeUpOnboarding(false); } 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 91cae0af6b13..5cf4c4c70974 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -120,7 +120,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private boolean mDisallowNextAnimation; private boolean mAnimationsEnabled = true; private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons; - private int mDarkOffsetX; // Keep track of the last visible icon so collapsed container can report on its location private IconState mLastVisibleIconState; @@ -378,14 +377,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { iconState.xTranslation = getWidth() - iconState.xTranslation - view.getWidth(); } } - - if (mDark && mDarkOffsetX != 0) { - for (int i = 0; i < childCount; i++) { - View view = getChildAt(i); - IconState iconState = mIconStates.get(view); - iconState.xTranslation += mDarkOffsetX; - } - } } private float getLayoutEnd() { @@ -534,10 +525,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mAnimationsEnabled = enabled; } - public void setDarkOffsetX(int offsetX) { - mDarkOffsetX = offsetX; - } - public void setReplacingIcons(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons) { mReplacingIcons = replacingIcons; } 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 2111d2ef5d87..cd2e77ae2591 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -47,6 +47,7 @@ import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.keyguard.KeyguardStatusView; @@ -67,15 +68,14 @@ import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; -import com.android.systemui.statusbar.notification.NotificationUtils; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.StackStateAnimator; -import java.util.Collection; import java.util.List; +import java.util.Collection; public class NotificationPanelView extends PanelView implements ExpandableView.OnHeightChangedListener, @@ -482,7 +482,7 @@ public class NotificationPanelView extends PanelView implements mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment; } mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding); - mNotificationStackScroller.setDarkShelfOffsetX(mClockPositionResult.clockX); + mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX); mKeyguardBottomArea.setBurnInXOffset(mClockPositionResult.clockX); requestScrollerTopPaddingUpdate(animate); } @@ -1571,7 +1571,7 @@ public class NotificationPanelView extends PanelView implements private void updatePanelExpanded() { boolean isExpanded = !isFullyCollapsed(); if (mPanelExpanded != isExpanded) { - mHeadsUpManager.setIsPanelExpanded(isExpanded); + mHeadsUpManager.setIsExpanded(isExpanded); mStatusBar.setPanelExpanded(isExpanded); mPanelExpanded = isExpanded; } @@ -2338,7 +2338,7 @@ public class NotificationPanelView extends PanelView implements } @Override - public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { + public void setHeadsUpManager(HeadsUpManager headsUpManager) { super.setHeadsUpManager(headsUpManager); mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller, this); @@ -2630,8 +2630,8 @@ public class NotificationPanelView extends PanelView implements } } - public void setPulsing(boolean pulsing) { - mKeyguardStatusView.setPulsing(pulsing); + public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) { + mKeyguardStatusView.setPulsing(pulsing != null); positionClockAndNotifications(); mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1] + mKeyguardStatusView.getClockBottom()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 6daabede7f32..2b7e4747a837 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -50,7 +50,7 @@ import com.android.systemui.classifier.FalsingManager; import com.android.systemui.doze.DozeLog; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.policy.HeadsUpManager; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -75,7 +75,7 @@ public abstract class PanelView extends FrameLayout { } protected StatusBar mStatusBar; - protected HeadsUpManagerPhone mHeadsUpManager; + protected HeadsUpManager mHeadsUpManager; private float mPeekHeight; private float mHintDistance; @@ -1252,7 +1252,7 @@ public abstract class PanelView extends FrameLayout { */ protected abstract int getClearAllHeight(); - public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { + public void setHeadsUpManager(HeadsUpManager headsUpManager) { mHeadsUpManager = headsUpManager; } diff --git a/services/core/java/com/android/server/am/LockTaskNotify.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java index 1dcb0addc18b..0d07ad9cdf2e 100644 --- a/services/core/java/com/android/server/am/LockTaskNotify.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.am; +package com.android.systemui.statusbar.phone; import android.content.Context; import android.os.SystemClock; @@ -22,36 +22,37 @@ import android.util.Slog; import android.view.WindowManager; import android.widget.Toast; -import com.android.internal.R; +import com.android.systemui.R; +import com.android.systemui.SysUIToast; /** * Helper to manage showing/hiding a image to notify them that they are entering or exiting screen * pinning mode. All exposed methods should be called from a handler thread. */ -public class LockTaskNotify { - private static final String TAG = "LockTaskNotify"; +public class ScreenPinningNotify { + private static final String TAG = "ScreenPinningNotify"; private static final long SHOW_TOAST_MINIMUM_INTERVAL = 1000; private final Context mContext; private Toast mLastToast; private long mLastShowToastTime; - public LockTaskNotify(Context context) { + public ScreenPinningNotify(Context context) { mContext = context; } /** Show "Screen pinned" toast. */ void showPinningStartToast() { - makeAllUserToastAndShow(R.string.lock_to_app_start); + makeAllUserToastAndShow(R.string.screen_pinning_start); } /** Show "Screen unpinned" toast. */ void showPinningExitToast() { - makeAllUserToastAndShow(R.string.lock_to_app_exit); + makeAllUserToastAndShow(R.string.screen_pinning_exit); } /** Show a toast that describes the gesture the user should use to escape pinned mode. */ - void showEscapeToast() { + void showEscapeToast(boolean isRecentsButtonVisible) { long showToastTime = SystemClock.elapsedRealtime(); if ((showToastTime - mLastShowToastTime) < SHOW_TOAST_MINIMUM_INTERVAL) { Slog.i(TAG, "Ignore toast since it is requested in very short interval."); @@ -60,14 +61,14 @@ public class LockTaskNotify { if (mLastToast != null) { mLastToast.cancel(); } - mLastToast = makeAllUserToastAndShow(R.string.lock_to_app_toast); + mLastToast = makeAllUserToastAndShow(isRecentsButtonVisible + ? R.string.screen_pinning_toast + : R.string.screen_pinning_toast_recents_invisible); mLastShowToastTime = showToastTime; } private Toast makeAllUserToastAndShow(int resId) { - Toast toast = Toast.makeText(mContext, resId, Toast.LENGTH_LONG); - toast.getWindowParams().privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + Toast toast = SysUIToast.makeText(mContext, resId, Toast.LENGTH_LONG); toast.show(); return toast; } 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 116e3f93bf8f..1bf719ae68af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -208,7 +208,6 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.VisualStabilityManager; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; @@ -220,7 +219,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; @@ -406,6 +404,13 @@ public class StatusBar extends SystemUI implements DemoMode, protected NotificationEntryManager mEntryManager; protected NotificationViewHierarchyManager mViewHierarchyManager; + /** + * Helper that is responsible for showing the right toast when a disallowed activity operation + * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in + * fully locked mode we only show that unlocking is blocked. + */ + private ScreenPinningNotify mScreenPinningNotify; + // for disabling the status bar private int mDisabled1 = 0; private int mDisabled2 = 0; @@ -804,14 +809,15 @@ public class StatusBar extends SystemUI implements DemoMode, .commit(); mIconController = Dependency.get(StatusBarIconController.class); - mHeadsUpManager = new HeadsUpManagerPhone(context, mStatusBarWindow, mGroupManager, this, - mVisualStabilityManager); + mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow, mGroupManager); + mHeadsUpManager.setBar(this); mHeadsUpManager.addListener(this); mHeadsUpManager.addListener(mNotificationPanel); mHeadsUpManager.addListener(mGroupManager); mHeadsUpManager.addListener(mVisualStabilityManager); mNotificationPanel.setHeadsUpManager(mHeadsUpManager); mGroupManager.setHeadsUpManager(mHeadsUpManager); + mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager); putComponent(HeadsUpManager.class, mHeadsUpManager); mEntryManager.setUpWithPresenter(this, mStackScroller, this, mHeadsUpManager); @@ -831,7 +837,7 @@ public class StatusBar extends SystemUI implements DemoMode, } catch (RemoteException ex) { // no window manager? good luck with that } - + mScreenPinningNotify = new ScreenPinningNotify(mContext); mStackScroller.setLongPressListener(mEntryManager.getNotificationLongClicker()); mStackScroller.setStatusBar(this); mStackScroller.setGroupManager(mGroupManager); @@ -1342,8 +1348,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onPerformRemoveNotification(StatusBarNotification n) { - if (mStackScroller.hasPulsingNotifications() && - !mHeadsUpManager.hasHeadsUpNotifications()) { + if (mStackScroller.hasPulsingNotifications() && mHeadsUpManager.getAllEntries().isEmpty()) { // We were showing a pulse for a notification, but no notifications are pulsing anymore. // Finish the pulse. mDozeScrimController.pulseOutNow(); @@ -2092,8 +2097,9 @@ public class StatusBar extends SystemUI implements DemoMode, } public void maybeEscalateHeadsUp() { - mHeadsUpManager.getAllEntries().forEach(entry -> { - final StatusBarNotification sbn = entry.notification; + Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries(); + for (HeadsUpManager.HeadsUpEntry entry : entries) { + final StatusBarNotification sbn = entry.entry.notification; final Notification notification = sbn.getNotification(); if (notification.fullScreenIntent != null) { if (DEBUG) { @@ -2103,11 +2109,11 @@ public class StatusBar extends SystemUI implements DemoMode, EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION, sbn.getKey()); notification.fullScreenIntent.send(); - entry.notifyFullScreenIntentLaunched(); + entry.entry.notifyFullScreenIntentLaunched(); } catch (PendingIntent.CanceledException e) { } } - }); + } mHeadsUpManager.releaseAllImmediately(); } @@ -2142,6 +2148,21 @@ public class StatusBar extends SystemUI implements DemoMode, } + @Override + public void showPinningEnterExitToast(boolean entering) { + if (entering) { + mScreenPinningNotify.showPinningStartToast(); + } else { + mScreenPinningNotify.showPinningExitToast(); + } + } + + @Override + public void showPinningEscapeToast() { + mScreenPinningNotify.showEscapeToast(getNavigationBarView() == null + || getNavigationBarView().isRecentsButtonVisible()); + } + boolean panelsEnabled() { return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0 && (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0 @@ -4637,22 +4658,24 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onPulseStarted() { callback.onPulseStarted(); - if (mHeadsUpManager.hasHeadsUpNotifications()) { + Collection<HeadsUpManager.HeadsUpEntry> pulsingEntries = + mHeadsUpManager.getAllEntries(); + if (!pulsingEntries.isEmpty()) { // Only pulse the stack scroller if there's actually something to show. // Otherwise just show the always-on screen. - setPulsing(true); + setPulsing(pulsingEntries); } } @Override public void onPulseFinished() { callback.onPulseFinished(); - setPulsing(false); + setPulsing(null); } - private void setPulsing(boolean pulsing) { + private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) { mNotificationPanel.setPulsing(pulsing); - mVisualStabilityManager.setPulsing(pulsing); + mVisualStabilityManager.setPulsing(pulsing != null); mIgnoreTouchWhilePulsing = false; } }, reason); @@ -4800,7 +4823,7 @@ public class StatusBar extends SystemUI implements DemoMode, // for heads up notifications - protected HeadsUpManagerPhone mHeadsUpManager; + protected HeadsUpManager mHeadsUpManager; private AboveShelfObserver mAboveShelfObserver; @@ -4903,7 +4926,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Release the HUN notification to the shade. if (isPresenterFullyCollapsed()) { - HeadsUpUtil.setIsClickedHeadsUpNotification(row, true); + HeadsUpManager.setIsClickedNotification(row, true); } // // In most cases, when FLAG_AUTO_CANCEL is set, the notification will 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 a2b896dc015b..53dfb244c776 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -16,68 +16,118 @@ package com.android.systemui.statusbar.policy; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; -import android.os.SystemClock; import android.os.Handler; import android.os.Looper; -import android.util.ArrayMap; +import android.os.SystemClock; import android.provider.Settings; +import android.support.v4.util.ArraySet; +import android.util.ArrayMap; import android.util.Log; +import android.util.Pools; +import android.view.View; +import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationData; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.StatusBar; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.Iterator; -import java.util.stream.Stream; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Stack; /** * A manager which handles heads up notifications which is a special mode where * they simply peek from the top of the screen. */ -public class HeadsUpManager { +public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener, + VisualStabilityManager.Callback { private static final String TAG = "HeadsUpManager"; private static final boolean DEBUG = false; private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms"; + private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag; - protected final Clock mClock = new Clock(); - protected final Context mContext; - protected final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>(); - protected final Handler mHandler = new Handler(Looper.getMainLooper()); - - protected int mHeadsUpNotificationDecay; - protected int mMinimumDisplayTime; - protected int mTouchAcceptanceDelay; - protected int mSnoozeLengthMs; - protected boolean mHasPinnedNotification; - protected int mUser; + private final int mHeadsUpNotificationDecay; + private final int mMinimumDisplayTime; - private final HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>(); + private final int mTouchAcceptanceDelay; private final ArrayMap<String, Long> mSnoozedPackages; - private final ContentObserver mSettingsObserver; + private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>(); + private final int mDefaultSnoozeLengthMs; + private final Handler mHandler = new Handler(Looper.getMainLooper()); + private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() { + + private Stack<HeadsUpEntry> mPoolObjects = new Stack<>(); - public HeadsUpManager(@NonNull final Context context) { + @Override + public HeadsUpEntry acquire() { + if (!mPoolObjects.isEmpty()) { + return mPoolObjects.pop(); + } + return new HeadsUpEntry(); + } + + @Override + public boolean release(HeadsUpEntry instance) { + instance.reset(); + mPoolObjects.push(instance); + return true; + } + }; + + private final View mStatusBarWindowView; + private final int mStatusBarHeight; + private final Context mContext; + private final NotificationGroupManager mGroupManager; + private StatusBar mBar; + private int mSnoozeLengthMs; + private ContentObserver mSettingsObserver; + private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>(); + private HashSet<String> mSwipedOutKeys = new HashSet<>(); + private int mUser; + private Clock mClock; + private boolean mReleaseOnExpandFinish; + private boolean mTrackingHeadsUp; + private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>(); + private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed + = new ArraySet<>(); + private boolean mIsExpanded; + private boolean mHasPinnedNotification; + private int[] mTmpTwoArray = new int[2]; + private boolean mHeadsUpGoingAway; + private boolean mWaitingOnCollapseWhenGoingAway; + private boolean mIsObserving; + private boolean mRemoteInputActive; + private float mExpandedHeight; + private VisualStabilityManager mVisualStabilityManager; + private int mStatusBarState; + + public HeadsUpManager(final Context context, View statusBarWindowView, + NotificationGroupManager groupManager) { mContext = context; - Resources resources = context.getResources(); - mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time); - mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay); + Resources resources = mContext.getResources(); mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay); mSnoozedPackages = new ArrayMap<>(); - int defaultSnoozeLengthMs = - resources.getInteger(R.integer.heads_up_default_snooze_length_ms); + mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms); + mSnoozeLengthMs = mDefaultSnoozeLengthMs; + mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time); + mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay); + mClock = new Clock(); mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(), - SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs); + SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs); mSettingsObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { @@ -92,26 +142,47 @@ public class HeadsUpManager { context.getContentResolver().registerContentObserver( Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false, mSettingsObserver); + mStatusBarWindowView = statusBarWindowView; + mGroupManager = groupManager; + mStatusBarHeight = resources.getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height); } - /** - * Adds an OnHeadUpChangedListener to observe events. - */ - public void addListener(@NonNull OnHeadsUpChangedListener listener) { + private void updateTouchableRegionListener() { + boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway + || mWaitingOnCollapseWhenGoingAway; + if (shouldObserve == mIsObserving) { + return; + } + if (shouldObserve) { + mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this); + mStatusBarWindowView.requestLayout(); + } else { + mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this); + } + mIsObserving = shouldObserve; + } + + public void setBar(StatusBar bar) { + mBar = bar; + } + + public void addListener(OnHeadsUpChangedListener listener) { mListeners.add(listener); } - /** - * Removes the OnHeadUpChangedListener from the observer list. - */ - public void removeListener(@NonNull OnHeadsUpChangedListener listener) { + public void removeListener(OnHeadsUpChangedListener listener) { mListeners.remove(listener); } + public StatusBar getBar() { + return mBar; + } + /** * Called when posting a new notification to the heads up. */ - public void showNotification(@NonNull NotificationData.Entry headsUp) { + public void showNotification(NotificationData.Entry headsUp) { if (DEBUG) Log.v(TAG, "showNotification"); addHeadsUpEntry(headsUp); updateNotification(headsUp, true); @@ -121,7 +192,7 @@ public class HeadsUpManager { /** * Called when updating or posting a notification to the heads up. */ - public void updateNotification(@NonNull NotificationData.Entry headsUp, boolean alert) { + public void updateNotification(NotificationData.Entry headsUp, boolean alert) { if (DEBUG) Log.v(TAG, "updateNotification"); headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); @@ -133,13 +204,14 @@ public class HeadsUpManager { // with the groupmanager return; } - headsUpEntry.updateEntry(true /* updatePostTime */); + headsUpEntry.updateEntry(); setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp)); } } - private void addHeadsUpEntry(@NonNull NotificationData.Entry entry) { - HeadsUpEntry headsUpEntry = createHeadsUpEntry(); + private void addHeadsUpEntry(NotificationData.Entry entry) { + HeadsUpEntry headsUpEntry = mEntryPool.acquire(); + // This will also add the entry to the sortedList headsUpEntry.setEntry(entry); mHeadsUpEntries.put(entry.key, headsUpEntry); @@ -151,17 +223,16 @@ public class HeadsUpManager { entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); } - protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationData.Entry entry) { - return hasFullScreenIntent(entry); + private boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) { + return mStatusBarState != StatusBarState.KEYGUARD + && !mIsExpanded || hasFullScreenIntent(entry); } - protected boolean hasFullScreenIntent(@NonNull NotificationData.Entry entry) { + private boolean hasFullScreenIntent(NotificationData.Entry entry) { return entry.notification.getNotification().fullScreenIntent != null; } - protected void setEntryPinned( - @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) { - if (DEBUG) Log.v(TAG, "setEntryPinned: " + isPinned); + private void setEntryPinned(HeadsUpEntry headsUpEntry, boolean isPinned) { ExpandableNotificationRow row = headsUpEntry.entry.row; if (row.isPinned() != isPinned) { row.setPinned(isPinned); @@ -176,35 +247,33 @@ public class HeadsUpManager { } } - protected void removeHeadsUpEntry(NotificationData.Entry entry) { + private void removeHeadsUpEntry(NotificationData.Entry entry) { HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key); - onHeadsUpEntryRemoved(remove); - releaseHeadsUpEntry(remove); - } - - protected void onHeadsUpEntryRemoved(HeadsUpEntry remove) { - NotificationData.Entry entry = remove.entry; entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); entry.row.setHeadsUp(false); setEntryPinned(remove, false /* isPinned */); for (OnHeadsUpChangedListener listener : mListeners) { listener.onHeadsUpStateChanged(entry, false); } + mEntryPool.release(remove); + } + + public void removeAllHeadsUpEntries() { + for (String key : mHeadsUpEntries.keySet()) { + removeHeadsUpEntry(mHeadsUpEntries.get(key).entry); + } } - protected void updatePinnedMode() { + private void updatePinnedMode() { boolean hasPinnedNotification = hasPinnedNotificationInternal(); if (hasPinnedNotification == mHasPinnedNotification) { return; } - if (DEBUG) { - Log.v(TAG, "Pinned mode changed: " + mHasPinnedNotification + " -> " + - hasPinnedNotification); - } mHasPinnedNotification = hasPinnedNotification; if (mHasPinnedNotification) { MetricsLogger.count(mContext, "note_peek", 1); } + updateTouchableRegionListener(); for (OnHeadsUpChangedListener listener : mListeners) { listener.onHeadsUpPinnedModeChanged(hasPinnedNotification); } @@ -216,36 +285,47 @@ public class HeadsUpManager { * @return true if the notification was removed and false if it still needs to be kept around * for a bit since it wasn't shown long enough */ - public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) { - if (DEBUG) Log.v(TAG, "removeNotification"); - releaseImmediately(key); - return true; + public boolean removeNotification(String key, boolean ignoreEarliestRemovalTime) { + if (DEBUG) Log.v(TAG, "remove"); + if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) { + releaseImmediately(key); + return true; + } else { + getHeadsUpEntry(key).removeAsSoonAsPossible(); + return false; + } + } + + private boolean wasShownLongEnough(String key) { + HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); + HeadsUpEntry topEntry = getTopEntry(); + if (mSwipedOutKeys.contains(key)) { + // We always instantly dismiss views being manually swiped out. + mSwipedOutKeys.remove(key); + return true; + } + if (headsUpEntry != topEntry) { + return true; + } + return headsUpEntry.wasShownLongEnough(); } - /** - * Returns if the given notification is in the Heads Up Notification list or not. - */ public boolean isHeadsUp(String key) { return mHeadsUpEntries.containsKey(key); } /** - * Pushes any current Heads Up notification down into the shade. + * Push any current Heads Up notification down into the shade. */ public void releaseAllImmediately() { if (DEBUG) Log.v(TAG, "releaseAllImmediately"); - Iterator<HeadsUpEntry> iterator = mHeadsUpEntries.values().iterator(); - while (iterator.hasNext()) { - HeadsUpEntry entry = iterator.next(); - iterator.remove(); - onHeadsUpEntryRemoved(entry); + ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet()); + for (String key : keys) { + releaseImmediately(key); } } - /** - * Pushes the given Heads Up notification down into the shade. - */ - public void releaseImmediately(@NonNull String key) { + public void releaseImmediately(String key) { HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); if (headsUpEntry == null) { return; @@ -254,14 +334,11 @@ public class HeadsUpManager { removeHeadsUpEntry(shadeEntry); } - /** - * Returns if the given notification is snoozed or not. - */ - public boolean isSnoozed(@NonNull String packageName) { + public boolean isSnoozed(String packageName) { final String key = snoozeKey(packageName, mUser); Long snoozedUntil = mSnoozedPackages.get(key); if (snoozedUntil != null) { - if (snoozedUntil > mClock.currentTimeMillis()) { + if (snoozedUntil > SystemClock.elapsedRealtime()) { if (DEBUG) Log.v(TAG, key + " snoozed"); return true; } @@ -270,61 +347,33 @@ public class HeadsUpManager { return false; } - /** - * Snoozes all current Heads Up Notifications. - */ public void snooze() { for (String key : mHeadsUpEntries.keySet()) { HeadsUpEntry entry = mHeadsUpEntries.get(key); String packageName = entry.entry.notification.getPackageName(); mSnoozedPackages.put(snoozeKey(packageName, mUser), - mClock.currentTimeMillis() + mSnoozeLengthMs); + SystemClock.elapsedRealtime() + mSnoozeLengthMs); } + mReleaseOnExpandFinish = true; } - private static String snoozeKey(@NonNull String packageName, int user) { + private static String snoozeKey(String packageName, int user) { return user + "," + packageName; } - protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) { + private HeadsUpEntry getHeadsUpEntry(String key) { return mHeadsUpEntries.get(key); } - /** - * Returns the entry of given Heads Up Notification. - * - * @param key Key of heads up notification - */ - public NotificationData.Entry getEntry(@NonNull String key) { - HeadsUpEntry entry = mHeadsUpEntries.get(key); - return entry != null ? entry.entry : null; - } - - /** - * Returns the stream of all current Heads Up Notifications. - */ - @NonNull - public Stream<NotificationData.Entry> getAllEntries() { - return mHeadsUpEntries.values().stream().map(headsUpEntry -> headsUpEntry.entry); - } - - /** - * Returns the top Heads Up Notification, which appeares to show at first. - */ - @Nullable - public NotificationData.Entry getTopEntry() { - HeadsUpEntry topEntry = getTopHeadsUpEntry(); - return (topEntry != null) ? topEntry.entry : null; + public NotificationData.Entry getEntry(String key) { + return mHeadsUpEntries.get(key).entry; } - /** - * Returns if any heads up notification is available or not. - */ - public boolean hasHeadsUpNotifications() { - return !mHeadsUpEntries.isEmpty(); + public Collection<HeadsUpEntry> getAllEntries() { + return mHeadsUpEntries.values(); } - protected HeadsUpEntry getTopHeadsUpEntry() { + public HeadsUpEntry getTopEntry() { if (mHeadsUpEntries.isEmpty()) { return null; } @@ -338,21 +387,56 @@ public class HeadsUpManager { } /** - * Sets the current user. + * Decides whether a click is invalid for a notification, i.e it has not been shown long enough + * that a user might have consciously clicked on it. + * + * @param key the key of the touched notification + * @return whether the touch is invalid and should be discarded */ + public boolean shouldSwallowClick(String key) { + HeadsUpEntry entry = mHeadsUpEntries.get(key); + if (entry != null && mClock.currentTimeMillis() < entry.postTime) { + return true; + } + return false; + } + + public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { + if (mIsExpanded || mBar.isBouncerShowing()) { + // The touchable region is always the full area when expanded + return; + } + if (mHasPinnedNotification) { + ExpandableNotificationRow topEntry = getTopEntry().entry.row; + if (topEntry.isChildInGroup()) { + final ExpandableNotificationRow groupSummary + = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification()); + if (groupSummary != null) { + topEntry = groupSummary; + } + } + topEntry.getLocationOnScreen(mTmpTwoArray); + int minX = mTmpTwoArray[0]; + int maxX = mTmpTwoArray[0] + topEntry.getWidth(); + int maxY = topEntry.getIntrinsicHeight(); + + info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); + info.touchableRegion.set(minX, 0, maxX, maxY); + } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) { + info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); + info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight); + } + } + public void setUser(int user) { mUser = user; } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("HeadsUpManager state:"); - dumpInternal(fd, pw, args); - } - - protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) { pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay); pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs); - pw.print(" now="); pw.println(mClock.currentTimeMillis()); + pw.print(" now="); pw.println(SystemClock.elapsedRealtime()); pw.print(" mUser="); pw.println(mUser); for (HeadsUpEntry entry: mHeadsUpEntries.values()) { pw.print(" HeadsUpEntry="); pw.println(entry.entry); @@ -365,9 +449,6 @@ public class HeadsUpManager { } } - /** - * Returns if there are any pinned Heads Up Notifications or not. - */ public boolean hasPinnedHeadsUp() { return mHasPinnedNotification; } @@ -383,8 +464,14 @@ public class HeadsUpManager { } /** - * Unpins all pinned Heads Up Notifications. + * Notifies that a notification was swiped out and will be removed. + * + * @param key the notification key */ + public void addSwipedOutNotification(String key) { + mSwipedOutKeys.add(key); + } + public void unpinAll() { for (String key : mHeadsUpEntries.keySet()) { HeadsUpEntry entry = mHeadsUpEntries.get(key); @@ -394,13 +481,60 @@ public class HeadsUpManager { } } + public void onExpandingFinished() { + if (mReleaseOnExpandFinish) { + releaseAllImmediately(); + mReleaseOnExpandFinish = false; + } else { + for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) { + if (isHeadsUp(entry.key)) { + // Maybe the heads-up was removed already + removeHeadsUpEntry(entry); + } + } + } + mEntriesToRemoveAfterExpand.clear(); + } + + public void setTrackingHeadsUp(boolean trackingHeadsUp) { + mTrackingHeadsUp = trackingHeadsUp; + } + + public boolean isTrackingHeadsUp() { + return mTrackingHeadsUp; + } + + public void setIsExpanded(boolean isExpanded) { + if (isExpanded != mIsExpanded) { + mIsExpanded = isExpanded; + if (isExpanded) { + // make sure our state is sane + mWaitingOnCollapseWhenGoingAway = false; + mHeadsUpGoingAway = false; + updateTouchableRegionListener(); + } + } + } + /** - * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as - * well. + * @return the height of the top heads up notification when pinned. This is different from the + * intrinsic height, which also includes whether the notification is system expanded and + * is mainly used when dragging down from a heads up notification. */ - public boolean isTrackingHeadsUp() { - // Might be implemented in subclass. - return false; + public int getTopHeadsUpPinnedHeight() { + HeadsUpEntry topEntry = getTopEntry(); + if (topEntry == null || topEntry.entry == null) { + return 0; + } + ExpandableNotificationRow row = topEntry.entry.row; + if (row.isChildInGroup()) { + final ExpandableNotificationRow groupSummary + = mGroupManager.getGroupSummary(row.getStatusBarNotification()); + if (groupSummary != null) { + row = groupSummary; + } + } + return row.getPinnedHeadsUpHeight(); } /** @@ -419,67 +553,147 @@ public class HeadsUpManager { } /** - * Sets an entry to be expanded and therefore stick in the heads up area if it's pinned - * until it's collapsed again. + * Set that we are exiting the headsUp pinned mode, but some notifications might still be + * animating out. This is used to keep the touchable regions in a sane state. */ + public void setHeadsUpGoingAway(boolean headsUpGoingAway) { + if (headsUpGoingAway != mHeadsUpGoingAway) { + mHeadsUpGoingAway = headsUpGoingAway; + if (!headsUpGoingAway) { + waitForStatusBarLayout(); + } + updateTouchableRegionListener(); + } + } + + /** + * We need to wait on the whole panel to collapse, before we can remove the touchable region + * listener. + */ + private void waitForStatusBarLayout() { + mWaitingOnCollapseWhenGoingAway = true; + mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, + int oldTop, int oldRight, int oldBottom) { + if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) { + mStatusBarWindowView.removeOnLayoutChangeListener(this); + mWaitingOnCollapseWhenGoingAway = false; + updateTouchableRegionListener(); + } + } + }); + } + + public static void setIsClickedNotification(View child, boolean clicked) { + child.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null); + } + + public static boolean isClickedHeadsUpNotification(View child) { + Boolean clicked = (Boolean) child.getTag(TAG_CLICKED_NOTIFICATION); + return clicked != null && clicked; + } + + public void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive) { + HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key); + if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) { + headsUpEntry.remoteInputActive = remoteInputActive; + if (remoteInputActive) { + headsUpEntry.removeAutoRemovalCallbacks(); + } else { + headsUpEntry.updateEntry(false /* updatePostTime */); + } + } + } /** * Set an entry to be expanded and therefore stick in the heads up area if it's pinned * until it's collapsed again. */ - public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) { - HeadsUpManager.HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key); - if (headsUpEntry != null && entry.row.isPinned()) { - headsUpEntry.expanded(expanded); + public void setExpanded(NotificationData.Entry entry, boolean expanded) { + HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key); + if (headsUpEntry != null && headsUpEntry.expanded != expanded && entry.row.isPinned()) { + headsUpEntry.expanded = expanded; + if (expanded) { + headsUpEntry.removeAutoRemovalCallbacks(); + } else { + headsUpEntry.updateEntry(false /* updatePostTime */); + } + } + } + + @Override + public void onReorderingAllowed() { + mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false); + for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) { + if (isHeadsUp(entry.key)) { + // Maybe the heads-up was removed already + removeHeadsUpEntry(entry); + } } + mEntriesToRemoveWhenReorderingAllowed.clear(); + mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true); } - @NonNull - protected HeadsUpEntry createHeadsUpEntry() { - return new HeadsUpEntry(); + public void setVisualStabilityManager(VisualStabilityManager visualStabilityManager) { + mVisualStabilityManager = visualStabilityManager; } - protected void releaseHeadsUpEntry(@NonNull HeadsUpEntry entry) { - // Do nothing for HeadsUpEntry. + public void setStatusBarState(int statusBarState) { + mStatusBarState = statusBarState; } /** * This represents a notification and how long it is in a heads up mode. It also manages its * lifecycle automatically when created. */ - protected class HeadsUpEntry implements Comparable<HeadsUpEntry> { - @Nullable public NotificationData.Entry entry; + public class HeadsUpEntry implements Comparable<HeadsUpEntry> { + public NotificationData.Entry entry; public long postTime; - public boolean remoteInputActive; public long earliestRemovaltime; - public boolean expanded; - private Runnable mRemoveHeadsUpRunnable; + public boolean remoteInputActive; + public boolean expanded; - public void setEntry(@Nullable final NotificationData.Entry entry) { - setEntry(entry, null); - } - - public void setEntry(@Nullable final NotificationData.Entry entry, - @Nullable Runnable removeHeadsUpRunnable) { + public void setEntry(final NotificationData.Entry entry) { this.entry = entry; - this.mRemoveHeadsUpRunnable = removeHeadsUpRunnable; // The actual post time will be just after the heads-up really slided in postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay; - updateEntry(true /* updatePostTime */); + mRemoveHeadsUpRunnable = new Runnable() { + @Override + public void run() { + if (!mVisualStabilityManager.isReorderingAllowed()) { + mEntriesToRemoveWhenReorderingAllowed.add(entry); + mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManager.this); + } else if (!mTrackingHeadsUp) { + removeHeadsUpEntry(entry); + } else { + mEntriesToRemoveAfterExpand.add(entry); + } + } + }; + updateEntry(); } - public void updateEntry(boolean updatePostTime) { - if (DEBUG) Log.v(TAG, "updateEntry"); + public void updateEntry() { + updateEntry(true); + } + public void updateEntry(boolean updatePostTime) { long currentTime = mClock.currentTimeMillis(); earliestRemovaltime = currentTime + mMinimumDisplayTime; if (updatePostTime) { postTime = Math.max(postTime, currentTime); } removeAutoRemovalCallbacks(); - + if (mEntriesToRemoveAfterExpand.contains(entry)) { + mEntriesToRemoveAfterExpand.remove(entry); + } + if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) { + mEntriesToRemoveWhenReorderingAllowed.remove(entry); + } if (!isSticky()) { long finishTime = postTime + mHeadsUpNotificationDecay; long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime); @@ -493,7 +707,7 @@ public class HeadsUpManager { } @Override - public int compareTo(@NonNull HeadsUpEntry o) { + public int compareTo(HeadsUpEntry o) { boolean isPinned = entry.row.isPinned(); boolean otherPinned = o.entry.row.isPinned(); if (isPinned && !otherPinned) { @@ -520,29 +734,26 @@ public class HeadsUpManager { : -1; } - public void expanded(boolean expanded) { - this.expanded = expanded; + public void removeAutoRemovalCallbacks() { + mHandler.removeCallbacks(mRemoveHeadsUpRunnable); } - public void reset() { - entry = null; - expanded = false; - remoteInputActive = false; - removeAutoRemovalCallbacks(); - mRemoveHeadsUpRunnable = null; + public boolean wasShownLongEnough() { + return earliestRemovaltime < mClock.currentTimeMillis(); } - public void removeAutoRemovalCallbacks() { - if (mRemoveHeadsUpRunnable != null) - mHandler.removeCallbacks(mRemoveHeadsUpRunnable); + public void removeAsSoonAsPossible() { + removeAutoRemovalCallbacks(); + mHandler.postDelayed(mRemoveHeadsUpRunnable, + earliestRemovaltime - mClock.currentTimeMillis()); } - public void removeAsSoonAsPossible() { - if (mRemoveHeadsUpRunnable != null) { - removeAutoRemovalCallbacks(); - mHandler.postDelayed(mRemoveHeadsUpRunnable, - earliestRemovaltime - mClock.currentTimeMillis()); - } + public void reset() { + removeAutoRemovalCallbacks(); + entry = null; + mRemoveHeadsUpRunnable = null; + expanded = false; + remoteInputActive = false; } } @@ -551,4 +762,5 @@ public class HeadsUpManager { return SystemClock.elapsedRealtime(); } } + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java deleted file mode 100644 index 1e3c123cfbc6..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java +++ /dev/null @@ -1,47 +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.systemui.statusbar.policy; - -import android.view.View; - -import com.android.systemui.R; - -/** - * A class of utility static methods for heads up notifications. - */ -public final class HeadsUpUtil { - private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag; - - /** - * Set the given view as clicked or not-clicked. - * @param view The view to be set the flag to. - * @param clicked True to set as clicked. False to not-clicked. - */ - public static void setIsClickedHeadsUpNotification(View view, boolean clicked) { - view.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null); - } - - /** - * Check if the given view has the flag of "clicked notification" - * @param view The view to be checked. - * @return True if the view has clicked. False othrewise. - */ - public static boolean isClickedHeadsUpNotification(View view) { - Boolean clicked = (Boolean) view.getTag(TAG_CLICKED_NOTIFICATION); - return clicked != null && clicked; - } -} 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 d7a810eca02e..424858a86e58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -64,7 +64,7 @@ public class AmbientState { private boolean mPanelTracking; private boolean mExpansionChanging; private boolean mPanelFullWidth; - private boolean mPulsing; + private Collection<HeadsUpManager.HeadsUpEntry> mPulsing; private boolean mUnlockHintRunning; private boolean mQsCustomizerShowing; private int mIntrinsicPadding; @@ -315,18 +315,23 @@ public class AmbientState { } public boolean hasPulsingNotifications() { - return mPulsing; + return mPulsing != null; } - public void setPulsing(boolean hasPulsing) { + public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> hasPulsing) { mPulsing = hasPulsing; } public boolean isPulsing(NotificationData.Entry entry) { - if (!mPulsing || mHeadsUpManager == null) { + if (mPulsing == null) { return false; } - return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry)); + for (HeadsUpManager.HeadsUpEntry e : mPulsing) { + if (e.entry == entry) { + return true; + } + } + return false; } public boolean isPanelTracking() { 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 b28e1a9cafef..c114a6f5a6d9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -92,11 +92,10 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.FakeShadowView; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.VisibilityLocationProvider; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.ScrimController; -import com.android.systemui.statusbar.policy.HeadsUpUtil; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ScrollAdapter; import android.support.v4.graphics.ColorUtils; @@ -289,7 +288,7 @@ public class NotificationStackScrollLayout extends ViewGroup private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>(); private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations = new HashSet<>(); - private HeadsUpManagerPhone mHeadsUpManager; + private HeadsUpManager mHeadsUpManager; private boolean mTrackingHeadsUp; private ScrimController mScrimController; private boolean mForceNoOverlappingRendering; @@ -359,7 +358,7 @@ public class NotificationStackScrollLayout extends ViewGroup } }; private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC); - private boolean mPulsing; + private Collection<HeadsUpManager.HeadsUpEntry> mPulsing; private boolean mDrawBackgroundAsSrc; private boolean mFadingOut; private boolean mParentNotFullyVisible; @@ -403,6 +402,7 @@ public class NotificationStackScrollLayout extends ViewGroup private final int mSeparatorThickness; private final Rect mTmpRect = new Rect(); private int mClockBottom; + private int mAntiBurnInOffsetX; public NotificationStackScrollLayout(Context context) { this(context, null); @@ -690,7 +690,7 @@ public class NotificationStackScrollLayout extends ViewGroup } private void updateAlgorithmHeightAndPadding() { - if (mPulsing) { + if (mPulsing != null) { mTopPadding = mClockBottom; } else { mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding; @@ -920,27 +920,6 @@ public class NotificationStackScrollLayout extends ViewGroup } /** - * @return the height of the top heads up notification when pinned. This is different from the - * intrinsic height, which also includes whether the notification is system expanded and - * is mainly used when dragging down from a heads up notification. - */ - private int getTopHeadsUpPinnedHeight() { - NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry(); - if (topEntry == null) { - return 0; - } - ExpandableNotificationRow row = topEntry.row; - if (row.isChildInGroup()) { - final ExpandableNotificationRow groupSummary - = mGroupManager.getGroupSummary(row.getStatusBarNotification()); - if (groupSummary != null) { - row = groupSummary; - } - } - return row.getPinnedHeadsUpHeight(); - } - - /** * @return the position from where the appear transition ends when expanding. * Measured in absolute height. */ @@ -951,7 +930,7 @@ public class NotificationStackScrollLayout extends ViewGroup int minNotificationsForShelf = 1; if (mTrackingHeadsUp || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) { - appearPosition = getTopHeadsUpPinnedHeight(); + appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight(); minNotificationsForShelf = 2; } else { appearPosition = 0; @@ -1219,9 +1198,9 @@ public class NotificationStackScrollLayout extends ViewGroup if (slidingChild instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild; if (!mIsExpanded && row.isHeadsUp() && row.isPinned() - && mHeadsUpManager.getTopEntry().row != row + && mHeadsUpManager.getTopEntry().entry.row != row && mGroupManager.getGroupSummary( - mHeadsUpManager.getTopEntry().row.getStatusBarNotification()) + mHeadsUpManager.getTopEntry().entry.row.getStatusBarNotification()) != row) { continue; } @@ -2141,7 +2120,7 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public boolean hasPulsingNotifications() { - return mPulsing; + return mPulsing != null; } private void updateScrollability() { @@ -2774,7 +2753,7 @@ public class NotificationStackScrollLayout extends ViewGroup } private boolean isClickedHeadsUp(View child) { - return HeadsUpUtil.isClickedHeadsUpNotification(child); + return HeadsUpManager.isClickedHeadsUpNotification(child); } /** @@ -3889,9 +3868,14 @@ public class NotificationStackScrollLayout extends ViewGroup applyCurrentBackgroundBounds(); updateWillNotDraw(); updateContentHeight(); + updateAntiBurnInTranslation(); notifyHeightChangeListener(mShelf); } + private void updateAntiBurnInTranslation() { + setTranslationX(mAmbientState.isDark() ? mAntiBurnInOffsetX : 0); + } + /** * Updates whether or not this Layout will perform its own custom drawing (i.e. whether or * not {@link #onDraw(Canvas)} is called). This method should be called whenever the @@ -4274,7 +4258,7 @@ public class NotificationStackScrollLayout extends ViewGroup mAnimationFinishedRunnables.add(runnable); } - public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { + public void setHeadsUpManager(HeadsUpManager headsUpManager) { mHeadsUpManager = headsUpManager; mAmbientState.setHeadsUpManager(headsUpManager); } @@ -4342,8 +4326,8 @@ public class NotificationStackScrollLayout extends ViewGroup return mIsExpanded; } - public void setPulsing(boolean pulsing, int clockBottom) { - if (!mPulsing && !pulsing) { + public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing, int clockBottom) { + if (mPulsing == null && pulsing == null) { return; } mPulsing = pulsing; @@ -4473,15 +4457,16 @@ public class NotificationStackScrollLayout extends ViewGroup mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed; } - public void setDarkShelfOffsetX(int shelfOffsetX) { - mShelf.setDarkOffsetX(shelfOffsetX); + public void setAntiBurnInOffsetX(int antiBurnInOffsetX) { + mAntiBurnInOffsetX = antiBurnInOffsetX; + updateAntiBurnInTranslation(); } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s" + " alpha:%f scrollY:%d]", this.getClass().getSimpleName(), - mPulsing ? "T":"f", + mPulsing != null ?"T":"f", mAmbientState.isQsCustomizerShowing() ? "T":"f", getVisibility() == View.VISIBLE ? "visible" : getVisibility() == View.GONE ? "gone" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java index 04a7bd79c6ca..682b8493e913 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java @@ -30,7 +30,7 @@ import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; -import com.android.systemui.statusbar.policy.HeadsUpUtil; +import com.android.systemui.statusbar.policy.HeadsUpManager; /** * A state of a view. This can be used to apply a set of view properties to a view with @@ -582,7 +582,7 @@ public class ViewState { animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - HeadsUpUtil.setIsClickedHeadsUpNotification(child, false); + HeadsUpManager.setIsClickedNotification(child, false); child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); child.setTag(TAG_START_TRANSLATION_Y, null); child.setTag(TAG_END_TRANSLATION_Y, null); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 001a582297af..a131a618512f 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -18,6 +18,7 @@ package com.android.systemui.volume; import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC; +import static android.media.AudioManager.STREAM_ACCESSIBILITY; import static com.android.systemui.volume.Events.DISMISS_REASON_OUTPUT_CHOOSER; import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED; @@ -36,7 +37,6 @@ import android.content.pm.PackageManager; import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.Color; -import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.media.AudioManager; import android.media.AudioSystem; @@ -48,6 +48,7 @@ import android.os.SystemClock; import android.provider.Settings; import android.provider.Settings.Global; import android.support.v7.media.MediaRouter; +import android.text.InputFilter; import android.text.TextUtils; import android.util.Log; import android.util.Slog; @@ -58,7 +59,6 @@ import android.view.View; import android.view.View.AccessibilityDelegate; import android.view.View.OnAttachStateChangeListener; import android.view.View.OnClickListener; -import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; @@ -105,6 +105,7 @@ public class VolumeDialogImpl implements VolumeDialog { private CustomDialog mDialog; private ViewGroup mDialogView; private ViewGroup mDialogRowsView; + private ViewGroup mFooter; private ImageButton mRingerIcon; private TextView mRingerStatus; private final List<VolumeRow> mRows = new ArrayList<>(); @@ -202,8 +203,9 @@ public class VolumeDialogImpl implements VolumeDialog { hardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE)); mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows); - mRingerIcon = mDialog.findViewById(R.id.ringer_icon); - mRingerStatus = mDialog.findViewById(R.id.ringer_status); + mFooter = mDialog.findViewById(R.id.footer); + mRingerIcon = mFooter.findViewById(R.id.ringer_icon); + mRingerStatus = mFooter.findViewById(R.id.ringer_status); if (mRows.isEmpty()) { addRow(AudioManager.STREAM_MUSIC, @@ -219,7 +221,7 @@ public class VolumeDialogImpl implements VolumeDialog { R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false, false); addRow(AudioManager.STREAM_SYSTEM, R.drawable.ic_volume_system, R.drawable.ic_volume_system_mute, false, false); - addRow(AudioManager.STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility, + addRow(STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility, R.drawable.ic_volume_accessibility, true, false); } } else { @@ -334,42 +336,17 @@ public class VolumeDialogImpl implements VolumeDialog { row.view.setTag(row); row.header = row.view.findViewById(R.id.volume_row_header); row.header.setId(20 * row.stream); + if (stream == STREAM_ACCESSIBILITY) { + row.header.setFilters(new InputFilter[] {new InputFilter.LengthFilter(13)}); + } row.slider = row.view.findViewById(R.id.volume_row_slider); row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row)); row.anim = null; row.outputChooser = row.view.findViewById(R.id.output_chooser); row.outputChooser.setOnClickListener(mClickOutputChooser); - row.outputChooser.findViewById(R.id.output_chooser_button) - .setOnClickListener(mClickOutputChooser); row.connectedDevice = row.view.findViewById(R.id.volume_row_connected_device); - // forward events above the slider into the slider - row.view.findViewById(R.id.volume_row_slider_frame) - .setOnTouchListener(new OnTouchListener() { - private final Rect mSliderHitRect = new Rect(); - private boolean mDragging; - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouch(View v, MotionEvent event) { - row.slider.getHitRect(mSliderHitRect); - if (!mDragging && event.getActionMasked() == MotionEvent.ACTION_DOWN - && event.getY() < mSliderHitRect.top) { - mDragging = true; - } - if (mDragging) { - event.offsetLocation(-mSliderHitRect.left, -mSliderHitRect.top); - row.slider.dispatchTouchEvent(event); - if (event.getActionMasked() == MotionEvent.ACTION_UP - || event.getActionMasked() == MotionEvent.ACTION_CANCEL) { - mDragging = false; - } - return true; - } - return false; - } - }); row.icon = row.view.findViewById(R.id.volume_row_icon); row.icon.setImageResource(iconRes); if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) { @@ -412,6 +389,8 @@ public class VolumeDialogImpl implements VolumeDialog { if (ss == null) { return; } + // normal -> vibrate -> silent -> normal (skip vibrate if device doesn't have + // a vibrator. final boolean hasVibrator = mController.hasVibrator(); if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) { if (hasVibrator) { @@ -419,7 +398,12 @@ public class VolumeDialogImpl implements VolumeDialog { } else { final boolean wasZero = ss.level == 0; mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0); + mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false); } + } else if (mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) { + final boolean wasZero = ss.level == 0; + mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0); + mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false); } else { mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false); if (ss.level == 0) { @@ -664,7 +648,7 @@ public class VolumeDialogImpl implements VolumeDialog { if (ss.level == row.requestedLevel) { row.requestedLevel = -1; } - final boolean isA11yStream = row.stream == AudioManager.STREAM_ACCESSIBILITY; + final boolean isA11yStream = row.stream == STREAM_ACCESSIBILITY; final boolean isRingStream = row.stream == AudioManager.STREAM_RING; final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM; final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM; @@ -908,7 +892,6 @@ public class VolumeDialogImpl implements VolumeDialog { private final OnClickListener mClickOutputChooser = new OnClickListener() { @Override public void onClick(View v) { - // TODO: log dismissH(DISMISS_REASON_OUTPUT_CHOOSER); showOutputChooserH(); } @@ -971,7 +954,7 @@ public class VolumeDialogImpl implements VolumeDialog { public void onAccessibilityModeChanged(Boolean showA11yStream) { mShowA11yStream = showA11yStream == null ? false : showA11yStream; VolumeRow activeRow = getActiveRow(); - if (!mShowA11yStream && AudioManager.STREAM_ACCESSIBILITY == activeRow.stream) { + if (!mShowA11yStream && STREAM_ACCESSIBILITY == activeRow.stream) { dismissH(Events.DISMISS_STREAM_GONE); } else { updateRowsH(activeRow); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java index 368194e57b9d..3d4438148c39 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java @@ -14,26 +14,22 @@ package com.android.systemui.volume; -import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE; -import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Rect; import android.util.AttributeSet; -import android.util.Slog; -import android.view.Gravity; +import android.view.DisplayCutout; import android.view.View; -import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver; import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.SeekBar; import com.android.systemui.R; import com.android.systemui.util.leak.RotationUtils; @@ -46,6 +42,9 @@ public class VolumeUiLayout extends FrameLayout { private AnimatorSet mAnimation; private boolean mHasOutsideTouch; private int mRotation = ROTATION_NONE; + @Nullable + private DisplayCutout mDisplayCutout; + public VolumeUiLayout(Context context, AttributeSet attrs) { super(context, attrs); } @@ -60,6 +59,7 @@ public class VolumeUiLayout extends FrameLayout { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener); + mDisplayCutout = null; } @Override @@ -86,200 +86,64 @@ public class VolumeUiLayout extends FrameLayout { updateRotation(); } - private void updateRotation() { - int rotation = RotationUtils.getRotation(getContext()); - if (rotation != mRotation) { - rotate(mRotation, rotation); - mRotation = rotation; - } - } - - private void rotate(View view, int from, int to, boolean swapDimens) { - if (from != ROTATION_NONE && to != ROTATION_NONE) { - // Rather than handling this confusing case, just do 2 rotations. - rotate(view, from, ROTATION_NONE, swapDimens); - rotate(view, ROTATION_NONE, to, swapDimens); - return; - } - if (from == ROTATION_LANDSCAPE || to == ROTATION_SEASCAPE) { - rotateRight(view); - } else { - rotateLeft(view); - } - if (to != ROTATION_NONE) { - if (swapDimens && view instanceof LinearLayout) { - LinearLayout linearLayout = (LinearLayout) view; - linearLayout.setOrientation(LinearLayout.HORIZONTAL); - swapDimens(view); - } - } else { - if (swapDimens && view instanceof LinearLayout) { - LinearLayout linearLayout = (LinearLayout) view; - linearLayout.setOrientation(LinearLayout.VERTICAL); - swapDimens(view); + private void setDisplayCutout() { + if (mDisplayCutout == null && getRootWindowInsets() != null) { + DisplayCutout cutout = getRootWindowInsets().getDisplayCutout(); + if (cutout != null) { + mDisplayCutout = cutout; } } } - private void rotate(int from, int to) { - View footer = mChild.findViewById(R.id.footer); - rotate(footer, from, to, false); - rotate(this, from, to, true); - rotate(mChild, from, to, true); - ViewGroup rows = mChild.findViewById(R.id.volume_dialog_rows); - rotate(rows, from, to, true); - swapOrientation((LinearLayout) rows); - int rowCount = rows.getChildCount(); - for (int i = 0; i < rowCount; i++) { - View row = rows.getChildAt(i); - if (to == ROTATION_SEASCAPE) { - rotateSeekBars(row, to, 180); - } else if (to == ROTATION_LANDSCAPE) { - rotateSeekBars(row, to, 0); - } else { - rotateSeekBars(row, to, 270); - } - rotate(row, from, to, true); - } - } - - private void swapOrientation(LinearLayout layout) { - if(layout.getOrientation() == LinearLayout.HORIZONTAL) { - layout.setOrientation(LinearLayout.VERTICAL); - } else { - layout.setOrientation(LinearLayout.HORIZONTAL); - } - } - - private void swapDimens(View v) { - if (v == null) { - return; + private void updateRotation() { + setDisplayCutout(); + int rotation = RotationUtils.getRotation(getContext()); + if (rotation != mRotation) { + updateSafeInsets(rotation); + mRotation = rotation; } - ViewGroup.LayoutParams params = v.getLayoutParams(); - int h = params.width; - params.width = params.height; - params.height = h; - v.setLayoutParams(params); } - private void rotateSeekBars(View row, int to, int rotation) { - SeekBar seekbar = row.findViewById(R.id.volume_row_slider); - if (seekbar != null) { - seekbar.setRotation((float) rotation); - } - - View parent = row.findViewById(R.id.volume_row_slider_frame); - swapDimens(parent); - ViewGroup.LayoutParams params = seekbar.getLayoutParams(); - ViewGroup.LayoutParams parentParams = parent.getLayoutParams(); - if (to != ROTATION_NONE) { - params.height = parentParams.height; - params.width = parentParams.width; - } else { - params.height = parentParams.width; - params.width = parentParams.height; - } - seekbar.setLayoutParams(params); - } + private void updateSafeInsets(int rotation) { + // Depending on our rotation, we may have to work around letterboxing from the right + // side from the navigation bar or a cutout. - private int rotateGravityRight(int gravity) { - int retGravity = 0; - int layoutDirection = getLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); - final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; + MarginLayoutParams lp = (MarginLayoutParams) mChild.getLayoutParams(); - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.CENTER_HORIZONTAL: - retGravity |= Gravity.CENTER_VERTICAL; + int margin = (int) getResources().getDimension(R.dimen.volume_dialog_base_margin); + switch (rotation) { + /* + * Landscape: <-|. Have to deal with the nav bar + * Seascape: |->. Have to deal with the cutout + */ + case RotationUtils.ROTATION_LANDSCAPE: + margin += getNavBarHeight(); break; - case Gravity.RIGHT: - retGravity |= Gravity.BOTTOM; + case RotationUtils.ROTATION_SEASCAPE: + margin += getDisplayCutoutHeight(); break; - case Gravity.LEFT: default: - retGravity |= Gravity.TOP; break; } - switch (verticalGravity) { - case Gravity.CENTER_VERTICAL: - retGravity |= Gravity.CENTER_HORIZONTAL; - break; - case Gravity.BOTTOM: - retGravity |= Gravity.LEFT; - break; - case Gravity.TOP: - default: - retGravity |= Gravity.RIGHT; - break; - } - return retGravity; + lp.rightMargin = margin; + mChild.setLayoutParams(lp); } - private int rotateGravityLeft(int gravity) { - if (gravity == -1) { - gravity = Gravity.TOP | Gravity.START; - } - int retGravity = 0; - int layoutDirection = getLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); - final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; - - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.CENTER_HORIZONTAL: - retGravity |= Gravity.CENTER_VERTICAL; - break; - case Gravity.RIGHT: - retGravity |= Gravity.TOP; - break; - case Gravity.LEFT: - default: - retGravity |= Gravity.BOTTOM; - break; - } - - switch (verticalGravity) { - case Gravity.CENTER_VERTICAL: - retGravity |= Gravity.CENTER_HORIZONTAL; - break; - case Gravity.BOTTOM: - retGravity |= Gravity.RIGHT; - break; - case Gravity.TOP: - default: - retGravity |= Gravity.LEFT; - break; - } - return retGravity; + private int getNavBarHeight() { + return (int) getResources().getDimension(R.dimen.navigation_bar_size); } - private void rotateLeft(View v) { - if (v.getParent() instanceof FrameLayout) { - LayoutParams p = (LayoutParams) v.getLayoutParams(); - p.gravity = rotateGravityLeft(p.gravity); + //TODO: Find a better way + private int getDisplayCutoutHeight() { + if (mDisplayCutout == null || mDisplayCutout.isEmpty()) { + return 0; } - v.setPadding(v.getPaddingTop(), v.getPaddingRight(), v.getPaddingBottom(), - v.getPaddingLeft()); - MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams(); - params.setMargins(params.topMargin, params.rightMargin, params.bottomMargin, - params.leftMargin); - v.setLayoutParams(params); + Rect r = mDisplayCutout.getBoundingRect(); + return r.bottom - r.top; } - private void rotateRight(View v) { - if (v.getParent() instanceof FrameLayout) { - LayoutParams p = (LayoutParams) v.getLayoutParams(); - p.gravity = rotateGravityRight(p.gravity); - } - - v.setPadding(v.getPaddingBottom(), v.getPaddingLeft(), v.getPaddingTop(), - v.getPaddingRight()); - MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams(); - params.setMargins(params.bottomMargin, params.leftMargin, params.topMargin, - params.rightMargin); - v.setLayoutParams(params); - } private void animateChild(int oldHeight, int newHeight) { if (true) return; diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java index 40f8059fecbf..dfc1852502e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java @@ -30,6 +30,7 @@ import com.android.systemui.Prefs; import com.android.systemui.Prefs.Key; import com.android.systemui.SysuiTestCase; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,6 +41,11 @@ public class AutoAddTrackerTest extends SysuiTestCase { private AutoAddTracker mAutoTracker; + @Before + public void setUp() { + Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, ""); + } + @Test public void testMigration() { Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true); @@ -50,7 +56,10 @@ public class AutoAddTrackerTest extends SysuiTestCase { assertTrue(mAutoTracker.isAdded(WORK)); assertFalse(mAutoTracker.isAdded(INVERSION)); + // These keys have been removed; retrieving their values should always return the default. + assertTrue(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true )); assertFalse(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, false)); + assertTrue(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, true)); assertFalse(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, false)); mAutoTracker.destroy(); @@ -96,5 +105,4 @@ public class AutoAddTrackerTest extends SysuiTestCase { mAutoTracker.destroy(); } - }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java index f3c1171f650c..6e7477fbac38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -32,7 +32,6 @@ import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.NotificationInflaterTest; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -52,7 +51,7 @@ public class NotificationTestHelper { public NotificationTestHelper(Context context) { mContext = context; mInstrumentation = InstrumentationRegistry.getInstrumentation(); - mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null); + mHeadsUpManager = new HeadsUpManager(mContext, null, mGroupManager); } public ExpandableNotificationRow createRow() throws Exception { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java index a95e3dbb1e3b..2d2db1bba735 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java @@ -18,45 +18,48 @@ package com.android.systemui.statusbar.phone; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.app.AlarmManager.AlarmClockInfo; +import android.os.Handler; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; - import com.android.internal.app.ColorDisplayController; import com.android.systemui.Dependency; -import com.android.systemui.Prefs; -import com.android.systemui.Prefs.Key; import com.android.systemui.SysuiTestCase; +import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSTileHost; - -import org.junit.After; +import com.android.systemui.statusbar.policy.NextAlarmController; +import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mockito; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @RunWithLooper @SmallTest public class AutoTileManagerTest extends SysuiTestCase { - private QSTileHost mQsTileHost; + @Mock private QSTileHost mQsTileHost; + @Mock private AutoAddTracker mAutoAddTracker; + @Captor private ArgumentCaptor<NextAlarmChangeCallback> mAlarmCallback; + private AutoTileManager mAutoTileManager; @Before public void setUp() throws Exception { - mDependency.injectTestDependency(Dependency.BG_LOOPER, - TestableLooper.get(this).getLooper()); - Prefs.putBoolean(mContext, Key.QS_NIGHTDISPLAY_ADDED, false); - mQsTileHost = Mockito.mock(QSTileHost.class); - mAutoTileManager = new AutoTileManager(mContext, mQsTileHost); - } - - @After - public void tearDown() throws Exception { - mAutoTileManager = null; + MockitoAnnotations.initMocks(this); + mDependency.injectMockDependency(NextAlarmController.class); + mAutoTileManager = new AutoTileManager(mContext, mAutoAddTracker, + mQsTileHost, new Handler(TestableLooper.get(this).getLooper())); + verify(Dependency.get(NextAlarmController.class)) + .addCallback(mAlarmCallback.capture()); } @Test @@ -106,4 +109,30 @@ public class AutoTileManagerTest extends SysuiTestCase { ColorDisplayController.AUTO_MODE_DISABLED); verify(mQsTileHost, never()).addTile("night"); } + + @Test + public void alarmTileAdded_whenAlarmSet() { + mAlarmCallback.getValue().onNextAlarmChanged(new AlarmClockInfo(0, null)); + + verify(mQsTileHost).addTile("alarm"); + verify(mAutoAddTracker).setTileAdded("alarm"); + } + + @Test + public void alarmTileNotAdded_whenAlarmNotSet() { + mAlarmCallback.getValue().onNextAlarmChanged(null); + + verify(mQsTileHost, never()).addTile("alarm"); + verify(mAutoAddTracker, never()).setTileAdded("alarm"); + } + + @Test + public void alarmTileNotAdded_whenAlreadyAdded() { + when(mAutoAddTracker.isAdded("alarm")).thenReturn(true); + + mAlarmCallback.getValue().onNextAlarmChanged(new AlarmClockInfo(0, null)); + + verify(mQsTileHost, never()).addTile("alarm"); + verify(mAutoAddTracker, never()).setTileAdded("alarm"); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java deleted file mode 100644 index 28f941779043..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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.app.ActivityManager; -import android.app.Notification; -import android.content.Context; -import android.os.Handler; -import android.os.Looper; -import android.os.UserHandle; -import android.view.View; -import android.service.notification.StatusBarNotification; -import android.support.test.filters.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; - -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.ExpandableNotificationRow; -import com.android.systemui.statusbar.NotificationData; -import com.android.systemui.statusbar.StatusBarIconView; -import com.android.systemui.statusbar.notification.VisualStabilityManager; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.assertFalse; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper -public class HeadsUpManagerPhoneTest extends SysuiTestCase { - @Rule public MockitoRule rule = MockitoJUnit.rule(); - - private static final String TEST_PACKAGE_NAME = "test"; - private static final int TEST_UID = 0; - - private HeadsUpManagerPhone mHeadsUpManager; - - private NotificationData.Entry mEntry; - private StatusBarNotification mSbn; - - private final Handler mHandler = new Handler(Looper.getMainLooper()); - - @Mock private NotificationGroupManager mGroupManager; - @Mock private View mStatusBarWindowView; - @Mock private StatusBar mBar; - @Mock private ExpandableNotificationRow mRow; - @Mock private VisualStabilityManager mVSManager; - - @Before - public void setUp() { - when(mVSManager.isReorderingAllowed()).thenReturn(true); - - mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarWindowView, mGroupManager, mBar, mVSManager); - - Notification.Builder n = new Notification.Builder(mContext, "") - .setSmallIcon(R.drawable.ic_person) - .setContentTitle("Title") - .setContentText("Text"); - mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, - 0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0); - - mEntry = new NotificationData.Entry(mSbn); - mEntry.row = mRow; - mEntry.expandedIcon = mock(StatusBarIconView.class); - } - - @Test - public void testBasicOperations() { - // Check the initial state. - assertNull(mHeadsUpManager.getEntry(mEntry.key)); - assertNull(mHeadsUpManager.getTopEntry()); - assertEquals(0, mHeadsUpManager.getAllEntries().count()); - assertFalse(mHeadsUpManager.hasHeadsUpNotifications()); - - // Add a notification. - mHeadsUpManager.showNotification(mEntry); - - assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key)); - assertEquals(mEntry, mHeadsUpManager.getTopEntry()); - assertEquals(1, mHeadsUpManager.getAllEntries().count()); - assertTrue(mHeadsUpManager.hasHeadsUpNotifications()); - - // Update the notification. - mHeadsUpManager.updateNotification(mEntry, false); - - assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key)); - assertEquals(mEntry, mHeadsUpManager.getTopEntry()); - assertEquals(1, mHeadsUpManager.getAllEntries().count()); - assertTrue(mHeadsUpManager.hasHeadsUpNotifications()); - - // Remove but defer, since the notification is visible on display. - mHeadsUpManager.removeNotification(mEntry.key, false); - - assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key)); - assertEquals(mEntry, mHeadsUpManager.getTopEntry()); - assertEquals(1, mHeadsUpManager.getAllEntries().count()); - assertTrue(mHeadsUpManager.hasHeadsUpNotifications()); - } -} 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 31442af5a04c..bdf9b1f6da9e 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 @@ -86,8 +86,8 @@ import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.VisualStabilityManager; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; @@ -110,7 +110,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private UnlockMethodCache mUnlockMethodCache; @Mock private KeyguardIndicationController mKeyguardIndicationController; @Mock private NotificationStackScrollLayout mStackScroller; - @Mock private HeadsUpManagerPhone mHeadsUpManager; + @Mock private HeadsUpManager mHeadsUpManager; @Mock private SystemServicesProxy mSystemServicesProxy; @Mock private NotificationPanelView mNotificationPanelView; @Mock private IStatusBarService mBarService; @@ -588,7 +588,7 @@ public class StatusBarTest extends SysuiTestCase { static class TestableStatusBar extends StatusBar { public TestableStatusBar(StatusBarKeyguardViewManager man, UnlockMethodCache unlock, KeyguardIndicationController key, - NotificationStackScrollLayout stack, HeadsUpManagerPhone hum, + NotificationStackScrollLayout stack, HeadsUpManager hum, PowerManager pm, NotificationPanelView panelView, IStatusBarService barService, NotificationListener notificationListener, NotificationLogger notificationLogger, @@ -650,7 +650,7 @@ public class StatusBarTest extends SysuiTestCase { public void setUpForTest(NotificationPresenter presenter, NotificationListContainer listContainer, Callback callback, - HeadsUpManagerPhone headsUpManager, + HeadsUpManager headsUpManager, NotificationData notificationData) { super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager); mNotificationData = notificationData; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java index 1c010b66056a..d9673d3552d8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java @@ -61,4 +61,15 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { Assert.assertFalse(mStackScroller.isDimmed()); } + @Test + public void testAntiBurnInOffset() { + final int burnInOffset = 30; + mStackScroller.setAntiBurnInOffsetX(burnInOffset); + mStackScroller.setDark(false /* dark */, false /* animated */, null /* touch */); + Assert.assertEquals(0 /* expected */, mStackScroller.getTranslationX(), 0.01 /* delta */); + mStackScroller.setDark(true /* dark */, false /* animated */, null /* touch */); + Assert.assertEquals(burnInOffset /* expected */, mStackScroller.getTranslationX(), + 0.01 /* delta */); + } + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java index c18ed732f244..f7bb0655b46e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java @@ -44,11 +44,13 @@ import com.android.systemui.statusbar.policy.BluetoothController; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +@Ignore @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -87,7 +89,7 @@ public class OutputChooserDialogTest extends SysuiTestCase { public void tearDown() throws Exception { TestableLooper.get(this).processAllMessages(); } - +/* @Test public void testClickMediaRouterItemConnectsMedia() { mDialog.show(); @@ -137,7 +139,7 @@ public class OutputChooserDialogTest extends SysuiTestCase { .getText().toString().contains("Phone")); mDialog.dismiss(); } - +*/ @Test public void testNoMediaScanIfInCall() { mDialog.setIsInCall(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index 2d28c9f214fb..43d60e41a6b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -16,12 +16,21 @@ package com.android.systemui.volume; +import static android.media.AudioManager.RINGER_MODE_NORMAL; +import static android.media.AudioManager.RINGER_MODE_SILENT; +import static android.media.AudioManager.RINGER_MODE_VIBRATE; +import static android.media.AudioManager.STREAM_RING; + import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN; import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN; import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS; import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import android.app.KeyguardManager; import android.media.AudioManager; import android.support.test.filters.SmallTest; @@ -32,11 +41,14 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.VolumeDialogController; +import com.android.systemui.plugins.VolumeDialogController.State; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -44,6 +56,7 @@ import org.mockito.MockitoAnnotations; import java.util.function.Predicate; +@Ignore @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -70,13 +83,19 @@ public class VolumeDialogImplTest extends SysuiTestCase { mDialog = new VolumeDialogImpl(getContext()); mDialog.init(0, null); - VolumeDialogController.State state = new VolumeDialogController.State(); + State state = createShellState(); + mDialog.onStateChangedH(state); + } + + private State createShellState() { + State state = new VolumeDialogController.State(); for (int i = AudioManager.STREAM_VOICE_CALL; i <= AudioManager.STREAM_ACCESSIBILITY; i++) { VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState(); ss.name = STREAMS.get(i); + ss.level = 1; state.states.append(i, ss); } - mDialog.onStateChangedH(state); + return state; } private void navigateViews(View view, Predicate<View> condition) { @@ -94,7 +113,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { + " failed test", condition.test(view)); } } - +/* @Test public void testContentDescriptions() { mDialog.show(SHOW_REASON_UNKNOWN); @@ -111,4 +130,95 @@ public class VolumeDialogImplTest extends SysuiTestCase { mDialog.dismiss(DISMISS_REASON_UNKNOWN); } + @Test + public void testNoDuplicationOfParentState() { + mDialog.show(SHOW_REASON_UNKNOWN); + ViewGroup dialog = mDialog.getDialogView(); + + navigateViews(dialog, view -> !view.isDuplicateParentStateEnabled()); + + mDialog.dismiss(DISMISS_REASON_UNKNOWN); + } + + @Test + public void testNoClickableViewGroups() { + mDialog.show(SHOW_REASON_UNKNOWN); + ViewGroup dialog = mDialog.getDialogView(); + + navigateViews(dialog, view -> { + if (view instanceof ViewGroup) { + return !view.isClickable(); + } else { + return true; + } + }); + + mDialog.dismiss(DISMISS_REASON_UNKNOWN); + } + + @Test + public void testTristateToggle_withVibrator() { + when(mController.hasVibrator()).thenReturn(true); + + State state = createShellState(); + state.ringerModeInternal = RINGER_MODE_NORMAL; + mDialog.onStateChangedH(state); + + mDialog.show(SHOW_REASON_UNKNOWN); + ViewGroup dialog = mDialog.getDialogView(); + + // click once, verify updates to vibrate + dialog.findViewById(R.id.ringer_icon).performClick(); + verify(mController, times(1)).setRingerMode(RINGER_MODE_VIBRATE, false); + + // fake the update back to the dialog with the new ringer mode + state = createShellState(); + state.ringerModeInternal = RINGER_MODE_VIBRATE; + mDialog.onStateChangedH(state); + + // click once, verify updates to silent + dialog.findViewById(R.id.ringer_icon).performClick(); + verify(mController, times(1)).setRingerMode(RINGER_MODE_SILENT, false); + verify(mController, times(1)).setStreamVolume(STREAM_RING, 0); + + // fake the update back to the dialog with the new ringer mode + state = createShellState(); + state.states.get(STREAM_RING).level = 0; + state.ringerModeInternal = RINGER_MODE_SILENT; + mDialog.onStateChangedH(state); + + // click once, verify updates to normal + dialog.findViewById(R.id.ringer_icon).performClick(); + verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false); + verify(mController, times(1)).setStreamVolume(STREAM_RING, 0); + } + + @Test + public void testTristateToggle_withoutVibrator() { + when(mController.hasVibrator()).thenReturn(false); + + State state = createShellState(); + state.ringerModeInternal = RINGER_MODE_NORMAL; + mDialog.onStateChangedH(state); + + mDialog.show(SHOW_REASON_UNKNOWN); + ViewGroup dialog = mDialog.getDialogView(); + + // click once, verify updates to silent + dialog.findViewById(R.id.ringer_icon).performClick(); + verify(mController, times(1)).setRingerMode(RINGER_MODE_SILENT, false); + verify(mController, times(1)).setStreamVolume(STREAM_RING, 0); + + // fake the update back to the dialog with the new ringer mode + state = createShellState(); + state.states.get(STREAM_RING).level = 0; + state.ringerModeInternal = RINGER_MODE_SILENT; + mDialog.onStateChangedH(state); + + // click once, verify updates to normal + dialog.findViewById(R.id.ringer_icon).performClick(); + verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false); + verify(mController, times(1)).setStreamVolume(STREAM_RING, 0); + } + */ } diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java deleted file mode 100644 index 4ab2063196d4..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java +++ /dev/null @@ -1,220 +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.systemui.volume; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.net.Uri; -import android.provider.Settings; -import android.service.notification.Condition; -import android.service.notification.ZenModeConfig; -import android.support.test.annotation.UiThreadTest; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; -import android.test.FlakyTest; -import android.view.LayoutInflater; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.policy.ZenModeController; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; - -@Ignore -@SmallTest -@RunWith(AndroidJUnit4.class) -public class ZenModePanelTest extends SysuiTestCase { - - ZenModePanel mPanel; - ZenModeController mController; - Uri mForeverId; - - @Before - public void setup() throws Exception { - final LayoutInflater layoutInflater = LayoutInflater.from(mContext); - mPanel = (ZenModePanel) layoutInflater.inflate(com.android.systemui.R.layout.zen_mode_panel, - null); - mController = mock(ZenModeController.class); - mForeverId = Condition.newId(mContext).appendPath("forever").build(); - - mPanel.init(mController); - } - - private void assertProperConditionTagTypes(boolean hasAlarm) { - final int N = mPanel.getVisibleConditions(); - assertEquals(hasAlarm ? 3 : 2, N); - - assertEquals(mForeverId, mPanel.getConditionTagAt(0).condition.id); - assertTrue(ZenModeConfig.isValidCountdownConditionId( - mPanel.getConditionTagAt(1).condition.id)); - assertFalse(ZenModeConfig.isValidCountdownToAlarmConditionId( - mPanel.getConditionTagAt(1).condition.id)); - if (hasAlarm) { - assertTrue(ZenModeConfig.isValidCountdownToAlarmConditionId( - mPanel.getConditionTagAt(2).condition.id)); - } - } - - @Test - public void testHandleUpdateConditions_foreverSelected_alarmExists() { - Condition forever = new Condition(mForeverId, "", Condition.STATE_TRUE); - - when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000); - - mPanel.handleUpdateConditions(forever); - assertProperConditionTagTypes(true); - assertTrue(mPanel.getConditionTagAt(0).rb.isChecked()); - } - - @Test - public void testHandleUpdateConditions_foreverSelected_noAlarm() { - Uri foreverId = Condition.newId(mContext).appendPath("forever").build(); - Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE); - - when(mController.getNextAlarm()).thenReturn((long) 0); - - mPanel.handleUpdateConditions(forever); - assertProperConditionTagTypes(false); - assertEquals(foreverId, mPanel.getConditionTagAt(0).condition.id); - } - - @Test - public void testHandleUpdateConditions_countdownSelected_alarmExists() { - Uri foreverId = Condition.newId(mContext).appendPath("forever").build(); - - Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId( - System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false), - "", Condition.STATE_TRUE); - - when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000); - - mPanel.handleUpdateConditions(countdown); - assertProperConditionTagTypes(true); - assertTrue(mPanel.getConditionTagAt(1).rb.isChecked()); - } - - @Test - public void testHandleUpdateConditions_countdownSelected_noAlarm() { - Uri foreverId = Condition.newId(mContext).appendPath("forever").build(); - - Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId( - System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false), - "", Condition.STATE_TRUE); - - when(mController.getNextAlarm()).thenReturn((long) 0); - - mPanel.handleUpdateConditions(countdown); - assertProperConditionTagTypes(false); - assertTrue(mPanel.getConditionTagAt(1).rb.isChecked()); - } - - @Test - public void testHandleUpdateConditions_nextAlarmSelected() { - Uri foreverId = Condition.newId(mContext).appendPath("forever").build(); - - Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId( - System.currentTimeMillis() + 1000, true), - "", Condition.STATE_TRUE); - when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 9000); - - mPanel.handleUpdateConditions(alarm); - - assertProperConditionTagTypes(true); - assertEquals(alarm, mPanel.getConditionTagAt(2).condition); - assertTrue(mPanel.getConditionTagAt(2).rb.isChecked()); - } - - @Test - public void testHandleUpdateConditions_foreverSelected_alarmConditionDoesNotChangeIfAttached() { - Uri foreverId = Condition.newId(mContext).appendPath("forever").build(); - Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE); - - Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId( - System.currentTimeMillis() + 9000, true), - "", Condition.STATE_TRUE); - when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000); - - mPanel.handleUpdateConditions(alarm); - mPanel.setAttached(true); - mPanel.handleUpdateConditions(forever); - - assertProperConditionTagTypes(true); - assertEquals(alarm, mPanel.getConditionTagAt(2).condition); - assertTrue(mPanel.getConditionTagAt(0).rb.isChecked()); - } - - @Test - public void testHandleUpdateConditions_foreverSelected_timeConditionDoesNotChangeIfAttached() { - Uri foreverId = Condition.newId(mContext).appendPath("forever").build(); - Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE); - - Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId( - System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false), - "", Condition.STATE_TRUE); - when(mController.getNextAlarm()).thenReturn((long) 0); - - mPanel.handleUpdateConditions(countdown); - mPanel.setAttached(true); - mPanel.handleUpdateConditions(forever); - - assertProperConditionTagTypes(false); - assertEquals(countdown, mPanel.getConditionTagAt(1).condition); - assertTrue(mPanel.getConditionTagAt(0).rb.isChecked()); - } - - @Test - @UiThreadTest - public void testHandleUpdateManualRule_stillSelectedAfterModeChange() { - ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); - - Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId( - System.currentTimeMillis() + 1000, true), - "", Condition.STATE_TRUE); - - rule.condition = alarm; - rule.conditionId = alarm.id; - rule.enabled = true; - rule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; - - mPanel.handleUpdateManualRule(rule); - - assertProperConditionTagTypes(true); - assertEquals(alarm, mPanel.getConditionTagAt(2).condition); - assertTrue(mPanel.getConditionTagAt(2).rb.isChecked()); - - assertEquals(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, - mPanel.getSelectedZen(Settings.Global.ZEN_MODE_OFF)); - - rule.zenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS; - - mPanel.handleUpdateManualRule(rule); - - assertProperConditionTagTypes(true); - assertEquals(alarm, mPanel.getConditionTagAt(2).condition); - assertTrue(mPanel.getConditionTagAt(2).rb.isChecked()); - - assertEquals(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS, - mPanel.getSelectedZen(Settings.Global.ZEN_MODE_OFF)); - } -} diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 7539d8847c2d..01714cf571b5 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -5173,6 +5173,18 @@ message MetricsEvent { // OS: P AUTOFILL_INVALID_PERMISSION = 1289; + // OPEN: QS Alarm tile shown + // ACTION: QS Alarm tile tapped + // SUBTYPE: 0 is off, 1 is on + // CATEGORY: QUICK_SETTINGS + // OS: P + QS_ALARM = 1290; + + // OPEN: Settings->Connected Devices->USB->(click on details link) + // CATEGORY: SETTINGS + // OS: P + USB_DEVICE_DETAILS = 1291; + // ---- End P Constants, all P constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index db70184e9e9f..08fdb9775d0a 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -200,6 +200,10 @@ message SystemMessage { // Package: android NOTE_CARRIER_NETWORK_AVAILABLE = 46; + // Inform that USB is configured for Tethering + // Package: android + NOTE_USB_TETHER = 47; + // ADD_NEW_IDS_ABOVE_THIS_LINE // Legacy IDs with arbitrary values appear below // Legacy IDs existed as stable non-conflicting constants prior to the O release diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto index f5349df0bd94..c77dcc01b7a1 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -63,7 +63,7 @@ message WifiLog { // Number scans that returned at least one result. optional int32 num_non_empty_scan_results = 13; - // Number of scans that were one time. + // Number of single scans requests. optional int32 num_oneshot_scans = 14; // Number of repeated background scans that were scheduled to the chip. @@ -373,6 +373,12 @@ message WifiLog { // Wps connection metrics optional WpsMetrics wps_metrics = 91; + + // Wifi power statistics + optional WifiPowerStats wifi_power_stats = 92; + + // Number of connectivity single scan requests. + optional int32 num_connectivity_oneshot_scans = 93; } // Information that gets logged for every WiFi connection. @@ -1134,3 +1140,22 @@ message WpsMetrics { // Total number of wps cancellation optional int32 num_wps_cancellation = 8; } + +// Power stats for Wifi +message WifiPowerStats { + + // Duration of log (ms) + optional int64 logging_duration_ms = 1; + + // Energy consumed by wifi (mAh) + optional double energy_consumed_mah = 2; + + // Amount of time wifi is in idle (ms) + optional int64 idle_time_ms = 3; + + // Amount of time wifi is in rx (ms) + optional int64 rx_time_ms = 4; + + // Amount of time wifi is in tx (ms) + optional int64 tx_time_ms = 5; +} diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 5ee3cbfed3f5..5f112c7fc773 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -27,6 +27,7 @@ import android.content.IntentSender; import android.graphics.Point; import android.graphics.Rect; import android.service.autofill.Dataset; +import android.service.autofill.Dataset.DatasetFieldFilter; import android.service.autofill.FillResponse; import android.text.TextUtils; import android.util.Slog; @@ -58,6 +59,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; +import java.util.stream.Collectors; final class FillUi { private static final String TAG = "FillUi"; @@ -185,7 +187,7 @@ final class FillUi { final ArrayList<ViewItem> items = new ArrayList<>(totalItems); if (header != null) { if (sVerbose) Slog.v(TAG, "adding header"); - items.add(new ViewItem(null, null, null, header)); + items.add(new ViewItem(null, null, false, null, header)); } for (int i = 0; i < datasetCount; i++) { final Dataset dataset = response.getDatasets().get(i); @@ -205,21 +207,32 @@ final class FillUi { Slog.e(TAG, "Error inflating remote views", e); continue; } - final Pattern filter = dataset.getFilter(index); + final DatasetFieldFilter filter = dataset.getFilter(index); + Pattern filterPattern = null; String valueText = null; + boolean filterable = true; if (filter == null) { final AutofillValue value = dataset.getFieldValues().get(index); if (value != null && value.isText()) { valueText = value.getTextValue().toString().toLowerCase(); } + } else { + filterPattern = filter.pattern; + if (filterPattern == null) { + if (sVerbose) { + Slog.v(TAG, "Explicitly disabling filter at id " + focusedViewId + + " for dataset #" + index); + } + filterable = false; + } } - items.add(new ViewItem(dataset, filter, valueText, view)); + items.add(new ViewItem(dataset, filterPattern, filterable, valueText, view)); } } if (footer != null) { if (sVerbose) Slog.v(TAG, "adding footer"); - items.add(new ViewItem(null, null, null, footer)); + items.add(new ViewItem(null, null, false, null, footer)); } mAdapter = new ItemsAdapter(items); @@ -354,7 +367,7 @@ final class FillUi { MeasureSpec.AT_MOST); final int itemCount = mAdapter.getCount(); for (int i = 0; i < itemCount; i++) { - View view = mAdapter.getItem(i).view; + final View view = mAdapter.getItem(i).view; view.measure(widthMeasureSpec, heightMeasureSpec); final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x); final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth); @@ -400,13 +413,62 @@ final class FillUi { public final @Nullable Dataset dataset; public final @NonNull View view; public final @Nullable Pattern filter; + public final boolean filterable; - ViewItem(@Nullable Dataset dataset, @Nullable Pattern filter, @Nullable String value, - @NonNull View view) { + /** + * Default constructor. + * + * @param dataset dataset associated with the item or {@code null} if it's a header or + * footer (TODO(b/69796626): make @NonNull if header/footer is refactored out of the list) + * @param filter optional filter set by the service to determine how the item should be + * filtered + * @param filterable optional flag set by the service to indicate this item should not be + * filtered (typically used when the dataset has value but it's sensitive, like a password) + * @param value dataset value + * @param view dataset presentation. + */ + ViewItem(@Nullable Dataset dataset, @Nullable Pattern filter, boolean filterable, + @Nullable String value, @NonNull View view) { this.dataset = dataset; this.value = value; this.view = view; this.filter = filter; + this.filterable = filterable; + } + + /** + * Returns whether this item matches the value input by the user so it can be included + * in the filtered datasets. + */ + public boolean matches(CharSequence filterText) { + if (TextUtils.isEmpty(filterText)) { + // Always show item when the user input is empty + return true; + } + if (!filterable) { + // Service explicitly disabled filtering using a null Pattern. + return false; + } + final String constraintLowerCase = filterText.toString().toLowerCase(); + if (filter != null) { + // Uses pattern provided by service + return filter.matcher(constraintLowerCase).matches(); + } else { + // Compares it with dataset value with dataset + return (value == null) + ? (dataset.getAuthentication() == null) + : value.toLowerCase().startsWith(constraintLowerCase); + } + } + + @Override + public String toString() { + return "ViewItem: [dataset=" + (dataset == null ? "null" : dataset.getId()) + + ", value=" + (value == null ? "null" : value.length() + "_chars") + + ", filterable=" + filterable + + ", filter=" + (filter == null ? "null" : filter.pattern().length() + "_chars") + + ", view=" + view.getAutofillId() + + "]"; } } @@ -509,7 +571,7 @@ final class FillUi { public void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mCallback: "); pw.println(mCallback != null); pw.print(prefix); pw.print("mListView: "); pw.println(mListView); - pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter != null); + pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter); pw.print(prefix); pw.print("mFilterText: "); Helper.printlnRedactedText(pw, mFilterText); pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth); @@ -556,33 +618,14 @@ final class FillUi { public Filter getFilter() { return new Filter() { @Override - protected FilterResults performFiltering(CharSequence constraint) { + protected FilterResults performFiltering(CharSequence filterText) { // No locking needed as mAllItems is final an immutable + final List<ViewItem> filtered = mAllItems.stream() + .filter((item) -> item.matches(filterText)) + .collect(Collectors.toList()); final FilterResults results = new FilterResults(); - if (TextUtils.isEmpty(constraint)) { - results.values = mAllItems; - results.count = mAllItems.size(); - return results; - } - final List<ViewItem> filteredItems = new ArrayList<>(); - final String constraintLowerCase = constraint.toString().toLowerCase(); - final int itemCount = mAllItems.size(); - for (int i = 0; i < itemCount; i++) { - final ViewItem item = mAllItems.get(i); - final boolean matches; - if (item.filter != null) { - matches = item.filter.matcher(constraintLowerCase).matches(); - } else { - matches = (item.value == null) - ? (item.dataset.getAuthentication() == null) - : item.value.toLowerCase().startsWith(constraintLowerCase); - } - if (matches) { - filteredItems.add(item); - } - } - results.values = filteredItems; - results.count = filteredItems.size(); + results.values = filtered; + results.count = filtered.size(); return results; } @@ -624,6 +667,11 @@ final class FillUi { public View getView(int position, View convertView, ViewGroup parent) { return getItem(position).view; } + + @Override + public String toString() { + return "ItemsAdapter: [all=" + mAllItems + ", filtered=" + mFilteredItems + "]"; + } } private final class AnnounceFilterResult implements Runnable { diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 219facd0b002..0502117cd73a 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -34,7 +34,7 @@ option java_package com.android.server 2731 power_soft_sleep_requested (savedwaketimems|2) # Power save state has changed. See BatterySaverController.java for the details. 2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5) -27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|6),(total_duration|2|3),(total_battery_drain|1|6) +27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|1),(delta_battery_drain_percent|1|6),(total_duration|2|3),(total_battery_drain|1|1),(total_battery_drain_percent|1|6) # # Leave IDs through 2740 for more power logs (2730 used by battery_discharge above) diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index fc91d0d7abf1..2f425859fec4 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -59,6 +59,7 @@ import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -468,7 +469,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub int mCurFocusedWindowSoftInputMode; /** - * The client by which {@link #mCurFocusedWindow} was reported. Used only for debugging. + * The client by which {@link #mCurFocusedWindow} was reported. */ ClientState mCurFocusedWindowClient; @@ -2989,8 +2990,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final int uid = Binder.getCallingUid(); if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { return true; - } else if (mCurClient != null && client != null - && mCurClient.client.asBinder() == client.asBinder()) { + } else if (mCurFocusedWindowClient != null && client != null + && mCurFocusedWindowClient.client.asBinder() == client.asBinder()) { return true; } else if (mCurIntent != null && InputMethodUtils.checkIfPackageBelongsToUid( mAppOpsManager, @@ -3026,6 +3027,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + public boolean isInputMethodPickerShownForTest() { + synchronized(mMethodMap) { + if (mSwitchingDialog == null) { + return false; + } + return mSwitchingDialog.isShowing(); + } + } + @Override public void setInputMethod(IBinder token, String id) { if (!calledFromValidUser()) { diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index fe4ac6d771bc..a07a982abc53 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -87,6 +87,7 @@ public class IpSecService extends IIpSecService.Stub { private static final String NETD_SERVICE_NAME = "netd"; private static final int[] DIRECTIONS = new int[] {IpSecManager.DIRECTION_OUT, IpSecManager.DIRECTION_IN}; + private static final String[] WILDCARD_ADDRESSES = new String[]{"0.0.0.0", "::"}; private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms private static final int MAX_PORT_BIND_ATTEMPTS = 10; @@ -413,12 +414,16 @@ public class IpSecService extends IIpSecService.Stub { .append(mTransformQuotaTracker) .append(", mSocketQuotaTracker=") .append(mSocketQuotaTracker) + .append(", mTunnelQuotaTracker=") + .append(mTunnelQuotaTracker) .append(", mSpiRecords=") .append(mSpiRecords) .append(", mTransformRecords=") .append(mTransformRecords) .append(", mEncapSocketRecords=") .append(mEncapSocketRecords) + .append(", mTunnelInterfaceRecords=") + .append(mTunnelInterfaceRecords) .append("}") .toString(); } @@ -815,12 +820,14 @@ public class IpSecService extends IIpSecService.Stub { try { mSrvConfig.getNetdInstance().removeVirtualTunnelInterface(mInterfaceName); - for (int direction : DIRECTIONS) { - int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey; - mSrvConfig - .getNetdInstance() - .ipSecDeleteSecurityPolicy( - 0, direction, mLocalAddress, mRemoteAddress, mark, 0xffffffff); + for(String wildcardAddr : WILDCARD_ADDRESSES) { + for (int direction : DIRECTIONS) { + int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey; + mSrvConfig + .getNetdInstance() + .ipSecDeleteSecurityPolicy( + 0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff); + } } } catch (ServiceSpecificException e) { // FIXME: get the error code and throw is at an IOException from Errno Exception @@ -1261,19 +1268,21 @@ public class IpSecService extends IIpSecService.Stub { .getNetdInstance() .addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey); - for (int direction : DIRECTIONS) { - int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey; + for(String wildcardAddr : WILDCARD_ADDRESSES) { + for (int direction : DIRECTIONS) { + int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey; - mSrvConfig - .getNetdInstance() - .ipSecAddSecurityPolicy( + mSrvConfig + .getNetdInstance() + .ipSecAddSecurityPolicy( 0, // Use 0 for reqId direction, - "", - "", + wildcardAddr, + wildcardAddr, 0, mark, 0xffffffff); + } } userRecord.mTunnelInterfaceRecords.put( @@ -1646,16 +1655,18 @@ public class IpSecService extends IIpSecService.Stub { c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork()); // If outbound, also add SPI to the policy. - mSrvConfig - .getNetdInstance() - .ipSecUpdateSecurityPolicy( - 0, // Use 0 for reqId - direction, - "", - "", - transformInfo.getSpiRecord().getSpi(), - mark, - 0xffffffff); + for(String wildcardAddr : WILDCARD_ADDRESSES) { + mSrvConfig + .getNetdInstance() + .ipSecUpdateSecurityPolicy( + 0, // Use 0 for reqId + direction, + wildcardAddr, + wildcardAddr, + transformInfo.getSpiRecord().getSpi(), + mark, + 0xffffffff); + } } // Update SA with tunnel mark (ikey or okey based on direction) diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 1dd92f3130a7..65e90bad41a7 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -1397,23 +1397,6 @@ public class LocationManagerService extends ILocationManager.Stub { } /** - * Returns "true" if access to the specified location provider is allowed by the specified - * user's settings. Access to all location providers is forbidden to non-location-provider - * processes belonging to background users. - * - * @param provider the name of the location provider - * @param uid the requestor's UID - * @param userId the user id to query - */ - private boolean isAllowedByUserSettingsLockedForUser( - String provider, int uid, int userId) { - if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) { - return false; - } - return isLocationProviderEnabledForUser(provider, userId); - } - - /** * Returns the permission string associated with the specified resolution level. * * @param resolutionLevel the resolution level @@ -2585,143 +2568,6 @@ public class LocationManagerService extends ILocationManager.Stub { } /** - * Method for enabling or disabling location. - * - * @param enabled true to enable location. false to disable location - * @param userId the user id to set - */ - @Override - public void setLocationEnabledForUser(boolean enabled, int userId) { - // Check INTERACT_ACROSS_USERS permission if userId is not current user id. - checkInteractAcrossUsersPermission(userId); - - // Enable or disable all location providers. Fused provider and passive provider are - // excluded. - synchronized (mLock) { - for(String provider : getAllProvidersForLocationSettings()) { - setProviderEnabledForUser(provider, enabled, userId); - } - } - } - - /** - * Returns the current enabled/disabled status of location - * - * @param userId the user id to query - * @return true if location is enabled. false if location is disabled. - */ - @Override - public boolean isLocationEnabledForUser(int userId) { - // Check INTERACT_ACROSS_USERS permission if userId is not current user id. - checkInteractAcrossUsersPermission(userId); - - // If at least one location provider is enabled, return true. Fused provider and passive - // provider are excluded. - synchronized (mLock) { - for (String provider : getAllProvidersForLocationSettings()) { - if (isProviderEnabledForUser(provider, userId)) { - return true; - } - } - return false; - } - } - - @Override - public boolean isProviderEnabled(String provider) { - return isProviderEnabledForUser(provider, UserHandle.getCallingUserId()); - } - - /** - * Method for determining if a location provider is enabled. - * - * @param provider the location provider to query - * @param userId the user id to query - * @return true if the provider is enabled - */ - @Override - public boolean isProviderEnabledForUser(String provider, int userId) { - // Check INTERACT_ACROSS_USERS permission if userId is not current user id. - checkInteractAcrossUsersPermission(userId); - - // Fused provider is accessed indirectly via criteria rather than the provider-based APIs, - // so we discourage its use - if (LocationManager.FUSED_PROVIDER.equals(provider)) return false; - - int uid = Binder.getCallingUid(); - long identity = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - LocationProviderInterface p = mProvidersByName.get(provider); - return p != null - && isAllowedByUserSettingsLockedForUser(provider, uid, userId); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Method for enabling or disabling a single location provider. - * - * @param provider the name of the provider - * @param enabled true to enable the provider. false to disable the provider - * @param userId the user id to set - * @return true if the value was set successfully. false on failure. - */ - @Override - public boolean setProviderEnabledForUser( - String provider, boolean enabled, int userId) { - mContext.enforceCallingPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS, - "Requires WRITE_SECURE_SETTINGS permission"); - - // Check INTERACT_ACROSS_USERS permission if userId is not current user id. - checkInteractAcrossUsersPermission(userId); - - final long identity = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - // to ensure thread safety, we write the provider name with a '+' or '-' - // and let the SettingsProvider handle it rather than reading and modifying - // the list of enabled providers. - if (enabled) { - provider = "+" + provider; - } else { - provider = "-" + provider; - } - return Settings.Secure.putStringForUser( - mContext.getContentResolver(), - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - provider, - userId); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Return all location providers except fused provider and passive provider. These two - * providers are not generating location by themselves, but only echo locations from other - * providers. - * - * @return All location providers except fused provider and passive provider, including - * providers that are not permitted to be accessed by the calling activity or are - * currently disabled. - */ - private List<String> getAllProvidersForLocationSettings() { - List<String> providersForSettings = new ArrayList<>(mProviders.size()); - for (String provider : getAllProviders()) { - if (provider.equals(LocationManager.PASSIVE_PROVIDER)) { - continue; - } - providersForSettings.add(provider); - } - return providersForSettings; - } - - /** * Read location provider status from Settings.Secure * * @param provider the location provider to query @@ -2742,23 +2588,6 @@ public class LocationManagerService extends ILocationManager.Stub { } /** - * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as - * current user id - * - * @param userId the user id to get or set value - */ - private void checkInteractAcrossUsersPermission(int userId) { - int uid = Binder.getCallingUid(); - if (UserHandle.getUserId(uid) != userId) { - if (ActivityManager.checkComponentPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true) - != PERMISSION_GRANTED) { - throw new SecurityException("Requires INTERACT_ACROSS_USERS permission"); - } - } - } - - /** * Returns "true" if the UID belongs to a bound location provider. * * @param uid the uid diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 6747be340d46..6743484b91c4 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -35,6 +35,7 @@ import android.os.UserHandle; import android.telephony.CellInfo; import android.telephony.CellLocation; import android.telephony.DisconnectCause; +import android.telephony.LocationAccessPolicy; import android.telephony.PhoneStateListener; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; @@ -93,7 +94,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { IPhoneStateListener callback; IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback; - int callerUserId; + int callerUid; + int callerPid; int events; @@ -117,7 +119,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { + " callback=" + callback + " onSubscriptionsChangedListenererCallback=" + onSubscriptionsChangedListenerCallback - + " callerUserId=" + callerUserId + " subId=" + subId + " phoneId=" + phoneId + + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId + " events=" + Integer.toHexString(events) + " canReadPhoneState=" + canReadPhoneState + "}"; } @@ -356,6 +358,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { public void addOnSubscriptionsChangedListener(String callingPackage, IOnSubscriptionsChangedListener callback) { int callerUserId = UserHandle.getCallingUserId(); + mContext.getSystemService(AppOpsManager.class) + .checkPackage(Binder.getCallingUid(), callingPackage); if (VDBG) { log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId + " callback=" + callback @@ -399,7 +403,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { r.onSubscriptionsChangedListenerCallback = callback; r.callingPackage = callingPackage; - r.callerUserId = callerUserId; + r.callerUid = Binder.getCallingUid(); + r.callerPid = Binder.getCallingPid(); r.events = 0; r.canReadPhoneState = true; // permission has been enforced above if (DBG) { @@ -470,6 +475,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private void listen(String callingPackage, IPhoneStateListener callback, int events, boolean notifyNow, int subId) { int callerUserId = UserHandle.getCallingUserId(); + mContext.getSystemService(AppOpsManager.class) + .checkPackage(Binder.getCallingUid(), callingPackage); if (VDBG) { log("listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events) + " notifyNow=" + notifyNow + " subId=" + subId + " myUserId=" @@ -514,7 +521,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callback = callback; r.callingPackage = callingPackage; - r.callerUserId = callerUserId; + r.callerUid = Binder.getCallingUid(); + r.callerPid = Binder.getCallingPid(); boolean isPhoneStateEvent = (events & (CHECK_PHONE_STATE_PERMISSION_MASK | ENFORCE_PHONE_STATE_PERMISSION_MASK)) != 0; r.canReadPhoneState = isPhoneStateEvent && canReadPhoneState(callingPackage); @@ -572,8 +580,10 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("listen: mCellLocation = " + mCellLocation[phoneId]); - r.callback.onCellLocationChanged( - new Bundle(mCellLocation[phoneId])); + if (checkLocationAccess(r)) { + r.callback.onCellLocationChanged( + new Bundle(mCellLocation[phoneId])); + } } catch (RemoteException ex) { remove(r.binder); } @@ -619,7 +629,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " + mCellInfo.get(phoneId)); - r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); + if (checkLocationAccess(r)) { + r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); + } } catch (RemoteException ex) { remove(r.binder); } @@ -979,7 +991,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mCellInfo.set(phoneId, cellInfo); for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) && - idMatch(r.subId, subId, phoneId)) { + idMatch(r.subId, subId, phoneId) && + checkLocationAccess(r)) { try { if (DBG_LOC) { log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r); @@ -1262,7 +1275,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mCellLocation[phoneId] = cellLocation; for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && - idMatch(r.subId, subId, phoneId)) { + idMatch(r.subId, subId, phoneId) && + checkLocationAccess(r)) { try { if (DBG_LOC) { log("notifyCellLocation: cellLocation=" + cellLocation @@ -1706,10 +1720,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { boolean valid = false; try { foregroundUser = ActivityManager.getCurrentUser(); - valid = r.callerUserId == foregroundUser && r.matchPhoneStateListenerEvent(events); + valid = UserHandle.getUserId(r.callerUid) == foregroundUser + && r.matchPhoneStateListenerEvent(events); if (DBG | DBG_LOC) { log("validateEventsAndUserLocked: valid=" + valid - + " r.callerUserId=" + r.callerUserId + " foregroundUser=" + foregroundUser + + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser + " r.events=" + r.events + " events=" + events); } } finally { @@ -1741,6 +1756,16 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + private boolean checkLocationAccess(Record r) { + long token = Binder.clearCallingIdentity(); + try { + return LocationAccessPolicy.canAccessCellLocation(mContext, + r.callingPackage, r.callerUid, r.callerPid); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private void checkPossibleMissNotify(Record r, int phoneId) { int events = r.events; @@ -1788,7 +1813,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = " + mCellInfo.get(phoneId)); } - r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); + if (checkLocationAccess(r)) { + r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); + } } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -1836,7 +1863,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = " + mCellLocation[phoneId]); - r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId])); + if (checkLocationAccess(r)) { + r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId])); + } } catch (RemoteException ex) { mRemoveList.add(r.binder); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5b8b6912cd19..8d1632a3cef6 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -802,6 +802,7 @@ public class ActivityManagerService extends IActivityManager.Stub doDump(fd, pw, new String[]{"recents"}, asProto); doDump(fd, pw, new String[]{"lastanr"}, asProto); doDump(fd, pw, new String[]{"starter"}, asProto); + doDump(fd, pw, new String[]{"containers"}, asProto); if (mAssociations.size() > 0) { doDump(fd, pw, new String[]{"associations"}, asProto); } @@ -4027,9 +4028,13 @@ public class ActivityManagerService extends IActivityManager.Stub runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; } String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info"); - if ("true".equals(genDebugInfoProperty)) { + if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) { runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; } + String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo"); + if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) { + runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO; + } if ("1".equals(SystemProperties.get("debug.jni.logging"))) { runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING; } @@ -4331,7 +4336,9 @@ public class ActivityManagerService extends IActivityManager.Stub final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED, component.userId, component.realActivity.getPackageName(), - component.realActivity.getShortClassName(), resumed ? 1 : 0); + component.realActivity.getShortClassName(), resumed ? + StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_FOREGROUND : + StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_BACKGROUND); if (resumed) { if (mUsageStatsService != null) { mUsageStatsService.reportEvent(component.realActivity, component.userId, @@ -12844,7 +12851,8 @@ public class ActivityManagerService extends IActivityManager.Stub } // TODO: Where should the corresponding '1' (start) write go? - StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED, 0); + StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED, + StatsLog.DEVICE_ON_STATUS_CHANGED__STATE__OFF); boolean timedout = false; diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index f0c90e0f1fab..24a77c7693d0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -115,6 +115,7 @@ final class ActivityManagerShellCommand extends ShellCommand { private int mActivityType; private int mTaskId; private boolean mIsTaskOverlay; + private boolean mIsLockTask; final boolean mDumping; @@ -278,6 +279,7 @@ final class ActivityManagerShellCommand extends ShellCommand { mActivityType = ACTIVITY_TYPE_UNDEFINED; mTaskId = INVALID_TASK_ID; mIsTaskOverlay = false; + mIsLockTask = false; return Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() { @Override @@ -334,6 +336,8 @@ final class ActivityManagerShellCommand extends ShellCommand { mTaskId = Integer.parseInt(getNextArgRequired()); } else if (opt.equals("--task-overlay")) { mIsTaskOverlay = true; + } else if (opt.equals("--lock-task")) { + mIsLockTask = true; } else { return false; } @@ -429,13 +433,22 @@ final class ActivityManagerShellCommand extends ShellCommand { options.setLaunchActivityType(mActivityType); } if (mTaskId != INVALID_TASK_ID) { - options = ActivityOptions.makeBasic(); + if (options == null) { + options = ActivityOptions.makeBasic(); + } options.setLaunchTaskId(mTaskId); if (mIsTaskOverlay) { options.setTaskOverlay(true, true /* canResume */); } } + android.util.Log.d("bfranz", "I was here: " + mIsLockTask); + if (mIsLockTask) { + if (options == null) { + options = ActivityOptions.makeBasic(); + } + options.setLockTaskMode(true); + } if (mWaitOption) { result = mInterface.startActivityAndWait(null, null, intent, mimeType, null, null, 0, mStartFlags, profilerInfo, diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 6beafcb94ead..bf3882587693 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1251,10 +1251,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D synchronized (mService) { try { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent"); + int modifiedFlags = flags + | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS; + if (intent.isBrowsableWebIntent() + || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { + modifiedFlags |= PackageManager.MATCH_INSTANT; + } return mService.getPackageManagerInternalLocked().resolveIntent( - intent, resolvedType, PackageManager.MATCH_INSTANT - | PackageManager.MATCH_DEFAULT_ONLY | flags - | ActivityManagerService.STOCK_PM_FLAGS, userId, true); + intent, resolvedType, modifiedFlags, userId, true); } finally { Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 8fd754af1a0f..eab88aa45e44 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -31,6 +31,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT; @@ -789,7 +790,7 @@ class ActivityStarter { // Instead, launch the ephemeral installer. Once the installer is finished, it // starts either the intent we resolved here [on install error] or the ephemeral // app [on install success]. - if (rInfo != null && rInfo.auxiliaryInfo != null) { + if (rInfo != null && rInfo.isInstantAppAvailable) { intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent, callingPackage, verificationBundle, resolvedType, userId); resolvedType = null; @@ -849,22 +850,27 @@ class ActivityStarter { /** * Creates a launch intent for the given auxiliary resolution data. */ - private @NonNull Intent createLaunchIntent(@NonNull AuxiliaryResolveInfo auxiliaryResponse, + private @NonNull Intent createLaunchIntent(@Nullable AuxiliaryResolveInfo auxiliaryResponse, Intent originalIntent, String callingPackage, Bundle verificationBundle, String resolvedType, int userId) { - if (auxiliaryResponse.needsPhaseTwo) { + if (auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo) { // request phase two resolution mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo( auxiliaryResponse, originalIntent, resolvedType, callingPackage, verificationBundle, userId); } return InstantAppResolver.buildEphemeralInstallerIntent( - Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE, originalIntent, - auxiliaryResponse.failureIntent, callingPackage, verificationBundle, - resolvedType, userId, auxiliaryResponse.packageName, auxiliaryResponse.splitName, - auxiliaryResponse.installFailureActivity, auxiliaryResponse.versionCode, - auxiliaryResponse.token, auxiliaryResponse.resolveInfo.getExtras(), - auxiliaryResponse.needsPhaseTwo); + originalIntent, + InstantAppResolver.sanitizeIntent(originalIntent), + auxiliaryResponse == null ? null : auxiliaryResponse.failureIntent, + callingPackage, + verificationBundle, + resolvedType, + userId, + auxiliaryResponse == null ? null : auxiliaryResponse.installFailureActivity, + auxiliaryResponse == null ? null : auxiliaryResponse.token, + auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo, + auxiliaryResponse == null ? null : auxiliaryResponse.filters); } void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) { @@ -924,12 +930,12 @@ class ActivityStarter { // Don't modify the client's object! intent = new Intent(intent); if (componentSpecified - && intent.getData() != null - && Intent.ACTION_VIEW.equals(intent.getAction()) + && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction()) + && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction()) && mService.getPackageManagerInternalLocked() .isInstantAppInstallerComponent(intent.getComponent())) { // intercept intents targeted directly to the ephemeral installer the - // ephemeral installer should never be started with a raw URL; instead + // ephemeral installer should never be started with a raw Intent; instead // adjust the intent so it looks like a "normal" instant app launch intent.setComponent(null /*component*/); componentSpecified = false; diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 0d96468761be..ea52782027ba 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -325,39 +325,38 @@ public final class BatteryStatsService extends IBatteryStats.Stub void noteProcessStart(String name, int uid) { synchronized (mStats) { mStats.noteProcessStartLocked(name, uid); - // TODO: decide where this should be and use a constant instead of a literal. - StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 1); + StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, + StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_STARTED); } } void noteProcessCrash(String name, int uid) { synchronized (mStats) { mStats.noteProcessCrashLocked(name, uid); - // TODO: decide where this should be and use a constant instead of a literal. - StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 2); + StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, + StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_CRASHED); } } void noteProcessAnr(String name, int uid) { synchronized (mStats) { mStats.noteProcessAnrLocked(name, uid); - // TODO: decide where this should be and use a constant instead of a literal. - StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 3); + StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, + StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_ANRED); } } void noteProcessFinish(String name, int uid) { synchronized (mStats) { mStats.noteProcessFinishLocked(name, uid); - // TODO: decide where this should be and use a constant instead of a literal. - StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 0); + StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, + StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_FINISHED); } } /** @param state Process state from ActivityManager.java. */ void noteUidProcessState(int uid, int state) { synchronized (mStats) { - // TODO: remove this once we figure out properly where and how StatsLog.write(StatsLog.UID_PROCESS_STATE_CHANGED, uid, ActivityManager.processStateAmToProto(state)); @@ -603,7 +602,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub enforceCallingPermission(); if (DBG) Slog.d(TAG, "begin noteScreenState"); synchronized (mStats) { - // TODO: remove this once we figure out properly where and how StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, state); mStats.noteScreenStateLocked(state); diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java index 21f9135bd03d..e5762d294922 100644 --- a/services/core/java/com/android/server/am/LockTaskController.java +++ b/services/core/java/com/android/server/am/LockTaskController.java @@ -38,6 +38,7 @@ import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE; import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; @@ -142,14 +143,6 @@ public class LockTaskController { TelecomManager mTelecomManager; /** - * Helper that is responsible for showing the right toast when a disallowed activity operation - * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in - * fully locked mode we only show that unlocking is blocked. - */ - @VisibleForTesting - LockTaskNotify mLockTaskNotify; - - /** * The chain of tasks in LockTask mode, in the order of when they first entered LockTask mode. * * The first task in the list, which started the current LockTask session, is called the root @@ -475,7 +468,7 @@ public class LockTaskController { getDevicePolicyManager().notifyLockTaskModeChanged(false, null, userId); } if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) { - getLockTaskNotify().showPinningExitToast(); + getStatusBarService().showPinningEnterExitToast(false /* entering */); } } catch (RemoteException ex) { throw new RuntimeException(ex); @@ -490,7 +483,11 @@ public class LockTaskController { */ void showLockTaskToast() { if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) { - mHandler.post(() -> getLockTaskNotify().showEscapeToast()); + try { + getStatusBarService().showPinningEscapeToast(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to send pinning escape toast", e); + } } } @@ -582,7 +579,7 @@ public class LockTaskController { // When lock task starts, we disable the status bars. try { if (lockTaskModeState == LOCK_TASK_MODE_PINNED) { - getLockTaskNotify().showPinningStartToast(); + getStatusBarService().showPinningEnterExitToast(true /* entering */); } mLockTaskModeState = lockTaskModeState; setStatusBarState(lockTaskModeState, userId); @@ -835,15 +832,6 @@ public class LockTaskController { return mTelecomManager; } - // Should only be called on the handler thread - @NonNull - private LockTaskNotify getLockTaskNotify() { - if (mLockTaskNotify == null) { - mLockTaskNotify = new LockTaskNotify(mContext); - } - return mLockTaskNotify; - } - public void dump(PrintWriter pw, String prefix) { pw.println(prefix + "LockTaskController"); prefix = prefix + " "; diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java index 3bb3d1fdd032..5af19eca5034 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.hardware.broadcastradio.V2_0.AmFmBandRange; import android.hardware.broadcastradio.V2_0.AmFmRegionConfig; import android.hardware.broadcastradio.V2_0.Announcement; +import android.hardware.broadcastradio.V2_0.DabTableEntry; import android.hardware.broadcastradio.V2_0.IdentifierType; import android.hardware.broadcastradio.V2_0.ProgramFilter; import android.hardware.broadcastradio.V2_0.ProgramIdentifier; @@ -196,9 +197,15 @@ class Convert { return bands.toArray(new RadioManager.BandDescriptor[bands.size()]); } + private static @Nullable Map<String, Integer> dabConfigFromHal( + @Nullable List<DabTableEntry> config) { + if (config == null) return null; + return config.stream().collect(Collectors.toMap(e -> e.label, e -> e.frequency)); + } + static @NonNull RadioManager.ModuleProperties propertiesFromHal(int id, @NonNull String serviceName, @NonNull Properties prop, - @Nullable AmFmRegionConfig amfmConfig) { + @Nullable AmFmRegionConfig amfmConfig, @Nullable List<DabTableEntry> dabConfig) { Objects.requireNonNull(serviceName); Objects.requireNonNull(prop); @@ -228,6 +235,7 @@ class Convert { true, // isBgScanSupported is deprecated supportedProgramTypes, supportedIdentifierTypes, + dabConfigFromHal(dabConfig), vendorInfoFromHal(prop.vendorInfo) ); } diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java index 50f032d659b7..daec97a0a166 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java @@ -24,6 +24,7 @@ import android.hardware.radio.ITuner; import android.hardware.radio.RadioManager; import android.hardware.broadcastradio.V2_0.AmFmRegionConfig; import android.hardware.broadcastradio.V2_0.Announcement; +import android.hardware.broadcastradio.V2_0.DabTableEntry; import android.hardware.broadcastradio.V2_0.IAnnouncementListener; import android.hardware.broadcastradio.V2_0.IBroadcastRadio; import android.hardware.broadcastradio.V2_0.ICloseHandle; @@ -58,12 +59,17 @@ class RadioModule { if (service == null) return null; Mutable<AmFmRegionConfig> amfmConfig = new Mutable<>(); - service.getAmFmRegionConfig(false, (int result, AmFmRegionConfig config) -> { + service.getAmFmRegionConfig(false, (result, config) -> { if (result == Result.OK) amfmConfig.value = config; }); - RadioManager.ModuleProperties prop = - Convert.propertiesFromHal(idx, fqName, service.getProperties(), amfmConfig.value); + Mutable<List<DabTableEntry>> dabConfig = new Mutable<>(); + service.getDabRegionConfig((result, config) -> { + if (result == Result.OK) dabConfig.value = config; + }); + + RadioManager.ModuleProperties prop = Convert.propertiesFromHal(idx, fqName, + service.getProperties(), amfmConfig.value, dabConfig.value); return new RadioModule(service, prop); } catch (RemoteException ex) { diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index be6c4a12d114..9a9cdbde3987 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -1095,7 +1095,8 @@ public class Tethering extends BaseNetworkObserver { if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")"); UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); synchronized (mPublicSync) { - usbManager.setCurrentFunction(enable ? UsbManager.USB_FUNCTION_RNDIS : null, false); + usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS + : UsbManager.FUNCTION_NONE); } return ConnectivityManager.TETHER_ERROR_NO_ERROR; } diff --git a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java new file mode 100644 index 000000000000..6e571bd75946 --- /dev/null +++ b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java @@ -0,0 +1,363 @@ +/* + * Copyright 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.display; + +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.hardware.display.AmbientBrightnessDayStats; +import android.os.SystemClock; +import android.os.UserManager; +import android.util.Slog; +import android.util.Xml; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FastXmlSerializer; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.Map; + +/** + * Class that stores stats of ambient brightness regions as histogram. + */ +public class AmbientBrightnessStatsTracker { + + private static final String TAG = "AmbientBrightnessStatsTracker"; + private static final boolean DEBUG = false; + + @VisibleForTesting + static final float[] BUCKET_BOUNDARIES_FOR_NEW_STATS = + {0, 0.1f, 0.3f, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000}; + @VisibleForTesting + static final int MAX_DAYS_TO_TRACK = 7; + + private final AmbientBrightnessStats mAmbientBrightnessStats; + private final Timer mTimer; + private final Injector mInjector; + private final UserManager mUserManager; + private float mCurrentAmbientBrightness; + private @UserIdInt int mCurrentUserId; + + public AmbientBrightnessStatsTracker(UserManager userManager, @Nullable Injector injector) { + mUserManager = userManager; + if (injector != null) { + mInjector = injector; + } else { + mInjector = new Injector(); + } + mAmbientBrightnessStats = new AmbientBrightnessStats(); + mTimer = new Timer(() -> mInjector.elapsedRealtimeMillis()); + mCurrentAmbientBrightness = -1; + } + + public synchronized void start() { + mTimer.reset(); + mTimer.start(); + } + + public synchronized void stop() { + if (mTimer.isRunning()) { + mAmbientBrightnessStats.log(mCurrentUserId, mInjector.getLocalDate(), + mCurrentAmbientBrightness, mTimer.totalDurationSec()); + } + mTimer.reset(); + mCurrentAmbientBrightness = -1; + } + + public synchronized void add(@UserIdInt int userId, float newAmbientBrightness) { + if (mTimer.isRunning()) { + if (userId == mCurrentUserId) { + mAmbientBrightnessStats.log(mCurrentUserId, mInjector.getLocalDate(), + mCurrentAmbientBrightness, mTimer.totalDurationSec()); + } else { + if (DEBUG) { + Slog.v(TAG, "User switched since last sensor event."); + } + mCurrentUserId = userId; + } + mTimer.reset(); + mTimer.start(); + mCurrentAmbientBrightness = newAmbientBrightness; + } else { + if (DEBUG) { + Slog.e(TAG, "Timer not running while trying to add brightness stats."); + } + } + } + + public synchronized void writeStats(OutputStream stream) throws IOException { + mAmbientBrightnessStats.writeToXML(stream); + } + + public synchronized void readStats(InputStream stream) throws IOException { + mAmbientBrightnessStats.readFromXML(stream); + } + + public synchronized ArrayList<AmbientBrightnessDayStats> getUserStats(int userId) { + return mAmbientBrightnessStats.getUserStats(userId); + } + + public synchronized void dump(PrintWriter pw) { + pw.println("AmbientBrightnessStats:"); + pw.print(mAmbientBrightnessStats); + } + + /** + * AmbientBrightnessStats tracks ambient brightness stats across users over multiple days. + * This class is not ThreadSafe. + */ + class AmbientBrightnessStats { + + private static final String TAG_AMBIENT_BRIGHTNESS_STATS = "ambient-brightness-stats"; + private static final String TAG_AMBIENT_BRIGHTNESS_DAY_STATS = + "ambient-brightness-day-stats"; + private static final String ATTR_USER = "user"; + private static final String ATTR_LOCAL_DATE = "local-date"; + private static final String ATTR_BUCKET_BOUNDARIES = "bucket-boundaries"; + private static final String ATTR_BUCKET_STATS = "bucket-stats"; + + private Map<Integer, Deque<AmbientBrightnessDayStats>> mStats; + + public AmbientBrightnessStats() { + mStats = new HashMap<>(); + } + + public void log(@UserIdInt int userId, LocalDate localDate, float ambientBrightness, + float durationSec) { + Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats(mStats, userId); + AmbientBrightnessDayStats dayStats = getOrCreateDayStats(userStats, localDate); + dayStats.log(ambientBrightness, durationSec); + } + + public ArrayList<AmbientBrightnessDayStats> getUserStats(@UserIdInt int userId) { + if (mStats.containsKey(userId)) { + return new ArrayList<>(mStats.get(userId)); + } else { + return null; + } + } + + public void writeToXML(OutputStream stream) throws IOException { + XmlSerializer out = new FastXmlSerializer(); + out.setOutput(stream, StandardCharsets.UTF_8.name()); + out.startDocument(null, true); + out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + final LocalDate cutOffDate = mInjector.getLocalDate().minusDays(MAX_DAYS_TO_TRACK); + out.startTag(null, TAG_AMBIENT_BRIGHTNESS_STATS); + for (Map.Entry<Integer, Deque<AmbientBrightnessDayStats>> entry : mStats.entrySet()) { + for (AmbientBrightnessDayStats userDayStats : entry.getValue()) { + int userSerialNumber = mInjector.getUserSerialNumber(mUserManager, + entry.getKey()); + if (userSerialNumber != -1 && userDayStats.getLocalDate().isAfter(cutOffDate)) { + out.startTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS); + out.attribute(null, ATTR_USER, Integer.toString(userSerialNumber)); + out.attribute(null, ATTR_LOCAL_DATE, + userDayStats.getLocalDate().toString()); + StringBuilder bucketBoundariesValues = new StringBuilder(); + StringBuilder timeSpentValues = new StringBuilder(); + for (int i = 0; i < userDayStats.getBucketBoundaries().length; i++) { + if (i > 0) { + bucketBoundariesValues.append(","); + timeSpentValues.append(","); + } + bucketBoundariesValues.append(userDayStats.getBucketBoundaries()[i]); + timeSpentValues.append(userDayStats.getStats()[i]); + } + out.attribute(null, ATTR_BUCKET_BOUNDARIES, + bucketBoundariesValues.toString()); + out.attribute(null, ATTR_BUCKET_STATS, timeSpentValues.toString()); + out.endTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS); + } + } + } + out.endTag(null, TAG_AMBIENT_BRIGHTNESS_STATS); + out.endDocument(); + stream.flush(); + } + + public void readFromXML(InputStream stream) throws IOException { + try { + Map<Integer, Deque<AmbientBrightnessDayStats>> parsedStats = new HashMap<>(); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(stream, StandardCharsets.UTF_8.name()); + + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + } + String tag = parser.getName(); + if (!TAG_AMBIENT_BRIGHTNESS_STATS.equals(tag)) { + throw new XmlPullParserException( + "Ambient brightness stats not found in tracker file " + tag); + } + + final LocalDate cutOffDate = mInjector.getLocalDate().minusDays(MAX_DAYS_TO_TRACK); + parser.next(); + int outerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + tag = parser.getName(); + if (TAG_AMBIENT_BRIGHTNESS_DAY_STATS.equals(tag)) { + String userSerialNumber = parser.getAttributeValue(null, ATTR_USER); + LocalDate localDate = LocalDate.parse( + parser.getAttributeValue(null, ATTR_LOCAL_DATE)); + String[] bucketBoundaries = parser.getAttributeValue(null, + ATTR_BUCKET_BOUNDARIES).split(","); + String[] bucketStats = parser.getAttributeValue(null, + ATTR_BUCKET_STATS).split(","); + if (bucketBoundaries.length != bucketStats.length + || bucketBoundaries.length < 1) { + throw new IOException("Invalid brightness stats string."); + } + float[] parsedBucketBoundaries = new float[bucketBoundaries.length]; + float[] parsedBucketStats = new float[bucketStats.length]; + for (int i = 0; i < bucketBoundaries.length; i++) { + parsedBucketBoundaries[i] = Float.parseFloat(bucketBoundaries[i]); + parsedBucketStats[i] = Float.parseFloat(bucketStats[i]); + } + int userId = mInjector.getUserId(mUserManager, + Integer.parseInt(userSerialNumber)); + if (userId != -1 && localDate.isAfter(cutOffDate)) { + Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats( + parsedStats, userId); + userStats.offer( + new AmbientBrightnessDayStats(localDate, + parsedBucketBoundaries, parsedBucketStats)); + } + } + } + mStats = parsedStats; + } catch (NullPointerException | NumberFormatException | XmlPullParserException | + DateTimeParseException | IOException e) { + throw new IOException("Failed to parse brightness stats file.", e); + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (Map.Entry<Integer, Deque<AmbientBrightnessDayStats>> entry : mStats.entrySet()) { + for (AmbientBrightnessDayStats dayStats : entry.getValue()) { + builder.append(" "); + builder.append(entry.getKey()).append(" "); + builder.append(dayStats).append("\n"); + } + } + return builder.toString(); + } + + private Deque<AmbientBrightnessDayStats> getOrCreateUserStats( + Map<Integer, Deque<AmbientBrightnessDayStats>> stats, @UserIdInt int userId) { + if (!stats.containsKey(userId)) { + stats.put(userId, new ArrayDeque<>()); + } + return stats.get(userId); + } + + private AmbientBrightnessDayStats getOrCreateDayStats( + Deque<AmbientBrightnessDayStats> userStats, LocalDate localDate) { + AmbientBrightnessDayStats lastBrightnessStats = userStats.peekLast(); + if (lastBrightnessStats != null && lastBrightnessStats.getLocalDate().equals( + localDate)) { + return lastBrightnessStats; + } else { + AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(localDate, + BUCKET_BOUNDARIES_FOR_NEW_STATS); + if (userStats.size() == MAX_DAYS_TO_TRACK) { + userStats.poll(); + } + userStats.offer(dayStats); + return dayStats; + } + } + } + + @VisibleForTesting + interface Clock { + long elapsedTimeMillis(); + } + + @VisibleForTesting + static class Timer { + + private final Clock clock; + private long startTimeMillis; + private boolean started; + + public Timer(Clock clock) { + this.clock = clock; + } + + public void reset() { + started = false; + } + + public void start() { + if (!started) { + startTimeMillis = clock.elapsedTimeMillis(); + started = true; + } + } + + public boolean isRunning() { + return started; + } + + public float totalDurationSec() { + if (started) { + return (float) ((clock.elapsedTimeMillis() - startTimeMillis) / 1000.0); + } + return 0; + } + } + + @VisibleForTesting + static class Injector { + public long elapsedRealtimeMillis() { + return SystemClock.elapsedRealtime(); + } + + public int getUserSerialNumber(UserManager userManager, int userId) { + return userManager.getUserSerialNumber(userId); + } + + public int getUserId(UserManager userManager, int userSerialNumber) { + return userManager.getUserHandle(userSerialNumber); + } + + public LocalDate getLocalDate() { + return LocalDate.now(); + } + } +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 6a88b1e1735d..e2aee88d331a 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -269,6 +269,14 @@ class AutomaticBrightnessController { } } + public boolean hasUserDataPoints() { + return mBrightnessMapper.hasUserDataPoints(); + } + + public boolean isDefaultConfig() { + return mBrightnessMapper.isDefaultConfig(); + } + private boolean setDisplayPolicy(int policy) { if (mDisplayPolicy == policy) { return false; diff --git a/services/core/java/com/android/server/display/BrightnessIdleJob.java b/services/core/java/com/android/server/display/BrightnessIdleJob.java index 876acf45fda8..b0a41cb589eb 100644 --- a/services/core/java/com/android/server/display/BrightnessIdleJob.java +++ b/services/core/java/com/android/server/display/BrightnessIdleJob.java @@ -70,7 +70,7 @@ public class BrightnessIdleJob extends JobService { Slog.d(BrightnessTracker.TAG, "Scheduled write of brightness events"); } DisplayManagerInternal dmi = LocalServices.getService(DisplayManagerInternal.class); - dmi.persistBrightnessSliderEvents(); + dmi.persistBrightnessTrackerState(); 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 001d4d903f3f..c0d259992a46 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -200,6 +200,12 @@ public abstract class BrightnessMappingStrategy { */ public abstract void clearUserDataPoints(); + /** @return true if there are any short term adjustments applied to the curve */ + public abstract boolean hasUserDataPoints(); + + /** @return true if the current brightness config is the default one */ + public abstract boolean isDefaultConfig(); + public abstract void dump(PrintWriter pw); private static float normalizeAbsoluteBrightness(int brightness) { @@ -390,6 +396,16 @@ public abstract class BrightnessMappingStrategy { } @Override + public boolean hasUserDataPoints() { + return mUserLux != -1; + } + + @Override + public boolean isDefaultConfig() { + return true; + } + + @Override public void dump(PrintWriter pw) { pw.println("SimpleMappingStrategy"); pw.println(" mSpline=" + mSpline); @@ -507,6 +523,16 @@ public abstract class BrightnessMappingStrategy { } @Override + public boolean hasUserDataPoints() { + return mUserLux != -1; + } + + @Override + public boolean isDefaultConfig() { + return mDefaultConfig.equals(mConfig); + } + + @Override public void dump(PrintWriter pw) { pw.println("PhysicalMappingStrategy"); pw.println(" mConfig=" + mConfig); diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index bcf8bfe6aaad..6b4666a679ed 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -17,6 +17,7 @@ package com.android.server.display; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -28,8 +29,8 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; -import android.net.Uri; import android.os.BatteryManager; import android.os.Environment; import android.os.Handler; @@ -81,6 +82,7 @@ public class BrightnessTracker { static final boolean DEBUG = false; private static final String EVENTS_FILE = "brightness_events.xml"; + private static final String AMBIENT_BRIGHTNESS_STATS_FILE = "ambient_brightness_stats.xml"; private static final int MAX_EVENTS = 100; // Discard events when reading or writing that are older than this. private static final long MAX_EVENT_AGE = TimeUnit.DAYS.toMillis(30); @@ -99,6 +101,9 @@ public class BrightnessTracker { private static final String ATTR_NIGHT_MODE = "nightMode"; private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature"; private static final String ATTR_LAST_NITS = "lastNits"; + private static final String ATTR_DEFAULT_CONFIG = "defaultConfig"; + private static final String ATTR_POWER_SAVE = "powerSaveFactor"; + private static final String ATTR_USER_POINT = "userPoint"; private static final int MSG_BACKGROUND_START = 0; private static final int MSG_BRIGHTNESS_CHANGED = 1; @@ -113,6 +118,10 @@ public class BrightnessTracker { private final Runnable mEventsWriter = () -> writeEvents(); private volatile boolean mWriteEventsScheduled; + private AmbientBrightnessStatsTracker mAmbientBrightnessStatsTracker; + private final Runnable mAmbientBrightnessStatsWriter = () -> writeAmbientBrightnessStats(); + private volatile boolean mWriteBrightnessStatsScheduled; + private UserManager mUserManager; private final Context mContext; private final ContentResolver mContentResolver; @@ -120,6 +129,7 @@ public class BrightnessTracker { // mBroadcastReceiver and mSensorListener should only be used on the mBgHandler thread. private BroadcastReceiver mBroadcastReceiver; private SensorListener mSensorListener; + private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL; // Lock held while collecting data related to brightness changes. private final Object mDataCollectionLock = new Object(); @@ -157,12 +167,19 @@ public class BrightnessTracker { } mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper()); mUserManager = mContext.getSystemService(UserManager.class); - + try { + final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack(); + mCurrentUserId = focusedStack.userId; + } catch (RemoteException e) { + // Really shouldn't be possible. + return; + } mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget(); } private void backgroundStart(float initialBrightness) { readEvents(); + readAmbientBrightnessStats(); mSensorListener = new SensorListener(); @@ -196,12 +213,20 @@ public class BrightnessTracker { mInjector.unregisterSensorListener(mContext, mSensorListener); mInjector.unregisterReceiver(mContext, mBroadcastReceiver); mInjector.cancelIdleJob(mContext); + mAmbientBrightnessStatsTracker.stop(); synchronized (mDataCollectionLock) { mStarted = false; } } + public void onSwitchUser(@UserIdInt int newUserId) { + if (DEBUG) { + Slog.d(TAG, "Used id updated from " + mCurrentUserId + " to " + newUserId); + } + mCurrentUserId = newUserId; + } + /** * @param userId userId to fetch data for. * @param includePackage if false we will null out BrightnessChangeEvent.packageName @@ -228,24 +253,29 @@ public class BrightnessTracker { return new ParceledListSlice<>(out); } - public void persistEvents() { - scheduleWriteEvents(); + public void persistBrightnessTrackerState() { + scheduleWriteBrightnessTrackerState(); } /** * Notify the BrightnessTracker that the user has changed the brightness of the display. */ - public void notifyBrightnessChanged(float brightness, boolean userInitiated) { + public void notifyBrightnessChanged(float brightness, boolean userInitiated, + float powerBrightnessFactor, boolean isUserSetBrightness, + boolean isDefaultBrightnessConfig) { if (DEBUG) { Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)", brightness, userInitiated)); } Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED, - userInitiated ? 1 : 0, 0 /*unused*/, (Float) brightness); + userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness, + powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig)); m.sendToTarget(); } - private void handleBrightnessChanged(float brightness, boolean userInitiated) { + private void handleBrightnessChanged(float brightness, boolean userInitiated, + float powerBrightnessFactor, boolean isUserSetBrightness, + boolean isDefaultBrightnessConfig) { BrightnessChangeEvent.Builder builder; synchronized (mDataCollectionLock) { @@ -267,6 +297,9 @@ public class BrightnessTracker { builder = new BrightnessChangeEvent.Builder(); builder.setBrightness(brightness); builder.setTimeStamp(mInjector.currentTimeMillis()); + builder.setPowerBrightnessFactor(powerBrightnessFactor); + builder.setUserBrightnessPoint(isUserSetBrightness); + builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig); final int readingCount = mLastSensorReadings.size(); if (readingCount == 0) { @@ -321,11 +354,15 @@ public class BrightnessTracker { } } - private void scheduleWriteEvents() { + private void scheduleWriteBrightnessTrackerState() { if (!mWriteEventsScheduled) { mBgHandler.post(mEventsWriter); mWriteEventsScheduled = true; } + if (!mWriteBrightnessStatsScheduled) { + mBgHandler.post(mAmbientBrightnessStatsWriter); + mWriteBrightnessStatsScheduled = true; + } } private void writeEvents() { @@ -336,7 +373,7 @@ public class BrightnessTracker { return; } - final AtomicFile writeTo = mInjector.getFile(); + final AtomicFile writeTo = mInjector.getFile(EVENTS_FILE); if (writeTo == null) { return; } @@ -360,12 +397,29 @@ public class BrightnessTracker { } } + private void writeAmbientBrightnessStats() { + mWriteBrightnessStatsScheduled = false; + final AtomicFile writeTo = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE); + if (writeTo == null) { + return; + } + FileOutputStream output = null; + try { + output = writeTo.startWrite(); + mAmbientBrightnessStatsTracker.writeStats(output); + writeTo.finishWrite(output); + } catch (IOException e) { + writeTo.failWrite(output); + Slog.e(TAG, "Failed to write ambient brightness stats.", e); + } + } + private void readEvents() { synchronized (mEventsLock) { // Read might prune events so mark as dirty. mEventsDirty = true; mEvents.clear(); - final AtomicFile readFrom = mInjector.getFile(); + final AtomicFile readFrom = mInjector.getFile(EVENTS_FILE); if (readFrom != null && readFrom.exists()) { FileInputStream input = null; try { @@ -381,6 +435,23 @@ public class BrightnessTracker { } } + private void readAmbientBrightnessStats() { + mAmbientBrightnessStatsTracker = new AmbientBrightnessStatsTracker(mUserManager, null); + final AtomicFile readFrom = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE); + if (readFrom != null && readFrom.exists()) { + FileInputStream input = null; + try { + input = readFrom.openRead(); + mAmbientBrightnessStatsTracker.readStats(input); + } catch (IOException e) { + readFrom.delete(); + Slog.e(TAG, "Failed to read ambient brightness stats.", e); + } finally { + IoUtils.closeQuietly(input); + } + } + } + @VisibleForTesting @GuardedBy("mEventsLock") void writeEventsLocked(OutputStream stream) throws IOException { @@ -412,6 +483,12 @@ public class BrightnessTracker { toWrite[i].colorTemperature)); out.attribute(null, ATTR_LAST_NITS, Float.toString(toWrite[i].lastBrightness)); + out.attribute(null, ATTR_DEFAULT_CONFIG, + Boolean.toString(toWrite[i].isDefaultBrightnessConfig)); + out.attribute(null, ATTR_POWER_SAVE, + Float.toString(toWrite[i].powerBrightnessFactor)); + out.attribute(null, ATTR_USER_POINT, + Boolean.toString(toWrite[i].isUserSetBrightness)); StringBuilder luxValues = new StringBuilder(); StringBuilder luxTimestamps = new StringBuilder(); for (int j = 0; j < toWrite[i].luxValues.length; ++j) { @@ -496,6 +573,21 @@ public class BrightnessTracker { builder.setLuxValues(luxValues); builder.setLuxTimestamps(luxTimestamps); + String defaultConfig = parser.getAttributeValue(null, ATTR_DEFAULT_CONFIG); + if (defaultConfig != null) { + builder.setIsDefaultBrightnessConfig(Boolean.parseBoolean(defaultConfig)); + } + String powerSave = parser.getAttributeValue(null, ATTR_POWER_SAVE); + if (powerSave != null) { + builder.setPowerBrightnessFactor(Float.parseFloat(powerSave)); + } else { + builder.setPowerBrightnessFactor(1.0f); + } + String userPoint = parser.getAttributeValue(null, ATTR_USER_POINT); + if (userPoint != null) { + builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint)); + } + BrightnessChangeEvent event = builder.build(); if (DEBUG) { Slog.i(TAG, "Read event " + event.brightness @@ -535,7 +627,11 @@ public class BrightnessTracker { BrightnessChangeEvent[] events = mEvents.toArray(); for (int i = 0; i < events.length; ++i) { pw.print(" " + events[i].timeStamp + ", " + events[i].userId); - pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness + ", {"); + pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness); + pw.print(", isUserSetBrightness=" + events[i].isUserSetBrightness); + pw.print(", powerBrightnessFactor=" + events[i].powerBrightnessFactor); + pw.print(", isDefaultBrightnessConfig=" + events[i].isDefaultBrightnessConfig); + pw.print(" {"); for (int j = 0; j < events[i].luxValues.length; ++j){ if (j != 0) { pw.print(", "); @@ -545,6 +641,13 @@ public class BrightnessTracker { pw.println("}"); } } + if (mAmbientBrightnessStatsTracker != null) { + mAmbientBrightnessStatsTracker.dump(pw); + } + } + + public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) { + return new ParceledListSlice<>(mAmbientBrightnessStatsTracker.getUserStats(userId)); } // Not allowed to keep the SensorEvent so used to copy the data we care about. @@ -584,6 +687,10 @@ public class BrightnessTracker { } } + private void recordAmbientBrightnessStats(SensorEvent event) { + mAmbientBrightnessStatsTracker.add(mCurrentUserId, event.values[0]); + } + private void batteryLevelChanged(int level, int scale) { synchronized (mDataCollectionLock) { mLastBatteryLevel = (float) level / (float) scale; @@ -594,6 +701,7 @@ public class BrightnessTracker { @Override public void onSensorChanged(SensorEvent event) { recordSensorEvent(event); + recordAmbientBrightnessStats(event); } @Override @@ -611,7 +719,7 @@ public class BrightnessTracker { String action = intent.getAction(); if (Intent.ACTION_SHUTDOWN.equals(action)) { stop(); - scheduleWriteEvents(); + scheduleWriteBrightnessTrackerState(); } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0); @@ -619,8 +727,10 @@ public class BrightnessTracker { batteryLevelChanged(level, scale); } } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { + mAmbientBrightnessStatsTracker.stop(); mInjector.unregisterSensorListener(mContext, mSensorListener); } else if (Intent.ACTION_SCREEN_ON.equals(action)) { + mAmbientBrightnessStatsTracker.start(); mInjector.registerSensorListener(mContext, mSensorListener, mInjector.getBackgroundHandler()); } @@ -637,14 +747,31 @@ public class BrightnessTracker { backgroundStart((float)msg.obj /*initial brightness*/); break; case MSG_BRIGHTNESS_CHANGED: - float newBrightness = (float) msg.obj; + BrightnessChangeValues values = (BrightnessChangeValues) msg.obj; boolean userInitiatedChange = (msg.arg1 == 1); - handleBrightnessChanged(newBrightness, userInitiatedChange); + handleBrightnessChanged(values.brightness, userInitiatedChange, + values.powerBrightnessFactor, values.isUserSetBrightness, + values.isDefaultBrightnessConfig); break; } } } + private static class BrightnessChangeValues { + final float brightness; + final float powerBrightnessFactor; + final boolean isUserSetBrightness; + final boolean isDefaultBrightnessConfig; + + BrightnessChangeValues(float brightness, float powerBrightnessFactor, + boolean isUserSetBrightness, boolean isDefaultBrightnessConfig) { + this.brightness = brightness; + this.powerBrightnessFactor = powerBrightnessFactor; + this.isUserSetBrightness = isUserSetBrightness; + this.isDefaultBrightnessConfig = isDefaultBrightnessConfig; + } + } + @VisibleForTesting static class Injector { public void registerSensorListener(Context context, @@ -679,8 +806,8 @@ public class BrightnessTracker { return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId); } - public AtomicFile getFile() { - return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), EVENTS_FILE)); + public AtomicFile getFile(String filename) { + return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), filename)); } public long currentTimeMillis() { diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 0c2ff0519615..a5c1fe299e4e 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -38,6 +38,7 @@ import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.graphics.Point; import android.hardware.SensorManager; +import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.DisplayManagerGlobal; @@ -359,6 +360,7 @@ public final class DisplayManagerService extends SystemService { mPersistentDataStore.getBrightnessConfiguration(userSerial); mDisplayPowerController.setBrightnessConfiguration(config); } + mDisplayPowerController.onSwitchUser(newUserId); } } @@ -1835,6 +1837,23 @@ public final class DisplayManagerService extends SystemService { } @Override // Binder call + public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats() { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS, + "Permission required to to access ambient light stats."); + final int callingUid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(callingUid); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + return mDisplayPowerController.getAmbientBrightnessStats(userId); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call public void setBrightnessConfigurationForUser( BrightnessConfiguration c, @UserIdInt int userId, String packageName) { mContext.enforceCallingOrSelfPermission( @@ -2039,9 +2058,9 @@ public final class DisplayManagerService extends SystemService { } @Override - public void persistBrightnessSliderEvents() { + public void persistBrightnessTrackerState() { synchronized (mSyncRoot) { - mDisplayPowerController.persistBrightnessSliderEvents(); + mDisplayPowerController.persistBrightnessTrackerState(); } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 80aec42b719c..b27f1ec3c653 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -34,6 +34,7 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; @@ -478,6 +479,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mScreenBrightnessForVr = getScreenBrightnessForVrSetting(); mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting(); mTemporaryScreenBrightness = -1; + mPendingScreenBrightnessSetting = -1; mTemporaryAutoBrightnessAdjustment = Float.NaN; } @@ -498,11 +500,20 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return mBrightnessTracker.getEvents(userId, includePackage); } + public void onSwitchUser(@UserIdInt int newUserId) { + mBrightnessTracker.onSwitchUser(newUserId); + } + + public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats( + @UserIdInt int userId) { + return mBrightnessTracker.getAmbientBrightnessStats(userId); + } + /** - * Persist the brightness slider events to disk. + * Persist the brightness slider events and ambient brightness stats to disk. */ - public void persistBrightnessSliderEvents() { - mBrightnessTracker.persistEvents(); + public void persistBrightnessTrackerState() { + mBrightnessTracker.persistBrightnessTrackerState(); } /** @@ -795,13 +806,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call brightness = PowerManager.BRIGHTNESS_ON; } - // If the brightness is already set then it's been overriden by something other than the + // If the brightness is already set then it's been overridden by something other than the // user, or is a temporary adjustment. final boolean userInitiatedChange = brightness < 0 && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged); + boolean hadUserBrightnessPoint = false; // Configure auto-brightness. if (mAutomaticBrightnessController != null) { + hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints(); mAutomaticBrightnessController.configure(autoBrightnessEnabled, mBrightnessConfiguration, mLastUserSetScreenBrightness / (float) PowerManager.BRIGHTNESS_ON, @@ -930,7 +943,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } if (!brightnessIsTemporary) { - notifyBrightnessChanged(brightness, userInitiatedChange); + notifyBrightnessChanged(brightness, userInitiatedChange, hadUserBrightnessPoint); } } @@ -1464,18 +1477,26 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mPendingScreenBrightnessSetting = -1; return false; } + mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting; mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting; mPendingScreenBrightnessSetting = -1; return true; } - private void notifyBrightnessChanged(int brightness, boolean userInitiated) { + private void notifyBrightnessChanged(int brightness, boolean userInitiated, + boolean hadUserDataPoint) { final float brightnessInNits = convertToNits(brightness); - if (brightnessInNits >= 0.0f) { + if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f + && mAutomaticBrightnessController != null) { // We only want to track changes on devices that can actually map the display backlight // values into a physical brightness unit since the value provided by the API is in // nits and not using the arbitrary backlight units. - mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated); + final float powerFactor = mPowerRequest.lowPowerMode + ? mPowerRequest.screenLowPowerBrightnessFactor + : 1.0f; + mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated, + powerFactor, hadUserDataPoint, + mAutomaticBrightnessController.isDefaultConfig()); } } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index e0baeee01c69..401c05e80307 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -862,7 +862,8 @@ public final class JobSchedulerService extends com.android.server.SystemService } startTrackingJobLocked(jobStatus, toCancel); StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, - uId, null, jobStatus.getBatteryName(), 2); + uId, null, jobStatus.getBatteryName(), + StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED); // If the job is immediately ready to run, then we can just immediately // put it in the pending list and try to schedule it. This is especially diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 28fa86b3a893..e71572483cca 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -383,8 +383,8 @@ public class LockSettingsService extends ILockSettings.Stub { return KeyStore.getInstance(); } - public RecoverableKeyStoreManager getRecoverableKeyStoreManager() { - return RecoverableKeyStoreManager.getInstance(mContext); + public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) { + return RecoverableKeyStoreManager.getInstance(mContext, keyStore); } public IStorageManager getStorageManager() { @@ -413,7 +413,7 @@ public class LockSettingsService extends ILockSettings.Stub { mInjector = injector; mContext = injector.getContext(); mKeyStore = injector.getKeyStore(); - mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager(); + mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager(mKeyStore); mHandler = injector.getHandler(); mStrongAuth = injector.getStrongAuth(); mActivityManager = injector.getActivityManager(); @@ -1887,7 +1887,7 @@ public class LockSettingsService extends ILockSettings.Stub { mSpManager.removeUser(userId); mStorage.removeUser(userId); mStrongAuth.removeUser(userId); - cleanSpCache(); + tryRemoveUserFromSpCacheLater(userId); final KeyStore ks = KeyStore.getInstance(); ks.onUserRemoved(userId); @@ -2064,6 +2064,16 @@ public class LockSettingsService extends ILockSettings.Stub { return mRecoverableKeyStoreManager.generateAndStoreKey(alias); } + @Override + public String generateKey(@NonNull String alias, byte[] account) throws RemoteException { + return mRecoverableKeyStoreManager.generateKey(alias, account); + } + + @Override + public String getKey(@NonNull String alias) throws RemoteException { + return mRecoverableKeyStoreManager.getKey(alias); + } + private static final String[] VALID_SETTINGS = new String[] { LockPatternUtils.LOCKOUT_PERMANENT_KEY, LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE, @@ -2141,6 +2151,13 @@ public class LockSettingsService extends ILockSettings.Stub { private SparseArray<AuthenticationToken> mSpCache = new SparseArray(); private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) { + // Preemptively cache the SP and then try to remove it in a handler. + Slog.i(TAG, "Caching SP for user " + userId); + synchronized (mSpManager) { + mSpCache.put(userId, auth); + } + tryRemoveUserFromSpCacheLater(userId); + // Pass the primary user's auth secret to the HAL if (mAuthSecretService != null && mUserManager.getUserInfo(userId).isPrimary()) { try { @@ -2154,33 +2171,25 @@ public class LockSettingsService extends ILockSettings.Stub { Slog.w(TAG, "Failed to pass primary user secret to AuthSecret HAL", e); } } - - // Update the SP cache, removing the entry when allowed - synchronized (mSpManager) { - if (shouldCacheSpForUser(userId)) { - Slog.i(TAG, "Caching SP for user " + userId); - mSpCache.put(userId, auth); - } else { - Slog.i(TAG, "Not caching SP for user " + userId); - mSpCache.delete(userId); - } - } } - /** Clean up the SP cache by removing unneeded entries. */ - private void cleanSpCache() { - synchronized (mSpManager) { - // Preserve indicies after removal by iterating backwards - for (int i = mSpCache.size() - 1; i >= 0; --i) { - final int userId = mSpCache.keyAt(i); - if (!shouldCacheSpForUser(userId)) { - Slog.i(TAG, "Uncaching SP for user " + userId); - mSpCache.removeAt(i); + private void tryRemoveUserFromSpCacheLater(@UserIdInt int userId) { + mHandler.post(() -> { + if (!shouldCacheSpForUser(userId)) { + // The transition from 'should not cache' to 'should cache' can only happen if + // certain admin apps are installed after provisioning e.g. via adb. This is not + // a common case and we do not seamlessly support; it may result in the SP not + // being cached when it is needed. The cache can be re-populated by verifying + // the credential again. + Slog.i(TAG, "Removing SP from cache for user " + userId); + synchronized (mSpManager) { + mSpCache.remove(userId); } } - } + }); } + /** Do not hold any of the locks from this service when calling. */ private boolean shouldCacheSpForUser(@UserIdInt int userId) { // Before the user setup has completed, an admin could be installed that requires the SP to // be cached (see below). @@ -2731,7 +2740,7 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public void onChange(boolean selfChange, Uri uri) { + public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) { if (mDeviceProvisionedUri.equals(uri)) { updateRegistration(); @@ -2741,7 +2750,7 @@ public class LockSettingsService extends ILockSettings.Stub { clearFrpCredentialIfOwnerNotSecure(); } } else if (mUserSetupCompleteUri.equals(uri)) { - cleanSpCache(); + tryRemoveUserFromSpCacheLater(userId); } } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java index 59132da7d4c7..285e722886c2 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java @@ -16,10 +16,13 @@ package com.android.server.locksettings.recoverablekeystore; +import java.io.IOException; +import java.security.cert.CertificateException; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; /** @@ -27,6 +30,7 @@ import java.security.UnrecoverableKeyException; */ public class KeyStoreProxyImpl implements KeyStoreProxy { + private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; private final KeyStore mKeyStore; /** @@ -57,4 +61,21 @@ public class KeyStoreProxyImpl implements KeyStoreProxy { public void deleteEntry(String alias) throws KeyStoreException { mKeyStore.deleteEntry(alias); } + + /** + * Returns AndroidKeyStore-provided {@link KeyStore}, having already invoked + * {@link KeyStore#load(KeyStore.LoadStoreParameter)}. + * + * @throws KeyStoreException if there was a problem getting or initializing the key store. + */ + public static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException { + KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER); + try { + keyStore.load(/*param=*/ null); + } catch (CertificateException | IOException | NoSuchAlgorithmException e) { + // Should never happen. + throw new KeyStoreException("Unable to load keystore.", e); + } + return keyStore; + } } 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 ec72b221931f..fda6cdf33e0c 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -37,21 +37,23 @@ import android.security.keystore.recovery.KeyChainProtectionParams; import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.RecoveryController; import android.security.keystore.recovery.WrappedApplicationKey; +import android.security.KeyStore; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; +import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage; import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb; import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage; import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage; import java.security.InvalidKeyException; -import java.security.KeyStoreException; import java.security.KeyFactory; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; -import java.security.UnrecoverableKeyException; import java.security.spec.InvalidKeySpecException; +import java.security.UnrecoverableKeyException; import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; import java.util.HashMap; @@ -82,18 +84,23 @@ public class RecoverableKeyStoreManager { private final RecoverableKeyGenerator mRecoverableKeyGenerator; private final RecoverySnapshotStorage mSnapshotStorage; private final PlatformKeyManager mPlatformKeyManager; + private final KeyStore mKeyStore; + private final ApplicationKeyStorage mApplicationKeyStorage; /** * Returns a new or existing instance. * * @hide */ - public static synchronized RecoverableKeyStoreManager getInstance(Context context) { + public static synchronized RecoverableKeyStoreManager + getInstance(Context context, KeyStore keystore) { if (mInstance == null) { RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(context); PlatformKeyManager platformKeyManager; + ApplicationKeyStorage applicationKeyStorage; try { platformKeyManager = PlatformKeyManager.getInstance(context, db); + applicationKeyStorage = ApplicationKeyStorage.getInstance(keystore); } catch (NoSuchAlgorithmException e) { // Impossible: all algorithms must be supported by AOSP throw new RuntimeException(e); @@ -103,12 +110,14 @@ public class RecoverableKeyStoreManager { mInstance = new RecoverableKeyStoreManager( context.getApplicationContext(), + keystore, db, new RecoverySessionStorage(), Executors.newSingleThreadExecutor(), new RecoverySnapshotStorage(), new RecoverySnapshotListenersStorage(), - platformKeyManager); + platformKeyManager, + applicationKeyStorage); } return mInstance; } @@ -116,19 +125,23 @@ public class RecoverableKeyStoreManager { @VisibleForTesting RecoverableKeyStoreManager( Context context, + KeyStore keystore, RecoverableKeyStoreDb recoverableKeyStoreDb, RecoverySessionStorage recoverySessionStorage, ExecutorService executorService, RecoverySnapshotStorage snapshotStorage, RecoverySnapshotListenersStorage listenersStorage, - PlatformKeyManager platformKeyManager) { + PlatformKeyManager platformKeyManager, + ApplicationKeyStorage applicationKeyStorage) { mContext = context; + mKeyStore = keystore; mDatabase = recoverableKeyStoreDb; mRecoverySessionStorage = recoverySessionStorage; mExecutorService = executorService; mListenersStorage = listenersStorage; mSnapshotStorage = snapshotStorage; mPlatformKeyManager = platformKeyManager; + mApplicationKeyStorage = applicationKeyStorage; try { mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase); @@ -406,6 +419,7 @@ public class RecoverableKeyStoreManager { } /** + * Deprecated * Generates a key named {@code alias} in the recoverable store for the calling uid. Then * returns the raw key material. * @@ -450,9 +464,55 @@ public class RecoverableKeyStoreManager { boolean wasRemoved = mDatabase.removeKey(uid, alias); if (wasRemoved) { mDatabase.setShouldCreateSnapshot(userId, uid, true); + mApplicationKeyStorage.deleteEntry(userId, uid, alias); } } + /** + * Generates a key named {@code alias} in caller's namespace. + * The key is stored in system service keystore namespace. + * + * @return grant alias, which caller can use to access the key. + */ + public String generateKey(@NonNull String alias, byte[] account) throws RemoteException { + int uid = Binder.getCallingUid(); + int userId = UserHandle.getCallingUserId(); + + PlatformEncryptionKey encryptionKey; + try { + encryptionKey = mPlatformKeyManager.getEncryptKey(userId); + } catch (NoSuchAlgorithmException e) { + // Impossible: all algorithms must be supported by AOSP + throw new RuntimeException(e); + } catch (KeyStoreException | UnrecoverableKeyException e) { + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); + } catch (InsecureUserException e) { + throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage()); + } + + try { + byte[] secretKey = + mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias); + mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey); + String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias); + return grantAlias; + } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); + } + } + + /** + * Gets a key named {@code alias} in caller's namespace. + * + * @return grant alias, which caller can use to access the key. + */ + 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; + } + private byte[] decryptRecoveryKey( RecoverySessionStorage.Entry sessionEntry, byte[] encryptedClaimResponse) throws RemoteException, ServiceSpecificException { 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 new file mode 100644 index 000000000000..600a534facf9 --- /dev/null +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.locksettings.recoverablekeystore.storage; + +import static android.security.keystore.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR; + +import android.annotation.Nullable; +import android.os.ServiceSpecificException; +import android.security.Credentials; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.security.keystore.KeyProtection; +import android.security.keystore.recovery.KeyChainSnapshot; +import android.security.KeyStore; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.locksettings.recoverablekeystore.KeyStoreProxy; +import com.android.server.locksettings.recoverablekeystore.KeyStoreProxyImpl; + +import java.security.KeyStore.SecretKeyEntry; +import java.security.KeyStoreException; +import javax.crypto.spec.SecretKeySpec; + +/** + * Storage for Application keys in LockSettings service KeyStore namespace. + * + * <p> Uses KeyStore's grant mechanism to make keys usable by application process without + * revealing key material + */ +public class ApplicationKeyStorage { + private static final String APPLICATION_KEY_ALIAS_PREFIX = + "com.android.server.locksettings.recoverablekeystore/application/"; + + KeyStoreProxy mKeyStore; + KeyStore mKeystoreService; + + public static ApplicationKeyStorage getInstance(KeyStore keystoreService) + throws KeyStoreException { + return new ApplicationKeyStorage( + new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()), + keystoreService); + } + + @VisibleForTesting + ApplicationKeyStorage(KeyStoreProxy keyStore, KeyStore keystoreService) { + mKeyStore = keyStore; + mKeystoreService = keystoreService; + } + + /** + * Returns grant alias, valid in Applications namespace. + */ + 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. + 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 { + try { + mKeyStore.setEntry( + getInternalAlias(userId, uid, alias), + new SecretKeyEntry( + new SecretKeySpec(secretKey, KeyProperties.KEY_ALGORITHM_AES)), + new KeyProtection.Builder( + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .build()); + } catch (KeyStoreException e) { + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); + } + } + + public void deleteEntry(int userId, int uid, String alias) { + try { + mKeyStore.deleteEntry(getInternalAlias(userId, uid, alias)); + } catch (KeyStoreException e) { + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); + } + } + + /** + * Returns the alias in locksettins service's KeyStore namespace used for given application key. + * + * <p>These IDs look as follows: + * {@code com.security.recoverablekeystore/application/<userId>/<uid>/<alias>} + * + * @param userId The ID of the user + * @param uid The uid + * @param alias - alias in application's namespace + * @return The alias. + */ + private String getInternalAlias(int userId, int uid, String alias) { + return APPLICATION_KEY_ALIAS_PREFIX + userId + "/" + uid + "/" + alias; + } +} diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java index d96671c5cd9d..79fd496ad11c 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java @@ -28,7 +28,7 @@ import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKe * Helper for creating the recoverable key database. */ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper { - private static final int DATABASE_VERSION = 1; + private static final int DATABASE_VERSION = 2; private static final String DATABASE_NAME = "recoverablekeystore.db"; private static final String SQL_CREATE_KEYS_ENTRY = diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 502760a2cfc4..fd435f952c10 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -771,7 +771,7 @@ abstract public class ManagedServices { * Called whenever packages change, the user switches, or the secure setting * is altered. (For example in response to USER_SWITCHED in our broadcast receiver) */ - private void rebindServices(boolean forceRebind) { + protected void rebindServices(boolean forceRebind) { if (DEBUG) Slog.d(TAG, "rebindServices"); final int[] userIds = mUserProfiles.getCurrentProfileIds(); final int nUserIds = userIds.length; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 727e7ee2ac18..b25124ac4562 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2891,6 +2891,7 @@ public class NotificationManagerService extends SystemService { // Backup/restore interface @Override public byte[] getBackupPayload(int user) { + checkCallerIsSystem(); if (DBG) Slog.d(TAG, "getBackupPayload u=" + user); //TODO: http://b/22388012 if (user != UserHandle.USER_SYSTEM) { @@ -2911,6 +2912,7 @@ public class NotificationManagerService extends SystemService { @Override public void applyRestore(byte[] payload, int user) { + checkCallerIsSystem(); if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload=" + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null)); if (payload == null) { @@ -5687,6 +5689,12 @@ public class NotificationManagerService extends SystemService { mListeners.unregisterService(removed.service, removed.userid); } + @Override + public void onUserUnlocked(int user) { + if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user); + rebindServices(true); + } + public void onNotificationEnqueued(final NotificationRecord r) { final StatusBarNotification sbn = r.sbn; TrimCache trimCache = new TrimCache(sbn); diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index c3f20af77b10..b79caca317a4 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -290,13 +290,14 @@ public class Installer extends SystemService { int dexoptNeeded, @Nullable String outputPath, int dexFlags, String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade, int targetSdkVersion, - @Nullable String profileName) throws InstallerException { + @Nullable String profileName, @Nullable String dexMetadataPath) + throws InstallerException { assertValidInstructionSet(instructionSet); if (!checkBeforeRemote()) return; try { mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade, - targetSdkVersion, profileName); + targetSdkVersion, profileName, dexMetadataPath); } catch (Exception e) { throw InstallerException.from(e); } diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java index 30072d45ccab..af446ba02914 100644 --- a/services/core/java/com/android/server/pm/InstantAppResolver.java +++ b/services/core/java/com/android/server/pm/InstantAppResolver.java @@ -40,26 +40,33 @@ import android.content.pm.InstantAppIntentFilter; import android.content.pm.InstantAppResolveInfo; import android.content.pm.InstantAppResolveInfo.InstantAppDigest; import android.metrics.LogMaker; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.text.TextUtils; import android.util.Log; +import android.util.Slog; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; -import com.android.server.pm.EphemeralResolverConnection.ConnectionException; -import com.android.server.pm.EphemeralResolverConnection.PhaseTwoCallback; +import com.android.server.pm.InstantAppResolverConnection.ConnectionException; +import com.android.server.pm.InstantAppResolverConnection.PhaseTwoCallback; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.UUID; /** @hide */ public abstract class InstantAppResolver { - private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE; + private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE; private static final String TAG = "PackageManager"; private static final int RESOLUTION_SUCCESS = 0; @@ -79,6 +86,7 @@ public abstract class InstantAppResolver { public @interface ResolutionStatus {} private static MetricsLogger sMetricsLogger; + private static MetricsLogger getLogger() { if (sMetricsLogger == null) { sMetricsLogger = new MetricsLogger(); @@ -86,26 +94,49 @@ public abstract class InstantAppResolver { return sMetricsLogger; } - public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(Context context, - EphemeralResolverConnection connection, InstantAppRequest requestObj) { + /** + * Returns an intent with potential PII removed from the original intent. Fields removed + * include extras and the host + path of the data, if defined. + */ + public static Intent sanitizeIntent(Intent origIntent) { + final Intent sanitizedIntent; + sanitizedIntent = new Intent(origIntent.getAction()); + Set<String> categories = origIntent.getCategories(); + if (categories != null) { + for (String category : categories) { + sanitizedIntent.addCategory(category); + } + } + Uri sanitizedUri = origIntent.getData() == null + ? null + : Uri.fromParts(origIntent.getScheme(), "", ""); + sanitizedIntent.setDataAndType(sanitizedUri, origIntent.getType()); + sanitizedIntent.addFlags(origIntent.getFlags()); + sanitizedIntent.setPackage(origIntent.getPackage()); + return sanitizedIntent; + } + + public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne( + InstantAppResolverConnection connection, InstantAppRequest requestObj) { final long startTime = System.currentTimeMillis(); final String token = UUID.randomUUID().toString(); - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Log.d(TAG, "[" + token + "] Phase1; resolving"); } - final Intent intent = requestObj.origIntent; - final InstantAppDigest digest = - new InstantAppDigest(intent.getData().getHost(), 5 /*maxDigests*/); + final Intent origIntent = requestObj.origIntent; + final Intent sanitizedIntent = sanitizeIntent(origIntent); + + final InstantAppDigest digest = getInstantAppDigest(origIntent); final int[] shaPrefix = digest.getDigestPrefix(); AuxiliaryResolveInfo resolveInfo = null; @ResolutionStatus int resolutionStatus = RESOLUTION_SUCCESS; try { final List<InstantAppResolveInfo> instantAppResolveInfoList = - connection.getInstantAppResolveInfoList(shaPrefix, token); + connection.getInstantAppResolveInfoList(sanitizedIntent, shaPrefix, token); if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) { resolveInfo = InstantAppResolver.filterInstantAppIntent( - instantAppResolveInfoList, intent, requestObj.resolvedType, - requestObj.userId, intent.getPackage(), digest, token); + instantAppResolveInfoList, origIntent, requestObj.resolvedType, + requestObj.userId, origIntent.getPackage(), digest, token); } } catch (ConnectionException e) { if (e.failure == ConnectionException.FAILURE_BIND) { @@ -121,7 +152,7 @@ public abstract class InstantAppResolver { logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE, startTime, token, resolutionStatus); } - if (DEBUG_EPHEMERAL && resolveInfo == null) { + if (DEBUG_INSTANT && resolveInfo == null) { if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) { Log.d(TAG, "[" + token + "] Phase1; bind timed out"); } else if (resolutionStatus == RESOLUTION_CALL_TIMEOUT) { @@ -135,81 +166,67 @@ public abstract class InstantAppResolver { return resolveInfo; } + private static InstantAppDigest getInstantAppDigest(Intent origIntent) { + return origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost()) + ? new InstantAppDigest(origIntent.getData().getHost(), 5 /*maxDigests*/) + : InstantAppDigest.UNDEFINED; + } + public static void doInstantAppResolutionPhaseTwo(Context context, - EphemeralResolverConnection connection, InstantAppRequest requestObj, + InstantAppResolverConnection connection, InstantAppRequest requestObj, ActivityInfo instantAppInstaller, Handler callbackHandler) { final long startTime = System.currentTimeMillis(); final String token = requestObj.responseObj.token; - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Log.d(TAG, "[" + token + "] Phase2; resolving"); } - final Intent intent = requestObj.origIntent; - final String hostName = intent.getData().getHost(); - final InstantAppDigest digest = new InstantAppDigest(hostName, 5 /*maxDigests*/); + final Intent origIntent = requestObj.origIntent; + final Intent sanitizedIntent = sanitizeIntent(origIntent); + final InstantAppDigest digest = getInstantAppDigest(origIntent); final int[] shaPrefix = digest.getDigestPrefix(); final PhaseTwoCallback callback = new PhaseTwoCallback() { @Override void onPhaseTwoResolved(List<InstantAppResolveInfo> instantAppResolveInfoList, long startTime) { - final String packageName; - final String splitName; - final long versionCode; final Intent failureIntent; - final Bundle extras; if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) { final AuxiliaryResolveInfo instantAppIntentInfo = InstantAppResolver.filterInstantAppIntent( - instantAppResolveInfoList, intent, null /*resolvedType*/, - 0 /*userId*/, intent.getPackage(), digest, token); - if (instantAppIntentInfo != null - && instantAppIntentInfo.resolveInfo != null) { - packageName = instantAppIntentInfo.resolveInfo.getPackageName(); - splitName = instantAppIntentInfo.splitName; - versionCode = instantAppIntentInfo.resolveInfo.getVersionCode(); + instantAppResolveInfoList, origIntent, null /*resolvedType*/, + 0 /*userId*/, origIntent.getPackage(), digest, token); + if (instantAppIntentInfo != null) { failureIntent = instantAppIntentInfo.failureIntent; - extras = instantAppIntentInfo.resolveInfo.getExtras(); } else { - packageName = null; - splitName = null; - versionCode = -1; failureIntent = null; - extras = null; } } else { - packageName = null; - splitName = null; - versionCode = -1; failureIntent = null; - extras = null; } final Intent installerIntent = buildEphemeralInstallerIntent( - Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE, requestObj.origIntent, + sanitizedIntent, failureIntent, requestObj.callingPackage, requestObj.verificationBundle, requestObj.resolvedType, requestObj.userId, - packageName, - splitName, requestObj.responseObj.installFailureActivity, - versionCode, token, - extras, - false /*needsPhaseTwo*/); + false /*needsPhaseTwo*/, + requestObj.responseObj.filters); installerIntent.setComponent(new ComponentName( instantAppInstaller.packageName, instantAppInstaller.name)); logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO, startTime, token, - packageName != null ? RESOLUTION_SUCCESS : RESOLUTION_FAILURE); + requestObj.responseObj.filters != null ? RESOLUTION_SUCCESS : RESOLUTION_FAILURE); context.startActivity(installerIntent); } }; try { - connection.getInstantAppIntentFilterList( - shaPrefix, token, hostName, callback, callbackHandler, startTime); + connection.getInstantAppIntentFilterList(sanitizedIntent, shaPrefix, token, callback, + callbackHandler, startTime); } catch (ConnectionException e) { @ResolutionStatus int resolutionStatus = RESOLUTION_FAILURE; if (e.failure == ConnectionException.FAILURE_BIND) { @@ -217,7 +234,7 @@ public abstract class InstantAppResolver { } logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO, startTime, token, resolutionStatus); - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) { Log.d(TAG, "[" + token + "] Phase2; bind timed out"); } else { @@ -231,49 +248,53 @@ public abstract class InstantAppResolver { * Builds and returns an intent to launch the instant installer. */ public static Intent buildEphemeralInstallerIntent( - @NonNull String action, @NonNull Intent origIntent, - @NonNull Intent failureIntent, + @NonNull Intent sanitizedIntent, + @Nullable Intent failureIntent, @NonNull String callingPackage, @Nullable Bundle verificationBundle, @NonNull String resolvedType, int userId, - @NonNull String instantAppPackageName, - @Nullable String instantAppSplitName, @Nullable ComponentName installFailureActivity, - long versionCode, @Nullable String token, - @Nullable Bundle extras, - boolean needsPhaseTwo) { + boolean needsPhaseTwo, + List<AuxiliaryResolveInfo.AuxiliaryFilter> filters) { // Construct the intent that launches the instant installer int flags = origIntent.getFlags(); - final Intent intent = new Intent(action); + final Intent intent = new Intent(); intent.setFlags(flags | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); if (token != null) { + // TODO(b/72700831): remove populating old extra intent.putExtra(Intent.EXTRA_EPHEMERAL_TOKEN, token); + intent.putExtra(Intent.EXTRA_INSTANT_APP_TOKEN, token); } if (origIntent.getData() != null) { + // TODO(b/72700831): remove populating old extra intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost()); + intent.putExtra(Intent.EXTRA_INSTANT_APP_HOSTNAME, origIntent.getData().getHost()); } intent.putExtra(Intent.EXTRA_INSTANT_APP_ACTION, origIntent.getAction()); - if (extras != null) { - intent.putExtra(Intent.EXTRA_INSTANT_APP_EXTRAS, extras); - } + intent.putExtra(Intent.EXTRA_INTENT, sanitizedIntent); - // We have all of the data we need; just start the installer without a second phase - if (!needsPhaseTwo) { - // Intent that is launched if the package couldn't be installed for any reason. + if (needsPhaseTwo) { + intent.setAction(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE); + } else { + // We have all of the data we need; just start the installer without a second phase if (failureIntent != null || installFailureActivity != null) { + // Intent that is launched if the package couldn't be installed for any reason. try { final Intent onFailureIntent; if (installFailureActivity != null) { onFailureIntent = new Intent(); onFailureIntent.setComponent(installFailureActivity); - onFailureIntent.putExtra(Intent.EXTRA_SPLIT_NAME, instantAppSplitName); + if (filters != null && filters.size() == 1) { + onFailureIntent.putExtra(Intent.EXTRA_SPLIT_NAME, + filters.get(0).splitName); + } onFailureIntent.putExtra(Intent.EXTRA_INTENT, origIntent); } else { onFailureIntent = failureIntent; @@ -288,8 +309,10 @@ public abstract class InstantAppResolver { | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE, null /*bOptions*/, userId); - intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, - new IntentSender(failureIntentTarget)); + IntentSender failureSender = new IntentSender(failureIntentTarget); + // TODO(b/72700831): remove populating old extra + intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, failureSender); + intent.putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, failureSender); } catch (RemoteException ignore) { /* ignore; same process */ } } @@ -306,20 +329,40 @@ public abstract class InstantAppResolver { PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE, null /*bOptions*/, userId); - intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, - new IntentSender(successIntentTarget)); + IntentSender successSender = new IntentSender(successIntentTarget); + // TODO(b/72700831): remove populating old extra + intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, successSender); + intent.putExtra(Intent.EXTRA_INSTANT_APP_SUCCESS, successSender); } catch (RemoteException ignore) { /* ignore; same process */ } - - intent.putExtra(Intent.EXTRA_PACKAGE_NAME, instantAppPackageName); - intent.putExtra(Intent.EXTRA_SPLIT_NAME, instantAppSplitName); - intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) (versionCode & 0x7fffffff)); - intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, versionCode); - intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage); if (verificationBundle != null) { intent.putExtra(Intent.EXTRA_VERIFICATION_BUNDLE, verificationBundle); } - } + intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage); + if (filters != null) { + Bundle resolvableFilters[] = new Bundle[filters.size()]; + for (int i = 0, max = filters.size(); i < max; i++) { + Bundle resolvableFilter = new Bundle(); + AuxiliaryResolveInfo.AuxiliaryFilter filter = filters.get(i); + resolvableFilter.putBoolean(Intent.EXTRA_UNKNOWN_INSTANT_APP, + filter.resolveInfo != null + && filter.resolveInfo.shouldLetInstallerDecide()); + resolvableFilter.putString(Intent.EXTRA_PACKAGE_NAME, filter.packageName); + resolvableFilter.putString(Intent.EXTRA_SPLIT_NAME, filter.splitName); + resolvableFilter.putLong(Intent.EXTRA_LONG_VERSION_CODE, filter.versionCode); + resolvableFilter.putBundle(Intent.EXTRA_INSTANT_APP_EXTRAS, filter.extras); + resolvableFilters[i] = resolvableFilter; + if (i == 0) { + // for backwards compat, always set the first result on the intent and add + // the int version code + intent.putExtras(resolvableFilter); + intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) filter.versionCode); + } + } + intent.putExtra(Intent.EXTRA_INSTANT_APP_BUNDLES, resolvableFilters); + } + intent.setAction(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE); + } return intent; } @@ -330,69 +373,134 @@ public abstract class InstantAppResolver { final int[] shaPrefix = digest.getDigestPrefix(); final byte[][] digestBytes = digest.getDigestBytes(); final Intent failureIntent = new Intent(origIntent); + boolean requiresSecondPhase = false; failureIntent.setFlags(failureIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL); failureIntent.setLaunchToken(token); - // Go in reverse order so we match the narrowest scope first. - for (int i = shaPrefix.length - 1; i >= 0 ; --i) { - for (InstantAppResolveInfo instantAppInfo : instantAppResolveInfoList) { - if (!Arrays.equals(digestBytes[i], instantAppInfo.getDigestBytes())) { - continue; + ArrayList<AuxiliaryResolveInfo.AuxiliaryFilter> filters = null; + boolean isWebIntent = origIntent.isBrowsableWebIntent(); + for (InstantAppResolveInfo instantAppResolveInfo : instantAppResolveInfoList) { + if (shaPrefix.length > 0 && instantAppResolveInfo.shouldLetInstallerDecide()) { + Slog.e(TAG, "InstantAppResolveInfo with mShouldLetInstallerDecide=true when digest" + + " provided; ignoring"); + continue; + } + byte[] filterDigestBytes = instantAppResolveInfo.getDigestBytes(); + // Only include matching digests if we have a prefix and we're either dealing with a + // web intent or the resolveInfo specifies digest details. + if (shaPrefix.length > 0 && (isWebIntent || filterDigestBytes.length > 0)) { + boolean matchFound = false; + // Go in reverse order so we match the narrowest scope first. + for (int i = shaPrefix.length - 1; i >= 0; --i) { + if (Arrays.equals(digestBytes[i], filterDigestBytes)) { + matchFound = true; + break; + } } - if (packageName != null - && !packageName.equals(instantAppInfo.getPackageName())) { + if (!matchFound) { continue; } - final List<InstantAppIntentFilter> instantAppFilters = - instantAppInfo.getIntentFilters(); - // No filters; we need to start phase two - if (instantAppFilters == null || instantAppFilters.isEmpty()) { - if (DEBUG_EPHEMERAL) { - Log.d(TAG, "No app filters; go to phase 2"); - } - return new AuxiliaryResolveInfo(instantAppInfo, - new IntentFilter(Intent.ACTION_VIEW) /*intentFilter*/, - null /*splitName*/, token, true /*needsPhase2*/, - null /*failureIntent*/); - } - // We have a domain match; resolve the filters to see if anything matches. - final PackageManagerService.EphemeralIntentResolver instantAppResolver = - new PackageManagerService.EphemeralIntentResolver(); - for (int j = instantAppFilters.size() - 1; j >= 0; --j) { - final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j); - final List<IntentFilter> splitFilters = instantAppFilter.getFilters(); - if (splitFilters == null || splitFilters.isEmpty()) { - continue; - } - for (int k = splitFilters.size() - 1; k >= 0; --k) { - final AuxiliaryResolveInfo intentInfo = - new AuxiliaryResolveInfo(instantAppInfo, - splitFilters.get(k), instantAppFilter.getSplitName(), - token, false /*needsPhase2*/, failureIntent); - instantAppResolver.addFilter(intentInfo); - } + } + // We matched a resolve info; resolve the filters to see if anything matches completely. + List<AuxiliaryResolveInfo.AuxiliaryFilter> matchFilters = computeResolveFilters( + origIntent, resolvedType, userId, packageName, token, instantAppResolveInfo); + if (matchFilters != null) { + if (matchFilters.isEmpty()) { + requiresSecondPhase = true; } - List<AuxiliaryResolveInfo> matchedResolveInfoList = instantAppResolver.queryIntent( - origIntent, resolvedType, false /*defaultOnly*/, userId); - if (!matchedResolveInfoList.isEmpty()) { - if (DEBUG_EPHEMERAL) { - final AuxiliaryResolveInfo info = matchedResolveInfoList.get(0); - Log.d(TAG, "[" + token + "] Found match;" - + " package: " + info.packageName - + ", split: " + info.splitName - + ", versionCode: " + info.versionCode); - } - return matchedResolveInfoList.get(0); - } else if (DEBUG_EPHEMERAL) { - Log.d(TAG, "[" + token + "] No matches found" - + " package: " + instantAppInfo.getPackageName() - + ", versionCode: " + instantAppInfo.getVersionCode()); + if (filters == null) { + filters = new ArrayList<>(matchFilters); + } else { + filters.addAll(matchFilters); } } } + if (filters != null && !filters.isEmpty()) { + return new AuxiliaryResolveInfo(token, requiresSecondPhase, failureIntent, filters); + } // Hash or filter mis-match; no instant apps for this domain. return null; } + /** + * Returns one of three states: <p/> + * <ul> + * <li>{@code null} if there are no matches will not be; resolution is unnecessary.</li> + * <li>An empty list signifying that a 2nd phase of resolution is required.</li> + * <li>A populated list meaning that matches were found and should be sent directly to the + * installer</li> + * </ul> + * + */ + private static List<AuxiliaryResolveInfo.AuxiliaryFilter> computeResolveFilters( + Intent origIntent, String resolvedType, int userId, String packageName, String token, + InstantAppResolveInfo instantAppInfo) { + if (instantAppInfo.shouldLetInstallerDecide()) { + return Collections.singletonList( + new AuxiliaryResolveInfo.AuxiliaryFilter( + instantAppInfo, null /* splitName */, + instantAppInfo.getExtras())); + } + if (packageName != null + && !packageName.equals(instantAppInfo.getPackageName())) { + return null; + } + final List<InstantAppIntentFilter> instantAppFilters = + instantAppInfo.getIntentFilters(); + if (instantAppFilters == null || instantAppFilters.isEmpty()) { + // No filters on web intent; no matches, 2nd phase unnecessary. + if (origIntent.isBrowsableWebIntent()) { + return null; + } + // No filters; we need to start phase two + if (DEBUG_INSTANT) { + Log.d(TAG, "No app filters; go to phase 2"); + } + return Collections.emptyList(); + } + final PackageManagerService.InstantAppIntentResolver instantAppResolver = + new PackageManagerService.InstantAppIntentResolver(); + for (int j = instantAppFilters.size() - 1; j >= 0; --j) { + final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j); + final List<IntentFilter> splitFilters = instantAppFilter.getFilters(); + if (splitFilters == null || splitFilters.isEmpty()) { + continue; + } + for (int k = splitFilters.size() - 1; k >= 0; --k) { + IntentFilter filter = splitFilters.get(k); + Iterator<IntentFilter.AuthorityEntry> authorities = + filter.authoritiesIterator(); + // ignore http/s-only filters. + if ((authorities == null || !authorities.hasNext()) + && (filter.hasDataScheme("http") || filter.hasDataScheme("https")) + && filter.hasAction(Intent.ACTION_VIEW) + && filter.hasCategory(Intent.CATEGORY_BROWSABLE)) { + continue; + } + instantAppResolver.addFilter( + new AuxiliaryResolveInfo.AuxiliaryFilter( + filter, + instantAppInfo, + instantAppFilter.getSplitName(), + instantAppInfo.getExtras() + )); + } + } + List<AuxiliaryResolveInfo.AuxiliaryFilter> matchedResolveInfoList = + instantAppResolver.queryIntent( + origIntent, resolvedType, false /*defaultOnly*/, userId); + if (!matchedResolveInfoList.isEmpty()) { + if (DEBUG_INSTANT) { + Log.d(TAG, "[" + token + "] Found match(es); " + matchedResolveInfoList); + } + return matchedResolveInfoList; + } else if (DEBUG_INSTANT) { + Log.d(TAG, "[" + token + "] No matches found" + + " package: " + instantAppInfo.getPackageName() + + ", versionCode: " + instantAppInfo.getVersionCode()); + } + return null; + } + private static void logMetrics(int action, long startTime, String token, @ResolutionStatus int status) { final LogMaker logMaker = new LogMaker(action) diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java index b5ddf8c511f1..a9ee41162ae1 100644 --- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java +++ b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java @@ -37,33 +37,29 @@ import android.util.Slog; import android.util.TimedRemoteCaller; import com.android.internal.annotations.GuardedBy; -import com.android.internal.os.TransferPipe; -import java.io.FileDescriptor; -import java.io.IOException; -import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.TimeoutException; /** - * Represents a remote ephemeral resolver. It is responsible for binding to the remote + * Represents a remote instant app resolver. It is responsible for binding to the remote * service and handling all interactions in a timely manner. * @hide */ -final class EphemeralResolverConnection implements DeathRecipient { +final class InstantAppResolverConnection implements DeathRecipient { private static final String TAG = "PackageManager"; // This is running in a critical section and the timeout must be sufficiently low private static final long BIND_SERVICE_TIMEOUT_MS = Build.IS_ENG ? 500 : 300; private static final long CALL_SERVICE_TIMEOUT_MS = Build.IS_ENG ? 200 : 100; - private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE; + private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE; private final Object mLock = new Object(); - private final GetEphemeralResolveInfoCaller mGetEphemeralResolveInfoCaller = - new GetEphemeralResolveInfoCaller(); + private final GetInstantAppResolveInfoCaller mGetInstantAppResolveInfoCaller = + new GetInstantAppResolveInfoCaller(); private final ServiceConnection mServiceConnection = new MyServiceConnection(); private final Context mContext; /** Intent used to bind to the service */ @@ -78,14 +74,14 @@ final class EphemeralResolverConnection implements DeathRecipient { @GuardedBy("mLock") private IInstantAppResolver mRemoteInstance; - public EphemeralResolverConnection( + public InstantAppResolverConnection( Context context, ComponentName componentName, String action) { mContext = context; mIntent = new Intent(action).setComponent(componentName); } - public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(int hashPrefix[], - String token) throws ConnectionException { + public final List<InstantAppResolveInfo> getInstantAppResolveInfoList(Intent sanitizedIntent, + int hashPrefix[], String token) throws ConnectionException { throwIfCalledOnMainThread(); IInstantAppResolver target = null; try { @@ -97,8 +93,8 @@ final class EphemeralResolverConnection implements DeathRecipient { throw new ConnectionException(ConnectionException.FAILURE_INTERRUPTED); } try { - return mGetEphemeralResolveInfoCaller - .getEphemeralResolveInfoList(target, hashPrefix, token); + return mGetInstantAppResolveInfoCaller + .getInstantAppResolveInfoList(target, sanitizedIntent, hashPrefix, token); } catch (TimeoutException e) { throw new ConnectionException(ConnectionException.FAILURE_CALL); } catch (RemoteException ignore) { @@ -111,26 +107,22 @@ final class EphemeralResolverConnection implements DeathRecipient { return null; } - public final void getInstantAppIntentFilterList(int hashPrefix[], String token, - String hostName, PhaseTwoCallback callback, Handler callbackHandler, - final long startTime) throws ConnectionException { + public final void getInstantAppIntentFilterList(Intent sanitizedIntent, int hashPrefix[], + String token, PhaseTwoCallback callback, Handler callbackHandler, final long startTime) + throws ConnectionException { final IRemoteCallback remoteCallback = new IRemoteCallback.Stub() { @Override public void sendResult(Bundle data) throws RemoteException { final ArrayList<InstantAppResolveInfo> resolveList = data.getParcelableArrayList( InstantAppResolverService.EXTRA_RESOLVE_INFO); - callbackHandler.post(new Runnable() { - @Override - public void run() { - callback.onPhaseTwoResolved(resolveList, startTime); - } - }); + callbackHandler.post(() -> callback.onPhaseTwoResolved(resolveList, startTime)); } }; try { getRemoteInstanceLazy(token) - .getInstantAppIntentFilterList(hashPrefix, token, hostName, remoteCallback); + .getInstantAppIntentFilterList(sanitizedIntent, hashPrefix, token, + remoteCallback); } catch (TimeoutException e) { throw new ConnectionException(ConnectionException.FAILURE_BIND); } catch (InterruptedException e) { @@ -174,7 +166,7 @@ final class EphemeralResolverConnection implements DeathRecipient { if (mBindState == STATE_PENDING) { // there is a pending bind, let's see if we can use it. - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.i(TAG, "[" + token + "] Previous bind timed out; waiting for connection"); } try { @@ -191,7 +183,7 @@ final class EphemeralResolverConnection implements DeathRecipient { if (mBindState == STATE_BINDING) { // someone was binding when we called bind(), or they raced ahead while we were // waiting in the PENDING case; wait for their result instead. Last chance! - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.i(TAG, "[" + token + "] Another thread is binding; waiting for connection"); } waitForBindLocked(token); @@ -209,12 +201,12 @@ final class EphemeralResolverConnection implements DeathRecipient { IInstantAppResolver instance = null; try { if (doUnbind) { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.i(TAG, "[" + token + "] Previous connection never established; rebinding"); } mContext.unbindService(mServiceConnection); } - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.v(TAG, "[" + token + "] Binding to instant app resolver"); } final int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; @@ -250,7 +242,7 @@ final class EphemeralResolverConnection implements DeathRecipient { @Override public void binderDied() { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.d(TAG, "Binder to instant app resolver died"); } synchronized (mLock) { @@ -289,7 +281,7 @@ final class EphemeralResolverConnection implements DeathRecipient { private final class MyServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.d(TAG, "Connected to instant app resolver"); } synchronized (mLock) { @@ -298,7 +290,7 @@ final class EphemeralResolverConnection implements DeathRecipient { mBindState = STATE_IDLE; } try { - service.linkToDeath(EphemeralResolverConnection.this, 0 /*flags*/); + service.linkToDeath(InstantAppResolverConnection.this, 0 /*flags*/); } catch (RemoteException e) { handleBinderDiedLocked(); } @@ -308,7 +300,7 @@ final class EphemeralResolverConnection implements DeathRecipient { @Override public void onServiceDisconnected(ComponentName name) { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.d(TAG, "Disconnected from instant app resolver"); } synchronized (mLock) { @@ -317,11 +309,11 @@ final class EphemeralResolverConnection implements DeathRecipient { } } - private static final class GetEphemeralResolveInfoCaller + private static final class GetInstantAppResolveInfoCaller extends TimedRemoteCaller<List<InstantAppResolveInfo>> { private final IRemoteCallback mCallback; - public GetEphemeralResolveInfoCaller() { + public GetInstantAppResolveInfoCaller() { super(CALL_SERVICE_TIMEOUT_MS); mCallback = new IRemoteCallback.Stub() { @Override @@ -336,11 +328,12 @@ final class EphemeralResolverConnection implements DeathRecipient { }; } - public List<InstantAppResolveInfo> getEphemeralResolveInfoList( - IInstantAppResolver target, int hashPrefix[], String token) + public List<InstantAppResolveInfo> getInstantAppResolveInfoList( + IInstantAppResolver target, Intent sanitizedIntent, int hashPrefix[], String token) throws RemoteException, TimeoutException { final int sequence = onBeforeRemoteCall(); - target.getInstantAppResolveInfoList(hashPrefix, token, sequence, mCallback); + target.getInstantAppResolveInfoList(sanitizedIntent, hashPrefix, token, sequence, + mCallback); return getResultTimed(sequence); } } diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 10e05cf34955..fc73142c4858 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -261,12 +261,12 @@ public class OtaDexoptService extends IOtaDexopt.Stub { String instructionSet, int dexoptNeeded, @Nullable String outputPath, int dexFlags, String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade, - int targetSdkVersion, @Nullable String profileName) - throws InstallerException { + int targetSdkVersion, @Nullable String profileName, + @Nullable String dexMetadataPath) throws InstallerException { final StringBuilder builder = new StringBuilder(); - // The version. Right now it's 5. - builder.append("5 "); + // The version. Right now it's 6. + builder.append("6 "); builder.append("dexopt"); @@ -284,6 +284,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { encodeParameter(builder, downgrade); encodeParameter(builder, targetSdkVersion); encodeParameter(builder, profileName); + encodeParameter(builder, dexMetadataPath); commands.add(builder.toString()); } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index cde8cb740590..2c68e67a3bd5 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageParser; import android.content.pm.dex.ArtManager; +import android.content.pm.dex.DexMetadataHelper; import android.os.FileUtils; import android.os.PowerManager; import android.os.SystemClock; @@ -209,6 +210,13 @@ public class PackageDexOptimizer { String profileName = ArtManager.getProfileName(i == 0 ? null : pkg.splitNames[i - 1]); + String dexMetadataPath = null; + if (options.isDexoptInstallWithDexMetadata()) { + File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path)); + dexMetadataPath = dexMetadataFile == null + ? null : dexMetadataFile.getAbsolutePath(); + } + final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary() || packageUseInfo.isUsedByOtherApps(path); final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo, @@ -223,7 +231,7 @@ public class PackageDexOptimizer { for (String dexCodeIsa : dexCodeInstructionSets) { int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter, profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid, - packageStats, options.isDowngrade(), profileName); + packageStats, options.isDowngrade(), profileName, dexMetadataPath); // The end result is: // - FAILED if any path failed, // - PERFORMED if at least one path needed compilation, @@ -248,7 +256,7 @@ public class PackageDexOptimizer { private int dexOptPath(PackageParser.Package pkg, String path, String isa, String compilerFilter, boolean profileUpdated, String classLoaderContext, int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade, - String profileName) { + String profileName, String dexMetadataPath) { int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext, profileUpdated, downgrade); if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) { @@ -275,7 +283,7 @@ public class PackageDexOptimizer { mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags, compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo, false /* downgrade*/, pkg.applicationInfo.targetSdkVersion, - profileName); + profileName, dexMetadataPath); if (packageStats != null) { long endTime = System.currentTimeMillis(); @@ -396,7 +404,8 @@ public class PackageDexOptimizer { mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0, /*oatDir*/ null, dexoptFlags, compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser, - options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null); + options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null, + /*dexMetadataPath*/ null); } return DEX_OPT_PERFORMED; @@ -511,9 +520,13 @@ public class PackageDexOptimizer { private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) { int flags = info.flags; boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; - // Profile guide compiled oat files should not be public. + // Profile guide compiled oat files should not be public unles they are based + // on profiles from dex metadata archives. + // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that + // the user does not have an existing profile. boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter); - boolean isPublic = !info.isForwardLocked() && !isProfileGuidedFilter; + boolean isPublic = !info.isForwardLocked() && + (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata()); int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0; // System apps are invoked with a runtime flag which exempts them from // restrictions on hidden API usage. We dexopt with the same runtime flag diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index a6ff4f7e5f70..3dd5a3415e35 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -77,6 +77,7 @@ import android.os.RevocableFileDescriptor; import android.os.UserHandle; import android.os.storage.StorageManager; import android.system.ErrnoException; +import android.system.Int64Ref; import android.system.Os; import android.system.OsConstants; import android.system.StructStat; @@ -575,14 +576,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) { try { - return openWriteInternal(name, offsetBytes, lengthBytes); + return doWriteInternal(name, offsetBytes, lengthBytes, null); } catch (IOException e) { throw ExceptionUtils.wrap(e); } } - private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes) - throws IOException { + @Override + public void write(String name, long offsetBytes, long lengthBytes, + ParcelFileDescriptor fd) { + try { + doWriteInternal(name, offsetBytes, lengthBytes, fd); + } catch (IOException e) { + throw ExceptionUtils.wrap(e); + } + } + + private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes, + ParcelFileDescriptor incomingFd) throws IOException { // Quick sanity check of state, and allocate a pipe for ourselves. We // then do heavy disk allocation outside the lock, but this open pipe // will block any attempted install transitions. @@ -636,7 +647,44 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); } - if (PackageInstaller.ENABLE_REVOCABLE_FD) { + if (incomingFd != null) { + switch (Binder.getCallingUid()) { + case android.os.Process.SHELL_UID: + case android.os.Process.ROOT_UID: + break; + default: + throw new SecurityException("Reverse mode only supported from shell"); + } + + // In "reverse" mode, we're streaming data ourselves from the + // incoming FD, which means we never have to hand out our + // sensitive internal FD. We still rely on a "bridge" being + // inserted above to hold the session active. + try { + final Int64Ref last = new Int64Ref(0); + FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, (long progress) -> { + if (params.sizeBytes > 0) { + final long delta = progress - last.value; + last.value = progress; + addClientProgress((float) delta / (float) params.sizeBytes); + } + }, null, lengthBytes); + } finally { + IoUtils.closeQuietly(targetFd); + IoUtils.closeQuietly(incomingFd); + + // We're done here, so remove the "bridge" that was holding + // the session active. + synchronized (mLock) { + if (PackageInstaller.ENABLE_REVOCABLE_FD) { + mFds.remove(fd); + } else { + mBridges.remove(bridge); + } + } + } + return null; + } else if (PackageInstaller.ENABLE_REVOCABLE_FD) { fd.init(mContext, targetFd); return fd.getRevocableFileDescriptor(); } else { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 01c44af586ed..a0cb7227ad9f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -423,7 +423,7 @@ public class PackageManagerService extends IPackageManager.Stub public static final boolean DEBUG_DEXOPT = false; private static final boolean DEBUG_ABI_SELECTION = false; - private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE; + private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE; private static final boolean DEBUG_TRIAGED_MISSING = false; private static final boolean DEBUG_APP_DATA = false; @@ -582,6 +582,7 @@ public class PackageManagerService extends IPackageManager.Stub sBrowserIntent.setAction(Intent.ACTION_VIEW); sBrowserIntent.addCategory(Intent.CATEGORY_BROWSABLE); sBrowserIntent.setData(Uri.parse("http:")); + sBrowserIntent.addFlags(Intent.FLAG_IGNORE_EPHEMERAL); } /** @@ -980,7 +981,7 @@ public class PackageManagerService extends IPackageManager.Stub private int mIntentFilterVerificationToken = 0; /** The service connection to the ephemeral resolver */ - final EphemeralResolverConnection mInstantAppResolverConnection; + final InstantAppResolverConnection mInstantAppResolverConnection; /** Component used to show resolver settings for Instant Apps */ final ComponentName mInstantAppResolverSettingsComponent; @@ -3148,10 +3149,10 @@ Slog.e("TODD", final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr(); if (instantAppResolverComponent != null) { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent); } - mInstantAppResolverConnection = new EphemeralResolverConnection( + mInstantAppResolverConnection = new InstantAppResolverConnection( mContext, instantAppResolverComponent.first, instantAppResolverComponent.second); mInstantAppResolverSettingsComponent = @@ -3531,7 +3532,7 @@ Slog.e("TODD", final String[] packageArray = mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage); if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.d(TAG, "Ephemeral resolver NOT found; empty package list"); } return null; @@ -3546,19 +3547,9 @@ Slog.e("TODD", final Intent resolverIntent = new Intent(actionName); List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null, resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); - // temporarily look for the old action - if (resolvers.size() == 0) { - if (DEBUG_EPHEMERAL) { - Slog.d(TAG, "Ephemeral resolver not found with new action; try old one"); - } - actionName = Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE; - resolverIntent.setAction(actionName); - resolvers = queryIntentServicesInternal(resolverIntent, null, - resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); - } final int N = resolvers.size(); if (N == 0) { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.d(TAG, "Ephemeral resolver NOT found; no matching intent filters"); } return null; @@ -3574,44 +3565,53 @@ Slog.e("TODD", final String packageName = info.serviceInfo.packageName; if (!possiblePackages.contains(packageName) && !Build.IS_DEBUGGABLE) { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.d(TAG, "Ephemeral resolver not in allowed package list;" + " pkg: " + packageName + ", info:" + info); } continue; } - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.v(TAG, "Ephemeral resolver found;" + " pkg: " + packageName + ", info:" + info); } return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName); } - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.v(TAG, "Ephemeral resolver NOT found"); } return null; } private @Nullable ActivityInfo getInstantAppInstallerLPr() { - final Intent intent = new Intent(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE); + String[] orderedActions = Build.IS_ENG + ? new String[]{ + Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST", + Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE} + : new String[]{ + Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE}; final int resolveFlags = MATCH_DIRECT_BOOT_AWARE - | MATCH_DIRECT_BOOT_UNAWARE - | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); - List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE, - resolveFlags, UserHandle.USER_SYSTEM); - // temporarily look for the old action - if (matches.isEmpty()) { - if (DEBUG_EPHEMERAL) { - Slog.d(TAG, "Ephemeral installer not found with new action; try old one"); - } - intent.setAction(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE); + | MATCH_DIRECT_BOOT_UNAWARE + | Intent.FLAG_IGNORE_EPHEMERAL + | (!Build.IS_ENG ? MATCH_SYSTEM_ONLY : 0); + final Intent intent = new Intent(); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE); + List<ResolveInfo> matches = null; + for (String action : orderedActions) { + intent.setAction(action); matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE, resolveFlags, UserHandle.USER_SYSTEM); + if (matches.isEmpty()) { + if (DEBUG_INSTANT) { + Slog.d(TAG, "Instant App installer not found with " + action); + } + } else { + break; + } } Iterator<ResolveInfo> iter = matches.iterator(); while (iter.hasNext()) { @@ -3619,7 +3619,8 @@ Slog.e("TODD", final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName); if (ps != null) { final PermissionsState permissionsState = ps.getPermissionsState(); - if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0)) { + if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0) + || Build.IS_ENG) { continue; } } @@ -3643,15 +3644,6 @@ Slog.e("TODD", final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags, UserHandle.USER_SYSTEM); - // temporarily look for the old action - if (matches.isEmpty()) { - if (DEBUG_EPHEMERAL) { - Slog.d(TAG, "Ephemeral resolver settings not found with new action; try old one"); - } - intent.setAction(Intent.ACTION_EPHEMERAL_RESOLVER_SETTINGS); - matches = queryIntentActivitiesInternal(intent, null, resolveFlags, - UserHandle.USER_SYSTEM); - } if (matches.isEmpty()) { return null; } @@ -3848,10 +3840,6 @@ Slog.e("TODD", if (ps == null) { return null; } - PackageParser.Package p = ps.pkg; - if (p == null) { - return null; - } final int callingUid = Binder.getCallingUid(); // Filter out ephemeral app metadata: // * The system/shell/root can see metadata for any app @@ -3863,32 +3851,58 @@ Slog.e("TODD", return null; } - final PermissionsState permissionsState = ps.getPermissionsState(); - - // Compute GIDs only if requested - final int[] gids = (flags & PackageManager.GET_GIDS) == 0 - ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId); - // Compute granted permissions only if package has requested permissions - final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions) - ? Collections.<String>emptySet() : permissionsState.getPermissions(userId); - final PackageUserState state = ps.readUserState(userId); - if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && ps.isSystem()) { flags |= MATCH_ANY_USER; } - PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags, - ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId); + final PackageUserState state = ps.readUserState(userId); + PackageParser.Package p = ps.pkg; + if (p != null) { + final PermissionsState permissionsState = ps.getPermissionsState(); - if (packageInfo == null) { - return null; - } + // Compute GIDs only if requested + final int[] gids = (flags & PackageManager.GET_GIDS) == 0 + ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId); + // Compute granted permissions only if package has requested permissions + final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions) + ? Collections.<String>emptySet() : permissionsState.getPermissions(userId); - packageInfo.packageName = packageInfo.applicationInfo.packageName = - resolveExternalPackageNameLPr(p); + PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags, + ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId); - return packageInfo; + if (packageInfo == null) { + return null; + } + + packageInfo.packageName = packageInfo.applicationInfo.packageName = + resolveExternalPackageNameLPr(p); + + return packageInfo; + } else if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && state.isAvailable(flags)) { + PackageInfo pi = new PackageInfo(); + pi.packageName = ps.name; + pi.setLongVersionCode(ps.versionCode); + pi.sharedUserId = (ps.sharedUser != null) ? ps.sharedUser.name : null; + pi.firstInstallTime = ps.firstInstallTime; + pi.lastUpdateTime = ps.lastUpdateTime; + + ApplicationInfo ai = new ApplicationInfo(); + ai.packageName = ps.name; + ai.uid = UserHandle.getUid(userId, ps.appId); + ai.primaryCpuAbi = ps.primaryCpuAbiString; + ai.secondaryCpuAbi = ps.secondaryCpuAbiString; + ai.versionCode = ps.versionCode; + ai.flags = ps.pkgFlags; + ai.privateFlags = ps.pkgPrivateFlags; + pi.applicationInfo = PackageParser.generateApplicationInfo(ai, flags, state, userId); + + if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for [" + + ps.name + "]. Provides a minimum info."); + return pi; + } else { + return null; + } } @Override @@ -4778,10 +4792,7 @@ Slog.e("TODD", flags |= PackageManager.MATCH_INSTANT; } else { final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0; - final boolean allowMatchInstant = - (wantInstantApps - && Intent.ACTION_VIEW.equals(intent.getAction()) - && hasWebURI(intent)) + final boolean allowMatchInstant = wantInstantApps || (wantMatchInstant && canViewInstantApps(callingUid, userId)); flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY | PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY); @@ -5971,8 +5982,14 @@ Slog.e("TODD", if (!skipPackageCheck && intent.getPackage() != null) { return false; } - final boolean isWebUri = hasWebURI(intent); - if (!isWebUri || intent.getData().getHost() == null) { + if (!intent.isBrowsableWebIntent()) { + // for non web intents, we should not resolve externally if an app already exists to + // handle it or if the caller didn't explicitly request it. + if ((resolvedActivities != null && resolvedActivities.size() != 0) + || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) { + return false; + } + } else if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) { return false; } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. @@ -5991,7 +6008,7 @@ Slog.e("TODD", final int status = (int) (packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName + ", status: " + status); } @@ -5999,7 +6016,7 @@ Slog.e("TODD", } } if (ps.getInstantApp(userId)) { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app installed;" + " pkg: " + packageName); } @@ -6370,7 +6387,7 @@ Slog.e("TODD", if (matches.get(i).getTargetUserId() == targetUserId) return true; } } - if (hasWebURI(intent)) { + if (intent.hasWebURI()) { // cross-profile app linking works only towards the parent. final int callingUid = Binder.getCallingUid(); final UserInfo parent = getProfileParent(sourceUserId); @@ -6545,7 +6562,7 @@ Slog.e("TODD", sortResult = true; } } - if (hasWebURI(intent)) { + if (intent.hasWebURI()) { CrossProfileDomainInfo xpDomainInfo = null; final UserInfo parent = getProfileParent(userId); if (parent != null) { @@ -6631,12 +6648,11 @@ Slog.e("TODD", if (ps.getInstantApp(userId)) { final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int)(packedStatus >> 32); - final int linkGeneration = (int)(packedStatus & 0xFFFFFFFF); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { // there's a local instant application installed, but, the user has // chosen to never use it; skip resolution and don't acknowledge // an instant application is even available - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName); } blockResolution = true; @@ -6644,7 +6660,7 @@ Slog.e("TODD", } else { // we have a locally installed instant application; skip resolution // but acknowledge there's an instant application available - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.v(TAG, "Found installed instant app; pkg: " + packageName); } localInstantApp = info; @@ -6663,9 +6679,8 @@ Slog.e("TODD", null /*responseObj*/, intent /*origIntent*/, resolvedType, null /*callingPackage*/, userId, null /*verificationBundle*/, resolveForStart); - auxiliaryResponse = - InstantAppResolver.doInstantAppResolutionPhaseOne( - mContext, mInstantAppResolverConnection, requestObject); + auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne( + mInstantAppResolverConnection, requestObject); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } else { // we have an instant application locally, but, we can't admit that since @@ -6674,35 +6689,40 @@ Slog.e("TODD", // instant application available externally. when it comes time to start // the instant application, we'll do the right thing. final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo; - auxiliaryResponse = new AuxiliaryResolveInfo( - ai.packageName, null /*splitName*/, null /*failureActivity*/, - ai.versionCode, null /*failureIntent*/); + auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */, + ai.packageName, ai.versionCode, null /* splitName */); } } - if (auxiliaryResponse != null) { - if (DEBUG_EPHEMERAL) { - Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); - } - final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo); - final PackageSetting ps = - mSettings.mPackages.get(mInstantAppInstallerActivity.packageName); - if (ps != null) { - ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo( - mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId); - ephemeralInstaller.activityInfo.launchToken = auxiliaryResponse.token; - ephemeralInstaller.auxiliaryInfo = auxiliaryResponse; - // make sure this resolver is the default - ephemeralInstaller.isDefault = true; - ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART - | IntentFilter.MATCH_ADJUSTMENT_NORMAL; - // add a non-generic filter - ephemeralInstaller.filter = new IntentFilter(intent.getAction()); - ephemeralInstaller.filter.addDataPath( - intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL); - ephemeralInstaller.isInstantAppAvailable = true; - result.add(ephemeralInstaller); - } + if (intent.isBrowsableWebIntent() && auxiliaryResponse == null) { + return result; } + final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName); + if (ps == null) { + return result; + } + final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo); + ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo( + mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId); + ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART + | IntentFilter.MATCH_ADJUSTMENT_NORMAL; + // add a non-generic filter + ephemeralInstaller.filter = new IntentFilter(); + if (intent.getAction() != null) { + ephemeralInstaller.filter.addAction(intent.getAction()); + } + if (intent.getData() != null && intent.getData().getPath() != null) { + ephemeralInstaller.filter.addDataPath( + intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL); + } + ephemeralInstaller.isInstantAppAvailable = true; + // make sure this resolver is the default + ephemeralInstaller.isDefault = true; + ephemeralInstaller.auxiliaryInfo = auxiliaryResponse; + if (DEBUG_INSTANT) { + Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); + } + + result.add(ephemeralInstaller); return result; } @@ -6817,10 +6837,11 @@ Slog.e("TODD", final ResolveInfo info = resolveInfos.get(i); // allow activities that are defined in the provided package if (allowDynamicSplits + && info.activityInfo != null && info.activityInfo.splitName != null && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames, info.activityInfo.splitName)) { - if (mInstantAppInstallerInfo == null) { + if (mInstantAppInstallerActivity == null) { if (DEBUG_INSTALL) { Slog.v(TAG, "No installer - not adding it to the ResolveInfo list"); } @@ -6832,14 +6853,15 @@ Slog.e("TODD", if (DEBUG_INSTALL) { Slog.v(TAG, "Adding installer to the ResolveInfo list"); } - final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo); + final ResolveInfo installerInfo = new ResolveInfo( + mInstantAppInstallerInfo); final ComponentName installFailureActivity = findInstallFailureActivity( info.activityInfo.packageName, filterCallingUid, userId); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( - info.activityInfo.packageName, info.activityInfo.splitName, installFailureActivity, + info.activityInfo.packageName, info.activityInfo.applicationInfo.versionCode, - null /*failureIntent*/); + info.activityInfo.splitName); installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART | IntentFilter.MATCH_ADJUSTMENT_NORMAL; // add a non-generic filter @@ -6856,6 +6878,7 @@ Slog.e("TODD", installerInfo.priority = info.priority; installerInfo.preferredOrder = info.preferredOrder; installerInfo.isDefault = info.isDefault; + installerInfo.isInstantAppAvailable = true; resolveInfos.set(i, installerInfo); continue; } @@ -6913,17 +6936,6 @@ Slog.e("TODD", return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0; } - private static boolean hasWebURI(Intent intent) { - if (intent.getData() == null) { - return false; - } - final String scheme = intent.getScheme(); - if (TextUtils.isEmpty(scheme)) { - return false; - } - return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS); - } - private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo, int userId) { @@ -7593,14 +7605,16 @@ Slog.e("TODD", info.serviceInfo.splitName)) { // requested service is defined in a split that hasn't been installed yet. // add the installer to the resolve list - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } - final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo); + final ResolveInfo installerInfo = new ResolveInfo( + mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( - info.serviceInfo.packageName, info.serviceInfo.splitName, - null /*failureActivity*/, info.serviceInfo.applicationInfo.versionCode, - null /*failureIntent*/); + null /* installFailureActivity */, + info.serviceInfo.packageName, + info.serviceInfo.applicationInfo.versionCode, + info.serviceInfo.splitName); // make sure this resolver is the default installerInfo.isDefault = true; installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART @@ -7713,14 +7727,16 @@ Slog.e("TODD", info.providerInfo.splitName)) { // requested provider is defined in a split that hasn't been installed yet. // add the installer to the resolve list - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } - final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo); + final ResolveInfo installerInfo = new ResolveInfo( + mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( - info.providerInfo.packageName, info.providerInfo.splitName, - null /*failureActivity*/, info.providerInfo.applicationInfo.versionCode, - null /*failureIntent*/); + null /*failureActivity*/, + info.providerInfo.packageName, + info.providerInfo.applicationInfo.versionCode, + info.providerInfo.splitName); // make sure this resolver is the default installerInfo.isDefault = true; installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART @@ -9991,8 +10007,7 @@ Slog.e("TODD", // priv-apps. synchronized (mPackages) { PackageSetting platformPkgSetting = mSettings.mPackages.get("android"); - if (!pkg.packageName.equals("android") - && (compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures, + if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures, pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH)) { scanFlags |= SCAN_AS_PRIVILEGED; } @@ -10459,7 +10474,20 @@ Slog.e("TODD", pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } - SELinuxMMAC.assignSeInfoValue(pkg); + // SELinux sandboxes become more restrictive as targetSdkVersion increases. + // To ensure that apps with sharedUserId are placed in the same selinux domain + // without breaking any assumptions about access, put them into the least + // restrictive targetSdkVersion=25 domain. + // TODO(b/72290969): Base this on the actual targetSdkVersion(s) of the apps within the + // sharedUserSetting, instead of defaulting to the least restrictive domain. + final int targetSdk = (sharedUserSetting != null) ? 25 + : pkg.applicationInfo.targetSdkVersion; + // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync. + // They currently can be if the sharedUser apps are signed with the platform key. + final boolean isPrivileged = (sharedUserSetting != null) ? + sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged(); + + SELinuxMMAC.assignSeInfoValue(pkg, isPrivileged, targetSdk); pkg.mExtras = pkgSetting; pkg.applicationInfo.processName = fixProcessName( @@ -11776,14 +11804,14 @@ Slog.e("TODD", private void setUpInstantAppInstallerActivityLP(ActivityInfo installerActivity) { if (installerActivity == null) { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.d(TAG, "Clear ephemeral installer activity"); } mInstantAppInstallerActivity = null; return; } - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.d(TAG, "Set ephemeral installer activity: " + installerActivity.getComponentName()); } @@ -11794,7 +11822,7 @@ Slog.e("TODD", mInstantAppInstallerActivity.exported = true; mInstantAppInstallerActivity.enabled = true; mInstantAppInstallerInfo.activityInfo = mInstantAppInstallerActivity; - mInstantAppInstallerInfo.priority = 0; + mInstantAppInstallerInfo.priority = 1; mInstantAppInstallerInfo.preferredOrder = 1; mInstantAppInstallerInfo.isDefault = true; mInstantAppInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART @@ -13243,8 +13271,9 @@ Slog.e("TODD", private int mFlags; } - static final class EphemeralIntentResolver - extends IntentResolver<AuxiliaryResolveInfo, AuxiliaryResolveInfo> { + static final class InstantAppIntentResolver + extends IntentResolver<AuxiliaryResolveInfo.AuxiliaryFilter, + AuxiliaryResolveInfo.AuxiliaryFilter> { /** * The result that has the highest defined order. Ordering applies on a * per-package basis. Mapping is from package name to Pair of order and @@ -13259,18 +13288,19 @@ Slog.e("TODD", final ArrayMap<String, Pair<Integer, InstantAppResolveInfo>> mOrderResult = new ArrayMap<>(); @Override - protected AuxiliaryResolveInfo[] newArray(int size) { - return new AuxiliaryResolveInfo[size]; + protected AuxiliaryResolveInfo.AuxiliaryFilter[] newArray(int size) { + return new AuxiliaryResolveInfo.AuxiliaryFilter[size]; } @Override - protected boolean isPackageForFilter(String packageName, AuxiliaryResolveInfo responseObj) { + protected boolean isPackageForFilter(String packageName, + AuxiliaryResolveInfo.AuxiliaryFilter responseObj) { return true; } @Override - protected AuxiliaryResolveInfo newResult(AuxiliaryResolveInfo responseObj, int match, - int userId) { + protected AuxiliaryResolveInfo.AuxiliaryFilter newResult( + AuxiliaryResolveInfo.AuxiliaryFilter responseObj, int match, int userId) { if (!sUserManager.exists(userId)) { return null; } @@ -13291,7 +13321,7 @@ Slog.e("TODD", } @Override - protected void filterResults(List<AuxiliaryResolveInfo> results) { + protected void filterResults(List<AuxiliaryResolveInfo.AuxiliaryFilter> results) { // only do work if ordering is enabled [most of the time it won't be] if (mOrderResult.size() == 0) { return; @@ -13611,7 +13641,7 @@ Slog.e("TODD", IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, String installerPackageName, int installerUid, UserHandle user, PackageParser.SigningDetails signingDetails) { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { Slog.d(TAG, "Ephemeral install of " + packageName); } @@ -15095,7 +15125,7 @@ Slog.e("TODD", pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride); - if (DEBUG_EPHEMERAL && ephemeral) { + if (DEBUG_INSTANT && ephemeral) { Slog.v(TAG, "pkgLite for install: " + pkgLite); } @@ -15162,7 +15192,7 @@ Slog.e("TODD", installFlags |= PackageManager.INSTALL_EXTERNAL; installFlags &= ~PackageManager.INSTALL_INTERNAL; } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) { - if (DEBUG_EPHEMERAL) { + if (DEBUG_INSTANT) { Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag"); } installFlags |= PackageManager.INSTALL_INSTANT_APP; @@ -17301,7 +17331,8 @@ Slog.e("TODD", // Also, don't fail application installs if the dexopt step fails. DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName, REASON_INSTALL, - DexoptOptions.DEXOPT_BOOT_COMPLETE); + DexoptOptions.DEXOPT_BOOT_COMPLETE | + DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE); mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles, null /* instructionSets */, getOrCreateCompilerPackageStats(pkg), diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index c02331da4ed6..5060c4df655e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -693,7 +693,7 @@ public class PackageManagerServiceUtils { InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile)); OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/); ) { - Streams.copy(fileIn, fileOut); + FileUtils.copy(fileIn, fileOut); Os.chmod(dstFile.getAbsolutePath(), 0644); return PackageManager.INSTALL_SUCCEEDED; } catch (IOException e) { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 686c4a5eb321..758c9d56d32a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -16,6 +16,12 @@ package com.android.server.pm; +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK; +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; +import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; + import android.accounts.IAccountManager; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -32,8 +38,10 @@ import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; +import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageParser; import android.content.pm.PackageParser.ApkLite; import android.content.pm.PackageParser.PackageLite; @@ -41,9 +49,6 @@ import android.content.pm.PackageParser.PackageParserException; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; -import android.content.pm.PackageInstaller.SessionInfo; -import android.content.pm.PackageInstaller.SessionParams; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.pm.VersionedPackage; @@ -73,7 +78,6 @@ import android.util.PrintWriterPrinter; import com.android.internal.content.PackageHelper; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.SizedInputStream; import com.android.server.LocalServices; import com.android.server.SystemConfig; @@ -81,9 +85,8 @@ import dalvik.system.DexFile; import libcore.io.IoUtils; +import java.io.FileDescriptor; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.io.PrintWriter; import java.net.URISyntaxException; import java.util.ArrayList; @@ -95,12 +98,6 @@ import java.util.WeakHashMap; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; - class PackageManagerShellCommand extends ShellCommand { /** Path for streaming APK content */ private static final String STDIN_PATH = "-"; @@ -2213,7 +2210,7 @@ class PackageManagerShellCommand extends ShellCommand { final PrintWriter pw = getOutPrintWriter(); final ParcelFileDescriptor fd; if (STDIN_PATH.equals(inPath)) { - fd = null; + fd = new ParcelFileDescriptor(getInFileDescriptor()); } else if (inPath != null) { fd = openFileForSystem(inPath, "r"); if (fd == null) { @@ -2225,53 +2222,27 @@ class PackageManagerShellCommand extends ShellCommand { return -1; } } else { - fd = null; + fd = new ParcelFileDescriptor(getInFileDescriptor()); } if (sizeBytes <= 0) { getErrPrintWriter().println("Error: must specify a APK size"); return 1; } - final SessionInfo info = mInterface.getPackageInstaller().getSessionInfo(sessionId); - PackageInstaller.Session session = null; - InputStream in = null; - OutputStream out = null; try { session = new PackageInstaller.Session( mInterface.getPackageInstaller().openSession(sessionId)); - - if (fd != null) { - in = new ParcelFileDescriptor.AutoCloseInputStream(fd); - } else { - in = new SizedInputStream(getRawInputStream(), sizeBytes); - } - out = session.openWrite(splitName, 0, sizeBytes); - - int total = 0; - byte[] buffer = new byte[1024 * 1024]; - int c; - while ((c = in.read(buffer)) != -1) { - total += c; - out.write(buffer, 0, c); - - if (info.sizeBytes > 0) { - final float fraction = ((float) c / (float) info.sizeBytes); - session.addProgress(fraction); - } - } - session.fsync(out); + session.write(splitName, 0, sizeBytes, fd); if (logSuccess) { - pw.println("Success: streamed " + total + " bytes"); + pw.println("Success: streamed " + sizeBytes + " bytes"); } return 0; } catch (IOException e) { getErrPrintWriter().println("Error: failed to write; " + e.getMessage()); return 1; } finally { - IoUtils.closeQuietly(out); - IoUtils.closeQuietly(in); IoUtils.closeQuietly(session); } } diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java index fe5b3d456b87..a9f15282133f 100644 --- a/services/core/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java @@ -315,7 +315,8 @@ public final class SELinuxMMAC { * * @param pkg object representing the package to be labeled. */ - public static void assignSeInfoValue(PackageParser.Package pkg) { + public static void assignSeInfoValue(PackageParser.Package pkg, boolean isPrivileged, + int targetSdkVersion) { synchronized (sPolicies) { if (!sPolicyRead) { if (DEBUG_POLICY) { @@ -335,10 +336,11 @@ public final class SELinuxMMAC { if (pkg.applicationInfo.targetSandboxVersion == 2) pkg.applicationInfo.seInfo += SANDBOX_V2_STR; - if (pkg.applicationInfo.isPrivilegedApp()) + if (isPrivileged) { pkg.applicationInfo.seInfo += PRIVILEGED_APP_STR; + } - pkg.applicationInfo.seInfo += TARGETSDKVERSION_STR + pkg.applicationInfo.targetSdkVersion; + pkg.applicationInfo.seInfo += TARGETSDKVERSION_STR + targetSdkVersion; if (DEBUG_POLICY_INSTALL) { Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " + diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index ebf6672cf57e..e4c74edf0f0f 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -703,7 +703,7 @@ class ShortcutPackage extends ShortcutPackageItem { */ public boolean rescanPackageIfNeeded(boolean isNewApp, boolean forceRescan) { final ShortcutService s = mShortcutUser.mService; - final long start = s.injectElapsedRealtime(); + final long start = s.getStatStartTime(); final PackageInfo pi; try { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index d2bc6d24e2e9..a85d6d838045 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -99,6 +99,7 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; +import com.android.server.StatLogger; import com.android.server.SystemService; import com.android.server.pm.ShortcutUser.PackageWithUser; @@ -367,7 +368,7 @@ public class ShortcutService extends IShortcutService.Stub { int COUNT = GET_DEFAULT_LAUNCHER + 1; } - private static final String[] STAT_LABELS = { + private final StatLogger mStatLogger = new StatLogger(new String[] { "getHomeActivities()", "Launcher permission check", "getPackageInfo()", @@ -385,15 +386,7 @@ public class ShortcutService extends IShortcutService.Stub { "packageUpdateCheck", "asyncPreloadUserDelay", "getDefaultLauncher()" - }; - - final Object mStatLock = new Object(); - - @GuardedBy("mStatLock") - private final int[] mCountStats = new int[Stats.COUNT]; - - @GuardedBy("mStatLock") - private final long[] mDurationStats = new long[Stats.COUNT]; + }); private static final int PROCESS_STATE_FOREGROUND_THRESHOLD = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; @@ -480,11 +473,12 @@ public class ShortcutService extends IShortcutService.Stub { | ActivityManager.UID_OBSERVER_GONE); } + long getStatStartTime() { + return mStatLogger.getTime(); + } + void logDurationStat(int statId, long start) { - synchronized (mStatLock) { - mCountStats[statId]++; - mDurationStats[statId] += (injectElapsedRealtime() - start); - } + mStatLogger.logDurationStat(statId, start); } public String injectGetLocaleTagsForUser(@UserIdInt int userId) { @@ -621,7 +615,7 @@ public class ShortcutService extends IShortcutService.Stub { // late since the launcher would already have started. // So we just create a new thread. This code runs rarely, so we don't use a thread pool // or anything. - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); injectRunOnNewThread(() -> { synchronized (mLock) { logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start); @@ -1289,7 +1283,7 @@ public class ShortcutService extends IShortcutService.Stub { if (DEBUG) { Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId); } - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); final ShortcutUser user = getUserShortcutsLocked(userId); @@ -1485,7 +1479,7 @@ public class ShortcutService extends IShortcutService.Stub { final Resources publisherRes = injectGetResourcesForApplicationAsUser( si.getPackage(), si.getUserId()); if (publisherRes != null) { - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); try { si.lookupAndFillInResourceNames(publisherRes); } finally { @@ -2264,7 +2258,7 @@ public class ShortcutService extends IShortcutService.Stub { if (canSeeAnyPinnedShortcut(callingPackage, userId, callingPid, callingUid)) { return true; } - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); try { return hasShortcutHostPermissionInner(callingPackage, userId); } finally { @@ -2327,7 +2321,7 @@ public class ShortcutService extends IShortcutService.Stub { @Nullable ComponentName getDefaultLauncher(@UserIdInt int userId) { - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); final long token = injectClearCallingIdentity(); try { synchronized (mLock) { @@ -2338,7 +2332,7 @@ public class ShortcutService extends IShortcutService.Stub { final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); // Default launcher from package manager. - final long startGetHomeActivitiesAsUser = injectElapsedRealtime(); + final long startGetHomeActivitiesAsUser = getStatStartTime(); final ComponentName defaultLauncher = mPackageManagerInternal .getHomeActivitiesAsUser(allHomeCandidates, userId); logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser); @@ -2910,7 +2904,7 @@ public class ShortcutService extends IShortcutService.Stub { return; } - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); try { final ArrayList<PackageWithUser> gonePackages = new ArrayList<>(); @@ -3087,7 +3081,7 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId, boolean getSignatures) { - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); final long token = injectClearCallingIdentity(); try { return mIPackageManager.getPackageInfo( @@ -3122,7 +3116,7 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting ApplicationInfo injectApplicationInfoWithUninstalled( String packageName, @UserIdInt int userId) { - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); final long token = injectClearCallingIdentity(); try { return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId); @@ -3153,7 +3147,7 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting ActivityInfo injectGetActivityInfoWithMetadataWithUninstalled( ComponentName activity, @UserIdInt int userId) { - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); final long token = injectClearCallingIdentity(); try { return mIPackageManager.getActivityInfo(activity, @@ -3175,7 +3169,7 @@ public class ShortcutService extends IShortcutService.Stub { @NonNull @VisibleForTesting final List<PackageInfo> getInstalledPackages(@UserIdInt int userId) { - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); final long token = injectClearCallingIdentity(); try { final List<PackageInfo> all = injectGetPackagesWithUninstalled(userId); @@ -3280,7 +3274,7 @@ public class ShortcutService extends IShortcutService.Stub { @Nullable Resources injectGetResourcesForApplicationAsUser(String packageName, int userId) { - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); final long token = injectClearCallingIdentity(); try { return mContext.getPackageManager().getResourcesForApplicationAsUser( @@ -3348,7 +3342,7 @@ public class ShortcutService extends IShortcutService.Stub { */ @Nullable ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) { - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); try { final List<ResolveInfo> resolved = queryActivities(getMainActivityIntent(), packageName, null, userId); @@ -3362,7 +3356,7 @@ public class ShortcutService extends IShortcutService.Stub { * Return whether an activity is enabled, exported and main. */ boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) { - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); try { if (activity == null) { wtf("null activity detected"); @@ -3397,7 +3391,7 @@ public class ShortcutService extends IShortcutService.Stub { */ @NonNull List<ResolveInfo> injectGetMainActivities(@NonNull String packageName, int userId) { - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); try { return queryActivities(getMainActivityIntent(), packageName, null, userId); } finally { @@ -3411,7 +3405,7 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting boolean injectIsActivityEnabledAndExported( @NonNull ComponentName activity, @UserIdInt int userId) { - final long start = injectElapsedRealtime(); + final long start = getStatStartTime(); try { return queryActivities(new Intent(), activity.getPackageName(), activity, userId) .size() > 0; @@ -3831,12 +3825,7 @@ public class ShortcutService extends IShortcutService.Stub { pw.println(mMaxShortcuts); pw.println(); - pw.println(" Stats:"); - synchronized (mStatLock) { - for (int i = 0; i < Stats.COUNT; i++) { - dumpStatLS(pw, " ", i); - } - } + mStatLogger.dump(pw, " "); pw.println(); pw.print(" #Failures: "); @@ -3902,15 +3891,6 @@ public class ShortcutService extends IShortcutService.Stub { pw.print(formatTime(injectCurrentTimeMillis())); } - private void dumpStatLS(PrintWriter pw, String prefix, int statId) { - pw.print(prefix); - final int count = mCountStats[statId]; - final long dur = mDurationStats[statId]; - pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms", - STAT_LABELS[statId], count, dur, - (count == 0 ? 0 : ((double) dur) / count))); - } - /** * Dumpsys for checkin. * diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 92fd9041b32d..b53d83b1291c 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -88,6 +88,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.logging.MetricsLogger; +import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; @@ -387,7 +388,9 @@ public class UserManagerService extends IUserManager.Stub { } final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT); final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL); - setQuietModeEnabled(userHandle, false, target); + // Call setQuietModeEnabled on bg thread to avoid ANR + BackgroundThread.getHandler() + .post(() -> setQuietModeEnabled(userHandle, false, target)); } }; diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index a42fcbdd94a0..842f8d0a42f5 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -117,7 +117,7 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_AUTOFILL, UserManager.DISALLOW_USER_SWITCH, UserManager.DISALLOW_UNIFIED_PASSWORD, - UserManager.DISALLOW_CONFIG_LOCATION_MODE, + UserManager.DISALLOW_CONFIG_LOCATION, UserManager.DISALLOW_AIRPLANE_MODE, UserManager.DISALLOW_CONFIG_BRIGHTNESS, UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java index 0966770d4897..d4f95cb6b99f 100644 --- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java +++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java @@ -59,6 +59,10 @@ public final class DexoptOptions { // When set, indicates that dexopt is invoked from the background service. public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9; + // When set, indicates that dexopt is invoked from the install time flow and + // should get the dex metdata file if present. + public static final int DEXOPT_INSTALL_WITH_DEX_METADATA_FILE = 1 << 10; + // The name of package to optimize. private final String mPackageName; @@ -90,7 +94,8 @@ public final class DexoptOptions { DEXOPT_ONLY_SHARED_DEX | DEXOPT_DOWNGRADE | DEXOPT_AS_SHARED_LIBRARY | - DEXOPT_IDLE_BACKGROUND_JOB; + DEXOPT_IDLE_BACKGROUND_JOB | + DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; if ((flags & (~validityMask)) != 0) { throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags)); } @@ -141,6 +146,10 @@ public final class DexoptOptions { return (mFlags & DEXOPT_IDLE_BACKGROUND_JOB) != 0; } + public boolean isDexoptInstallWithDexMetadata() { + return (mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) != 0; + } + public String getSplitName() { return mSplitName; } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 0502848d698e..177d6af489b3 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -5603,9 +5603,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int fl = PolicyControl.getWindowFlags(null, mTopFullscreenOpaqueWindowState.getAttrs()); if (localLOGV) { - Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw() - + " shown position: " - + mTopFullscreenOpaqueWindowState.getShownPositionLw()); + Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()); Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs() + " lp.flags=0x" + Integer.toHexString(fl)); } diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index e9c4c5c8138f..3af3fcbbf7a8 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -232,14 +232,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public Rect getFrameLw(); /** - * Retrieve the current position of the window that is actually shown. - * Must be called with the window manager lock held. - * - * @return Point The point holding the shown window position. - */ - public Point getShownPositionLw(); - - /** * Retrieve the frame of the display that this window was last * laid out in. Must be called with the * window manager lock held. diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java index 946635043c12..b0b07ea767f6 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java @@ -32,6 +32,8 @@ import java.io.PrintWriter; /** * This class keeps track of battery drain rate. * + * TODO: The use of the terms "percent" and "level" in this class is not standard. Fix it. + * * Test: atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java */ @@ -96,8 +98,12 @@ public class BatterySavingStats { public int startBatteryLevel; public int endBatteryLevel; + public int startBatteryPercent; + public int endBatteryPercent; + public long totalTimeMillis; public int totalBatteryDrain; + public int totalBatteryDrainPercent; public long totalMinutes() { return totalTimeMillis / 60_000; @@ -110,14 +116,26 @@ public class BatterySavingStats { return (double) totalBatteryDrain / (totalTimeMillis / (60.0 * 60 * 1000)); } + public double drainPercentPerHour() { + if (totalTimeMillis == 0) { + return 0; + } + return (double) totalBatteryDrainPercent / (totalTimeMillis / (60.0 * 60 * 1000)); + } + @VisibleForTesting String toStringForTest() { return "{" + totalMinutes() + "m," + totalBatteryDrain + "," - + String.format("%.2f", drainPerHour()) + "}"; + + String.format("%.2f", drainPerHour()) + "uA/H," + + String.format("%.2f", drainPercentPerHour()) + "%" + + "}"; } } @VisibleForTesting + static final String COUNTER_POWER_PERCENT_PREFIX = "battery_saver_stats_percent_"; + + @VisibleForTesting static final String COUNTER_POWER_MILLIAMPS_PREFIX = "battery_saver_stats_milliamps_"; @VisibleForTesting @@ -166,6 +184,9 @@ public class BatterySavingStats { private BatteryManagerInternal getBatteryManagerInternal() { if (mBatteryManagerInternal == null) { mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); + if (mBatteryManagerInternal == null) { + Slog.wtf(TAG, "BatteryManagerInternal not initialized"); + } } return mBatteryManagerInternal; } @@ -229,12 +250,20 @@ public class BatterySavingStats { int injectBatteryLevel() { final BatteryManagerInternal bmi = getBatteryManagerInternal(); if (bmi == null) { - Slog.wtf(TAG, "BatteryManagerInternal not initialized"); return 0; } return bmi.getBatteryChargeCounter(); } + @VisibleForTesting + int injectBatteryPercent() { + final BatteryManagerInternal bmi = getBatteryManagerInternal(); + if (bmi == null) { + return 0; + } + return bmi.getBatteryLevel(); + } + /** * Called from the outside whenever any of the states changes, when the device is not plugged * in. @@ -262,33 +291,39 @@ public class BatterySavingStats { } final long now = injectCurrentTime(); final int batteryLevel = injectBatteryLevel(); + final int batteryPercent = injectBatteryPercent(); - endLastStateLocked(now, batteryLevel); - startNewStateLocked(newState, now, batteryLevel); - mMetricsLoggerHelper.transitionState(newState, now, batteryLevel); + endLastStateLocked(now, batteryLevel, batteryPercent); + startNewStateLocked(newState, now, batteryLevel, batteryPercent); + mMetricsLoggerHelper.transitionState(newState, now, batteryLevel, batteryPercent); } - private void endLastStateLocked(long now, int batteryLevel) { + private void endLastStateLocked(long now, int batteryLevel, int batteryPercent) { if (mCurrentState < 0) { return; } final Stat stat = getStat(mCurrentState); stat.endBatteryLevel = batteryLevel; + stat.endBatteryPercent = batteryPercent; stat.endTime = now; final long deltaTime = stat.endTime - stat.startTime; final int deltaDrain = stat.startBatteryLevel - stat.endBatteryLevel; + final int deltaPercent = stat.startBatteryPercent - stat.endBatteryPercent; stat.totalTimeMillis += deltaTime; stat.totalBatteryDrain += deltaDrain; + stat.totalBatteryDrainPercent += deltaPercent; if (DEBUG) { Slog.d(TAG, "State summary: " + stateToString(mCurrentState) + ": " + (deltaTime / 1_000) + "s " + "Start level: " + stat.startBatteryLevel + "uA " + "End level: " + stat.endBatteryLevel + "uA " - + deltaDrain + "uA"); + + "Start percent: " + stat.startBatteryPercent + "% " + + "End percent: " + stat.endBatteryPercent + "% " + + "Drain " + deltaDrain + "uA"); } EventLogTags.writeBatterySavingStats( BatterySaverState.fromIndex(mCurrentState), @@ -296,12 +331,14 @@ public class BatterySavingStats { DozeState.fromIndex(mCurrentState), deltaTime, deltaDrain, + deltaPercent, stat.totalTimeMillis, - stat.totalBatteryDrain); + stat.totalBatteryDrain, + stat.totalBatteryDrainPercent); } - private void startNewStateLocked(int newState, long now, int batteryLevel) { + private void startNewStateLocked(int newState, long now, int batteryLevel, int batteryPercent) { if (DEBUG) { Slog.d(TAG, "New state: " + stateToString(newState)); } @@ -313,6 +350,7 @@ public class BatterySavingStats { final Stat stat = getStat(mCurrentState); stat.startBatteryLevel = batteryLevel; + stat.startBatteryPercent = batteryPercent; stat.startTime = now; stat.endTime = 0; } @@ -325,7 +363,7 @@ public class BatterySavingStats { indent = indent + " "; pw.print(indent); - pw.println("Battery Saver: Off On"); + pw.println("Battery Saver: Off On"); dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", DozeState.NOT_DOZING, "NonDoze"); dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", @@ -357,12 +395,14 @@ public class BatterySavingStats { final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState); final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState); - pw.println(String.format("%6dm %6dmA %8.1fmA/h %6dm %6dmA %8.1fmA/h", + pw.println(String.format("%6dm %6dmA (%3d%%) %8.1fmA/h %6dm %6dmA (%3d%%) %8.1fmA/h", offStat.totalMinutes(), offStat.totalBatteryDrain / 1000, + offStat.totalBatteryDrainPercent, offStat.drainPerHour() / 1000.0, onStat.totalMinutes(), onStat.totalBatteryDrain / 1000, + onStat.totalBatteryDrainPercent, onStat.drainPerHour() / 1000.0)); } @@ -371,12 +411,13 @@ public class BatterySavingStats { private int mLastState = STATE_NOT_INITIALIZED; private long mStartTime; private int mStartBatteryLevel; + private int mStartPercent; private static final int STATE_CHANGE_DETECT_MASK = (BatterySaverState.MASK << BatterySaverState.SHIFT) | (InteractiveState.MASK << InteractiveState.SHIFT); - public void transitionState(int newState, long now, int batteryLevel) { + public void transitionState(int newState, long now, int batteryLevel, int batteryPercent) { final boolean stateChanging = ((mLastState >= 0) ^ (newState >= 0)) || (((mLastState ^ newState) & STATE_CHANGE_DETECT_MASK) != 0); @@ -384,11 +425,13 @@ public class BatterySavingStats { if (mLastState >= 0) { final long deltaTime = now - mStartTime; final int deltaBattery = mStartBatteryLevel - batteryLevel; + final int deltaPercent = mStartPercent - batteryPercent; - report(mLastState, deltaTime, deltaBattery); + report(mLastState, deltaTime, deltaBattery, deltaPercent); } mStartTime = now; mStartBatteryLevel = batteryLevel; + mStartPercent = batteryPercent; } mLastState = newState; } @@ -405,9 +448,10 @@ public class BatterySavingStats { } } - void report(int state, long deltaTimeMs, int deltaBatteryUa) { + void report(int state, long deltaTimeMs, int deltaBatteryUa, int deltaPercent) { final String suffix = getCounterSuffix(state); mMetricsLogger.count(COUNTER_POWER_MILLIAMPS_PREFIX + suffix, deltaBatteryUa / 1000); + mMetricsLogger.count(COUNTER_POWER_PERCENT_PREFIX + suffix, deltaPercent); mMetricsLogger.count(COUNTER_TIME_SECONDS_PREFIX + suffix, (int) (deltaTimeMs / 1000)); } } diff --git a/services/core/java/com/android/server/slice/SliceFullAccessList.java b/services/core/java/com/android/server/slice/SliceFullAccessList.java index 591e809ad3d1..6f5afa207d31 100644 --- a/services/core/java/com/android/server/slice/SliceFullAccessList.java +++ b/services/core/java/com/android/server/slice/SliceFullAccessList.java @@ -16,9 +16,9 @@ package com.android.server.slice; import android.content.Context; import android.content.pm.UserInfo; +import android.os.UserHandle; import android.os.UserManager; import android.util.ArraySet; -import android.util.Log; import android.util.SparseArray; import com.android.internal.util.XmlUtils; @@ -63,7 +63,16 @@ public class SliceFullAccessList { pkgs.add(pkg); } - public void writeXml(XmlSerializer out) throws IOException { + public void removeGrant(String pkg, int userId) { + ArraySet<String> pkgs = mFullAccessPkgs.get(userId, null); + if (pkgs == null) { + pkgs = new ArraySet<>(); + mFullAccessPkgs.put(userId, pkgs); + } + pkgs.remove(pkg); + } + + public void writeXml(XmlSerializer out, int user) throws IOException { out.startTag(null, TAG_LIST); out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION)); @@ -71,6 +80,9 @@ public class SliceFullAccessList { for (int i = 0 ; i < N; i++) { final int userId = mFullAccessPkgs.keyAt(i); final ArraySet<String> pkgs = mFullAccessPkgs.valueAt(i); + if (user != UserHandle.USER_ALL && user != userId) { + continue; + } out.startTag(null, TAG_USER); out.attribute(null, ATT_USER_ID, Integer.toString(userId)); if (pkgs != null) { @@ -79,7 +91,6 @@ public class SliceFullAccessList { out.startTag(null, TAG_PKG); out.text(pkgs.valueAt(j)); out.endTag(null, TAG_PKG); - } } out.endTag(null, TAG_USER); diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index 5db0fc0d9e00..a1def440a007 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -21,6 +21,7 @@ import static android.content.ContentProvider.getUserIdFromUri; import static android.content.ContentProvider.maybeAddUserId; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.Process.SYSTEM_UID; import android.Manifest.permission; import android.app.ActivityManager; @@ -31,9 +32,11 @@ import android.app.slice.ISliceListener; import android.app.slice.ISliceManager; import android.app.slice.SliceManager; import android.app.slice.SliceSpec; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.database.ContentObserver; @@ -66,8 +69,9 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlSerializer; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -91,7 +95,9 @@ public class SliceManagerService extends ISliceManager.Stub { private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>(); private final Handler mHandler; private final ContentObserver mObserver; + @GuardedBy("mSliceAccessFile") private final AtomicFile mSliceAccessFile; + @GuardedBy("mAccessList") private final SliceFullAccessList mAccessList; public SliceManagerService(Context context) { @@ -127,11 +133,19 @@ public class SliceManagerService extends ISliceManager.Stub { InputStream input = mSliceAccessFile.openRead(); XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); parser.setInput(input, Encoding.UTF_8.name()); - mAccessList.readXml(parser); + synchronized (mAccessList) { + mAccessList.readXml(parser); + } } catch (IOException | XmlPullParserException e) { Slog.d(TAG, "Can't read slice access file", e); } } + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme("package"); + mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); } /// ----- Lifecycle stuff ----- @@ -223,7 +237,9 @@ public class SliceManagerService extends ISliceManager.Stub { getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS, "Slice granting requires MANAGE_SLICE_PERMISSIONS"); if (allSlices) { - mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier()); + synchronized (mAccessList) { + mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier()); + } mHandler.post(mSaveAccessList); } else { synchronized (mLock) { @@ -244,7 +260,71 @@ public class SliceManagerService extends ISliceManager.Stub { } } + // Backup/restore interface + @Override + public byte[] getBackupPayload(int user) { + if (Binder.getCallingUid() != SYSTEM_UID) { + throw new SecurityException("Caller must be system"); + } + //TODO: http://b/22388012 + if (user != UserHandle.USER_SYSTEM) { + Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user); + return null; + } + synchronized(mSliceAccessFile) { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer(); + out.setOutput(baos, Encoding.UTF_8.name()); + synchronized (mAccessList) { + mAccessList.writeXml(out, user); + } + out.flush(); + return baos.toByteArray(); + } catch (IOException | XmlPullParserException e) { + Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e); + } + } + return null; + } + + @Override + public void applyRestore(byte[] payload, int user) { + if (Binder.getCallingUid() != SYSTEM_UID) { + throw new SecurityException("Caller must be system"); + } + if (payload == null) { + Slog.w(TAG, "applyRestore: no payload to restore for user " + user); + return; + } + //TODO: http://b/22388012 + if (user != UserHandle.USER_SYSTEM) { + Slog.w(TAG, "applyRestore: cannot restore policy for user " + user); + return; + } + synchronized(mSliceAccessFile) { + final ByteArrayInputStream bais = new ByteArrayInputStream(payload); + try { + XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); + parser.setInput(bais, Encoding.UTF_8.name()); + synchronized (mAccessList) { + mAccessList.readXml(parser); + } + mHandler.post(mSaveAccessList); + } catch (NumberFormatException | XmlPullParserException | IOException e) { + Slog.w(TAG, "applyRestore: error reading payload", e); + } + } + } + /// ----- internal code ----- + private void removeFullAccess(String pkg, int userId) { + synchronized (mAccessList) { + mAccessList.removeGrant(pkg, userId); + } + mHandler.post(mSaveAccessList); + } + protected void removePinnedSlice(Uri uri) { synchronized (mLock) { mPinnedSlicesByUri.remove(uri).destroy(); @@ -444,7 +524,9 @@ public class SliceManagerService extends ISliceManager.Stub { } private boolean isGrantedFullAccess(String pkg, int userId) { - return mAccessList.hasFullAccess(pkg, userId); + synchronized (mAccessList) { + return mAccessList.hasFullAccess(pkg, userId); + } } private static ServiceThread createHandler() { @@ -469,7 +551,9 @@ public class SliceManagerService extends ISliceManager.Stub { try { XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer(); out.setOutput(stream, Encoding.UTF_8.name()); - mAccessList.writeXml(out); + synchronized (mAccessList) { + mAccessList.writeXml(out, UserHandle.USER_ALL); + } out.flush(); mSliceAccessFile.finishWrite(stream); } catch (IOException | XmlPullParserException e) { @@ -480,6 +564,35 @@ public class SliceManagerService extends ISliceManager.Stub { } }; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + if (userId == UserHandle.USER_NULL) { + Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent); + return; + } + Uri data = intent.getData(); + String pkg = data != null ? data.getSchemeSpecificPart() : null; + if (pkg == null) { + Slog.w(TAG, "Intent broadcast does not contain package name: " + intent); + return; + } + switch (intent.getAction()) { + case Intent.ACTION_PACKAGE_REMOVED: + final boolean replacing = + intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + if (!replacing) { + removeFullAccess(pkg, userId); + } + break; + case Intent.ACTION_PACKAGE_DATA_CLEARED: + removeFullAccess(pkg, userId); + break; + } + } + }; + public static class Lifecycle extends SystemService { private SliceManagerService mService; diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 648fa6af48d1..f498cdd85ee8 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -161,7 +161,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override public void sendBroadcast(String pkg, String cls) { - // TODO: Use a pending intent, and enfoceCallingPermission. + // TODO: Use a pending intent. + enforceCallingPermission(); mContext.sendBroadcastAsUser(new Intent(ACTION_TRIGGER_COLLECTION).setClassName(pkg, cls), UserHandle.SYSTEM); } @@ -239,7 +240,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - public final static class AppUpdateReceiver extends BroadcastReceiver { + private final static class AppUpdateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { /** @@ -284,7 +285,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - public final static class AnomalyAlarmReceiver extends BroadcastReceiver { + private final static class AnomalyAlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred."); @@ -304,7 +305,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - public final static class PullingAlarmReceiver extends BroadcastReceiver { + private final static class PullingAlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) @@ -325,7 +326,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - public final static class ShutdownEventReceiver extends BroadcastReceiver { + private final static class ShutdownEventReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { /** diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index adb368b074c0..7c170aee92fa 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -56,7 +56,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; - /** * A note on locking: We rely on the fact that calls onto mBar are oneway or * if they are local, that they just enqueue messages to not deadlock. @@ -525,6 +524,26 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override + public void showPinningEnterExitToast(boolean entering) throws RemoteException { + if (mBar != null) { + try { + mBar.showPinningEnterExitToast(entering); + } catch (RemoteException ex) { + } + } + } + + @Override + public void showPinningEscapeToast() throws RemoteException { + if (mBar != null) { + try { + mBar.showPinningEscapeToast(); + } catch (RemoteException ex) { + } + } + } + + @Override public void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) { if (mBar != null) { try { diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java index 3f320793826f..b00e595db7a1 100644 --- a/services/core/java/com/android/server/wm/AlertWindowNotification.java +++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java @@ -72,20 +72,23 @@ class AlertWindowNotification { } /** Cancels the notification */ - void cancel() { + void cancel(boolean deleteChannel) { // We can't call into NotificationManager with WM lock held since it might call into AM. // So, we post a message to do it later. - mService.mH.post(this::onCancelNotification); + mService.mH.post(() -> onCancelNotification(deleteChannel)); } /** Don't call with the window manager lock held! */ - private void onCancelNotification() { + private void onCancelNotification(boolean deleteChannel) { if (!mPosted) { // Notification isn't currently posted... return; } mPosted = false; mNotificationManager.cancel(mNotificationTag, NOTIFICATION_ID); + if (deleteChannel) { + mNotificationManager.deleteNotificationChannel(mNotificationTag); + } } /** Don't call with the window manager lock held! */ @@ -146,8 +149,12 @@ class AlertWindowNotification { final String nameChannel = context.getString(R.string.alert_windows_notification_channel_name, appName); - final NotificationChannel channel = - new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN); + + NotificationChannel channel = mNotificationManager.getNotificationChannel(mNotificationTag); + if (channel != null) { + return; + } + channel = new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN); channel.enableLights(false); channel.enableVibration(false); channel.setBlockableSystem(true); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 7674b5e9ed2c..2512dbd6a12b 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -662,7 +662,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mWallpaperController.updateWallpaperVisibility(); } - w.handleWindowMovedIfNeeded(mPendingTransaction); + w.handleWindowMovedIfNeeded(); final WindowStateAnimator winAnimator = w.mWinAnimator; @@ -1542,11 +1542,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * Callback used to trigger bounds update after configuration change and get ids of stacks whose * bounds were updated. */ - void updateStackBoundsAfterConfigChange(@NonNull List<Integer> changedStackList) { + void updateStackBoundsAfterConfigChange(@NonNull List<TaskStack> changedStackList) { for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) { final TaskStack stack = mTaskStackContainers.getChildAt(i); if (stack.updateBoundsAfterConfigChange()) { - changedStackList.add(stack.mStackId); + changedStackList.add(stack); } } @@ -3599,8 +3599,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } private final class AboveAppWindowContainers extends NonAppWindowContainers { - private final Dimmer mDimmer = new Dimmer(this); - private final Rect mTmpDimBoundsRect = new Rect(); AboveAppWindowContainers(String name, WindowManagerService service) { super(name, service); } @@ -3632,22 +3630,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE); } } - - @Override - Dimmer getDimmer() { - return mDimmer; - } - - @Override - void prepareSurfaces() { - mDimmer.resetDimStates(); - super.prepareSurfaces(); - getBounds(mTmpDimBoundsRect); - - if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) { - scheduleAnimation(); - } - } } /** @@ -3679,6 +3661,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo }; private final String mName; + private final Dimmer mDimmer = new Dimmer(this); + private final Rect mTmpDimBoundsRect = new Rect(); + NonAppWindowContainers(String name, WindowManagerService service) { super(service); mName = name; @@ -3722,6 +3707,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo String getName() { return mName; } + + @Override + Dimmer getDimmer() { + return mDimmer; + } + + @Override + void prepareSurfaces() { + mDimmer.resetDimStates(); + super.prepareSurfaces(); + getBounds(mTmpDimBoundsRect); + + if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) { + scheduleAnimation(); + } + } } private class NonMagnifiableWindowContainers extends NonAppWindowContainers { diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 8269a3b783c4..5bc739ee33b2 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -92,14 +92,21 @@ class RemoteAnimationController { onAnimationFinished(); return; } - mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS); - try { - mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(), - mFinishedCallback); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to start remote animation", e); - onAnimationFinished(); - } + + // Scale the timeout with the animator scale the controlling app is using. + mHandler.postDelayed(mTimeoutRunnable, + (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale())); + + final RemoteAnimationTarget[] animations = createAnimations(); + mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { + try { + mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, + mFinishedCallback); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to start remote animation", e); + onAnimationFinished(); + } + }); } private RemoteAnimationTarget[] createAnimations() { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index deed7f17e4e6..8d1a82250206 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -47,6 +47,7 @@ import com.android.server.EventLogTags; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; import static android.app.AppOpsManager.MODE_ALLOWED; @@ -126,7 +127,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { boolean mOrientationChangeComplete = true; boolean mWallpaperActionPending = false; - private final ArrayList<Integer> mChangedStackList = new ArrayList(); + private final ArrayList<TaskStack> mTmpStackList = new ArrayList(); + private final ArrayList<Integer> mTmpStackIds = new ArrayList<>(); // State for the RemoteSurfaceTrace system used in testing. If this is enabled SurfaceControl // instances will be replaced with an instance that writes a binary representation of all @@ -333,7 +335,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { /** * Set new display override config and return array of ids of stacks that were changed during - * update. If called for the default display, global configuration will also be updated. + * update. If called for the default display, global configuration will also be updated. Stacks + * that are marked for deferred removal are excluded from the returned array. */ int[] setDisplayOverrideConfigurationIfNeeded(Configuration newConfiguration, int displayId) { final DisplayContent displayContent = getDisplayContent(displayId); @@ -346,24 +349,42 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { if (!configChanged) { return null; } + displayContent.onOverrideConfigurationChanged(newConfiguration); + mTmpStackList.clear(); if (displayId == DEFAULT_DISPLAY) { // Override configuration of the default display duplicates global config. In this case // we also want to update the global config. - return setGlobalConfigurationIfNeeded(newConfiguration); + setGlobalConfigurationIfNeeded(newConfiguration, mTmpStackList); } else { - return updateStackBoundsAfterConfigChange(displayId); + updateStackBoundsAfterConfigChange(displayId, mTmpStackList); + } + + mTmpStackIds.clear(); + final int stackCount = mTmpStackList.size(); + + for (int i = 0; i < stackCount; ++i) { + final TaskStack stack = mTmpStackList.get(i); + + // We only include stacks that are not marked for removal as they do not exist outside + // of WindowManager at this point. + if (!stack.mDeferRemoval) { + mTmpStackIds.add(stack.mStackId); + } } + + return mTmpStackIds.isEmpty() ? null : ArrayUtils.convertToIntArray(mTmpStackIds); } - private int[] setGlobalConfigurationIfNeeded(Configuration newConfiguration) { + private void setGlobalConfigurationIfNeeded(Configuration newConfiguration, + List<TaskStack> changedStacks) { final boolean configChanged = getConfiguration().diff(newConfiguration) != 0; if (!configChanged) { - return null; + return; } onConfigurationChanged(newConfiguration); - return updateStackBoundsAfterConfigChange(); + updateStackBoundsAfterConfigChange(changedStacks); } @Override @@ -378,26 +399,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { * Callback used to trigger bounds update after configuration change and get ids of stacks whose * bounds were updated. */ - private int[] updateStackBoundsAfterConfigChange() { - mChangedStackList.clear(); - + private void updateStackBoundsAfterConfigChange(List<TaskStack> changedStacks) { final int numDisplays = mChildren.size(); for (int i = 0; i < numDisplays; ++i) { final DisplayContent dc = mChildren.get(i); - dc.updateStackBoundsAfterConfigChange(mChangedStackList); + dc.updateStackBoundsAfterConfigChange(changedStacks); } - - return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList); } /** Same as {@link #updateStackBoundsAfterConfigChange()} but only for a specific display. */ - private int[] updateStackBoundsAfterConfigChange(int displayId) { - mChangedStackList.clear(); - + private void updateStackBoundsAfterConfigChange(int displayId, List<TaskStack> changedStacks) { final DisplayContent dc = getDisplayContent(displayId); - dc.updateStackBoundsAfterConfigChange(mChangedStackList); - - return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList); + dc.updateStackBoundsAfterConfigChange(changedStacks); } private void prepareFreezingTaskBounds() { @@ -599,6 +612,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces"); } + mService.mAnimator.executeAfterPrepareSurfacesRunnables(); + final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked; // If we are ready to perform an app transition, check through all of the app tokens to be @@ -903,10 +918,26 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { boolean handleNotObscuredLocked(WindowState w, boolean obscured, boolean syswin) { final WindowManager.LayoutParams attrs = w.mAttrs; final int attrFlags = attrs.flags; + final boolean onScreen = w.isOnScreen(); final boolean canBeSeen = w.isDisplayedLw(); final int privateflags = attrs.privateFlags; boolean displayHasContent = false; + if (DEBUG_KEEP_SCREEN_ON) { + Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked w: " + w + + ", w.mHasSurface: " + w.mHasSurface + + ", w.isOnScreen(): " + onScreen + + ", w.isDisplayedLw(): " + w.isDisplayedLw() + + ", w.mAttrs.userActivityTimeout: " + w.mAttrs.userActivityTimeout); + } + if (w.mHasSurface && onScreen) { + if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) { + mUserActivityTimeout = w.mAttrs.userActivityTimeout; + if (DEBUG_KEEP_SCREEN_ON) { + Slog.d(TAG, "mUserActivityTimeout set to " + mUserActivityTimeout); + } + } + } if (w.mHasSurface && canBeSeen) { if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) { mHoldScreen = w.mSession; @@ -919,9 +950,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { if (!syswin && w.mAttrs.screenBrightness >= 0 && mScreenBrightness < 0) { mScreenBrightness = w.mAttrs.screenBrightness; } - if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) { - mUserActivityTimeout = w.mAttrs.userActivityTimeout; - } final int type = attrs.type; // This function assumes that the contents of the default display are processed first diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 04ae38ec33b1..f09a294be75b 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -547,7 +547,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { if (allowed) { mAlertWindowNotification.post(); } else { - mAlertWindowNotification.cancel(); + mAlertWindowNotification.cancel(false /* deleteChannel */); } } } @@ -586,7 +586,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { if (mAlertWindowNotification == null) { return; } - mAlertWindowNotification.cancel(); + mAlertWindowNotification.cancel(true /* deleteChannel */); mAlertWindowNotification = null; } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 212a0d70927a..a7a2b534131d 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -27,6 +27,7 @@ import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.content.pm.PackageManager; import android.graphics.Bitmap; +import android.graphics.Color; import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.os.Environment; @@ -40,6 +41,7 @@ import android.view.ThreadedRenderer; import android.view.WindowManager.LayoutParams; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.graphics.ColorUtils; import com.android.server.policy.WindowManagerPolicy.ScreenOffListener; import com.android.server.policy.WindowManagerPolicy.StartingSurface; import com.android.server.wm.TaskSnapshotSurface.SystemBarBackgroundPainter; @@ -324,7 +326,8 @@ class TaskSnapshotController { if (mainWindow == null) { return null; } - final int color = task.getTaskDescription().getBackgroundColor(); + final int color = ColorUtils.setAlphaComponent( + task.getTaskDescription().getBackgroundColor(), 255); final int statusBarColor = task.getTaskDescription().getStatusBarColor(); final int navigationBarColor = task.getTaskDescription().getNavigationBarColor(); final LayoutParams attrs = mainWindow.getAttrs(); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 259f8df15e31..e4db3b075e91 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -47,6 +47,7 @@ import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityThread; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.GraphicBuffer; import android.graphics.Paint; import android.graphics.Rect; @@ -516,7 +517,7 @@ class TaskSnapshotSurface implements StartingSurface { @VisibleForTesting void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, int statusBarHeight) { - if (statusBarHeight > 0 + if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0 && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) { final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right, mContentInsets.right); @@ -531,7 +532,7 @@ class TaskSnapshotSurface implements StartingSurface { getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets, navigationBarRect); final boolean visible = isNavigationBarColorViewVisible(); - if (visible && !navigationBarRect.isEmpty()) { + if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) { c.drawRect(navigationBarRect, mNavigationBarPaint); } } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index ae5341bd8e45..a0d1480711a7 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -21,6 +21,7 @@ import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED; @@ -53,6 +54,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.RemoteException; +import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Slog; import android.util.SparseArray; @@ -741,14 +743,32 @@ public class TaskStack extends WindowContainer<Task> implements scheduleAnimation(); } + /** + * Calculate an amount by which to expand the stack bounds in each direction. + * Used to make room for shadows in the pinned windowing mode. + */ + int getStackOutset() { + if (inPinnedWindowingMode()) { + final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics(); + return mService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP, + displayMetrics); + } + return 0; + } + private void updateSurfaceSize(SurfaceControl.Transaction transaction) { if (mSurfaceControl == null) { return; } final Rect stackBounds = getBounds(); - final int width = stackBounds.width(); - final int height = stackBounds.height(); + int width = stackBounds.width(); + int height = stackBounds.height(); + + final int outset = getStackOutset(); + width += 2*outset; + height += 2*outset; + if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) { return; } @@ -1749,4 +1769,12 @@ public class TaskStack extends WindowContainer<Task> implements mDimmer.stopDim(getPendingTransaction()); scheduleAnimation(); } + + @Override + void getRelativePosition(Point outPos) { + super.getRelativePosition(outPos); + final int outset = getStackOutset(); + outPos.x -= outset; + outPos.y -= outset; + } } diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index f2ad6fb7a888..da3a035ad8df 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -272,6 +272,8 @@ class WallpaperController { } boolean updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync) { + int xOffset = 0; + int yOffset = 0; boolean rawChanged = false; // Set the default wallpaper x-offset to either edge of the screen (depending on RTL), to // match the behavior of most Launchers @@ -283,11 +285,8 @@ class WallpaperController { if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) { offset += mLastWallpaperDisplayOffsetX; } - boolean changed = wallpaperWin.mXOffset != offset; - if (changed) { - if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " x: " + offset); - wallpaperWin.mXOffset = offset; - } + xOffset = offset; + if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) { wallpaperWin.mWallpaperX = wpx; wallpaperWin.mWallpaperXStep = wpxs; @@ -301,17 +300,16 @@ class WallpaperController { if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) { offset += mLastWallpaperDisplayOffsetY; } - if (wallpaperWin.mYOffset != offset) { - if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper " + wallpaperWin + " y: " + offset); - changed = true; - wallpaperWin.mYOffset = offset; - } + yOffset = offset; + if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) { wallpaperWin.mWallpaperY = wpy; wallpaperWin.mWallpaperYStep = wpys; rawChanged = true; } + boolean changed = wallpaperWin.mWinAnimator.setWallpaperOffset(xOffset, yOffset); + if (rawChanged && (wallpaperWin.mAttrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) { try { diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index 2ae5c7bd9c25..ddda027595da 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -74,10 +74,6 @@ class WallpaperWindowToken extends WindowToken { for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) { final WindowState wallpaper = mChildren.get(wallpaperNdx); if (wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, sync)) { - final WindowStateAnimator winAnimator = wallpaper.mWinAnimator; - winAnimator.computeShownFrameLocked(); - // No need to lay out the windows - we can just set the wallpaper position directly. - winAnimator.setWallpaperOffset(wallpaper.mShownPosition); // We only want to be synchronous with one wallpaper. sync = false; } diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index cec13abd823d..b0d42f21fac3 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -92,6 +92,7 @@ public class WindowAnimator { * executed and the corresponding transaction is closed and applied. */ private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>(); + private boolean mInExecuteAfterPrepareSurfacesRunnables; WindowAnimator(final WindowManagerService service) { mService = service; @@ -438,7 +439,13 @@ public class WindowAnimator { scheduleAnimation(); } - private void executeAfterPrepareSurfacesRunnables() { + void executeAfterPrepareSurfacesRunnables() { + + // Don't even think about to start recursing! + if (mInExecuteAfterPrepareSurfacesRunnables) { + return; + } + mInExecuteAfterPrepareSurfacesRunnables = true; // Traverse in order they were added. final int size = mAfterPrepareSurfacesRunnables.size(); @@ -446,5 +453,6 @@ public class WindowAnimator { mAfterPrepareSurfacesRunnables.get(i).run(); } mAfterPrepareSurfacesRunnables.clear(); + mInExecuteAfterPrepareSurfacesRunnables = false; } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 066e4e6a8c67..d565a6a7a476 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -24,8 +24,6 @@ import static android.Manifest.permission.RESTRICTED_VR_ACCESS; import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.StatusBarManager.DISABLE_MASK; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_USER_HANDLE; @@ -125,7 +123,6 @@ import android.app.ActivityThread; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.IAssistDataReceiver; -import android.app.WindowConfiguration; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -2466,6 +2463,7 @@ public class WindowManagerService extends IWindowManager.Stub mWaitingForConfig = false; mLastFinishedFreezeSource = "new-config"; } + return mRoot.setDisplayOverrideConfigurationIfNeeded(overrideConfig, displayId); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 53a8d82f551e..3bee1e8ab628 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -137,7 +137,6 @@ import static com.android.server.wm.proto.WindowStateProto.REMOVED; import static com.android.server.wm.proto.WindowStateProto.REMOVE_ON_EXIT; import static com.android.server.wm.proto.WindowStateProto.REQUESTED_HEIGHT; import static com.android.server.wm.proto.WindowStateProto.REQUESTED_WIDTH; -import static com.android.server.wm.proto.WindowStateProto.SHOWN_POSITION; import static com.android.server.wm.proto.WindowStateProto.STABLE_INSETS; import static com.android.server.wm.proto.WindowStateProto.STACK_ID; import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS; @@ -297,12 +296,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration(); /** - * Actual position of the surface shown on-screen (may be modified by animation). These are - * in the screen's coordinate space (WITH the compatibility scale applied). - */ - final Point mShownPosition = new Point(); - - /** * Insets that determine the actually visible area. These are in the application's * coordinate space (without compatibility scale applied). */ @@ -461,10 +454,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP int mWallpaperDisplayOffsetX = Integer.MIN_VALUE; int mWallpaperDisplayOffsetY = Integer.MIN_VALUE; - // Wallpaper windows: pixels offset based on above variables. - int mXOffset; - int mYOffset; - /** * This is set after IWindowSession.relayout() has been called at * least once for the window. It allows us to detect the situation @@ -745,8 +734,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mRequestedHeight = 0; mLastRequestedWidth = 0; mLastRequestedHeight = 0; - mXOffset = 0; - mYOffset = 0; mLayer = 0; mInputWindowHandle = new InputWindowHandle( mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, c, @@ -1112,11 +1099,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } @Override - public Point getShownPositionLw() { - return mShownPosition; - } - - @Override public Rect getDisplayFrameLw() { return mDisplayFrame; } @@ -1758,7 +1740,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * listeners and optionally animate it. Simply checking a change of position is not enough, * because being move due to dock divider is not a trigger for animation. */ - void handleWindowMovedIfNeeded(Transaction t) { + void handleWindowMovedIfNeeded() { if (!hasMoved()) { return; } @@ -1776,7 +1758,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP && !isDragResizing() && !adjustedForMinimizedDockOrIme && getWindowConfiguration().hasMovementAnimations() && !mWinAnimator.mLastHidden) { - startMoveAnimation(t, left, top); + startMoveAnimation(left, top); } //TODO (multidisplay): Accessibility supported only for the default display. @@ -3134,7 +3116,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mContentInsets.writeToProto(proto, CONTENT_INSETS); mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS); mSurfacePosition.writeToProto(proto, SURFACE_POSITION); - mShownPosition.writeToProto(proto, SHOWN_POSITION); mWinAnimator.writeToProto(proto, ANIMATOR); proto.write(ANIMATING_EXIT, mAnimatingExit); for (int i = 0; i < mChildren.size(); i++) { @@ -3250,10 +3231,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.print(prefix); pw.print("mRelayoutCalled="); pw.print(mRelayoutCalled); pw.print(" mLayoutNeeded="); pw.println(mLayoutNeeded); } - if (mXOffset != 0 || mYOffset != 0) { - pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset); - pw.print(" y="); pw.println(mYOffset); - } if (dumpAll) { pw.print(prefix); pw.print("mGivenContentInsets="); mGivenContentInsets.printShortString(pw); @@ -3272,7 +3249,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.println(getLastReportedConfiguration()); } pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface); - pw.print(" mShownPosition="); mShownPosition.printShortString(pw); pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay()); pw.print(" mWindowRemovalAllowed="); pw.println(mWindowRemovalAllowed); if (dumpAll) { @@ -4195,9 +4171,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final int width = mFrame.width(); final int height = mFrame.height(); - // Compute the offset of the window in relation to the decor rect. - final int left = mXOffset + mFrame.left; - final int top = mYOffset + mFrame.top; + final int left = mFrame.left; + final int top = mFrame.top; // Initialize the decor rect to the entire frame. if (isDockedResizing()) { @@ -4360,7 +4335,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP commitPendingTransaction(); } - private void startMoveAnimation(Transaction t, int left, int top) { + private void startMoveAnimation(int left, int top) { if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this); final Point oldPosition = new Point(); final Point newPosition = new Point(); @@ -4369,7 +4344,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final AnimationAdapter adapter = new LocalAnimationAdapter( new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y), mService.mSurfaceAnimationRunner); - startAnimation(t, adapter); + startAnimation(getPendingTransaction(), adapter); } private void startAnimation(Transaction t, AnimationAdapter adapter) { @@ -4392,8 +4367,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP float9[Matrix.MSKEW_Y] = mWinAnimator.mDtDx; float9[Matrix.MSKEW_X] = mWinAnimator.mDtDy; float9[Matrix.MSCALE_Y] = mWinAnimator.mDsDy; - int x = mSurfacePosition.x + mShownPosition.x; - int y = mSurfacePosition.y + mShownPosition.y; + int x = mSurfacePosition.x; + int y = mSurfacePosition.y; // If changed, also adjust transformFrameToSurfacePosition final WindowContainer parent = getParent(); @@ -4576,6 +4551,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP outPoint.offset(-parentBounds.left, -parentBounds.top); } + TaskStack stack = getStack(); + + // If we have stack outsets, that means the top-left + // will be outset, and we need to inset ourselves + // to account for it. If we actually have shadows we will + // then un-inset ourselves by the surfaceInsets. + if (stack != null) { + final int outset = stack.getStackOutset(); + outPoint.offset(outset, outset); + } + // Expand for surface insets. See WindowState.expandForSurfaceInsets. outPoint.offset(-mAttrs.surfaceInsets.left, -mAttrs.surfaceInsets.top); } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 1cfa95625a88..499322c0b17a 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -99,7 +99,6 @@ class WindowStateAnimator { // Unchanging local convenience fields. final WindowManagerService mService; final WindowState mWin; - private final WindowStateAnimator mParentWinAnimator; final WindowAnimator mAnimator; final Session mSession; final WindowManagerPolicy mPolicy; @@ -135,8 +134,6 @@ class WindowStateAnimator { float mAlpha = 0; float mLastAlpha = 0; - boolean mHasClipRect; - Rect mClipRect = new Rect(); Rect mTmpClipRect = new Rect(); Rect mTmpFinalClipRect = new Rect(); Rect mLastClipRect = new Rect(); @@ -150,7 +147,6 @@ class WindowStateAnimator { * system decorations. */ private final Rect mSystemDecorRect = new Rect(); - private final Rect mLastSystemDecorRect = new Rect(); float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1; private float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1; @@ -213,6 +209,12 @@ class WindowStateAnimator { float mExtraHScale = (float) 1.0; float mExtraVScale = (float) 1.0; + // An offset in pixel of the surface contents from the window position. Used for Wallpaper + // to provide the effect of scrolling within a large surface. We just use these values as + // a cache. + int mXOffset = 0; + int mYOffset = 0; + private final Rect mTmpSize = new Rect(); WindowStateAnimator(final WindowState win) { @@ -224,7 +226,6 @@ class WindowStateAnimator { mContext = service.mContext; mWin = win; - mParentWinAnimator = !win.isChildWindow() ? null : win.getParentWindow().mWinAnimator; mSession = win.mSession; mAttrType = win.mAttrs.type; mIsWallpaper = win.mIsWallpaper; @@ -441,7 +442,7 @@ class WindowStateAnimator { flags |= SurfaceControl.SECURE; } - mTmpSize.set(w.mFrame.left + w.mXOffset, w.mFrame.top + w.mYOffset, 0, 0); + mTmpSize.set(0, 0, 0, 0); calculateSurfaceBounds(w, attrs); final int width = mTmpSize.width(); final int height = mTmpSize.height(); @@ -455,9 +456,6 @@ class WindowStateAnimator { } // We may abort, so initialize to defaults. - mLastSystemDecorRect.set(0, 0, 0, 0); - mHasClipRect = false; - mClipRect.set(0, 0, 0, 0); mLastClipRect.set(0, 0, 0, 0); // Set up surface control with initial size. @@ -649,14 +647,12 @@ class WindowStateAnimator { } void computeShownFrameLocked() { - final int displayId = mWin.getDisplayId(); final ScreenRotationAnimation screenRotationAnimation = mAnimator.getScreenRotationAnimationLocked(displayId); final boolean screenAnimation = screenRotationAnimation != null && screenRotationAnimation.isAnimating(); - mHasClipRect = false; if (screenAnimation) { // cache often used attributes locally final Rect frame = mWin.mFrame; @@ -687,8 +683,8 @@ class WindowStateAnimator { // WindowState.prepareSurfaces expands for surface insets (in order they don't get // clipped by the WindowState surface), so we need to go into the other direction here. - tmpMatrix.postTranslate(mWin.mXOffset + mWin.mAttrs.surfaceInsets.left, - mWin.mYOffset + mWin.mAttrs.surfaceInsets.top); + tmpMatrix.postTranslate(mWin.mAttrs.surfaceInsets.left, + mWin.mAttrs.surfaceInsets.top); // "convert" it into SurfaceFlinger's format @@ -703,9 +699,6 @@ class WindowStateAnimator { mDtDx = tmpFloats[Matrix.MSKEW_Y]; mDtDy = tmpFloats[Matrix.MSKEW_X]; mDsDy = tmpFloats[Matrix.MSCALE_Y]; - float x = tmpFloats[Matrix.MTRANS_X]; - float y = tmpFloats[Matrix.MTRANS_Y]; - mWin.mShownPosition.set(Math.round(x), Math.round(y)); // Now set the alpha... but because our current hardware // can't do alpha transformation on a non-opaque surface, @@ -715,8 +708,7 @@ class WindowStateAnimator { mShownAlpha = mAlpha; if (!mService.mLimitedAlphaCompositing || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format) - || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy) - && x == frame.left && y == frame.top))) { + || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)))) { //Slog.i(TAG_WM, "Applying alpha transform"); if (screenAnimation) { mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha(); @@ -746,10 +738,6 @@ class WindowStateAnimator { TAG, "computeShownFrameLocked: " + this + " not attached, mAlpha=" + mAlpha); - // WindowState.prepareSurfaces expands for surface insets (in order they don't get - // clipped by the WindowState surface), so we need to go into the other direction here. - mWin.mShownPosition.set(mWin.mXOffset + mWin.mAttrs.surfaceInsets.left, - mWin.mYOffset + mWin.mAttrs.surfaceInsets.top); mShownAlpha = mAlpha; mHaveMatrix = false; mDsDx = mWin.mGlobalScale; @@ -796,24 +784,12 @@ class WindowStateAnimator { // We use the clip rect as provided by the tranformation for non-fullscreen windows to // avoid premature clipping with the system decor rect. - clipRect.set((mHasClipRect && !fullscreen) ? mClipRect : mSystemDecorRect); + clipRect.set(mSystemDecorRect); if (DEBUG_WINDOW_CROP) Slog.d(TAG, "win=" + w + " Initial clip rect: " + clipRect - + " mHasClipRect=" + mHasClipRect + " fullscreen=" + fullscreen); - - if (isFreeformResizing && !w.isChildWindow()) { - // For freeform resizing non child windows, we are using the big surface positioned - // at 0,0. Thus we must express the crop in that coordinate space. - clipRect.offset(w.mShownPosition.x, w.mShownPosition.y); - } + + " fullscreen=" + fullscreen); w.expandForSurfaceInsets(clipRect); - if (mHasClipRect && fullscreen) { - // We intersect the clip rect specified by the transformation with the expanded system - // decor rect to prevent artifacts from drawing during animation if the transformation - // clip rect extends outside the system decor rect. - clipRect.intersect(mClipRect); - } // The clip rect was generated assuming (0,0) as the window origin, // so we need to translate to match the actual surface coordinates. clipRect.offset(w.mAttrs.surfaceInsets.left, w.mAttrs.surfaceInsets.top); @@ -853,7 +829,7 @@ class WindowStateAnimator { return; } - mTmpSize.set(w.mShownPosition.x, w.mShownPosition.y, 0, 0); + mTmpSize.set(0, 0, 0, 0); calculateSurfaceBounds(w, attrs); mExtraHScale = (float) 1.0; @@ -987,11 +963,6 @@ class WindowStateAnimator { // then take over the scaling until the new buffer arrives, and things // will be seamless. mForceScaleUntilResize = true; - } else { - if (!w.mSeamlesslyRotated) { - mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, - recoveringMemory); - } } // If we are ending the scaling mode. We switch to SCALING_MODE_FREEZE @@ -1183,24 +1154,26 @@ class WindowStateAnimator { mSurfaceController.setTransparentRegionHint(region); } - void setWallpaperOffset(Point shownPosition) { - final LayoutParams attrs = mWin.getAttrs(); - final int left = shownPosition.x - attrs.surfaceInsets.left; - final int top = shownPosition.y - attrs.surfaceInsets.top; + boolean setWallpaperOffset(int dx, int dy) { + if (mXOffset == dx && mYOffset == dy) { + return false; + } + mXOffset = dx; + mYOffset = dy; try { if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset"); mService.openSurfaceTransaction(); - mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left, - mWin.mFrame.top + top, false); + mSurfaceController.setPositionInTransaction(dx, dy, false); applyCrop(null, false); } catch (RuntimeException e) { Slog.w(TAG, "Error positioning surface of " + mWin - + " pos=(" + left + "," + top + ")", e); + + " pos=(" + dx + "," + dy + ")", e); } finally { mService.closeSurfaceTransaction("setWallpaperOffset"); if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setWallpaperOffset"); + return true; } } @@ -1378,8 +1351,6 @@ class WindowStateAnimator { pw.print(prefix); pw.print("mDrawState="); pw.print(drawStateToString()); pw.print(prefix); pw.print(" mLastHidden="); pw.println(mLastHidden); pw.print(prefix); pw.print("mSystemDecorRect="); mSystemDecorRect.printShortString(pw); - pw.print(" last="); mLastSystemDecorRect.printShortString(pw); - pw.print(" mHasClipRect="); pw.print(mHasClipRect); pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw); if (!mLastFinalClipRect.isEmpty()) { diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp index 8c38e0a9e363..847222ae4ba1 100644 --- a/services/core/jni/BroadcastRadio/convert.cpp +++ b/services/core/jni/BroadcastRadio/convert.cpp @@ -395,7 +395,8 @@ static JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Propert gjni.ModuleProperties.cstor, moduleId, jServiceName.get(), prop10.classId, jImplementor.get(), jProduct.get(), jVersion.get(), jSerial.get(), prop10.numTuners, prop10.numAudioSources, prop10.supportsCapture, jBands.get(), isBgScanSupported, - jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(), jVendorInfo.get())); + jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(), nullptr, + jVendorInfo.get())); } JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &properties, @@ -712,7 +713,7 @@ void register_android_server_broadcastradio_convert(JNIEnv *env) { gjni.ModuleProperties.cstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;IIZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z" - "[I[ILjava/util/Map;)V"); + "[I[ILjava/util/Map;Ljava/util/Map;)V"); auto programInfoClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$ProgramInfo"); gjni.ProgramInfo.clazz = MakeGlobalRefOrDie(env, programInfoClass); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 210fd473ccd4..d5ed98e373bc 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -723,21 +723,9 @@ public final class SystemServer { MmsServiceBroker mmsService = null; HardwarePropertiesManagerService hardwarePropertiesService = null; - boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false); boolean disableRtt = SystemProperties.getBoolean("config.disable_rtt", false); - boolean disableMediaProjection = SystemProperties.getBoolean("config.disable_mediaproj", - false); - boolean disableSerial = SystemProperties.getBoolean("config.disable_serial", false); - boolean disableSearchManager = SystemProperties.getBoolean("config.disable_searchmanager", - false); - boolean disableTrustManager = SystemProperties.getBoolean("config.disable_trustmanager", - false); - boolean disableTextServices = SystemProperties.getBoolean("config.disable_textservices", - false); boolean disableSystemTextClassifier = SystemProperties.getBoolean( "config.disable_systemtextclassifier", false); - boolean disableConsumerIr = SystemProperties.getBoolean("config.disable_consumerir", false); - boolean disableVrManager = SystemProperties.getBoolean("config.disable_vrmanager", false); boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice", false); boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false); @@ -745,6 +733,9 @@ public final class SystemServer { boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1"); + boolean isWatch = context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WATCH); + // For debugging RescueParty if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_system", false)) { throw new RuntimeException(); @@ -819,7 +810,7 @@ public final class SystemServer { ServiceManager.addService("vibrator", vibrator); traceEnd(); - if (!disableConsumerIr) { + if (!isWatch) { traceBeginAndSlog("StartConsumerIrService"); consumerIr = new ConsumerIrService(context); ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr); @@ -862,7 +853,7 @@ public final class SystemServer { traceLog.traceEnd(); }, START_HIDL_SERVICES); - if (!disableVrManager) { + if (!isWatch) { traceBeginAndSlog("StartVrManagerService"); mSystemServiceManager.startService(VrManagerService.class); traceEnd(); @@ -1030,7 +1021,7 @@ public final class SystemServer { mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class); traceEnd(); - if (!disableSystemUI) { + if (!isWatch) { traceBeginAndSlog("StartStatusBarManagerService"); try { statusBar = new StatusBarManagerService(context, wm); @@ -1063,11 +1054,9 @@ public final class SystemServer { } traceEnd(); - if (!disableTextServices) { - traceBeginAndSlog("StartTextServicesManager"); - mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class); - traceEnd(); - } + traceBeginAndSlog("StartTextServicesManager"); + mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class); + traceEnd(); if (!disableSystemTextClassifier) { traceBeginAndSlog("StartTextClassificationManagerService"); @@ -1229,7 +1218,7 @@ public final class SystemServer { } traceEnd(); - if (!disableSearchManager) { + if (!isWatch) { traceBeginAndSlog("StartSearchManagerService"); try { mSystemServiceManager.startService(SEARCH_MANAGER_SERVICE_CLASS); @@ -1259,7 +1248,7 @@ public final class SystemServer { mSystemServiceManager.startService(DockObserver.class); traceEnd(); - if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { + if (isWatch) { traceBeginAndSlog("StartThermalObserver"); mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS); traceEnd(); @@ -1291,7 +1280,7 @@ public final class SystemServer { traceEnd(); } - if (!disableSerial) { + if (!isWatch) { traceBeginAndSlog("StartSerialService"); try { // Serial port support @@ -1331,11 +1320,9 @@ public final class SystemServer { mSystemServiceManager.startService(SoundTriggerService.class); traceEnd(); - if (!disableTrustManager) { - traceBeginAndSlog("StartTrustManager"); - mSystemServiceManager.startService(TrustManagerService.class); - traceEnd(); - } + traceBeginAndSlog("StartTrustManager"); + mSystemServiceManager.startService(TrustManagerService.class); + traceEnd(); if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) { traceBeginAndSlog("StartBackupManager"); @@ -1392,14 +1379,16 @@ public final class SystemServer { traceEnd(); } - traceBeginAndSlog("StartNetworkTimeUpdateService"); - try { - networkTimeUpdater = new NetworkTimeUpdateService(context); - ServiceManager.addService("network_time_update_service", networkTimeUpdater); - } catch (Throwable e) { - reportWtf("starting NetworkTimeUpdate service", e); + if (!isWatch) { + traceBeginAndSlog("StartNetworkTimeUpdateService"); + try { + networkTimeUpdater = new NetworkTimeUpdateService(context); + ServiceManager.addService("network_time_update_service", networkTimeUpdater); + } catch (Throwable e) { + reportWtf("starting NetworkTimeUpdate service", e); + } + traceEnd(); } - traceEnd(); traceBeginAndSlog("StartCommonTimeManagementService"); try { @@ -1535,13 +1524,13 @@ public final class SystemServer { traceEnd(); } - if (!disableMediaProjection) { + if (!isWatch) { traceBeginAndSlog("StartMediaProjectionManager"); mSystemServiceManager.startService(MediaProjectionManagerService.class); traceEnd(); } - if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { + if (isWatch) { traceBeginAndSlog("StartWearConnectivityService"); mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS); traceEnd(); diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java index d2ae22b43445..1cbb3991e461 100644 --- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java @@ -90,7 +90,6 @@ public class LockTaskControllerTest { @Mock private IStatusBarService mStatusBarService; @Mock private WindowManagerService mWindowManager; @Mock private LockPatternUtils mLockPatternUtils; - @Mock private LockTaskNotify mLockTaskNotify; @Mock private StatusBarManagerInternal mStatusBarManagerInternal; @Mock private TelecomManager mTelecomManager; @Mock private RecentTasks mRecentTasks; @@ -123,7 +122,6 @@ public class LockTaskControllerTest { mLockTaskController.mDevicePolicyManager = mDevicePolicyManager; mLockTaskController.mTelecomManager = mTelecomManager; mLockTaskController.mLockPatternUtils = mLockPatternUtils; - mLockTaskController.mLockTaskNotify = mLockTaskNotify; LocalServices.removeServiceForTest(StatusBarManagerInternal.class); LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal); @@ -208,7 +206,7 @@ public class LockTaskControllerTest { // THEN lock task mode should be started verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE); // THEN screen pinning toast should be shown - verify(mLockTaskNotify).showPinningStartToast(); + verify(mStatusBarService).showPinningEnterExitToast(true /* entering */); } @Test @@ -377,7 +375,7 @@ public class LockTaskControllerTest { // THEN the keyguard should be shown verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL); // THEN screen pinning toast should be shown - verify(mLockTaskNotify).showPinningExitToast(); + verify(mStatusBarService).showPinningEnterExitToast(false /* entering */); } @Test diff --git a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java new file mode 100644 index 000000000000..8502e69dd060 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java @@ -0,0 +1,443 @@ +/* + * Copyright 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.display; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.hardware.display.AmbientBrightnessDayStats; +import android.os.SystemClock; +import android.os.UserManager; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.util.ArrayList; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AmbientBrightnessStatsTrackerTest { + + private TestInjector mTestInjector; + + @Before + public void setUp() { + mTestInjector = new TestInjector(); + } + + @Test + public void testBrightnessStatsTrackerOverSingleDay() { + AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker(); + ArrayList<AmbientBrightnessDayStats> userStats; + float[] expectedStats; + // Test case where no user data + userStats = statsTracker.getUserStats(0); + assertNull(userStats); + // Test after adding some user data + statsTracker.start(); + statsTracker.add(0, 0); + mTestInjector.incrementTime(1000); + statsTracker.stop(); + userStats = statsTracker.getUserStats(0); + assertEquals(1, userStats.size()); + assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate()); + expectedStats = getEmptyStatsArray(); + expectedStats[0] = 1; + assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0); + // Test after adding some more user data + statsTracker.start(); + statsTracker.add(0, 0.05f); + mTestInjector.incrementTime(1000); + statsTracker.add(0, 0.2f); + mTestInjector.incrementTime(1500); + statsTracker.add(0, 50000); + mTestInjector.incrementTime(2500); + statsTracker.stop(); + userStats = statsTracker.getUserStats(0); + assertEquals(1, userStats.size()); + assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate()); + expectedStats = getEmptyStatsArray(); + expectedStats[0] = 2; + expectedStats[1] = 1.5f; + expectedStats[11] = 2.5f; + assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0); + } + + @Test + public void testBrightnessStatsTrackerOverMultipleDays() { + AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker(); + ArrayList<AmbientBrightnessDayStats> userStats; + float[] expectedStats; + // Add data for day 1 + statsTracker.start(); + statsTracker.add(0, 0.05f); + mTestInjector.incrementTime(1000); + statsTracker.add(0, 0.2f); + mTestInjector.incrementTime(1500); + statsTracker.add(0, 1); + mTestInjector.incrementTime(2500); + statsTracker.stop(); + // Add data for day 2 + mTestInjector.incrementDate(1); + statsTracker.start(); + statsTracker.add(0, 0); + mTestInjector.incrementTime(3500); + statsTracker.add(0, 5); + mTestInjector.incrementTime(5000); + statsTracker.stop(); + // Test that the data is tracked as expected + userStats = statsTracker.getUserStats(0); + assertEquals(2, userStats.size()); + assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate()); + expectedStats = getEmptyStatsArray(); + expectedStats[0] = 1; + expectedStats[1] = 1.5f; + expectedStats[3] = 2.5f; + assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0); + assertEquals(mTestInjector.getLocalDate(), userStats.get(1).getLocalDate()); + expectedStats = getEmptyStatsArray(); + expectedStats[0] = 3.5f; + expectedStats[4] = 5; + assertArrayEquals(expectedStats, userStats.get(1).getStats(), 0); + } + + @Test + public void testBrightnessStatsTrackerOverMultipleUsers() { + AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker(); + ArrayList<AmbientBrightnessDayStats> userStats; + float[] expectedStats; + // Add data for user 1 + statsTracker.start(); + statsTracker.add(0, 0.05f); + mTestInjector.incrementTime(1000); + statsTracker.add(0, 0.2f); + mTestInjector.incrementTime(1500); + statsTracker.add(0, 1); + mTestInjector.incrementTime(2500); + statsTracker.stop(); + // Add data for user 2 + mTestInjector.incrementDate(1); + statsTracker.start(); + statsTracker.add(1, 0); + mTestInjector.incrementTime(3500); + statsTracker.add(1, 5); + mTestInjector.incrementTime(5000); + statsTracker.stop(); + // Test that the data is tracked as expected + userStats = statsTracker.getUserStats(0); + assertEquals(1, userStats.size()); + assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate()); + expectedStats = getEmptyStatsArray(); + expectedStats[0] = 1; + expectedStats[1] = 1.5f; + expectedStats[3] = 2.5f; + assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0); + userStats = statsTracker.getUserStats(1); + assertEquals(1, userStats.size()); + assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate()); + expectedStats = getEmptyStatsArray(); + expectedStats[0] = 3.5f; + expectedStats[4] = 5; + assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0); + } + + @Test + public void testBrightnessStatsTrackerOverMaxDays() { + AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker(); + ArrayList<AmbientBrightnessDayStats> userStats; + // Add 10 extra days of data over the buffer limit + for (int i = 0; i < AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK + 10; i++) { + mTestInjector.incrementDate(1); + statsTracker.start(); + statsTracker.add(0, 10); + mTestInjector.incrementTime(1000); + statsTracker.add(0, 20); + mTestInjector.incrementTime(1000); + statsTracker.stop(); + } + // Assert that we are only tracking last "MAX_DAYS_TO_TRACK" + userStats = statsTracker.getUserStats(0); + assertEquals(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK, userStats.size()); + LocalDate runningDate = mTestInjector.getLocalDate(); + for (int i = AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK - 1; i >= 0; i--) { + assertEquals(runningDate, userStats.get(i).getLocalDate()); + runningDate = runningDate.minusDays(1); + } + } + + @Test + public void testReadAmbientBrightnessStats() throws IOException { + AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker(); + LocalDate date = mTestInjector.getLocalDate(); + ArrayList<AmbientBrightnessDayStats> userStats; + String statsFile = + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n" + + "<ambient-brightness-stats>\r\n" + // Old stats that shouldn't be read + + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + + date.minusDays(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK) + + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0," + + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0," + + "0.0,0.0,0.0\" />\r\n" + // Valid stats that should get read + + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + + date.minusDays(1) + + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0," + + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0," + + "0.0,0.0,0.0\" />\r\n" + // Valid stats that should get read + + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date + + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0," + + "3000.0,10000.0\" bucket-stats=\"0.0,0.0,0.0,0.0,4.482,0.0,0.0,0.0,0.0," + + "0.0\" />\r\n" + + "</ambient-brightness-stats>"; + statsTracker.readStats(getInputStream(statsFile)); + userStats = statsTracker.getUserStats(0); + assertEquals(2, userStats.size()); + assertEquals(new AmbientBrightnessDayStats(date.minusDays(1), + new float[]{0, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000}, + new float[]{1.088f, 0, 0.726f, 0, 25.868f, 0, 0, 0, 0, 0}), userStats.get(0)); + assertEquals(new AmbientBrightnessDayStats(date, + new float[]{0, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000}, + new float[]{0, 0, 0, 0, 4.482f, 0, 0, 0, 0, 0}), userStats.get(1)); + } + + @Test + public void testFailedReadAmbientBrightnessStatsWithException() { + AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker(); + LocalDate date = mTestInjector.getLocalDate(); + String statsFile; + // Test with parse error + statsFile = + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n" + + "<ambient-brightness-stats>\r\n" + // Incorrect since bucket boundaries not parsable + + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date + + "\" bucket-boundaries=\"asdf,1.0,3.0,10.0,30.0,100.0,300.0,1000.0," + + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0," + + "0.0,0.0,0.0\" />\r\n" + + "</ambient-brightness-stats>"; + try { + statsTracker.readStats(getInputStream(statsFile)); + } catch (IOException e) { + // Expected + } + assertNull(statsTracker.getUserStats(0)); + // Test with incorrect data (bucket boundaries length not equal to stats length) + statsFile = + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n" + + "<ambient-brightness-stats>\r\n" + // Correct data + + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + + date.minusDays(1) + + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0," + + "3000.0,10000.0\" bucket-stats=\"0.0,0.0,0.0,0.0,4.482,0.0,0.0,0.0,0.0," + + "0.0\" />\r\n" + // Incorrect data + + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date + + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,1000.0," + + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0," + + "0.0,0.0,0.0\" />\r\n" + + "</ambient-brightness-stats>"; + try { + statsTracker.readStats(getInputStream(statsFile)); + } catch (Exception e) { + // Expected + } + assertNull(statsTracker.getUserStats(0)); + // Test with missing attribute + statsFile = + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n" + + "<ambient-brightness-stats>\r\n" + + "<ambientBrightnessDayStats user=\"10\" local-date=\"" + date + + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0," + + "3000.0,10000.0\" />\r\n" + + "</ambient-brightness-stats>"; + try { + statsTracker.readStats(getInputStream(statsFile)); + } catch (Exception e) { + // Expected + } + assertNull(statsTracker.getUserStats(0)); + } + + @Test + public void testWriteThenReadAmbientBrightnessStats() throws IOException { + AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker(); + ArrayList<AmbientBrightnessDayStats> userStats; + float[] expectedStats; + // Generate some dummy data + // Data: very old which should not be read + statsTracker.start(); + statsTracker.add(0, 0.05f); + mTestInjector.incrementTime(1000); + statsTracker.add(0, 0.2f); + mTestInjector.incrementTime(1500); + statsTracker.add(0, 1); + mTestInjector.incrementTime(2500); + statsTracker.stop(); + // Data: day 1 user 1 + mTestInjector.incrementDate(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK - 1); + statsTracker.start(); + statsTracker.add(0, 0.05f); + mTestInjector.incrementTime(1000); + statsTracker.add(0, 0.2f); + mTestInjector.incrementTime(1500); + statsTracker.add(0, 1); + mTestInjector.incrementTime(2500); + statsTracker.stop(); + // Data: day 1 user 2 + statsTracker.start(); + statsTracker.add(1, 0); + mTestInjector.incrementTime(3500); + statsTracker.add(1, 5); + mTestInjector.incrementTime(5000); + statsTracker.stop(); + // Data: day 2 user 1 + mTestInjector.incrementDate(1); + statsTracker.start(); + statsTracker.add(0, 0); + mTestInjector.incrementTime(3500); + statsTracker.add(0, 50000); + mTestInjector.incrementTime(5000); + statsTracker.stop(); + // Write them + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + statsTracker.writeStats(baos); + baos.flush(); + // Read them back and assert that it's the same + ByteArrayInputStream input = new ByteArrayInputStream(baos.toByteArray()); + AmbientBrightnessStatsTracker newStatsTracker = getTestStatsTracker(); + newStatsTracker.readStats(input); + userStats = newStatsTracker.getUserStats(0); + assertEquals(2, userStats.size()); + // Check day 1 user 1 + assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate()); + expectedStats = getEmptyStatsArray(); + expectedStats[0] = 1; + expectedStats[1] = 1.5f; + expectedStats[3] = 2.5f; + assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0); + // Check day 2 user 1 + assertEquals(mTestInjector.getLocalDate(), userStats.get(1).getLocalDate()); + expectedStats = getEmptyStatsArray(); + expectedStats[0] = 3.5f; + expectedStats[11] = 5; + assertArrayEquals(expectedStats, userStats.get(1).getStats(), 0); + userStats = newStatsTracker.getUserStats(1); + assertEquals(1, userStats.size()); + // Check day 1 user 2 + assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate()); + expectedStats = getEmptyStatsArray(); + expectedStats[0] = 3.5f; + expectedStats[4] = 5; + assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0); + } + + @Test + public void testTimer() { + AmbientBrightnessStatsTracker.Timer timer = new AmbientBrightnessStatsTracker.Timer( + () -> mTestInjector.elapsedRealtimeMillis()); + assertEquals(0, timer.totalDurationSec(), 0); + mTestInjector.incrementTime(1000); + assertEquals(0, timer.totalDurationSec(), 0); + assertFalse(timer.isRunning()); + // Start timer + timer.start(); + assertTrue(timer.isRunning()); + assertEquals(0, timer.totalDurationSec(), 0); + mTestInjector.incrementTime(1000); + assertTrue(timer.isRunning()); + assertEquals(1, timer.totalDurationSec(), 0); + // Reset timer + timer.reset(); + assertEquals(0, timer.totalDurationSec(), 0); + assertFalse(timer.isRunning()); + // Start again + timer.start(); + assertTrue(timer.isRunning()); + assertEquals(0, timer.totalDurationSec(), 0); + mTestInjector.incrementTime(2000); + assertTrue(timer.isRunning()); + assertEquals(2, timer.totalDurationSec(), 0); + // Reset again + timer.reset(); + assertEquals(0, timer.totalDurationSec(), 0); + assertFalse(timer.isRunning()); + } + + private class TestInjector extends AmbientBrightnessStatsTracker.Injector { + + private long mElapsedRealtimeMillis = SystemClock.elapsedRealtime(); + private LocalDate mLocalDate = LocalDate.now(); + + public void incrementTime(long timeMillis) { + mElapsedRealtimeMillis += timeMillis; + } + + public void incrementDate(int numDays) { + mLocalDate = mLocalDate.plusDays(numDays); + } + + @Override + public long elapsedRealtimeMillis() { + return mElapsedRealtimeMillis; + } + + @Override + public int getUserSerialNumber(UserManager userManager, int userId) { + return userId + 10; + } + + @Override + public int getUserId(UserManager userManager, int userSerialNumber) { + return userSerialNumber - 10; + } + + @Override + public LocalDate getLocalDate() { + return LocalDate.from(mLocalDate); + } + } + + private AmbientBrightnessStatsTracker getTestStatsTracker() { + return new AmbientBrightnessStatsTracker( + InstrumentationRegistry.getContext().getSystemService(UserManager.class), + mTestInjector); + } + + private float[] getEmptyStatsArray() { + return new float[AmbientBrightnessStatsTracker.BUCKET_BOUNDARIES_FOR_NEW_STATS.length]; + } + + private InputStream getInputStream(String data) { + return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java index edc7d74b47cb..501f9666f5c4 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java @@ -30,7 +30,6 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.database.ContentObserver; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.display.BrightnessChangeEvent; @@ -195,7 +194,9 @@ public class BrightnessTrackerTest { mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1)); final int systemUpdatedBrightness = 20; - notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/); + notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/, + 0.5f /*powerBrightnessFactor(*/, false /*isUserSetBrightness*/, + false /*isDefaultBrightnessConfig*/); List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); // No events because we filtered out our change. assertEquals(0, events.size()); @@ -285,7 +286,8 @@ public class BrightnessTrackerTest { + "lastNits=\"32.333\" " + "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\"\n" + "lux=\"32.2,31.1\" luxTimestamps=\"" - + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"/>" + + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"" + + "defaultConfig=\"true\" powerSaveFactor=\"0.5\" userPoint=\"true\" />" + "<event nits=\"71\" timestamp=\"" + Long.toString(someTimeAgo) + "\" packageName=\"" + "com.android.anapp\" user=\"11\" " @@ -315,6 +317,9 @@ public class BrightnessTrackerTest { assertFalse(event.nightMode); assertEquals(1.0f, event.batteryLevel, FLOAT_DELTA); assertEquals("com.example.app", event.packageName); + assertTrue(event.isDefaultBrightnessConfig); + assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA); + assertTrue(event.isUserSetBrightness); events = tracker.getEvents(1, true).getList(); assertEquals(1, events.size()); @@ -329,6 +334,10 @@ public class BrightnessTrackerTest { assertEquals(3235, event.colorTemperature); assertEquals(0.5f, event.batteryLevel, FLOAT_DELTA); assertEquals("com.android.anapp", event.packageName); + // Not present in the event so default to false. + assertFalse(event.isDefaultBrightnessConfig); + assertEquals(1.0, event.powerBrightnessFactor, FLOAT_DELTA); + assertFalse(event.isUserSetBrightness); } @Test @@ -379,7 +388,9 @@ public class BrightnessTrackerTest { mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f)); final long secondSensorTime = mInjector.currentTimeMillis(); mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3)); - notifyBrightnessChanged(mTracker, brightness); + notifyBrightnessChanged(mTracker, brightness, true /*userInitiated*/, + 0.5f /*powerPolicyDim(*/, true /*hasUserBrightnessPoints*/, + false /*isDefaultBrightnessConfig*/); ByteArrayOutputStream baos = new ByteArrayOutputStream(); mTracker.writeEventsLocked(baos); mTracker.stop(); @@ -399,6 +410,9 @@ public class BrightnessTrackerTest { assertEquals(0.3, event.batteryLevel, FLOAT_DELTA); assertTrue(event.nightMode); assertEquals(3339, event.colorTemperature); + assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA); + assertTrue(event.isUserSetBrightness); + assertFalse(event.isDefaultBrightnessConfig); } @Test @@ -539,12 +553,16 @@ public class BrightnessTrackerTest { } private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness) { - notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/); + notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/, + 1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/, + false /*isDefaultBrightnessConfig*/); } private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness, - boolean userInitiated) { - tracker.notifyBrightnessChanged(brightness, userInitiated); + boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness, + boolean isDefaultBrightnessConfig) { + tracker.notifyBrightnessChanged(brightness, userInitiated, powerBrightnessFactor, + isUserSetBrightness, isDefaultBrightnessConfig); mInjector.waitForHandler(); } @@ -573,7 +591,6 @@ public class BrightnessTrackerTest { private class TestInjector extends BrightnessTracker.Injector { SensorEventListener mSensorListener; BroadcastReceiver mBroadcastReceiver; - Map<String, Integer> mSystemIntSettings = new HashMap<>(); Map<String, Integer> mSecureIntSettings = new HashMap<>(); long mCurrentTimeMillis = System.currentTimeMillis(); long mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); @@ -639,7 +656,7 @@ public class BrightnessTrackerTest { } @Override - public AtomicFile getFile() { + public AtomicFile getFile(String filename) { // Don't have the test write / read from anywhere. return 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 c863aabb905f..473a813c3838 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 @@ -39,6 +39,7 @@ import android.Manifest; import android.os.Binder; import android.os.ServiceSpecificException; import android.os.UserHandle; +import android.security.KeyStore; import android.security.keystore.AndroidKeyStoreSecretKey; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; @@ -49,6 +50,7 @@ import android.support.test.filters.SmallTest; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; +import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage; import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb; import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage; import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage; @@ -135,6 +137,8 @@ public class RecoverableKeyStoreManagerTest { @Mock private RecoverySnapshotListenersStorage mMockListenersStorage; @Mock private KeyguardManager mKeyguardManager; @Mock private PlatformKeyManager mPlatformKeyManager; + @Mock private KeyStore mKeyStore; + @Mock private ApplicationKeyStorage mApplicationKeyStorage; private RecoverableKeyStoreDb mRecoverableKeyStoreDb; private File mDatabaseFile; @@ -164,12 +168,14 @@ public class RecoverableKeyStoreManagerTest { mRecoverableKeyStoreManager = new RecoverableKeyStoreManager( mMockContext, + mKeyStore, mRecoverableKeyStoreDb, mRecoverySessionStorage, Executors.newSingleThreadExecutor(), mRecoverySnapshotStorage, mMockListenersStorage, - mPlatformKeyManager); + mPlatformKeyManager, + mApplicationKeyStorage); } @After diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index 56d4b7e8c1e8..857925b3ed17 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -5149,7 +5149,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { .forAllShortcuts(si -> { switch (package1DisabledReason) { case ShortcutInfo.DISABLED_REASON_VERSION_LOWER: - assertEquals("This shortcut requires latest app", + assertEquals("App version downgraded, or isn’t compatible" + + " with this shortcut", si.getDisabledMessage()); break; case ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH: diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java index c2072df75bd8..f559986a6f15 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java @@ -53,6 +53,7 @@ public class DexoptOptionsTests { assertFalse(opt.isDowngrade()); assertFalse(opt.isForce()); assertFalse(opt.isDexoptIdleBackgroundJob()); + assertFalse(opt.isDexoptInstallWithDexMetadata()); } @Test @@ -65,7 +66,8 @@ public class DexoptOptionsTests { DexoptOptions.DEXOPT_ONLY_SHARED_DEX | DexoptOptions.DEXOPT_DOWNGRADE | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY | - DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB; + DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB | + DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags); assertEquals(mPackageName, opt.getPackageName()); @@ -79,6 +81,7 @@ public class DexoptOptionsTests { assertTrue(opt.isForce()); assertTrue(opt.isDexoptAsSharedLibrary()); assertTrue(opt.isDexoptIdleBackgroundJob()); + assertTrue(opt.isDexoptInstallWithDexMetadata()); } @Test diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java index a628b7b70c15..5de393c7ae2b 100644 --- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java +++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java @@ -79,11 +79,6 @@ public class FakeWindowState implements WindowManagerPolicy.WindowState { } @Override - public Point getShownPositionLw() { - return new Point(parentFrame.left, parentFrame.top); - } - - @Override public Rect getDisplayFrameLw() { return displayFrame; } diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java index c3714c85e895..f7516b2c3694 100644 --- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java +++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java @@ -63,6 +63,11 @@ public class BatterySavingStatsTest { return mBatteryLevel; } + @Override + int injectBatteryPercent() { + return mBatteryLevel / 10; + } + void assertDumpable() { final ByteArrayOutputStream out = new ByteArrayOutputStream(); dump(new PrintWriter(out), ""); // Just make sure it won't crash. @@ -102,7 +107,7 @@ public class BatterySavingStatsTest { target.assertDumpable(); target.advanceClock(1); - target.drainBattery(2); + target.drainBattery(200); target.transitionState( BatterySaverState.OFF, @@ -110,7 +115,7 @@ public class BatterySavingStatsTest { DozeState.NOT_DOZING); target.advanceClock(4); - target.drainBattery(1); + target.drainBattery(100); target.transitionState( BatterySaverState.OFF, @@ -118,7 +123,7 @@ public class BatterySavingStatsTest { DozeState.NOT_DOZING); target.advanceClock(2); - target.drainBattery(5); + target.drainBattery(500); target.transitionState( BatterySaverState.OFF, @@ -126,7 +131,7 @@ public class BatterySavingStatsTest { DozeState.NOT_DOZING); target.advanceClock(4); - target.drainBattery(1); + target.drainBattery(100); target.transitionState( BatterySaverState.OFF, @@ -134,7 +139,7 @@ public class BatterySavingStatsTest { DozeState.NOT_DOZING); target.advanceClock(2); - target.drainBattery(5); + target.drainBattery(500); target.transitionState( BatterySaverState.OFF, @@ -142,7 +147,7 @@ public class BatterySavingStatsTest { DozeState.NOT_DOZING); target.advanceClock(3); - target.drainBattery(1); + target.drainBattery(100); target.transitionState( BatterySaverState.OFF, @@ -150,7 +155,7 @@ public class BatterySavingStatsTest { DozeState.LIGHT); target.advanceClock(5); - target.drainBattery(1); + target.drainBattery(100); target.transitionState( BatterySaverState.OFF, @@ -158,7 +163,7 @@ public class BatterySavingStatsTest { DozeState.DEEP); target.advanceClock(1); - target.drainBattery(2); + target.drainBattery(200); target.transitionState( BatterySaverState.ON, @@ -166,7 +171,7 @@ public class BatterySavingStatsTest { DozeState.NOT_DOZING); target.advanceClock(1); - target.drainBattery(3); + target.drainBattery(300); target.transitionState( BatterySaverState.OFF, @@ -174,7 +179,7 @@ public class BatterySavingStatsTest { DozeState.NOT_DOZING); target.advanceClock(3); - target.drainBattery(5); + target.drainBattery(500); target.transitionState( BatterySaverState.ON, @@ -182,12 +187,12 @@ public class BatterySavingStatsTest { DozeState.NOT_DOZING); target.advanceClock(3); - target.drainBattery(5); + target.drainBattery(500); target.startCharging(); target.advanceClock(5); - target.drainBattery(10); + target.drainBattery(1000); target.transitionState( BatterySaverState.ON, @@ -195,25 +200,25 @@ public class BatterySavingStatsTest { DozeState.NOT_DOZING); target.advanceClock(5); - target.drainBattery(1); + target.drainBattery(100); target.startCharging(); target.assertDumpable(); assertEquals( - "BS=0,I=0,D=0:{4m,10,150.00}\n" + - "BS=1,I=0,D=0:{0m,0,0.00}\n" + - "BS=0,I=1,D=0:{14m,8,34.29}\n" + - "BS=1,I=1,D=0:{9m,9,60.00}\n" + - "BS=0,I=0,D=1:{5m,1,12.00}\n" + - "BS=1,I=0,D=1:{0m,0,0.00}\n" + - "BS=0,I=1,D=1:{0m,0,0.00}\n" + - "BS=1,I=1,D=1:{0m,0,0.00}\n" + - "BS=0,I=0,D=2:{1m,2,120.00}\n" + - "BS=1,I=0,D=2:{0m,0,0.00}\n" + - "BS=0,I=1,D=2:{0m,0,0.00}\n" + - "BS=1,I=1,D=2:{0m,0,0.00}", + "BS=0,I=0,D=0:{4m,1000,15000.00uA/H,1500.00%}\n" + + "BS=1,I=0,D=0:{0m,0,0.00uA/H,0.00%}\n" + + "BS=0,I=1,D=0:{14m,800,3428.57uA/H,342.86%}\n" + + "BS=1,I=1,D=0:{9m,900,6000.00uA/H,600.00%}\n" + + "BS=0,I=0,D=1:{5m,100,1200.00uA/H,120.00%}\n" + + "BS=1,I=0,D=1:{0m,0,0.00uA/H,0.00%}\n" + + "BS=0,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" + + "BS=1,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" + + "BS=0,I=0,D=2:{1m,200,12000.00uA/H,1200.00%}\n" + + "BS=1,I=0,D=2:{0m,0,0.00uA/H,0.00%}\n" + + "BS=0,I=1,D=2:{0m,0,0.00uA/H,0.00%}\n" + + "BS=1,I=1,D=2:{0m,0,0.00uA/H,0.00%}", target.toDebugString()); } @@ -245,6 +250,7 @@ public class BatterySavingStatsTest { DozeState.NOT_DOZING); assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "01", 2); + assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "01", 200); assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "01", 60); target.advanceClock(1); @@ -277,15 +283,17 @@ public class BatterySavingStatsTest { DozeState.NOT_DOZING); assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "00", 2 * 3); + assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "00", 200 * 3); assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "00", 60 * 3); target.advanceClock(10); - target.drainBattery(10_000); + target.drainBattery(10000); reset(mMetricsLogger); target.startCharging(); assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "11", 10); + assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "11", 1000); assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "11", 60 * 10); target.advanceClock(1); @@ -305,6 +313,7 @@ public class BatterySavingStatsTest { target.startCharging(); assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "10", 2); + assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "10", 200); assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "10", 60); } } 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 40964c03db81..78b6077dc1bd 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -24,6 +24,7 @@ import static android.app.usage.UsageStatsManager.REASON_PREDICTED; import static android.app.usage.UsageStatsManager.REASON_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_USAGE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; @@ -35,6 +36,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doReturn; @@ -80,6 +82,8 @@ public class AppStandbyControllerTests { private static final String PACKAGE_1 = "com.example.foo"; private static final int UID_1 = 10000; + private static final String PACKAGE_EXEMPTED_1 = "com.android.exempted"; + private static final int UID_EXEMPTED_1 = 10001; private static final int USER_ID = 0; private static final int USER_ID2 = 10; @@ -116,7 +120,7 @@ public class AppStandbyControllerTests { List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>(); boolean mDisplayOn; DisplayManager.DisplayListener mDisplayListener; - String mBoundWidgetPackage; + String mBoundWidgetPackage = PACKAGE_EXEMPTED_1; MyInjector(Context context, Looper looper) { super(context, looper); @@ -223,10 +227,21 @@ public class AppStandbyControllerTests { pi.packageName = PACKAGE_1; packages.add(pi); + PackageInfo pie = new PackageInfo(); + pie.applicationInfo = new ApplicationInfo(); + pie.applicationInfo.uid = UID_EXEMPTED_1; + pie.packageName = PACKAGE_EXEMPTED_1; + packages.add(pie); + doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt()); try { - doReturn(UID_1).when(mockPm).getPackageUidAsUser(anyString(), anyInt(), anyInt()); - doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(anyString(), anyInt()); + doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt(), anyInt()); + doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1), + anyInt(), anyInt()); + doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(eq(pi.packageName), + anyInt()); + doReturn(pie.applicationInfo).when(mockPm).getApplicationInfo(eq(pie.packageName), + anyInt()); } catch (PackageManager.NameNotFoundException nnfe) {} } @@ -239,14 +254,21 @@ public class AppStandbyControllerTests { private AppStandbyController setupController() throws Exception { mInjector.mElapsedRealtime = 0; + setupPm(mInjector.getContext().getPackageManager()); AppStandbyController controller = new AppStandbyController(mInjector); + controller.initializeDefaultsForSystemApps(USER_ID); controller.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); mInjector.setDisplayOn(false); mInjector.setDisplayOn(true); setChargingState(controller, false); - setupPm(mInjector.getContext().getPackageManager()); controller.checkIdleStates(USER_ID); + assertEquals(STANDBY_BUCKET_EXEMPTED, + controller.getAppStandbyBucket(PACKAGE_EXEMPTED_1, USER_ID, + mInjector.mElapsedRealtime, false)); + assertNotEquals(STANDBY_BUCKET_EXEMPTED, + controller.getAppStandbyBucket(PACKAGE_1, USER_ID, + mInjector.mElapsedRealtime, false)); return controller; } diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java index f860195bd6ea..26a7313b71d8 100644 --- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -19,12 +19,12 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import android.graphics.Point; import android.graphics.Rect; -import android.platform.test.annotations.Postsubmit; import android.support.test.filters.FlakyTest; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -137,6 +137,32 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { } @Test + public void testTimeout_scaled() throws Exception { + sWm.setAnimationScale(2, 5.0f); + try{ + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); + final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, + new Point(50, 100), new Rect(50, 100, 150, 150)); + adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); + mController.goodToGo(); + + mClock.fastForward(2500); + mHandler.timeAdvance(); + + verify(mMockRunner, never()).onAnimationCancelled(); + + mClock.fastForward(10000); + mHandler.timeAdvance(); + + verify(mMockRunner).onAnimationCancelled(); + verify(mFinishedCallback).onAnimationFinished(eq(adapter)); + } finally { + sWm.setAnimationScale(2, 1.0f); + } + + } + + @Test public void testZeroAnimations() throws Exception { mController.goodToGo(); verifyZeroInteractions(mMockRunner); diff --git a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java new file mode 100644 index 000000000000..51b019a4d61e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java @@ -0,0 +1,50 @@ +package com.android.server.wm; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import android.content.res.Configuration; +import android.graphics.Rect; + +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import static org.junit.Assert.assertTrue; + +/** + * Tests for the {@link RootWindowContainer} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:com.android.server.wm.RootWindowContainerTests + */ +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class RootWindowContainerTests extends WindowTestsBase { + @Test + public void testSetDisplayOverrideConfigurationIfNeeded() throws Exception { + // Add first stack we expect to be updated with configuration change. + final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); + stack.getOverrideConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 5, 5)); + + // Add second task that will be set for deferred removal that should not be returned + // with the configuration change. + final TaskStack deferredDeletedStack = createTaskStackOnDisplay(mDisplayContent); + deferredDeletedStack.getOverrideConfiguration().windowConfiguration.setBounds( + new Rect(0, 0, 5, 5)); + deferredDeletedStack.mDeferRemoval = true; + + final Configuration override = new Configuration( + mDisplayContent.getOverrideConfiguration()); + override.windowConfiguration.setBounds(new Rect(0, 0, 10, 10)); + + // Set display override. + final int[] results = sWm.mRoot.setDisplayOverrideConfigurationIfNeeded(override, + mDisplayContent.getDisplayId()); + + // Ensure only first stack is returned. + assertTrue(results.length == 1); + assertTrue(results[0] == stack.mStackId); + } +} diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java index 99eb8468c20d..e36586eba26c 100644 --- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java +++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java @@ -210,6 +210,9 @@ public class ShortcutManagerTestUtils { runCommand(instrumentation, "cmd package set-home-activity --user " + instrumentation.getContext().getUserId() + " " + component, result -> result.contains("Success")); + runCommand(instrumentation, "cmd shortcut clear-default-launcher --user " + + instrumentation.getContext().getUserId(), + result -> result.contains("Success")); } public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) { 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 9ae6f00f44ca..6b6df294e47c 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -117,7 +117,7 @@ import java.util.Set; public class NotificationManagerServiceTest extends UiServiceTestCase { private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"; private final int mUid = Binder.getCallingUid(); - private NotificationManagerService mService; + private TestableNotificationManagerService mService; private INotificationManager mBinderService; private NotificationManagerInternal mInternalService; @Mock @@ -152,17 +152,21 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // Use a Testable subclass so we can simulate calls from the system without failing. private static class TestableNotificationManagerService extends NotificationManagerService { + int countSystemChecks = 0; + public TestableNotificationManagerService(Context context) { super(context); } @Override protected boolean isCallingUidSystem() { + countSystemChecks++; return true; } @Override protected boolean isCallerSystemOrPhone() { + countSystemChecks++; return true; } @@ -2429,4 +2433,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .setUid(user2.sbn.getUid()) .setLastNotified(user2.sbn.getPostTime()))); } + + @Test + public void testRestore() throws Exception { + int systemChecks = mService.countSystemChecks; + mBinderService.applyRestore(null, UserHandle.USER_SYSTEM); + assertEquals(1, mService.countSystemChecks - systemChecks); + } + + @Test + public void testBackup() throws Exception { + int systemChecks = mService.countSystemChecks; + mBinderService.getBackupPayload(1); + assertEquals(1, mService.countSystemChecks - systemChecks); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java index 7c14d083d3c9..bc2815099fdb 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.os.UserHandle; import android.support.test.filters.SmallTest; import android.util.Xml.Encoding; @@ -67,6 +68,15 @@ public class SliceFullAccessListTest extends UiServiceTestCase { } @Test + public void testRemoveAccess() { + mAccessList.grantFullAccess("pkg", 0); + assertTrue(mAccessList.hasFullAccess("pkg", 0)); + + mAccessList.removeGrant("pkg", 0); + assertFalse(mAccessList.hasFullAccess("pkg", 0)); + } + + @Test public void testSerialization() throws XmlPullParserException, IOException { mAccessList.grantFullAccess("pkg", 0); mAccessList.grantFullAccess("pkg1", 0); @@ -76,7 +86,7 @@ public class SliceFullAccessListTest extends UiServiceTestCase { ByteArrayOutputStream output = new ByteArrayOutputStream(); XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer(); out.setOutput(output, Encoding.UTF_8.name()); - mAccessList.writeXml(out); + mAccessList.writeXml(out, UserHandle.USER_ALL); out.flush(); assertEquals(TEST_XML, output.toString(Encoding.UTF_8.name())); diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java index 2becdf230dbc..b654a66f7d30 100644 --- a/services/usage/java/com/android/server/usage/AppIdleHistory.java +++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java @@ -228,7 +228,9 @@ public class AppIdleHistory { } if (timeout > elapsedRealtime) { // Convert to elapsed timebase - appUsageHistory.bucketTimeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot); + appUsageHistory.bucketTimeoutTime = + Math.max(appUsageHistory.bucketTimeoutTime, + mElapsedDuration + (timeout - mElapsedSnapshot)); } } appUsageHistory.bucketingReason = REASON_USAGE; diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 8cb2eecc654a..cc21199edfd9 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -183,6 +183,8 @@ public class AppStandbyController { boolean mCharging; private long mLastAppIdleParoledTime; private boolean mSystemServicesReady = false; + // There was a system update, defaults need to be initialized after services are ready + private boolean mPendingInitializeDefaults; private final DeviceStateReceiver mDeviceStateReceiver; @@ -279,6 +281,7 @@ public class AppStandbyController { public void onBootPhase(int phase) { mInjector.onBootPhase(phase); if (phase == PHASE_SYSTEM_SERVICES_READY) { + Slog.d(TAG, "Setting app idle enabled state"); setAppIdleEnabled(mInjector.isAppIdleEnabled()); // Observe changes to the threshold SettingsObserver settingsObserver = new SettingsObserver(mHandler); @@ -293,11 +296,15 @@ public class AppStandbyController { mAppIdleHistory.updateDisplay(isDisplayOn(), mInjector.elapsedRealtime()); } + mSystemServicesReady = true; + + if (mPendingInitializeDefaults) { + initializeDefaultsForSystemApps(UserHandle.USER_SYSTEM); + } + if (mPendingOneTimeCheckIdleStates) { postOneTimeCheckIdleStates(); } - - mSystemServicesReady = true; } else if (phase == PHASE_BOOT_COMPLETED) { setChargingState(mInjector.isCharging()); } @@ -451,7 +458,8 @@ public class AppStandbyController { UserHandle.getAppId(pi.applicationInfo.uid), userId); if (DEBUG) { - Slog.d(TAG, " Checking idle state for " + packageName); + Slog.d(TAG, " Checking idle state for " + packageName + " special=" + + isSpecial); } if (isSpecial) { synchronized (mAppIdleLock) { @@ -523,6 +531,7 @@ public class AppStandbyController { elapsedRealtime, bucket)) { StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId, bucket, userStartedInteracting); + if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket); mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, StandbyUpdateRecord.obtain(packageName, userId, bucket, userStartedInteracting))); @@ -1087,7 +1096,13 @@ public class AppStandbyController { } void initializeDefaultsForSystemApps(int userId) { - Slog.d(TAG, "Initializing defaults for system apps on user " + userId); + if (!mSystemServicesReady) { + // Do it later, since SettingsProvider wasn't queried yet for app_standby_enabled + mPendingInitializeDefaults = true; + return; + } + Slog.d(TAG, "Initializing defaults for system apps on user " + userId + ", " + + "appIdleEnabled=" + mAppIdleEnabled); final long elapsedRealtime = mInjector.elapsedRealtime(); List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser( PackageManager.MATCH_DISABLED_COMPONENTS, @@ -1102,11 +1117,6 @@ public class AppStandbyController { // past usage pattern was. mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE, 0, elapsedRealtime + 4 * ONE_HOUR); - if (isAppSpecial(packageName, UserHandle.getAppId(pi.applicationInfo.uid), - userId)) { - mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, - STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT); - } } } } diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java new file mode 100644 index 000000000000..74280fff2dfc --- /dev/null +++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.usb; + +/** + * Represents the ALSA specification, and attributes of an ALSA device. + */ +public final class UsbAlsaDevice { + private static final String TAG = "UsbAlsaDevice"; + protected static final boolean DEBUG = false; + + private final int mCardNum; + private final int mDeviceNum; + private final boolean mHasPlayback; + private final boolean mHasCapture; + + private final boolean mIsInputHeadset; + private final boolean mIsOutputHeadset; + + private final String mDeviceAddress; + + private String mDeviceName = ""; + private String mDeviceDescription = ""; + + public UsbAlsaDevice(int card, int device, String deviceAddress, + boolean hasPlayback, boolean hasCapture, + boolean isInputHeadset, boolean isOutputHeadset) { + mCardNum = card; + mDeviceNum = device; + mDeviceAddress = deviceAddress; + mHasPlayback = hasPlayback; + mHasCapture = hasCapture; + mIsInputHeadset = isInputHeadset; + mIsOutputHeadset = isOutputHeadset; + } + + /** + * @returns the ALSA card number associated with this peripheral. + */ + public int getCardNum() { + return mCardNum; + } + + /** + * @returns the ALSA device number associated with this peripheral. + */ + public int getDeviceNum() { + return mDeviceNum; + } + + /** + * @returns the USB device device address associated with this peripheral. + */ + public String getDeviceAddress() { + return mDeviceAddress; + } + + /** + * @returns true if the device supports playback. + */ + public boolean hasPlayback() { + return mHasPlayback; + } + + /** + * @returns true if the device supports capture (recording). + */ + public boolean hasCapture() { + return mHasCapture; + } + + /** + * @returns true if the device is a headset for purposes of capture. + */ + public boolean isInputHeadset() { + return mIsInputHeadset; + } + + /** + * @returns true if the device is a headset for purposes of playback. + */ + public boolean isOutputHeadset() { + return mIsOutputHeadset; + } + + /** + * @Override + * @returns a string representation of the object. + */ + public String toString() { + return "UsbAlsaDevice: [card: " + mCardNum + + ", device: " + mDeviceNum + + ", name: " + mDeviceName + + ", hasPlayback: " + mHasPlayback + + ", hasCapture: " + mHasCapture + "]"; + } + + // called by logDevices + String toShortString() { + return "[card:" + mCardNum + " device:" + mDeviceNum + " " + mDeviceName + "]"; + } + + String getDeviceName() { + return mDeviceName; + } + + void setDeviceNameAndDescription(String deviceName, String deviceDescription) { + mDeviceName = deviceName; + mDeviceDescription = deviceDescription; + } + + /** + * @Override + * @returns true if the objects are equivalent. + */ + public boolean equals(Object obj) { + if (!(obj instanceof UsbAlsaDevice)) { + return false; + } + UsbAlsaDevice other = (UsbAlsaDevice) obj; + return (mCardNum == other.mCardNum + && mDeviceNum == other.mDeviceNum + && mHasPlayback == other.mHasPlayback + && mHasCapture == other.mHasCapture + && mIsInputHeadset == other.mIsInputHeadset + && mIsOutputHeadset == other.mIsOutputHeadset); + } + + /** + * @Override + * @returns a hash code generated from the object contents. + */ + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mCardNum; + result = prime * result + mDeviceNum; + result = prime * result + (mHasPlayback ? 0 : 1); + result = prime * result + (mHasCapture ? 0 : 1); + result = prime * result + (mIsInputHeadset ? 0 : 1); + result = prime * result + (mIsOutputHeadset ? 0 : 1); + + return result; + } +} + diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java index 7bea8a11133b..0a94828e595e 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java @@ -10,7 +10,7 @@ * 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 an + * See the License for the specific language governing permissions and * limitations under the License. */ @@ -19,28 +19,24 @@ package com.android.server.usb; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; -import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbInterface; import android.media.AudioSystem; import android.media.IAudioService; import android.media.midi.MidiDeviceInfo; import android.os.Bundle; -import android.os.FileObserver; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemClock; import android.provider.Settings; import android.util.Slog; import com.android.internal.alsa.AlsaCardsParser; -import com.android.internal.alsa.AlsaDevicesParser; import com.android.internal.util.IndentingPrintWriter; import com.android.server.audio.AudioService; +import com.android.server.usb.descriptors.UsbDescriptorParser; import libcore.io.IoUtils; -import java.io.File; +import java.util.ArrayList; import java.util.HashMap; /** @@ -57,107 +53,36 @@ public final class UsbAlsaManager { private final boolean mHasMidiFeature; private final AlsaCardsParser mCardsParser = new AlsaCardsParser(); - private final AlsaDevicesParser mDevicesParser = new AlsaDevicesParser(); // this is needed to map USB devices to ALSA Audio Devices, especially to remove an // ALSA device when we are notified that its associated USB device has been removed. + private final ArrayList<UsbAlsaDevice> mAlsaDevices = new ArrayList<UsbAlsaDevice>(); - private final HashMap<UsbDevice,UsbAudioDevice> - mAudioDevices = new HashMap<UsbDevice,UsbAudioDevice>(); - - private boolean mIsInputHeadset; // as reported by UsbDescriptorParser - private boolean mIsOutputHeadset; // as reported by UsbDescriptorParser - - private final HashMap<UsbDevice,UsbMidiDevice> - mMidiDevices = new HashMap<UsbDevice,UsbMidiDevice>(); - - private final HashMap<String,AlsaDevice> - mAlsaDevices = new HashMap<String,AlsaDevice>(); - - private UsbAudioDevice mAccessoryAudioDevice = null; + /** + * List of connected MIDI devices + */ + private final HashMap<String, UsbMidiDevice> + mMidiDevices = new HashMap<String, UsbMidiDevice>(); // UsbMidiDevice for USB peripheral mode (gadget) device private UsbMidiDevice mPeripheralMidiDevice = null; - private final class AlsaDevice { - public static final int TYPE_UNKNOWN = 0; - public static final int TYPE_PLAYBACK = 1; - public static final int TYPE_CAPTURE = 2; - public static final int TYPE_MIDI = 3; - - public int mCard; - public int mDevice; - public int mType; - - public AlsaDevice(int type, int card, int device) { - mType = type; - mCard = card; - mDevice = device; - } - - public boolean equals(Object obj) { - if (! (obj instanceof AlsaDevice)) { - return false; - } - AlsaDevice other = (AlsaDevice)obj; - return (mType == other.mType && mCard == other.mCard && mDevice == other.mDevice); - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("AlsaDevice: [card: " + mCard); - sb.append(", device: " + mDevice); - sb.append(", type: " + mType); - sb.append("]"); - return sb.toString(); - } - } - - private final FileObserver mAlsaObserver = new FileObserver(ALSA_DIRECTORY, - FileObserver.CREATE | FileObserver.DELETE) { - public void onEvent(int event, String path) { - switch (event) { - case FileObserver.CREATE: - alsaFileAdded(path); - break; - case FileObserver.DELETE: - alsaFileRemoved(path); - break; - } - } - }; - /* package */ UsbAlsaManager(Context context) { mContext = context; mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI); - - // initial scan - if (mCardsParser.scan() != AlsaCardsParser.SCANSTATUS_SUCCESS) { - Slog.e(TAG, "Error scanning ASLA cards file."); - } } public void systemReady() { mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); - - mAlsaObserver.startWatching(); - - // add existing alsa devices - File[] files = new File(ALSA_DIRECTORY).listFiles(); - if (files != null) { - for (int i = 0; i < files.length; i++) { - alsaFileAdded(files[i].getName()); - } - } } // Notifies AudioService when a device is added or removed // audioDevice - the AudioDevice that was added or removed // enabled - if true, we're connecting a device (it's arrived), else disconnecting - private void notifyDeviceState(UsbAudioDevice audioDevice, boolean enabled) { + private void notifyDeviceState(UsbAlsaDevice alsaDevice, boolean enabled) { if (DEBUG) { - Slog.d(TAG, "notifyDeviceState " + enabled + " " + audioDevice); + Slog.d(TAG, "notifyDeviceState " + enabled + " " + alsaDevice); } if (mAudioService == null) { @@ -177,276 +102,151 @@ public final class UsbAlsaManager { } int state = (enabled ? 1 : 0); - int alsaCard = audioDevice.mCard; - int alsaDevice = audioDevice.mDevice; - if (alsaCard < 0 || alsaDevice < 0) { - Slog.e(TAG, "Invalid alsa card or device alsaCard: " + alsaCard + - " alsaDevice: " + alsaDevice); + int cardNum = alsaDevice.getCardNum(); + int deviceNum = alsaDevice.getDeviceNum(); + if (cardNum < 0 || deviceNum < 0) { + Slog.e(TAG, "Invalid alsa card or device alsaCard: " + cardNum + + " alsaDevice: " + deviceNum); return; } - String address = AudioService.makeAlsaAddressString(alsaCard, alsaDevice); + String address = AudioService.makeAlsaAddressString(cardNum, deviceNum); try { // Playback Device - if (audioDevice.mHasPlayback) { - int device; - if (mIsOutputHeadset) { - device = AudioSystem.DEVICE_OUT_USB_HEADSET; - } else { - device = (audioDevice == mAccessoryAudioDevice - ? AudioSystem.DEVICE_OUT_USB_ACCESSORY - : AudioSystem.DEVICE_OUT_USB_DEVICE); - } + if (alsaDevice.hasPlayback()) { + int device = alsaDevice.isOutputHeadset() + ? AudioSystem.DEVICE_OUT_USB_HEADSET + : AudioSystem.DEVICE_OUT_USB_DEVICE; if (DEBUG) { Slog.i(TAG, "pre-call device:0x" + Integer.toHexString(device) + - " addr:" + address + " name:" + audioDevice.getDeviceName()); + " addr:" + address + " name:" + alsaDevice.getDeviceName()); } mAudioService.setWiredDeviceConnectionState( - device, state, address, audioDevice.getDeviceName(), TAG); + device, state, address, alsaDevice.getDeviceName(), TAG); } // Capture Device - if (audioDevice.mHasCapture) { - int device; - if (mIsInputHeadset) { - device = AudioSystem.DEVICE_IN_USB_HEADSET; - } else { - device = (audioDevice == mAccessoryAudioDevice - ? AudioSystem.DEVICE_IN_USB_ACCESSORY - : AudioSystem.DEVICE_IN_USB_DEVICE); - } + if (alsaDevice.hasCapture()) { + int device = alsaDevice.isInputHeadset() + ? AudioSystem.DEVICE_IN_USB_HEADSET + : AudioSystem.DEVICE_IN_USB_DEVICE; mAudioService.setWiredDeviceConnectionState( - device, state, address, audioDevice.getDeviceName(), TAG); + device, state, address, alsaDevice.getDeviceName(), TAG); } } catch (RemoteException e) { Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState"); } } - private AlsaDevice waitForAlsaDevice(int card, int device, int type) { - if (DEBUG) { - Slog.e(TAG, "waitForAlsaDevice(c:" + card + " d:" + device + ")"); - } - - AlsaDevice testDevice = new AlsaDevice(type, card, device); - - // This value was empirically determined. - final int kWaitTimeMs = 2500; - - synchronized(mAlsaDevices) { - long timeoutMs = SystemClock.elapsedRealtime() + kWaitTimeMs; - do { - if (mAlsaDevices.values().contains(testDevice)) { - return testDevice; - } - long waitTimeMs = timeoutMs - SystemClock.elapsedRealtime(); - if (waitTimeMs > 0) { - try { - mAlsaDevices.wait(waitTimeMs); - } catch (InterruptedException e) { - Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file."); - } - } - } while (timeoutMs > SystemClock.elapsedRealtime()); - } - - Slog.e(TAG, "waitForAlsaDevice failed for " + testDevice); - return null; - } - - private void alsaFileAdded(String name) { - Slog.i(TAG, "alsaFileAdded(" + name + ")"); - int type = AlsaDevice.TYPE_UNKNOWN; - int card = -1, device = -1; - - if (name.startsWith("pcmC")) { - if (name.endsWith("p")) { - type = AlsaDevice.TYPE_PLAYBACK; - } else if (name.endsWith("c")) { - type = AlsaDevice.TYPE_CAPTURE; - } - } else if (name.startsWith("midiC")) { - type = AlsaDevice.TYPE_MIDI; - } - - if (type != AlsaDevice.TYPE_UNKNOWN) { - try { - int c_index = name.indexOf('C'); - int d_index = name.indexOf('D'); - int end = name.length(); - if (type == AlsaDevice.TYPE_PLAYBACK || type == AlsaDevice.TYPE_CAPTURE) { - // skip trailing 'p' or 'c' - end--; - } - card = Integer.parseInt(name.substring(c_index + 1, d_index)); - device = Integer.parseInt(name.substring(d_index + 1, end)); - } catch (Exception e) { - Slog.e(TAG, "Could not parse ALSA file name " + name, e); - return; - } - synchronized(mAlsaDevices) { - if (mAlsaDevices.get(name) == null) { - AlsaDevice alsaDevice = new AlsaDevice(type, card, device); - Slog.d(TAG, "Adding ALSA device " + alsaDevice); - mAlsaDevices.put(name, alsaDevice); - mAlsaDevices.notifyAll(); - } + private int getAlsaDeviceListIndexFor(String deviceAddress) { + for (int index = 0; index < mAlsaDevices.size(); index++) { + if (mAlsaDevices.get(index).getDeviceAddress().equals(deviceAddress)) { + return index; } } + return -1; } - private void alsaFileRemoved(String path) { - synchronized(mAlsaDevices) { - AlsaDevice device = mAlsaDevices.remove(path); - if (device != null) { - Slog.d(TAG, "ALSA device removed: " + device); - } - } - } - - /* - * Select the default device of the specified card. - */ - /* package */ UsbAudioDevice selectAudioCard(int card) { - if (DEBUG) { - Slog.d(TAG, "selectAudioCard() card:" + card - + " isCardUsb(): " + mCardsParser.isCardUsb(card)); - } - if (!mCardsParser.isCardUsb(card)) { - // Don't. AudioPolicyManager has logic for falling back to internal devices. - return null; - } - - if (mDevicesParser.scan() != AlsaDevicesParser.SCANSTATUS_SUCCESS) { - Slog.e(TAG, "Error parsing ALSA devices file."); + private UsbAlsaDevice removeAlsaDeviceFromList(String deviceAddress) { + int index = getAlsaDeviceListIndexFor(deviceAddress); + if (index > -1) { + return mAlsaDevices.remove(index); + } else { return null; } + } - int device = mDevicesParser.getDefaultDeviceNum(card); - - boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card); - boolean hasCapture = mDevicesParser.hasCaptureDevices(card); + /* package */ UsbAlsaDevice selectDefaultDevice() { if (DEBUG) { - Slog.d(TAG, "usb: hasPlayback:" + hasPlayback + " hasCapture:" + hasCapture); - } - - int deviceClass = - (mCardsParser.isCardUsb(card) - ? UsbAudioDevice.kAudioDeviceClass_External - : UsbAudioDevice.kAudioDeviceClass_Internal) | - UsbAudioDevice.kAudioDeviceMeta_Alsa; - - // Playback device file needed/present? - if (hasPlayback && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_PLAYBACK) == null)) { - return null; + Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()"); } - // Capture device file needed/present? - if (hasCapture && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_CAPTURE) == null)) { + if (mAlsaDevices.size() > 0) { + UsbAlsaDevice alsaDevice = mAlsaDevices.get(0); + if (DEBUG) { + Slog.d(TAG, " alsaDevice:" + alsaDevice); + } + if (alsaDevice != null) { + notifyDeviceState(alsaDevice, true /*enabled*/); + } + return alsaDevice; + } else { return null; } - - UsbAudioDevice audioDevice = - new UsbAudioDevice(card, device, hasPlayback, hasCapture, deviceClass); - AlsaCardsParser.AlsaCardRecord cardRecord = mCardsParser.getCardRecordFor(card); - audioDevice.setDeviceNameAndDescription(cardRecord.mCardName, cardRecord.mCardDescription); - - notifyDeviceState(audioDevice, true /*enabled*/); - - return audioDevice; - } - - /* package */ UsbAudioDevice selectDefaultDevice() { - if (DEBUG) { - Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()"); - } - return selectAudioCard(mCardsParser.getDefaultCard()); } - /* package */ void usbDeviceAdded(UsbDevice usbDevice, - boolean isInputHeadset, boolean isOutputHeadset) { + /* package */ void usbDeviceAdded(String deviceAddress, UsbDevice usbDevice, + UsbDescriptorParser parser) { if (DEBUG) { - Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName() + Slog.d(TAG, "usbDeviceAdded(): " + usbDevice.getManufacturerName() + " nm:" + usbDevice.getProductName()); } - mIsInputHeadset = isInputHeadset; - mIsOutputHeadset = isOutputHeadset; - - // Is there an audio interface in there? - boolean isAudioDevice = false; + // Scan the Alsa File Space + mCardsParser.scan(); - // FIXME - handle multiple configurations? - int interfaceCount = usbDevice.getInterfaceCount(); - for (int ntrfaceIndex = 0; !isAudioDevice && ntrfaceIndex < interfaceCount; - ntrfaceIndex++) { - UsbInterface ntrface = usbDevice.getInterface(ntrfaceIndex); - if (ntrface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO) { - isAudioDevice = true; - } + // Find the ALSA spec for this device address + AlsaCardsParser.AlsaCardRecord cardRec = + mCardsParser.findCardNumFor(deviceAddress); + if (cardRec == null) { + return; } + // Add it to the devices list + boolean hasInput = parser.hasInput(); + boolean hasOutput = parser.hasOutput(); if (DEBUG) { - Slog.d(TAG, " isAudioDevice: " + isAudioDevice); - } - if (!isAudioDevice) { - return; + Slog.d(TAG, "hasInput: " + hasInput + " hasOutput:" + hasOutput); + } + if (hasInput || hasOutput) { + boolean isInputHeadset = parser.isInputHeadset(); + boolean isOutputHeadset = parser.isOutputHeadset(); + UsbAlsaDevice alsaDevice = + new UsbAlsaDevice(cardRec.getCardNum(), 0 /*device*/, deviceAddress, + hasOutput, hasInput, isInputHeadset, isOutputHeadset); + alsaDevice.setDeviceNameAndDescription( + cardRec.getCardName(), cardRec.getCardDescription()); + mAlsaDevices.add(0, alsaDevice); + + // Select it + if (alsaDevice != null) { + notifyDeviceState(alsaDevice, true /*enabled*/); + } } - int addedCard = mCardsParser.getDefaultUsbCard(); - - // If the default isn't a USB device, let the existing "select internal mechanism" - // handle the selection. + // look for MIDI devices + boolean hasMidi = parser.hasMIDIInterface(); if (DEBUG) { - Slog.d(TAG, " mCardsParser.isCardUsb(" + addedCard + ") = " - + mCardsParser.isCardUsb(addedCard)); + Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature); } - if (mCardsParser.isCardUsb(addedCard)) { - UsbAudioDevice audioDevice = selectAudioCard(addedCard); - if (audioDevice != null) { - mAudioDevices.put(usbDevice, audioDevice); - Slog.i(TAG, "USB Audio Device Added: " + audioDevice); + if (hasMidi && mHasMidiFeature) { + int device = 0; + Bundle properties = new Bundle(); + String manufacturer = usbDevice.getManufacturerName(); + String product = usbDevice.getProductName(); + String version = usbDevice.getVersion(); + String name; + if (manufacturer == null || manufacturer.isEmpty()) { + name = product; + } else if (product == null || product.isEmpty()) { + name = manufacturer; + } else { + name = manufacturer + " " + product; } - - // look for MIDI devices - - // Don't need to call mDevicesParser.scan() because selectAudioCard() does this above. - // Uncomment this next line if that behavior changes in the fugure. - // mDevicesParser.scan() - - boolean hasMidi = mDevicesParser.hasMIDIDevices(addedCard); - if (hasMidi && mHasMidiFeature) { - int device = mDevicesParser.getDefaultDeviceNum(addedCard); - AlsaDevice alsaDevice = waitForAlsaDevice(addedCard, device, AlsaDevice.TYPE_MIDI); - if (alsaDevice != null) { - Bundle properties = new Bundle(); - String manufacturer = usbDevice.getManufacturerName(); - String product = usbDevice.getProductName(); - String version = usbDevice.getVersion(); - String name; - if (manufacturer == null || manufacturer.isEmpty()) { - name = product; - } else if (product == null || product.isEmpty()) { - name = manufacturer; - } else { - name = manufacturer + " " + product; - } - properties.putString(MidiDeviceInfo.PROPERTY_NAME, name); - properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer); - properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product); - properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version); - properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER, - usbDevice.getSerialNumber()); - properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, alsaDevice.mCard); - properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, alsaDevice.mDevice); - properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice); - - UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties, - alsaDevice.mCard, alsaDevice.mDevice); - if (usbMidiDevice != null) { - mMidiDevices.put(usbDevice, usbMidiDevice); - } - } + properties.putString(MidiDeviceInfo.PROPERTY_NAME, name); + properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer); + properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product); + properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version); + properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER, + usbDevice.getSerialNumber()); + properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, cardRec.getCardNum()); + properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, 0 /*deviceNum*/); + properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice); + + UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties, + cardRec.getCardNum(), 0 /*device*/); + if (usbMidiDevice != null) { + mMidiDevices.put(deviceAddress, usbMidiDevice); } } @@ -455,42 +255,31 @@ public final class UsbAlsaManager { } } - /* package */ void usbDeviceRemoved(UsbDevice usbDevice) { + /* package */ void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) { if (DEBUG) { - Slog.d(TAG, "deviceRemoved(): " + usbDevice.getManufacturerName() + - " " + usbDevice.getProductName()); + Slog.d(TAG, "deviceRemoved(" + deviceAddress + ")"); } - UsbAudioDevice audioDevice = mAudioDevices.remove(usbDevice); - Slog.i(TAG, "USB Audio Device Removed: " + audioDevice); - if (audioDevice != null) { - if (audioDevice.mHasPlayback || audioDevice.mHasCapture) { - notifyDeviceState(audioDevice, false /*enabled*/); + // Audio + UsbAlsaDevice alsaDevice = removeAlsaDeviceFromList(deviceAddress); + Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice); + if (alsaDevice != null) { + if (alsaDevice.hasPlayback() || alsaDevice.hasCapture()) { + notifyDeviceState(alsaDevice, false /*enabled*/); // if there any external devices left, select one of them selectDefaultDevice(); } } - UsbMidiDevice usbMidiDevice = mMidiDevices.remove(usbDevice); + + // MIDI + UsbMidiDevice usbMidiDevice = mMidiDevices.remove(deviceAddress); if (usbMidiDevice != null) { + Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice); IoUtils.closeQuietly(usbMidiDevice); } } - /* package */ void setAccessoryAudioState(boolean enabled, int card, int device) { - if (DEBUG) { - Slog.d(TAG, "setAccessoryAudioState " + enabled + " " + card + " " + device); - } - if (enabled) { - mAccessoryAudioDevice = new UsbAudioDevice(card, device, true, false, - UsbAudioDevice.kAudioDeviceClass_External); - notifyDeviceState(mAccessoryAudioDevice, true /*enabled*/); - } else if (mAccessoryAudioDevice != null) { - notifyDeviceState(mAccessoryAudioDevice, false /*enabled*/); - mAccessoryAudioDevice = null; - } - } - /* package */ void setPeripheralMidiState(boolean enabled, int card, int device) { if (!mHasMidiFeature) { return; @@ -515,44 +304,31 @@ public final class UsbAlsaManager { } // - // Devices List - // -/* - //import java.util.ArrayList; - public ArrayList<UsbAudioDevice> getConnectedDevices() { - ArrayList<UsbAudioDevice> devices = new ArrayList<UsbAudioDevice>(mAudioDevices.size()); - for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) { - devices.add(entry.getValue()); - } - return devices; - } -*/ - - // // Logging // // called by UsbService.dump public void dump(IndentingPrintWriter pw) { pw.println("Parsers Scan Status:"); pw.println(" Cards Parser: " + mCardsParser.getScanStatus()); - pw.println(" Devices Parser: " + mDevicesParser.getScanStatus()); +// pw.println(" Devices Parser: " + mDevicesParser.getScanStatus()); pw.println("USB Audio Devices:"); - for (UsbDevice device : mAudioDevices.keySet()) { - pw.println(" " + device.getDeviceName() + ": " + mAudioDevices.get(device)); + for (UsbAlsaDevice usbAlsaDevice : mAlsaDevices) { + pw.println(" " + usbAlsaDevice.getDeviceAddress() + ": " + usbAlsaDevice); } pw.println("USB MIDI Devices:"); - for (UsbDevice device : mMidiDevices.keySet()) { - pw.println(" " + device.getDeviceName() + ": " + mMidiDevices.get(device)); + for (String deviceAddr : mMidiDevices.keySet()) { + UsbMidiDevice midiDevice = mMidiDevices.get(deviceAddr); + pw.println(" " + deviceAddr + ": " + midiDevice); } } /* public void logDevicesList(String title) { if (DEBUG) { - for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) { + for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) { Slog.i(TAG, "UsbDevice-------------------"); Slog.i(TAG, "" + (entry != null ? entry.getKey() : "[none]")); - Slog.i(TAG, "UsbAudioDevice--------------"); + Slog.i(TAG, "UsbAlsaDevice--------------"); Slog.i(TAG, "" + entry.getValue()); } } @@ -564,7 +340,7 @@ public final class UsbAlsaManager { public void logDevices(String title) { if (DEBUG) { Slog.i(TAG, title); - for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) { + for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) { Slog.i(TAG, entry.getValue().toShortString()); } } diff --git a/services/usb/java/com/android/server/usb/UsbAudioDevice.java b/services/usb/java/com/android/server/usb/UsbAudioDevice.java deleted file mode 100644 index 4b17dfe0f27d..000000000000 --- a/services/usb/java/com/android/server/usb/UsbAudioDevice.java +++ /dev/null @@ -1,77 +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 an - * limitations under the License. - */ - -package com.android.server.usb; - -public final class UsbAudioDevice { - private static final String TAG = "UsbAudioDevice"; - protected static final boolean DEBUG = false; - - public final int mCard; - public final int mDevice; - public final boolean mHasPlayback; - public final boolean mHasCapture; - - // Device "class" flags - public static final int kAudioDeviceClassMask = 0x00FFFFFF; - public static final int kAudioDeviceClass_Undefined = 0x00000000; - public static final int kAudioDeviceClass_Internal = 0x00000001; - public static final int kAudioDeviceClass_External = 0x00000002; - // Device meta-data flags - public static final int kAudioDeviceMetaMask = 0xFF000000; - public static final int kAudioDeviceMeta_Alsa = 0x80000000; - // This member is a combination of the above bit-flags - public final int mDeviceClass; - - private String mDeviceName = ""; - private String mDeviceDescription = ""; - - public UsbAudioDevice(int card, int device, - boolean hasPlayback, boolean hasCapture, int deviceClass) { - mCard = card; - mDevice = device; - mHasPlayback = hasPlayback; - mHasCapture = hasCapture; - mDeviceClass = deviceClass; - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("UsbAudioDevice: [card: " + mCard); - sb.append(", device: " + mDevice); - sb.append(", name: " + mDeviceName); - sb.append(", hasPlayback: " + mHasPlayback); - sb.append(", hasCapture: " + mHasCapture); - sb.append(", class: 0x" + Integer.toHexString(mDeviceClass) + "]"); - return sb.toString(); - } - - // called by logDevices - String toShortString() { - return "[card:" + mCard + " device:" + mDevice + " " + mDeviceName + "]"; - } - - String getDeviceName() { - return mDeviceName; - } - - void setDeviceNameAndDescription(String deviceName, String deviceDescription) { - mDeviceName = deviceName; - mDeviceDescription = deviceDescription; - } - -} - diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index e3e5e3e1b10b..a67e7f37f947 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -41,7 +41,6 @@ import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.hardware.usb.UsbPort; import android.hardware.usb.UsbPortStatus; -import android.hardware.usb.gadget.V1_0.GadgetFunction; import android.hardware.usb.gadget.V1_0.IUsbGadget; import android.hardware.usb.gadget.V1_0.IUsbGadgetCallback; import android.hardware.usb.gadget.V1_0.Status; @@ -68,6 +67,8 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.SomeArgs; @@ -86,21 +87,20 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Scanner; import java.util.Set; -import java.util.StringJoiner; /** * UsbDeviceManager manages USB state in device mode. */ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver { - private static final String TAG = "UsbDeviceManager"; + private static final String TAG = UsbDeviceManager.class.getSimpleName(); private static final boolean DEBUG = false; /** * The SharedPreference setting per user that stores the screen unlocked functions between * sessions. */ - private static final String UNLOCKED_CONFIG_PREF = "usb-screen-unlocked-config-%d"; + static final String UNLOCKED_CONFIG_PREF = "usb-screen-unlocked-config-%d"; /** * ro.bootmode value when phone boots into usual Android. @@ -156,8 +156,6 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv"; private UsbHandler mHandler; - private boolean mBootCompleted; - private boolean mSystemReady; private final Object mLock = new Object(); @@ -165,22 +163,13 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver private final ContentResolver mContentResolver; @GuardedBy("mLock") private UsbProfileGroupSettingsManager mCurrentSettings; - private NotificationManager mNotificationManager; private final boolean mHasUsbAccessory; - private boolean mUseUsbNotification; - private boolean mAdbEnabled; - private boolean mAudioSourceEnabled; - private boolean mMidiEnabled; - private int mMidiCard; - private int mMidiDevice; + @GuardedBy("mLock") private String[] mAccessoryStrings; private UsbDebuggingManager mDebuggingManager; - private final UsbAlsaManager mUsbAlsaManager; - private final UsbSettingsManager mSettingsManager; - private Intent mBroadcastedIntent; - private boolean mPendingBootBroadcast; + private final UEventObserver mUEventObserver; + private static Set<Integer> sBlackListedInterfaces; - private SharedPreferences mSettings; static { sBlackListedInterfaces = new HashSet<>(); @@ -213,7 +202,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver /* * Listens for uevent messages from the kernel to monitor the USB state */ - private final UEventObserver mUEventObserver = new UEventObserver() { + private final class UsbUEventObserver extends UEventObserver { @Override public void onUEvent(UEventObserver.UEvent event) { if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString()); @@ -227,7 +216,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver startAccessoryMode(); } } - }; + } @Override public void onKeyguardStateChanged(boolean isShowing) { @@ -257,8 +246,6 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver public UsbDeviceManager(Context context, UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) { mContext = context; - mUsbAlsaManager = alsaManager; - mSettingsManager = settingsManager; mContentResolver = context.getContentResolver(); PackageManager pm = mContext.getPackageManager(); mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY); @@ -274,16 +261,24 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver Slog.i(TAG, "USB GADGET HAL not present in the device", e); } + boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false); + boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt")); + if (secureAdbEnabled && !dataEncrypted) { + mDebuggingManager = new UsbDebuggingManager(context); + } + if (halNotPresent) { /** * Initialze the legacy UsbHandler */ - mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext); + mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext, this, + mDebuggingManager, alsaManager, settingsManager); } else { /** * Initialize HAL based UsbHandler */ - mHandler = new UsbHandlerHal(FgThread.get().getLooper()); + mHandler = new UsbHandlerHal(FgThread.get().getLooper(), mContext, this, + mDebuggingManager, alsaManager, settingsManager); } if (nativeIsStartRequested()) { @@ -291,12 +286,6 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver startAccessoryMode(); } - boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false); - boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt")); - if (secureAdbEnabled && !dataEncrypted) { - mDebuggingManager = new UsbDebuggingManager(context); - } - BroadcastReceiver portReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -347,41 +336,35 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver mContext.registerReceiver(languageChangedReceiver, new IntentFilter(Intent.ACTION_LOCALE_CHANGED)); + + // Watch for USB configuration changes + mUEventObserver = new UsbUEventObserver(); + mUEventObserver.startObserving(USB_STATE_MATCH); + mUEventObserver.startObserving(ACCESSORY_START_MATCH); + + // register observer to listen for settings changes + mContentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ADB_ENABLED), + false, new AdbSettingsObserver()); } - private UsbProfileGroupSettingsManager getCurrentSettings() { + UsbProfileGroupSettingsManager getCurrentSettings() { synchronized (mLock) { return mCurrentSettings; } } + String[] getAccessoryStrings() { + synchronized (mLock) { + return mAccessoryStrings; + } + } + public void systemReady() { if (DEBUG) Slog.d(TAG, "systemReady"); LocalServices.getService(ActivityManagerInternal.class).registerScreenObserver(this); - mNotificationManager = (NotificationManager) - mContext.getSystemService(Context.NOTIFICATION_SERVICE); - - // Ensure that the notification channels are set up - if (isTv()) { - // TV-specific notification channel - mNotificationManager.createNotificationChannel( - new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV, - mContext.getString( - com.android.internal.R.string - .adb_debugging_notification_channel_tv), - NotificationManager.IMPORTANCE_HIGH)); - } - - // We do not show the USB notification if the primary volume supports mass storage. - // The legacy mass storage UI will be used instead. - boolean massStorageSupported; - final StorageManager storageManager = StorageManager.from(mContext); - final StorageVolume primary = storageManager.getPrimaryVolume(); - massStorageSupported = primary != null && primary.allowMassStorage(); - mUseUsbNotification = !massStorageSupported && mContext.getResources().getBoolean( - com.android.internal.R.bool.config_usbChargingMessage); mHandler.sendEmptyMessage(MSG_SYSTEM_READY); } @@ -410,21 +393,19 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver boolean enableAccessory = (mAccessoryStrings != null && mAccessoryStrings[UsbAccessory.MANUFACTURER_STRING] != null && mAccessoryStrings[UsbAccessory.MODEL_STRING] != null); - String functions = null; - if (enableAccessory && enableAudio) { - functions = UsbManager.USB_FUNCTION_ACCESSORY + "," - + UsbManager.USB_FUNCTION_AUDIO_SOURCE; - } else if (enableAccessory) { - functions = UsbManager.USB_FUNCTION_ACCESSORY; - } else if (enableAudio) { - functions = UsbManager.USB_FUNCTION_AUDIO_SOURCE; + long functions = UsbManager.FUNCTION_NONE; + if (enableAccessory) { + functions |= UsbManager.FUNCTION_ACCESSORY; + } + if (enableAudio) { + functions |= UsbManager.FUNCTION_AUDIO_SOURCE; } - if (functions != null) { + if (functions != UsbManager.FUNCTION_NONE) { mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_MODE_ENTER_TIMEOUT), ACCESSORY_REQUEST_TIMEOUT); - setCurrentFunctions(functions, false); + setCurrentFunctions(functions); } } @@ -451,19 +432,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } } - private boolean isTv() { - return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); - } - - private SharedPreferences getPinnedSharedPrefs(Context context) { - final File prefsFile = new File(new File( - Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL, - context.getUserId(), context.getPackageName()), "shared_prefs"), - UsbDeviceManager.class.getSimpleName() + ".xml"); - return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE); - } - - private abstract class UsbHandler extends Handler { + abstract static class UsbHandler extends Handler { // current USB state private boolean mConnected; @@ -471,21 +440,40 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver private boolean mSourcePower; private boolean mSinkPower; private boolean mConfigured; - protected boolean mUsbDataUnlocked; private boolean mAudioAccessoryConnected; private boolean mAudioAccessorySupported; - protected String mCurrentFunctions; - protected boolean mCurrentFunctionsApplied; + private UsbAccessory mCurrentAccessory; private int mUsbNotificationId; private boolean mAdbNotificationShown; - private int mCurrentUser; private boolean mUsbCharging; private boolean mHideUsbNotification; private boolean mSupportsAllCombinations; - private String mScreenUnlockedFunctions = UsbManager.USB_FUNCTION_NONE; private boolean mScreenLocked; - protected boolean mCurrentUsbFunctionsRequested; + private boolean mSystemReady; + private Intent mBroadcastedIntent; + private boolean mPendingBootBroadcast; + private boolean mAudioSourceEnabled; + private boolean mMidiEnabled; + private int mMidiCard; + private int mMidiDevice; + + private final Context mContext; + private final UsbDebuggingManager mDebuggingManager; + private final UsbAlsaManager mUsbAlsaManager; + private final UsbSettingsManager mSettingsManager; + private NotificationManager mNotificationManager; + + protected long mScreenUnlockedFunctions; + protected boolean mAdbEnabled; + protected boolean mBootCompleted; + protected boolean mCurrentFunctionsApplied; + protected boolean mUseUsbNotification; + protected long mCurrentFunctions; + protected final UsbDeviceManager mUsbDeviceManager; + protected final ContentResolver mContentResolver; + protected SharedPreferences mSettings; + protected int mCurrentUser; protected boolean mCurrentUsbFunctionsReceived; /** @@ -494,31 +482,36 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver */ protected static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config"; - public UsbHandler(Looper looper) { + UsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager, + UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager, + UsbSettingsManager settingsManager) { super(looper); + mContext = context; + mDebuggingManager = debuggingManager; + mUsbDeviceManager = deviceManager; + mUsbAlsaManager = alsaManager; + mSettingsManager = settingsManager; + mContentResolver = context.getContentResolver(); mCurrentUser = ActivityManager.getCurrentUser(); + mScreenUnlockedFunctions = UsbManager.FUNCTION_NONE; mScreenLocked = true; /* * Use the normal bootmode persistent prop to maintain state of adb across * all boot modes. */ - mAdbEnabled = UsbManager.containsFunction( - SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY), - UsbManager.USB_FUNCTION_ADB); + mAdbEnabled = UsbHandlerLegacy.containsFunction(getSystemProperty( + USB_PERSISTENT_CONFIG_PROPERTY, ""), UsbManager.USB_FUNCTION_ADB); - /* - * Previous versions can set persist config to mtp/ptp but it does not - * get reset on OTA. Reset the property here instead. - */ - String persisted = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY); - if (UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_MTP) - || UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_PTP)) { - SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, - UsbManager.removeFunction(UsbManager.removeFunction(persisted, - UsbManager.USB_FUNCTION_MTP), UsbManager.USB_FUNCTION_PTP)); - } + // We do not show the USB notification if the primary volume supports mass storage. + // The legacy mass storage UI will be used instead. + final StorageManager storageManager = StorageManager.from(mContext); + final StorageVolume primary = storageManager.getPrimaryVolume(); + + boolean massStorageSupported = primary != null && primary.allowMassStorage(); + mUseUsbNotification = !massStorageSupported && mContext.getResources().getBoolean( + com.android.internal.R.bool.config_usbChargingMessage); } public void sendMessage(int what, boolean arg) { @@ -602,20 +595,14 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable); if (enable != mAdbEnabled) { mAdbEnabled = enable; - String oldFunctions = mCurrentFunctions; - - // Persist the adb setting - String newFunction = applyAdbFunction(SystemProperties.get( - USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE)); - SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunction); - // Remove mtp from the config if file transfer is not enabled - if (oldFunctions.equals(UsbManager.USB_FUNCTION_MTP) && - !mUsbDataUnlocked && enable) { - oldFunctions = UsbManager.USB_FUNCTION_NONE; + if (enable) { + setSystemProperty(USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_ADB); + } else { + setSystemProperty(USB_PERSISTENT_CONFIG_PROPERTY, ""); } - setEnabledFunctions(oldFunctions, true, mUsbDataUnlocked); + setEnabledFunctions(mCurrentFunctions, true); updateAdbNotification(false); } @@ -624,21 +611,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } } - protected String applyAdbFunction(String functions) { - // Do not pass null pointer to the UsbManager. - // There isnt a check there. - if (functions == null) { - functions = ""; - } - if (mAdbEnabled) { - functions = UsbManager.addFunction(functions, UsbManager.USB_FUNCTION_ADB); - } else { - functions = UsbManager.removeFunction(functions, UsbManager.USB_FUNCTION_ADB); - } - return functions; - } - - private boolean isUsbTransferAllowed() { + protected boolean isUsbTransferAllowed() { UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); return !userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER); } @@ -650,12 +623,13 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver if (mConfigured && enteringAccessoryMode) { // successfully entered accessory mode - if (mAccessoryStrings != null) { - mCurrentAccessory = new UsbAccessory(mAccessoryStrings); + String[] accessoryStrings = mUsbDeviceManager.getAccessoryStrings(); + if (accessoryStrings != null) { + mCurrentAccessory = new UsbAccessory(accessoryStrings); Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); // defer accessoryAttached if system is not ready if (mBootCompleted) { - getCurrentSettings().accessoryAttached(mCurrentAccessory); + mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory); } // else handle in boot completed } else { Slog.e(TAG, "nativeGetAccessoryStrings failed"); @@ -673,17 +647,24 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver // make sure accessory mode is off // and restore default functions Slog.d(TAG, "exited USB accessory mode"); - setEnabledFunctions(null, false, false); + setEnabledFunctions(UsbManager.FUNCTION_NONE, false); if (mCurrentAccessory != null) { if (mBootCompleted) { mSettingsManager.usbAccessoryRemoved(mCurrentAccessory); } mCurrentAccessory = null; - mAccessoryStrings = null; } } + protected SharedPreferences getPinnedSharedPrefs(Context context) { + final File prefsFile = new File(new File( + Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL, + context.getUserId(), context.getPackageName()), "shared_prefs"), + UsbDeviceManager.class.getSimpleName() + ".xml"); + return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE); + } + private boolean isUsbStateChanged(Intent intent) { final Set<String> keySet = intent.getExtras().keySet(); if (mBroadcastedIntent == null) { @@ -706,7 +687,8 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver return false; } - protected void updateUsbStateBroadcastIfNeeded(boolean configChanged) { + protected void updateUsbStateBroadcastIfNeeded(long functions, + boolean configChanged) { // send a sticky broadcast containing current USB state Intent intent = new Intent(UsbManager.ACTION_USB_STATE); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING @@ -716,18 +698,14 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver intent.putExtra(UsbManager.USB_HOST_CONNECTED, mHostConnected); intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured); intent.putExtra(UsbManager.USB_DATA_UNLOCKED, - isUsbTransferAllowed() && mUsbDataUnlocked); + isUsbTransferAllowed() && isUsbDataTransferActive(mCurrentFunctions)); intent.putExtra(UsbManager.USB_CONFIG_CHANGED, configChanged); - if (mCurrentFunctions != null) { - String[] functions = mCurrentFunctions.split(","); - for (int i = 0; i < functions.length; i++) { - final String function = functions[i]; - if (UsbManager.USB_FUNCTION_NONE.equals(function)) { - continue; - } - intent.putExtra(function, true); - } + long remainingFunctions = functions; + while (remainingFunctions != 0) { + intent.putExtra(UsbManager.usbFunctionsToString( + Long.highestOneBit(remainingFunctions)), true); + remainingFunctions -= Long.highestOneBit(remainingFunctions); } // send broadcast intent only if the USB state has changed @@ -739,44 +717,20 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } if (DEBUG) Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras()); - mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + sendStickyBroadcast(intent); mBroadcastedIntent = intent; } - private void updateUsbFunctions() { - updateAudioSourceFunction(); - updateMidiFunction(); + protected void sendStickyBroadcast(Intent intent) { + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } - private void updateAudioSourceFunction() { - boolean enabled = UsbManager.containsFunction(mCurrentFunctions, - UsbManager.USB_FUNCTION_AUDIO_SOURCE); - if (enabled != mAudioSourceEnabled) { - int card = -1; - int device = -1; - - if (enabled) { - Scanner scanner = null; - try { - scanner = new Scanner(new File(AUDIO_SOURCE_PCM_PATH)); - card = scanner.nextInt(); - device = scanner.nextInt(); - } catch (FileNotFoundException e) { - Slog.e(TAG, "could not open audio source PCM file", e); - } finally { - if (scanner != null) { - scanner.close(); - } - } - } - mUsbAlsaManager.setAccessoryAudioState(enabled, card, device); - mAudioSourceEnabled = enabled; - } + private void updateUsbFunctions() { + updateMidiFunction(); } private void updateMidiFunction() { - boolean enabled = UsbManager.containsFunction(mCurrentFunctions, - UsbManager.USB_FUNCTION_MIDI); + boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_MIDI) != 0; if (enabled != mMidiEnabled) { if (enabled) { Scanner scanner = null; @@ -800,11 +754,21 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } private void setScreenUnlockedFunctions() { - setEnabledFunctions(mScreenUnlockedFunctions, false, - UsbManager.containsFunction(mScreenUnlockedFunctions, - UsbManager.USB_FUNCTION_MTP) - || UsbManager.containsFunction(mScreenUnlockedFunctions, - UsbManager.USB_FUNCTION_PTP)); + setEnabledFunctions(mScreenUnlockedFunctions, false); + } + + /** + * Returns the functions that are passed down to the low level driver once adb and + * charging are accounted for. + */ + long getAppliedFunctions(long functions) { + if (functions == UsbManager.FUNCTION_NONE) { + return getChargingFunctions(); + } + if (mAdbEnabled) { + return functions | UsbManager.FUNCTION_ADB; + } + return functions; } @Override @@ -817,10 +781,10 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver updateUsbNotification(false); updateAdbNotification(false); if (mBootCompleted) { - updateUsbStateBroadcastIfNeeded(false); + updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), + false); } - if (UsbManager.containsFunction(mCurrentFunctions, - UsbManager.USB_FUNCTION_ACCESSORY)) { + if ((mCurrentFunctions & UsbManager.FUNCTION_ACCESSORY) != 0) { updateCurrentAccessory(); } if (mBootCompleted) { @@ -828,11 +792,10 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver && !hasMessages(MSG_FUNCTION_SWITCH_TIMEOUT)) { // restore defaults when USB is disconnected if (!mScreenLocked - && !UsbManager.USB_FUNCTION_NONE.equals( - mScreenUnlockedFunctions)) { + && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) { setScreenUnlockedFunctions(); } else { - setEnabledFunctions(null, !mAdbEnabled, false); + setEnabledFunctions(UsbManager.FUNCTION_NONE, !mAdbEnabled); } } updateUsbFunctions(); @@ -867,7 +830,8 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver updateUsbNotification(false); if (mBootCompleted) { if (mHostConnected || prevHostConnected) { - updateUsbStateBroadcastIfNeeded(false); + updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), + false); } } else { mPendingBootBroadcast = true; @@ -913,17 +877,17 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver setAdbEnabled(msg.arg1 == 1); break; case MSG_SET_CURRENT_FUNCTIONS: - String functions = (String) msg.obj; - setEnabledFunctions(functions, false, msg.arg1 == 1); + long functions = (Long) msg.obj; + setEnabledFunctions(functions, false); break; case MSG_SET_SCREEN_UNLOCKED_FUNCTIONS: - mScreenUnlockedFunctions = (String) msg.obj; + mScreenUnlockedFunctions = (Long) msg.obj; SharedPreferences.Editor editor = mSettings.edit(); editor.putString(String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, - mCurrentUser), mScreenUnlockedFunctions); + mCurrentUser), + UsbManager.usbFunctionsToString(mScreenUnlockedFunctions)); editor.commit(); - if (!mScreenLocked && !UsbManager.USB_FUNCTION_NONE.equals( - mScreenUnlockedFunctions)) { + if (!mScreenLocked && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) { // If the screen is unlocked, also set current functions. setScreenUnlockedFunctions(); } @@ -936,22 +900,21 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver if (mSettings == null && !mScreenLocked) { // Shared preferences aren't accessible until the user has been unlocked. mSettings = getPinnedSharedPrefs(mContext); - mScreenUnlockedFunctions = mSettings.getString( + mScreenUnlockedFunctions = UsbManager.usbFunctionsFromString( + mSettings.getString( String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser), - UsbManager.USB_FUNCTION_NONE); + "")); } if (!mBootCompleted) { break; } if (mScreenLocked) { if (!mConnected) { - setEnabledFunctions(null, false, false); + setEnabledFunctions(UsbManager.FUNCTION_NONE, false); } } else { - if (!UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions) - && (UsbManager.USB_FUNCTION_ADB.equals(mCurrentFunctions) - || (UsbManager.USB_FUNCTION_MTP.equals(mCurrentFunctions) - && !mUsbDataUnlocked))) { + if (mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE + && mCurrentFunctions == UsbManager.FUNCTION_NONE) { // Set the screen unlocked functions if current function is charging. setScreenUnlockedFunctions(); } @@ -959,13 +922,24 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver break; case MSG_UPDATE_USER_RESTRICTIONS: // Restart the USB stack if USB transfer is enabled but no longer allowed. - final boolean forceRestart = mUsbDataUnlocked - && isUsbDataTransferActive() - && !isUsbTransferAllowed(); - setEnabledFunctions( - mCurrentFunctions, forceRestart, mUsbDataUnlocked && !forceRestart); + if (isUsbDataTransferActive(mCurrentFunctions) && !isUsbTransferAllowed()) { + setEnabledFunctions(UsbManager.FUNCTION_NONE, true); + } break; case MSG_SYSTEM_READY: + mNotificationManager = (NotificationManager) + mContext.getSystemService(Context.NOTIFICATION_SERVICE); + + // Ensure that the notification channels are set up + if (isTv()) { + // TV-specific notification channel + mNotificationManager.createNotificationChannel( + new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV, + mContext.getString( + com.android.internal.R.string + .adb_debugging_notification_channel_tv), + NotificationManager.IMPORTANCE_HIGH)); + } mSystemReady = true; finishBoot(); break; @@ -984,10 +958,14 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } mCurrentUser = msg.arg1; mScreenLocked = true; - mScreenUnlockedFunctions = mSettings.getString( - String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser), - UsbManager.USB_FUNCTION_NONE); - setEnabledFunctions(null, false, false); + if (mSettings != null) { + mScreenUnlockedFunctions = UsbManager.usbFunctionsFromString( + mSettings.getString( + String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, + mCurrentUser), + "")); + } + setEnabledFunctions(UsbManager.FUNCTION_NONE, false); } break; } @@ -995,9 +973,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver if (DEBUG) { Slog.v(TAG, "Accessory mode enter timeout: " + mConnected); } - if (!mConnected || !UsbManager.containsFunction( - mCurrentFunctions, - UsbManager.USB_FUNCTION_ACCESSORY)) { + if (!mConnected || (mCurrentFunctions & UsbManager.FUNCTION_ACCESSORY) == 0) { notifyAccessoryModeExit(); } break; @@ -1008,17 +984,17 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver protected void finishBoot() { if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) { if (mPendingBootBroadcast) { - updateUsbStateBroadcastIfNeeded(false); + updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), false); mPendingBootBroadcast = false; } if (!mScreenLocked - && !UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions)) { + && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) { setScreenUnlockedFunctions(); } else { - setEnabledFunctions(null, false, false); + setEnabledFunctions(UsbManager.FUNCTION_NONE, false); } if (mCurrentAccessory != null) { - getCurrentSettings().accessoryAttached(mCurrentAccessory); + mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory); } if (mDebuggingManager != null) { mDebuggingManager.setAdbEnabled(mAdbEnabled); @@ -1026,8 +1002,8 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver // make sure the ADB_ENABLED setting value matches the current state try { - Settings.Global.putInt(mContentResolver, - Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0); + putGlobalSettings(mContentResolver, Settings.Global.ADB_ENABLED, + mAdbEnabled ? 1 : 0); } catch (SecurityException e) { // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't // be changed. @@ -1040,9 +1016,9 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } } - private boolean isUsbDataTransferActive() { - return UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP) - || UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP); + protected boolean isUsbDataTransferActive(long functions) { + return (functions & UsbManager.FUNCTION_MTP) != 0 + || (functions & UsbManager.FUNCTION_PTP) != 0; } public UsbAccessory getCurrentAccessory() { @@ -1051,7 +1027,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver protected void updateUsbNotification(boolean force) { if (mNotificationManager == null || !mUseUsbNotification - || ("0".equals(SystemProperties.get("persist.charging.notify")))) { + || ("0".equals(getSystemProperty("persist.charging.notify", "")))) { return; } @@ -1070,30 +1046,37 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver int id = 0; int titleRes = 0; Resources r = mContext.getResources(); + CharSequence message = r.getText( + com.android.internal.R.string.usb_notification_message); if (mAudioAccessoryConnected && !mAudioAccessorySupported) { titleRes = com.android.internal.R.string.usb_unsupported_audio_accessory_title; id = SystemMessage.NOTE_USB_AUDIO_ACCESSORY_NOT_SUPPORTED; } else if (mConnected) { - if (UsbManager.containsFunction(mCurrentFunctions, - UsbManager.USB_FUNCTION_MTP) && mUsbDataUnlocked) { + if (mCurrentFunctions == UsbManager.FUNCTION_MTP) { titleRes = com.android.internal.R.string.usb_mtp_notification_title; id = SystemMessage.NOTE_USB_MTP; - } else if (UsbManager.containsFunction(mCurrentFunctions, - UsbManager.USB_FUNCTION_PTP) && mUsbDataUnlocked) { + } else if (mCurrentFunctions == UsbManager.FUNCTION_PTP) { titleRes = com.android.internal.R.string.usb_ptp_notification_title; id = SystemMessage.NOTE_USB_PTP; - } else if (UsbManager.containsFunction(mCurrentFunctions, - UsbManager.USB_FUNCTION_MIDI)) { + } else if (mCurrentFunctions == UsbManager.FUNCTION_MIDI) { titleRes = com.android.internal.R.string.usb_midi_notification_title; id = SystemMessage.NOTE_USB_MIDI; - } else if (UsbManager.containsFunction(mCurrentFunctions, - UsbManager.USB_FUNCTION_ACCESSORY)) { + } else if (mCurrentFunctions == UsbManager.FUNCTION_RNDIS) { + titleRes = com.android.internal.R.string.usb_tether_notification_title; + id = SystemMessage.NOTE_USB_TETHER; + } else if (mCurrentFunctions == UsbManager.FUNCTION_ACCESSORY) { titleRes = com.android.internal.R.string.usb_accessory_notification_title; id = SystemMessage.NOTE_USB_ACCESSORY; - } else if (mSourcePower) { - titleRes = com.android.internal.R.string.usb_supplying_notification_title; - id = SystemMessage.NOTE_USB_SUPPLYING; - } else { + } + if (mSourcePower) { + if (titleRes != 0) { + message = r.getText( + com.android.internal.R.string.usb_power_notification_message); + } else { + titleRes = com.android.internal.R.string.usb_supplying_notification_title; + id = SystemMessage.NOTE_USB_SUPPLYING; + } + } else if (titleRes == 0) { titleRes = com.android.internal.R.string.usb_charging_notification_title; id = SystemMessage.NOTE_USB_CHARGING; } @@ -1113,7 +1096,6 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver mUsbNotificationId = 0; } if (id != 0) { - CharSequence message; CharSequence title = r.getText(titleRes); PendingIntent pi; String channel; @@ -1123,12 +1105,10 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver .usb_unsupported_audio_accessory_title) { Intent intent = Intent.makeRestartActivityTask( new ComponentName("com.android.settings", - "com.android.settings.deviceinfo.UsbModeChooserActivity")); + "com.android.settings.Settings$UsbDetailsActivity")); pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0, null, UserHandle.CURRENT); channel = SystemNotificationChannels.USB; - message = r.getText( - com.android.internal.R.string.usb_notification_message); } else { final Intent intent = new Intent(); intent.setClassName("com.android.settings", @@ -1184,7 +1164,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver final int titleRes = com.android.internal.R.string.adb_active_notification_title; if (mAdbEnabled && mConnected) { - if ("0".equals(SystemProperties.get("persist.adb.notify"))) return; + if ("0".equals(getSystemProperty("persist.adb.notify", ""))) return; if (force && mAdbNotificationShown) { mAdbNotificationShown = false; @@ -1230,20 +1210,41 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } } - protected String getChargingFunctions() { + private boolean isTv() { + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); + } + + protected long getChargingFunctions() { // if ADB is enabled, reset functions to ADB // else enable MTP as usual. if (mAdbEnabled) { - return UsbManager.USB_FUNCTION_ADB; + return UsbManager.FUNCTION_ADB; } else { - return UsbManager.USB_FUNCTION_MTP; + return UsbManager.FUNCTION_MTP; } } - public boolean isFunctionEnabled(String function) { - return UsbManager.containsFunction(mCurrentFunctions, function); + protected void setSystemProperty(String prop, String val) { + SystemProperties.set(prop, val); + } + + protected String getSystemProperty(String prop, String def) { + return SystemProperties.get(prop, def); + } + + protected void putGlobalSettings(ContentResolver contentResolver, String setting, int val) { + Settings.Global.putInt(contentResolver, setting, val); + } + + public long getEnabledFunctions() { + return mCurrentFunctions; } + public long getScreenUnlockedFunctions() { + return mScreenUnlockedFunctions; + } + + public void dump(IndentingPrintWriter pw) { pw.println("USB Device State:"); pw.println(" mCurrentFunctions: " + mCurrentFunctions); @@ -1252,7 +1253,6 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver pw.println(" mScreenLocked: " + mScreenLocked); pw.println(" mConnected: " + mConnected); pw.println(" mConfigured: " + mConfigured); - pw.println(" mUsbDataUnlocked: " + mUsbDataUnlocked); pw.println(" mCurrentAccessory: " + mCurrentAccessory); pw.println(" mHostConnected: " + mHostConnected); pw.println(" mSourcePower: " + mSourcePower); @@ -1275,12 +1275,10 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver /** * Evaluates USB function policies and applies the change accordingly. */ - protected abstract void setEnabledFunctions(String functions, boolean forceRestart, - boolean usbDataUnlocked); - + protected abstract void setEnabledFunctions(long functions, boolean forceRestart); } - private final class UsbHandlerLegacy extends UsbHandler { + private static final class UsbHandlerLegacy extends UsbHandler { /** * The non-persistent property which stores the current USB settings. */ @@ -1293,46 +1291,44 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver private HashMap<String, HashMap<String, Pair<String, String>>> mOemModeMap; private String mCurrentOemFunctions; + private String mCurrentFunctionsStr; + private boolean mUsbDataUnlocked; - UsbHandlerLegacy(Looper looper, Context context) { - super(looper); + UsbHandlerLegacy(Looper looper, Context context, UsbDeviceManager deviceManager, + UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager, + UsbSettingsManager settingsManager) { + super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager); try { readOemUsbOverrideConfig(context); // Restore default functions. - mCurrentOemFunctions = SystemProperties.get(getPersistProp(false), + mCurrentOemFunctions = getSystemProperty(getPersistProp(false), UsbManager.USB_FUNCTION_NONE); if (isNormalBoot()) { - mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY, + mCurrentFunctionsStr = getSystemProperty(USB_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE); - mCurrentFunctionsApplied = mCurrentFunctions.equals( - SystemProperties.get(USB_STATE_PROPERTY)); + mCurrentFunctionsApplied = mCurrentFunctionsStr.equals( + getSystemProperty(USB_STATE_PROPERTY, UsbManager.USB_FUNCTION_NONE)); } else { - mCurrentFunctions = SystemProperties.get(getPersistProp(true), + mCurrentFunctionsStr = getSystemProperty(getPersistProp(true), UsbManager.USB_FUNCTION_NONE); - mCurrentFunctionsApplied = SystemProperties.get(USB_CONFIG_PROPERTY, + mCurrentFunctionsApplied = getSystemProperty(USB_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE).equals( - SystemProperties.get(USB_STATE_PROPERTY)); + getSystemProperty(USB_STATE_PROPERTY, UsbManager.USB_FUNCTION_NONE)); } + // Mask out adb, since it is stored in mAdbEnabled + mCurrentFunctions = UsbManager.usbFunctionsFromString(mCurrentFunctionsStr) + & ~UsbManager.FUNCTION_ADB; mCurrentUsbFunctionsReceived = true; String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim(); updateState(state); - - // register observer to listen for settings changes - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ADB_ENABLED), - false, new AdbSettingsObserver()); - - // Watch for USB configuration changes - mUEventObserver.startObserving(USB_STATE_MATCH); - mUEventObserver.startObserving(ACCESSORY_START_MATCH); } catch (Exception e) { Slog.e(TAG, "Error initializing UsbHandler", e); } } private void readOemUsbOverrideConfig(Context context) { - String[] configList = mContext.getResources().getStringArray( + String[] configList = context.getResources().getStringArray( com.android.internal.R.array.config_oemUsbModeOverride); if (configList != null) { @@ -1367,7 +1363,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver return usbFunctions; } - String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown"); + String bootMode = getSystemProperty(BOOT_MODE_PROPERTY, "unknown"); Slog.d(TAG, "applyOemOverride usbfunctions=" + usbFunctions + " bootmode=" + bootMode); Map<String, Pair<String, String>> overridesMap = @@ -1386,25 +1382,22 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver if (!overrideFunctions.second.equals("")) { String newFunction; if (mAdbEnabled) { - newFunction = UsbManager.addFunction(overrideFunctions.second, + newFunction = addFunction(overrideFunctions.second, UsbManager.USB_FUNCTION_ADB); } else { newFunction = overrideFunctions.second; } Slog.d(TAG, "OEM USB override persisting: " + newFunction + "in prop: " + getPersistProp(false)); - SystemProperties.set(getPersistProp(false), - newFunction); + setSystemProperty(getPersistProp(false), newFunction); } return overrideFunctions.first; } else if (mAdbEnabled) { - String newFunction = UsbManager.addFunction(UsbManager.USB_FUNCTION_NONE, + String newFunction = addFunction(UsbManager.USB_FUNCTION_NONE, UsbManager.USB_FUNCTION_ADB); - SystemProperties.set(getPersistProp(false), - newFunction); + setSystemProperty(getPersistProp(false), newFunction); } else { - SystemProperties.set(getPersistProp(false), - UsbManager.USB_FUNCTION_NONE); + setSystemProperty(getPersistProp(false), UsbManager.USB_FUNCTION_NONE); } } // return passed in functions as is. @@ -1417,7 +1410,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver String value = null; for (int i = 0; i < 20; i++) { // State transition is done when sys.usb.state is set to the new configuration - value = SystemProperties.get(USB_STATE_PROPERTY); + value = getSystemProperty(USB_STATE_PROPERTY, ""); if (state.equals(value)) return true; SystemClock.sleep(50); } @@ -1432,14 +1425,14 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver * we always set it due to b/23631400, where adbd was getting killed * and not restarted due to property timeouts on some devices */ - SystemProperties.set(USB_CONFIG_PROPERTY, config); + setSystemProperty(USB_CONFIG_PROPERTY, config); } @Override - protected void setEnabledFunctions(String functions, boolean forceRestart, - boolean usbDataUnlocked) { + protected void setEnabledFunctions(long usbFunctions, boolean forceRestart) { + boolean usbDataUnlocked = isUsbDataTransferActive(usbFunctions); if (DEBUG) { - Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", " + Slog.d(TAG, "setEnabledFunctions functions=" + usbFunctions + ", " + "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked); } @@ -1452,9 +1445,9 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver /** * Try to set the enabled functions. */ - final String oldFunctions = mCurrentFunctions; + final long oldFunctions = mCurrentFunctions; final boolean oldFunctionsApplied = mCurrentFunctionsApplied; - if (trySetEnabledFunctions(functions, forceRestart)) { + if (trySetEnabledFunctions(usbFunctions, forceRestart)) { return; } @@ -1464,7 +1457,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver * user restrictions independently of any other new functions we were * trying to activate. */ - if (oldFunctionsApplied && !oldFunctions.equals(functions)) { + if (oldFunctionsApplied && oldFunctions != usbFunctions) { Slog.e(TAG, "Failsafe 1: Restoring previous USB functions."); if (trySetEnabledFunctions(oldFunctions, false)) { return; @@ -1475,7 +1468,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver * Still didn't work. Try to restore the default functions. */ Slog.e(TAG, "Failsafe 2: Restoring default USB functions."); - if (trySetEnabledFunctions(null, false)) { + if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) { return; } @@ -1484,7 +1477,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver * Try to get ADB working if enabled. */ Slog.e(TAG, "Failsafe 3: Restoring empty function list (with ADB if enabled)."); - if (trySetEnabledFunctions(UsbManager.USB_FUNCTION_NONE, false)) { + if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) { return; } @@ -1495,30 +1488,49 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } private boolean isNormalBoot() { - String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown"); + String bootMode = getSystemProperty(BOOT_MODE_PROPERTY, "unknown"); return bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown"); } - private boolean trySetEnabledFunctions(String functions, boolean forceRestart) { + protected String applyAdbFunction(String functions) { + // Do not pass null pointer to the UsbManager. + // There isn't a check there. + if (functions == null) { + functions = ""; + } + if (mAdbEnabled) { + functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB); + } else { + functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB); + } + return functions; + } + + private boolean trySetEnabledFunctions(long usbFunctions, boolean forceRestart) { + String functions = null; + if (usbFunctions != UsbManager.FUNCTION_NONE) { + functions = UsbManager.usbFunctionsToString(usbFunctions); + } + mCurrentFunctions = usbFunctions; if (functions == null || applyAdbFunction(functions) .equals(UsbManager.USB_FUNCTION_NONE)) { - functions = getChargingFunctions(); + functions = UsbManager.usbFunctionsToString(getChargingFunctions()); } functions = applyAdbFunction(functions); String oemFunctions = applyOemOverrideFunction(functions); - if (!isNormalBoot() && !mCurrentFunctions.equals(functions)) { - SystemProperties.set(getPersistProp(true), functions); + if (!isNormalBoot() && !mCurrentFunctionsStr.equals(functions)) { + setSystemProperty(getPersistProp(true), functions); } if ((!functions.equals(oemFunctions) && !mCurrentOemFunctions.equals(oemFunctions)) - || !mCurrentFunctions.equals(functions) + || !mCurrentFunctionsStr.equals(functions) || !mCurrentFunctionsApplied || forceRestart) { Slog.i(TAG, "Setting USB config to " + functions); - mCurrentFunctions = functions; + mCurrentFunctionsStr = functions; mCurrentOemFunctions = oemFunctions; mCurrentFunctionsApplied = false; @@ -1538,12 +1550,12 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver setUsbConfig(oemFunctions); if (mBootCompleted - && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP) - || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) { + && (containsFunction(functions, UsbManager.USB_FUNCTION_MTP) + || containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) { /** * Start up dependent services. */ - updateUsbStateBroadcastIfNeeded(true); + updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), true); } if (!waitForState(oemFunctions)) { @@ -1557,7 +1569,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } private String getPersistProp(boolean functions) { - String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown"); + String bootMode = getSystemProperty(BOOT_MODE_PROPERTY, "unknown"); String persistProp = USB_PERSISTENT_CONFIG_PROPERTY; if (!(bootMode.equals(NORMAL_BOOT) || bootMode.equals("unknown"))) { if (functions) { @@ -1568,9 +1580,54 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } return persistProp; } + + private static String addFunction(String functions, String function) { + if (UsbManager.USB_FUNCTION_NONE.equals(functions)) { + return function; + } + if (!containsFunction(functions, function)) { + if (functions.length() > 0) { + functions += ","; + } + functions += function; + } + return functions; + } + + private static String removeFunction(String functions, String function) { + String[] split = functions.split(","); + for (int i = 0; i < split.length; i++) { + if (function.equals(split[i])) { + split[i] = null; + } + } + if (split.length == 1 && split[0] == null) { + return UsbManager.USB_FUNCTION_NONE; + } + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < split.length; i++) { + String s = split[i]; + if (s != null) { + if (builder.length() > 0) { + builder.append(","); + } + builder.append(s); + } + } + return builder.toString(); + } + + static boolean containsFunction(String functions, String function) { + int index = functions.indexOf(function); + if (index < 0) return false; + if (index > 0 && functions.charAt(index - 1) != ',') return false; + int charAfter = index + function.length(); + if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false; + return true; + } } - private final class UsbHandlerHal extends UsbHandler { + private static final class UsbHandlerHal extends UsbHandler { /** * Proxy object for the usb gadget hal daemon. @@ -1627,9 +1684,12 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver */ protected static final String ADBD = "adbd"; + protected boolean mCurrentUsbFunctionsRequested; - UsbHandlerHal(Looper looper) { - super(looper); + UsbHandlerHal(Looper looper, Context context, UsbDeviceManager deviceManager, + UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager, + UsbSettingsManager settingsManager) { + super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager); try { ServiceNotification serviceNotification = new ServiceNotification(); @@ -1645,25 +1705,12 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver mGadgetProxy = IUsbGadget.getService(true); mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(), USB_GADGET_HAL_DEATH_COOKIE); - mCurrentFunctions = UsbManager.USB_FUNCTION_NONE; + mCurrentFunctions = UsbManager.FUNCTION_NONE; mGadgetProxy.getCurrentUsbFunctions(new UsbGadgetCallback()); mCurrentUsbFunctionsRequested = true; } String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim(); updateState(state); - - /** - * Register observer to listen for settings changes. - */ - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ADB_ENABLED), - false, new AdbSettingsObserver()); - - /** - * Watch for USB configuration changes. - */ - mUEventObserver.startObserving(USB_STATE_MATCH); - mUEventObserver.startObserving(ACCESSORY_START_MATCH); } catch (NoSuchElementException e) { Slog.e(TAG, "Usb gadget hal not found", e); } catch (RemoteException e) { @@ -1696,7 +1743,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(), USB_GADGET_HAL_DEATH_COOKIE); if (!mCurrentFunctionsApplied) { - setCurrentFunctions(mCurrentFunctions, mUsbDataUnlocked); + setEnabledFunctions(mCurrentFunctions, false); } } catch (NoSuchElementException e) { Slog.e(TAG, "Usb gadget hal not found", e); @@ -1711,12 +1758,12 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver public void handleMessage(Message msg) { switch (msg.what) { case MSG_SET_CHARGING_FUNCTIONS: - setEnabledFunctions(null, false, mUsbDataUnlocked); + setEnabledFunctions(UsbManager.FUNCTION_NONE, false); break; case MSG_SET_FUNCTIONS_TIMEOUT: Slog.e(TAG, "Set functions timed out! no reply from usb hal"); if (msg.arg1 != 1) { - setEnabledFunctions(null, false, mUsbDataUnlocked); + setEnabledFunctions(UsbManager.FUNCTION_NONE, false); } break; case MSG_GET_CURRENT_USB_FUNCTIONS: @@ -1725,7 +1772,8 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver if (mCurrentUsbFunctionsRequested) { Slog.e(TAG, "updating mCurrentFunctions"); - mCurrentFunctions = functionListToString((Long) msg.obj); + // Mask out adb, since it is stored in mAdbEnabled + mCurrentFunctions = ((Long) msg.obj) & ~UsbManager.FUNCTION_ADB; Slog.e(TAG, "mCurrentFunctions:" + mCurrentFunctions + "applied:" + msg.arg1); mCurrentFunctionsApplied = msg.arg1 == 1; @@ -1737,7 +1785,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver * Dont force to default when the configuration is already set to default. */ if (msg.arg1 != 1) { - setEnabledFunctions(null, !mAdbEnabled, false); + setEnabledFunctions(UsbManager.FUNCTION_NONE, !mAdbEnabled); } break; default: @@ -1789,70 +1837,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } } - private long stringToFunctionList(String config) { - long functionsMask = 0; - String[] functions = config.split(","); - for (int i = 0; i < functions.length; i++) { - switch (functions[i]) { - case "none": - functionsMask |= GadgetFunction.NONE; - break; - case "adb": - functionsMask |= GadgetFunction.ADB; - break; - case "mtp": - functionsMask |= GadgetFunction.MTP; - break; - case "ptp": - functionsMask |= GadgetFunction.PTP; - break; - case "midi": - functionsMask |= GadgetFunction.MIDI; - break; - case "accessory": - functionsMask |= GadgetFunction.ACCESSORY; - break; - case "rndis": - functionsMask |= GadgetFunction.RNDIS; - break; - } - } - return functionsMask; - } - - private String functionListToString(Long functionList) { - StringJoiner functions = new StringJoiner(","); - if (functionList == GadgetFunction.NONE) { - functions.add("none"); - return functions.toString(); - } - if ((functionList & GadgetFunction.ADB) != 0) { - functions.add("adb"); - } - if ((functionList & GadgetFunction.MTP) != 0) { - functions.add("mtp"); - } - if ((functionList & GadgetFunction.PTP) != 0) { - functions.add("ptp"); - } - if ((functionList & GadgetFunction.MIDI) != 0) { - functions.add("midi"); - } - if ((functionList & GadgetFunction.ACCESSORY) != 0) { - functions.add("accessory"); - } - if ((functionList & GadgetFunction.RNDIS) != 0) { - functions.add("rndis"); - } - /** - * Remove the trailing comma. - */ - return functions.toString(); - } - - - private void setUsbConfig(String config, boolean chargingFunctions) { - Long functions = stringToFunctionList(config); + private void setUsbConfig(long config, boolean chargingFunctions) { if (true) Slog.d(TAG, "setUsbConfig(" + config + ") request:" + ++mCurrentRequest); /** * Cancel any ongoing requests, if present. @@ -1867,20 +1852,20 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver return; } try { - if ((functions & GadgetFunction.ADB) != 0) { + if ((config & UsbManager.FUNCTION_ADB) != 0) { /** * Start adbd if ADB function is included in the configuration. */ - SystemProperties.set(CTL_START, ADBD); + setSystemProperty(CTL_START, ADBD); } else { /** * Stop adbd otherwise. */ - SystemProperties.set(CTL_STOP, ADBD); + setSystemProperty(CTL_STOP, ADBD); } UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest, - functions, chargingFunctions); - mGadgetProxy.setCurrentUsbFunctions(functions, usbGadgetCallback, + config, chargingFunctions); + mGadgetProxy.setCurrentUsbFunctions(config, usbGadgetCallback, SET_FUNCTIONS_TIMEOUT_MS - SET_FUNCTIONS_LEEWAY_MS); sendMessageDelayed(MSG_SET_FUNCTIONS_TIMEOUT, chargingFunctions, SET_FUNCTIONS_TIMEOUT_MS); @@ -1894,49 +1879,29 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver } @Override - protected void setEnabledFunctions(String functions, boolean forceRestart, - boolean usbDataUnlocked) { + protected void setEnabledFunctions(long functions, boolean forceRestart) { if (DEBUG) { Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", " - + "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked); + + "forceRestart=" + forceRestart); } - - if (usbDataUnlocked != mUsbDataUnlocked) { - mUsbDataUnlocked = usbDataUnlocked; - updateUsbNotification(false); - forceRestart = true; - } - - trySetEnabledFunctions(functions, forceRestart); - } - - private void trySetEnabledFunctions(String functions, boolean forceRestart) { - boolean chargingFunctions = false; - - if (functions == null || applyAdbFunction(functions) - .equals(UsbManager.USB_FUNCTION_NONE)) { - functions = getChargingFunctions(); - chargingFunctions = true; - } - functions = applyAdbFunction(functions); - - if (!mCurrentFunctions.equals(functions) + if (mCurrentFunctions != functions || !mCurrentFunctionsApplied || forceRestart) { - Slog.i(TAG, "Setting USB config to " + functions); + Slog.i(TAG, "Setting USB config to " + UsbManager.usbFunctionsToString(functions)); mCurrentFunctions = functions; mCurrentFunctionsApplied = false; // set the flag to false as that would be stale value mCurrentUsbFunctionsRequested = false; + boolean chargingFunctions = functions == UsbManager.FUNCTION_NONE; + functions = getAppliedFunctions(functions); + // Set the new USB configuration. - setUsbConfig(mCurrentFunctions, chargingFunctions); + setUsbConfig(functions, chargingFunctions); - if (mBootCompleted - && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP) - || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) { + if (mBootCompleted && isUsbDataTransferActive(functions)) { // Start up dependent services. - updateUsbStateBroadcastIfNeeded(true); + updateUsbStateBroadcastIfNeeded(functions, true); } } } @@ -1968,27 +1933,37 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver return nativeOpenAccessory(); } - /** - * Checks whether the function is present in the USB configuration. - * - * @param function function to be checked. - */ - public boolean isFunctionEnabled(String function) { - return mHandler.isFunctionEnabled(function); + public long getCurrentFunctions() { + return mHandler.getEnabledFunctions(); + } + + public long getScreenUnlockedFunctions() { + return mHandler.getScreenUnlockedFunctions(); } /** * Adds function to the current USB configuration. * - * @param functions name of the USB function, or null to restore the default function. - * @param usbDataUnlocked whether user data is accessible. + * @param functions The functions to set, or empty to set the charging function. */ - public void setCurrentFunctions(String functions, boolean usbDataUnlocked) { + public void setCurrentFunctions(long functions) { if (DEBUG) { - Slog.d(TAG, "setCurrentFunctions(" + functions + ", " - + usbDataUnlocked + ")"); - } - mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked); + Slog.d(TAG, "setCurrentFunctions(" + UsbManager.usbFunctionsToString(functions) + ")"); + } + if (functions == UsbManager.FUNCTION_NONE) { + MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_CHARGING); + } else if (functions == UsbManager.FUNCTION_MTP) { + MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MTP); + } else if (functions == UsbManager.FUNCTION_PTP) { + MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_PTP); + } else if (functions == UsbManager.FUNCTION_MIDI) { + MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MIDI); + } else if (functions == UsbManager.FUNCTION_RNDIS) { + MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_RNDIS); + } else if (functions == UsbManager.FUNCTION_ACCESSORY) { + MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_ACCESSORY); + } + mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions); } /** @@ -1996,9 +1971,10 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver * * @param functions Functions to set. */ - public void setScreenUnlockedFunctions(String functions) { + public void setScreenUnlockedFunctions(long functions) { if (DEBUG) { - Slog.d(TAG, "setScreenUnlockedFunctions(" + functions + ")"); + Slog.d(TAG, "setScreenUnlockedFunctions(" + + UsbManager.usbFunctionsToString(functions) + ")"); } mHandler.sendMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, functions); } diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java index 7a352a4dc69d..58f914773071 100644 --- a/services/usb/java/com/android/server/usb/UsbHostManager.java +++ b/services/usb/java/com/android/server/usb/UsbHostManager.java @@ -44,7 +44,7 @@ import java.util.LinkedList; */ public class UsbHostManager { private static final String TAG = UsbHostManager.class.getSimpleName(); - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private final Context mContext; @@ -230,6 +230,7 @@ public class UsbHostManager { } private boolean isBlackListed(String deviceAddress) { + Slog.i(TAG, "isBlackListed(" + deviceAddress + ")"); int count = mHostBlacklist.length; for (int i = 0; i < count; i++) { if (deviceAddress.startsWith(mHostBlacklist[i])) { @@ -241,6 +242,7 @@ public class UsbHostManager { /* returns true if the USB device should not be accessible by applications */ private boolean isBlackListed(int clazz, int subClass) { + Slog.i(TAG, "isBlackListed(" + clazz + ", " + subClass + ")"); // blacklist hubs if (clazz == UsbConstants.USB_CLASS_HUB) return true; @@ -312,13 +314,7 @@ public class UsbHostManager { usbDeviceConnectionHandler); } - // Headset? - boolean isInputHeadset = parser.isInputHeadset(); - boolean isOutputHeadset = parser.isOutputHeadset(); - Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset - + " , out: " + isOutputHeadset + "]"); - - mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset); + mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser); // Tracking addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT, @@ -343,10 +339,13 @@ public class UsbHostManager { /* Called from JNI in monitorUsbHostBus to report USB device removal */ @SuppressWarnings("unused") private void usbDeviceRemoved(String deviceAddress) { + if (DEBUG) { + Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") - start"); + } synchronized (mLock) { UsbDevice device = mDevices.remove(deviceAddress); if (device != null) { - mUsbAlsaManager.usbDeviceRemoved(device); + mUsbAlsaManager.usbDeviceRemoved(deviceAddress/*device*/); mSettingsManager.usbDeviceRemoved(device); getCurrentUserSettings().usbDeviceRemoved(device); diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 1a20819b9a80..2f6e53143312 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -382,59 +382,44 @@ public class UsbService extends IUsbManager.Stub { } @Override - public boolean isFunctionEnabled(String function) { + public void setCurrentFunctions(long functions) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); - return mDeviceManager != null && mDeviceManager.isFunctionEnabled(function); + Preconditions.checkArgument(UsbManager.areSettableFunctions(functions)); + Preconditions.checkState(mDeviceManager != null); + mDeviceManager.setCurrentFunctions(functions); } @Override - public void setCurrentFunction(String function, boolean usbDataUnlocked) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); - - if (!isSupportedCurrentFunction(function)) { - Slog.w(TAG, "Caller of setCurrentFunction() requested unsupported USB function: " - + function); - function = UsbManager.USB_FUNCTION_NONE; - } + public void setCurrentFunction(String functions, boolean usbDataUnlocked) { + setCurrentFunctions(UsbManager.usbFunctionsFromString(functions)); + } - if (mDeviceManager != null) { - mDeviceManager.setCurrentFunctions(function, usbDataUnlocked); - } else { - throw new IllegalStateException("USB device mode not supported"); - } + @Override + public boolean isFunctionEnabled(String function) { + return (getCurrentFunctions() & UsbManager.usbFunctionsFromString(function)) != 0; } @Override - public void setScreenUnlockedFunctions(String function) { + public long getCurrentFunctions() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); - - if (!isSupportedCurrentFunction(function)) { - Slog.w(TAG, "Caller of setScreenUnlockedFunctions() requested unsupported USB function:" - + function); - function = UsbManager.USB_FUNCTION_NONE; - } - - if (mDeviceManager != null) { - mDeviceManager.setScreenUnlockedFunctions(function); - } else { - throw new IllegalStateException("USB device mode not supported"); - } + Preconditions.checkState(mDeviceManager != null); + return mDeviceManager.getCurrentFunctions(); } - private static boolean isSupportedCurrentFunction(String function) { - if (function == null) return true; + @Override + public void setScreenUnlockedFunctions(long functions) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + Preconditions.checkArgument(UsbManager.areSettableFunctions(functions)); + Preconditions.checkState(mDeviceManager != null); - switch (function) { - case UsbManager.USB_FUNCTION_NONE: - case UsbManager.USB_FUNCTION_AUDIO_SOURCE: - case UsbManager.USB_FUNCTION_MIDI: - case UsbManager.USB_FUNCTION_MTP: - case UsbManager.USB_FUNCTION_PTP: - case UsbManager.USB_FUNCTION_RNDIS: - return true; - } + mDeviceManager.setScreenUnlockedFunctions(functions); + } - return false; + @Override + public long getScreenUnlockedFunctions() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + Preconditions.checkState(mDeviceManager != null); + return mDeviceManager.getScreenUnlockedFunctions(); } @Override diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java index 7a1e9e2f9896..297a6eab4a78 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java @@ -26,7 +26,7 @@ import java.util.ArrayList; */ public final class UsbDescriptorParser { private static final String TAG = "UsbDescriptorParser"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private final String mDeviceAddr; @@ -358,50 +358,93 @@ public final class UsbDescriptorParser { return list; } + /* + * Attribute predicates + */ /** * @hide */ - public boolean hasHIDDescriptor() { - ArrayList<UsbDescriptor> descriptors = - getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_HID); - return !descriptors.isEmpty(); + public boolean hasInput() { + if (DEBUG) { + Log.d(TAG, "---- hasInput()"); + } + ArrayList<UsbDescriptor> acDescriptors = + getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL, + UsbACInterface.AUDIO_AUDIOCONTROL); + boolean hasInput = false; + for (UsbDescriptor descriptor : acDescriptors) { + if (descriptor instanceof UsbACTerminal) { + UsbACTerminal inDescr = (UsbACTerminal) descriptor; + // Check for input and bi-directional terminal types + int type = inDescr.getTerminalType(); + if (DEBUG) { + Log.d(TAG, " type:0x" + Integer.toHexString(type)); + } + if ((type >= UsbTerminalTypes.TERMINAL_IN_UNDEFINED + && type <= UsbTerminalTypes.TERMINAL_IN_PROC_MIC_ARRAY) + || (type >= UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED + && type <= UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL) + || (type == UsbTerminalTypes.TERMINAL_USB_STREAMING)) { + hasInput = true; + break; + } + } else { + Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength() + + " t:0x" + Integer.toHexString(descriptor.getType())); + } + } + + if (DEBUG) { + Log.d(TAG, "hasInput() = " + hasInput); + } + return hasInput; } /** * @hide */ - public boolean hasMIDIInterface() { - ArrayList<UsbDescriptor> descriptors = - getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO); - for (UsbDescriptor descriptor : descriptors) { - // enusure that this isn't an unrecognized interface descriptor - if (descriptor instanceof UsbInterfaceDescriptor) { - UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor; - if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) { - return true; + public boolean hasOutput() { + if (DEBUG) { + Log.d(TAG, "---- hasOutput()"); + } + ArrayList<UsbDescriptor> acDescriptors = + getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL, + UsbACInterface.AUDIO_AUDIOCONTROL); + boolean hasOutput = false; + for (UsbDescriptor descriptor : acDescriptors) { + if (descriptor instanceof UsbACTerminal) { + UsbACTerminal outDescr = (UsbACTerminal) descriptor; + // Check for output and bi-directional terminal types + int type = outDescr.getTerminalType(); + if (DEBUG) { + Log.d(TAG, " type:0x" + Integer.toHexString(type)); + } + if ((type >= UsbTerminalTypes.TERMINAL_OUT_UNDEFINED + && type <= UsbTerminalTypes.TERMINAL_OUT_LFSPEAKER) + || (type >= UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED + && type <= UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL)) { + hasOutput = true; + break; } } else { - Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength() + Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength() + " t:0x" + Integer.toHexString(descriptor.getType())); } } - return false; + if (DEBUG) { + Log.d(TAG, "hasOutput() = " + hasOutput); + } + return hasOutput; } /** * @hide */ - public float getInputHeadsetProbability() { - if (hasMIDIInterface()) { - return 0.0f; - } - - float probability = 0.0f; - ArrayList<UsbDescriptor> acDescriptors; - - // Look for a microphone + public boolean hasMic() { boolean hasMic = false; - acDescriptors = getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL, + + ArrayList<UsbDescriptor> acDescriptors = + getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL, UsbACInterface.AUDIO_AUDIOCONTROL); for (UsbDescriptor descriptor : acDescriptors) { if (descriptor instanceof UsbACTerminal) { @@ -418,18 +461,23 @@ public final class UsbDescriptorParser { + " t:0x" + Integer.toHexString(descriptor.getType())); } } + return hasMic; + } - // Look for a "speaker" + /** + * @hide + */ + public boolean hasSpeaker() { boolean hasSpeaker = false; - acDescriptors = + + ArrayList<UsbDescriptor> acDescriptors = getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL, UsbACInterface.AUDIO_AUDIOCONTROL); for (UsbDescriptor descriptor : acDescriptors) { if (descriptor instanceof UsbACTerminal) { UsbACTerminal outDescr = (UsbACTerminal) descriptor; if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER - || outDescr.getTerminalType() - == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES + || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) { hasSpeaker = true; break; @@ -440,6 +488,55 @@ public final class UsbDescriptorParser { } } + return hasSpeaker; + } + + /** + * @hide + */ + public boolean hasHIDDescriptor() { + ArrayList<UsbDescriptor> descriptors = + getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_HID); + return !descriptors.isEmpty(); + } + + /** + * @hide + */ + public boolean hasMIDIInterface() { + ArrayList<UsbDescriptor> descriptors = + getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO); + for (UsbDescriptor descriptor : descriptors) { + // enusure that this isn't an unrecognized interface descriptor + if (descriptor instanceof UsbInterfaceDescriptor) { + UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor; + if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) { + return true; + } + } else { + Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength() + + " t:0x" + Integer.toHexString(descriptor.getType())); + } + } + return false; + } + + /** + * @hide + */ + public float getInputHeadsetProbability() { + if (hasMIDIInterface()) { + return 0.0f; + } + + float probability = 0.0f; + + // Look for a microphone + boolean hasMic = hasMic(); + + // Look for a "speaker" + boolean hasSpeaker = hasSpeaker(); + if (hasMic && hasSpeaker) { probability += 0.75f; } diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index fcfc5931ac7b..95eb14ada354 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -134,6 +134,25 @@ public final class PhoneAccount implements Parcelable { "android.telecom.extra.LOG_SELF_MANAGED_CALLS"; /** + * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which + * indicates whether calls for a {@link PhoneAccount} should generate a "call recording tone" + * when the user is recording audio on the device. + * <p> + * The call recording tone is played over the telephony audio stream so that the remote party + * has an audible indication that it is possible their call is being recorded using a call + * recording app on the device. + * <p> + * This extra only has an effect for calls placed via Telephony (e.g. + * {@link #CAPABILITY_SIM_SUBSCRIPTION}). + * <p> + * The call recording tone is a 1400 hz tone which repeats every 15 seconds while recording is + * in progress. + * @hide + */ + public static final String EXTRA_PLAY_CALL_RECORDING_TONE = + "android.telecom.extra.PLAY_CALL_RECORDING_TONE"; + + /** * Flag indicating that this {@code PhoneAccount} can act as a connection manager for * other connections. The {@link ConnectionService} associated with this {@code PhoneAccount} * will be allowed to manage phone calls including using its own proprietary phone-call diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index a34e9f9481fa..63ab76677dc7 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -77,6 +77,14 @@ public class CarrierConfigManager { public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool"; + /** + * Boolean indicating if the "Call barring" item is visible in the Call Settings menu. + * true means visible. false means gone. + * @hide + */ + public static final String KEY_CALL_BARRING_VISIBILITY_BOOL = + "call_barring_visibility_bool"; + /** * Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED * events from the Sim. @@ -146,6 +154,15 @@ public class CarrierConfigManager { public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool"; /** + * Determines if the carrier requires that a tone be played to the remote party when an app is + * recording audio during a call (e.g. using a call recording app). + * <p> + * Note: This requires the Telephony config_supports_telephony_audio_device overlay to be true + * in order to work. + * @hide + */ + public static final String KEY_PLAY_CALL_RECORDING_TONE_BOOL = "play_call_recording_tone_bool"; + /** * Determines if the carrier requires converting the destination number before sending out an * SMS. Certain networks and numbering plans require different formats. */ @@ -1381,6 +1398,14 @@ public class CarrierConfigManager { public static final String KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO = "video_calls_can_be_hd_audio"; /** + * When true, indicates that the HD audio icon in the in-call screen should be shown for + * GSM/CDMA calls. + * @hide + */ + public static final String KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO = + "gsm_cdma_calls_can_be_hd_audio"; + + /** * Whether system apps are allowed to use fallback if carrier video call is not available. * Defaults to {@code true}. * @@ -1793,6 +1818,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true); sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false); sDefaults.putBoolean(KEY_ALLOW_LOCAL_DTMF_TONES_BOOL, true); + sDefaults.putBoolean(KEY_PLAY_CALL_RECORDING_TONE_BOOL, false); sDefaults.putBoolean(KEY_APN_EXPAND_BOOL, true); sDefaults.putBoolean(KEY_AUTO_RETRY_ENABLED_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false); @@ -1833,6 +1859,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false); + sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false); sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false); sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false); sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true); @@ -2033,6 +2060,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true); sDefaults.putBoolean(KEY_WIFI_CALLS_CAN_BE_HD_AUDIO, true); sDefaults.putBoolean(KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO, true); + sDefaults.putBoolean(KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO, false); sDefaults.putBoolean(KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL, true); sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null); diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java index dfaaab918012..ece1ee378170 100644 --- a/telephony/java/android/telephony/CellSignalStrengthCdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java @@ -36,48 +36,14 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements private int mEvdoEcio; // This value is the EVDO Ec/Io private int mEvdoSnr; // Valid values are 0-8. 8 is the highest signal to noise ratio - /** - * Empty constructor - * - * @hide - */ + /** @hide */ public CellSignalStrengthCdma() { setDefaultValues(); } - /** - * Constructor - * - * @hide - */ + /** @hide */ public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio, int evdoSnr) { - initialize(cdmaDbm, cdmaEcio, evdoDbm, evdoEcio, evdoSnr); - } - - /** - * Copy constructors - * - * @param s Source SignalStrength - * - * @hide - */ - public CellSignalStrengthCdma(CellSignalStrengthCdma s) { - copyFrom(s); - } - - /** - * Initialize all the values - * - * @param cdmaDbm - * @param cdmaEcio - * @param evdoDbm - * @param evdoEcio - * @param evdoSnr - * - * @hide - */ - public void initialize(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio, int evdoSnr) { mCdmaDbm = cdmaDbm; mCdmaEcio = cdmaEcio; mEvdoDbm = evdoDbm; @@ -85,9 +51,12 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements mEvdoSnr = evdoSnr; } - /** - * @hide - */ + /** @hide */ + public CellSignalStrengthCdma(CellSignalStrengthCdma s) { + copyFrom(s); + } + + /** @hide */ protected void copyFrom(CellSignalStrengthCdma s) { mCdmaDbm = s.mCdmaDbm; mCdmaEcio = s.mCdmaEcio; @@ -96,9 +65,7 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements mEvdoSnr = s.mEvdoSnr; } - /** - * @hide - */ + /** @hide */ @Override public CellSignalStrengthCdma copy() { return new CellSignalStrengthCdma(this); diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index f68d2cad1226..8687cd1c454b 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -34,80 +34,40 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P private static final int GSM_SIGNAL_STRENGTH_GOOD = 8; private static final int GSM_SIGNAL_STRENGTH_MODERATE = 5; - private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5 + private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5 private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 - private int mTimingAdvance; + private int mTimingAdvance; // range from 0-219 or Integer.MAX_VALUE if unknown - /** - * Empty constructor - * - * @hide - */ + /** @hide */ public CellSignalStrengthGsm() { setDefaultValues(); } - /** - * Constructor - * - * @hide - */ + /** @hide */ public CellSignalStrengthGsm(int ss, int ber) { - initialize(ss, ber); - } - - /** - * Copy constructors - * - * @param s Source SignalStrength - * - * @hide - */ - public CellSignalStrengthGsm(CellSignalStrengthGsm s) { - copyFrom(s); + this(ss, ber, Integer.MAX_VALUE); } - /** - * Initialize all the values - * - * @param ss SignalStrength as ASU value - * @param ber is Bit Error Rate - * - * @hide - */ - public void initialize(int ss, int ber) { + /** @hide */ + public CellSignalStrengthGsm(int ss, int ber, int ta) { mSignalStrength = ss; mBitErrorRate = ber; - mTimingAdvance = Integer.MAX_VALUE; + mTimingAdvance = ta; } - /** - * Initialize all the values - * - * @param ss SignalStrength as ASU value - * @param ber is Bit Error Rate - * @param ta timing advance - * - * @hide - */ - public void initialize(int ss, int ber, int ta) { - mSignalStrength = ss; - mBitErrorRate = ber; - mTimingAdvance = ta; + /** @hide */ + public CellSignalStrengthGsm(CellSignalStrengthGsm s) { + copyFrom(s); } - /** - * @hide - */ + /** @hide */ protected void copyFrom(CellSignalStrengthGsm s) { mSignalStrength = s.mSignalStrength; mBitErrorRate = s.mBitErrorRate; mTimingAdvance = s.mTimingAdvance; } - /** - * @hide - */ + /** @hide */ @Override public CellSignalStrengthGsm copy() { return new CellSignalStrengthGsm(this); diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java index 6ffc8b6e497c..f009fb145fc2 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -37,50 +37,15 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P private int mCqi; private int mTimingAdvance; - /** - * Empty constructor - * - * @hide - */ + /** @hide */ public CellSignalStrengthLte() { setDefaultValues(); } - /** - * Constructor - * - * @hide - */ + /** @hide */ public CellSignalStrengthLte(int signalStrength, int rsrp, int rsrq, int rssnr, int cqi, int timingAdvance) { - initialize(signalStrength, rsrp, rsrq, rssnr, cqi, timingAdvance); - } - - /** - * Copy constructors - * - * @param s Source SignalStrength - * - * @hide - */ - public CellSignalStrengthLte(CellSignalStrengthLte s) { - copyFrom(s); - } - - /** - * Initialize all the values - * - * @param lteSignalStrength - * @param rsrp - * @param rsrq - * @param rssnr - * @param cqi - * - * @hide - */ - public void initialize(int lteSignalStrength, int rsrp, int rsrq, int rssnr, int cqi, - int timingAdvance) { - mSignalStrength = lteSignalStrength; + mSignalStrength = signalStrength; mRsrp = rsrp; mRsrq = rsrq; mRssnr = rssnr; @@ -88,25 +53,12 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mTimingAdvance = timingAdvance; } - /** - * Initialize from the SignalStrength structure. - * - * @param ss - * - * @hide - */ - public void initialize(SignalStrength ss, int timingAdvance) { - mSignalStrength = ss.getLteSignalStrength(); - mRsrp = ss.getLteRsrp(); - mRsrq = ss.getLteRsrq(); - mRssnr = ss.getLteRssnr(); - mCqi = ss.getLteCqi(); - mTimingAdvance = timingAdvance; + /** @hide */ + public CellSignalStrengthLte(CellSignalStrengthLte s) { + copyFrom(s); } - /** - * @hide - */ + /** @hide */ protected void copyFrom(CellSignalStrengthLte s) { mSignalStrength = s.mSignalStrength; mRsrp = s.mRsrp; @@ -116,9 +68,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mTimingAdvance = s.mTimingAdvance; } - /** - * @hide - */ + /** @hide */ @Override public CellSignalStrengthLte copy() { return new CellSignalStrengthLte(this); diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java index 2cd56b8500a6..dd32a960db91 100644 --- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java @@ -34,62 +34,32 @@ public final class CellSignalStrengthWcdma extends CellSignalStrength implements private static final int WCDMA_SIGNAL_STRENGTH_GOOD = 8; private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5; - private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5 - private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 + private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5 + private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 - /** - * Empty constructor - * - * @hide - */ + /** @hide */ public CellSignalStrengthWcdma() { setDefaultValues(); } - /** - * Constructor - * - * @hide - */ + /** @hide */ public CellSignalStrengthWcdma(int ss, int ber) { - initialize(ss, ber); + mSignalStrength = ss; + mBitErrorRate = ber; } - /** - * Copy constructors - * - * @param s Source SignalStrength - * - * @hide - */ + /** @hide */ public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) { copyFrom(s); } - /** - * Initialize all the values - * - * @param ss SignalStrength as ASU value - * @param ber is Bit Error Rate - * - * @hide - */ - public void initialize(int ss, int ber) { - mSignalStrength = ss; - mBitErrorRate = ber; - } - - /** - * @hide - */ + /** @hide */ protected void copyFrom(CellSignalStrengthWcdma s) { mSignalStrength = s.mSignalStrength; mBitErrorRate = s.mBitErrorRate; } - /** - * @hide - */ + /** @hide */ @Override public CellSignalStrengthWcdma copy() { return new CellSignalStrengthWcdma(this); diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java new file mode 100644 index 000000000000..b362df9ff677 --- /dev/null +++ b/telephony/java/android/telephony/LocationAccessPolicy.java @@ -0,0 +1,160 @@ +/* + * 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 android.telephony; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.app.AppOpsManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.location.LocationManager; +import android.os.Binder; +import android.os.Build; +import android.os.Process; +import android.os.Trace; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.util.SparseBooleanArray; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Helper for performing location access checks. + * @hide + */ +public final class LocationAccessPolicy { + /** + * API to determine if the caller has permissions to get cell location. + * + * @param pkgName Package name of the application requesting access + * @param uid The uid of the package + * @param pid The pid of the package + * @return boolean true or false if permissions is granted + */ + public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName, + int uid, int pid) throws SecurityException { + Trace.beginSection("TelephonyLocationCheck"); + try { + // Always allow the phone process to access location. This avoid breaking legacy code + // that rely on public-facing APIs to access cell location, and it doesn't create a + // info leak risk because the cell location is stored in the phone process anyway. + if (uid == Process.PHONE_UID) { + return true; + } + + // We always require the location permission and also require the + // location mode to be on for non-legacy apps. Legacy apps are + // required to be in the foreground to at least mitigate the case + // where a legacy app the user is not using tracks their location. + // Granting ACCESS_FINE_LOCATION to an app automatically grants it + // ACCESS_COARSE_LOCATION. + + if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid) == + PackageManager.PERMISSION_DENIED) { + return false; + } + final int opCode = AppOpsManager.permissionToOpCode( + Manifest.permission.ACCESS_COARSE_LOCATION); + if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class) + .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) { + return false; + } + if (!isLocationModeEnabled(context, UserHandle.getUserId(uid)) + && !isLegacyForeground(context, pkgName, uid)) { + return false; + } + // If the user or profile is current, permission is granted. + // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. + return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context); + } finally { + Trace.endSection(); + } + } + + private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) { + int locationMode = Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId); + return locationMode != Settings.Secure.LOCATION_MODE_OFF + && locationMode != Settings.Secure.LOCATION_MODE_SENSORS_ONLY; + } + + private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName, + int uid) { + long token = Binder.clearCallingIdentity(); + try { + return isLegacyVersion(context, pkgName) && isForegroundApp(context, uid); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private static boolean isLegacyVersion(@NonNull Context context, @NonNull String pkgName) { + try { + if (context.getPackageManager().getApplicationInfo(pkgName, 0) + .targetSdkVersion <= Build.VERSION_CODES.O) { + return true; + } + } catch (PackageManager.NameNotFoundException e) { + // In case of exception, assume known app (more strict checking) + // Note: This case will never happen since checkPackage is + // called to verify validity before checking app's version. + } + return false; + } + + private static boolean isForegroundApp(@NonNull Context context, int uid) { + final ActivityManager am = context.getSystemService(ActivityManager.class); + return am.getUidImportance(uid) <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; + } + + private static boolean checkInteractAcrossUsersFull(@NonNull Context context) { + return context.checkCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + == PackageManager.PERMISSION_GRANTED; + } + + private static boolean isCurrentProfile(@NonNull Context context, int uid) { + long token = Binder.clearCallingIdentity(); + try { + final int currentUser = ActivityManager.getCurrentUser(); + final int callingUserId = UserHandle.getUserId(uid); + if (callingUserId == currentUser) { + return true; + } else { + List<UserInfo> userProfiles = context.getSystemService( + UserManager.class).getProfiles(currentUser); + for (UserInfo user : userProfiles) { + if (user.id == callingUserId) { + return true; + } + } + } + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } +} diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 90a3677d1796..1176491907ce 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -22,13 +22,13 @@ import android.annotation.SystemApi; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.text.TextUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; - import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -1340,6 +1340,39 @@ public class ServiceState implements Parcelable { } /** @hide */ + public static int rilRadioTechnologyToAccessNetworkType(@RilRadioTechnology int rt) { + switch(rt) { + case RIL_RADIO_TECHNOLOGY_GPRS: + case RIL_RADIO_TECHNOLOGY_EDGE: + case RIL_RADIO_TECHNOLOGY_GSM: + return AccessNetworkType.GERAN; + case RIL_RADIO_TECHNOLOGY_UMTS: + case RIL_RADIO_TECHNOLOGY_HSDPA: + case RIL_RADIO_TECHNOLOGY_HSPAP: + case RIL_RADIO_TECHNOLOGY_HSUPA: + case RIL_RADIO_TECHNOLOGY_HSPA: + case RIL_RADIO_TECHNOLOGY_TD_SCDMA: + return AccessNetworkType.UTRAN; + case RIL_RADIO_TECHNOLOGY_IS95A: + case RIL_RADIO_TECHNOLOGY_IS95B: + case RIL_RADIO_TECHNOLOGY_1xRTT: + case RIL_RADIO_TECHNOLOGY_EVDO_0: + case RIL_RADIO_TECHNOLOGY_EVDO_A: + case RIL_RADIO_TECHNOLOGY_EVDO_B: + case RIL_RADIO_TECHNOLOGY_EHRPD: + return AccessNetworkType.CDMA2000; + case RIL_RADIO_TECHNOLOGY_LTE: + case RIL_RADIO_TECHNOLOGY_LTE_CA: + return AccessNetworkType.EUTRAN; + case RIL_RADIO_TECHNOLOGY_IWLAN: + return AccessNetworkType.IWLAN; + case RIL_RADIO_TECHNOLOGY_UNKNOWN: + default: + return AccessNetworkType.UNKNOWN; + } + } + + /** @hide */ public int getDataNetworkType() { return rilRadioTechnologyToNetworkType(mRilDataRadioTechnology); } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index debf43da79b4..34f2dac028c7 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -612,9 +612,9 @@ public class SubscriptionManager { * onSubscriptionsChanged overridden. */ public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { - String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>"; + String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>"; if (DBG) { - logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug + logd("register OnSubscriptionsChangedListener pkgName=" + pkgName + " listener=" + listener); } try { @@ -623,7 +623,7 @@ public class SubscriptionManager { ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( "telephony.registry")); if (tr != null) { - tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback); + tr.addOnSubscriptionsChangedListener(pkgName, listener.callback); } } catch (RemoteException ex) { // Should not happen diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index cdee9e6f2d73..a3a30807986e 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -220,11 +220,6 @@ public interface RILConstants { String SETUP_DATA_PROTOCOL_IPV6 = "IPV6"; String SETUP_DATA_PROTOCOL_IPV4V6 = "IPV4V6"; - /* Deactivate data call reasons */ - int DEACTIVATE_REASON_NONE = 0; - int DEACTIVATE_REASON_RADIO_OFF = 1; - int DEACTIVATE_REASON_PDP_RESET = 2; - /* NV config radio reset types. */ int NV_CONFIG_RELOAD_RESET = 1; int NV_CONFIG_ERASE_RESET = 2; diff --git a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml index 71451101ac59..021e3861d882 100644 --- a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml +++ b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml @@ -14,12 +14,16 @@ limitations under the License. --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.frameworks.perftests.amteststestapp"> + package="com.android.frameworks.perftests.amteststestapp"> <application android:name=".TestApplication"> <activity android:name=".TestActivity" android:exported="true"/> + <provider + android:authorities="com.android.frameworks.perftests.amteststestapp" + android:name=".TestContentProvider" + android:exported="true" /> <receiver - android:name=".TestBroadcastReceiver" - android:exported="true"> + android:name=".TestBroadcastReceiver" + android:exported="true"> <intent-filter> <action android:name="com.android.frameworks.perftests.ACTION_BROADCAST_MANIFEST_RECEIVE" /> <category android:name="android.intent.category.DEFAULT" /> diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java new file mode 100644 index 000000000000..0940578e4b28 --- /dev/null +++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestContentProvider.java @@ -0,0 +1,55 @@ +/* + * 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.frameworks.perftests.amteststestapp; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class TestContentProvider extends ContentProvider { + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public boolean onCreate() { + return false; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java new file mode 100644 index 000000000000..3bf56ce8b085 --- /dev/null +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ContentProviderPerfTest.java @@ -0,0 +1,70 @@ +/* + * 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.frameworks.perftests.am.tests; + +import android.content.ContentProviderClient; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.frameworks.perftests.am.util.TargetPackageUtils; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ContentProviderPerfTest extends BasePerfTest { + /** + * Benchmark time to call ContentResolver.acquireContentProviderClient() when target package is + * running. + */ + @Test + public void contentProviderRunning() { + runPerfFunction(() -> { + startTargetPackage(); + + long startTimeNs = System.nanoTime(); + final ContentProviderClient contentProviderClient = + mContext.getContentResolver() + .acquireContentProviderClient(TargetPackageUtils.PACKAGE_NAME); + final long endTimeNs = System.nanoTime(); + + contentProviderClient.close(); + + return endTimeNs - startTimeNs; + }); + } + + /** + * Benchmark time to call ContentResolver.acquireContentProviderClient() when target package is + * not running. + */ + @Test + public void contentProviderNotRunning() { + runPerfFunction(() -> { + final long startTimeNs = System.nanoTime(); + final ContentProviderClient contentProviderClient = + mContext.getContentResolver().acquireContentProviderClient( + TargetPackageUtils.PACKAGE_NAME); + final long endTimeNs = System.nanoTime(); + + contentProviderClient.close(); + + return endTimeNs - startTimeNs; + }); + } +} diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 1443fc1ae3ee..063060f166dc 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -83,7 +83,7 @@ public class AppLaunch extends InstrumentationTestCase { private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval"; private static final String WEARABLE_ACTION_GOOGLE = "com.google.android.wearable.action.GOOGLE"; - private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 60000; //60s to allow app to idle + private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; //5s to allow app to idle private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; //5s between launching apps private static final String LAUNCH_SUB_DIRECTORY = "launch_logs"; diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index ebf5f6854c6f..c8f96c9f0670 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -285,7 +285,8 @@ <activity android:name="ColoredShadowsActivity" - android:label="View/ColoredShadows"> + android:label="View/ColoredShadows" + android:theme="@style/ThemeColoredShadows"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="com.android.test.hwui.TEST" /> diff --git a/tests/HwAccelerationTest/res/values/styles.xml b/tests/HwAccelerationTest/res/values/styles.xml index 108709bd76b2..fa5437f38ace 100644 --- a/tests/HwAccelerationTest/res/values/styles.xml +++ b/tests/HwAccelerationTest/res/values/styles.xml @@ -34,4 +34,11 @@ <item name="android:translationZ">400dp</item> <item name="android:layout_alignParentBottom">true</item> </style> + + <style name="ThemeColoredShadows" parent="@android:style/Theme.Material.Light"> + <!-- + <item name="android:ambientShadowAlpha">0</item> + <item name="android:spotShadowAlpha">1</item> + --> + </style> </resources> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java index 135c93c97af2..901d90eed70a 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java @@ -17,6 +17,9 @@ package com.android.test.hwui; import android.app.Activity; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; @@ -36,7 +39,9 @@ public class ColoredShadowsActivity extends Activity { private void setShadowColors(ViewGroup row, int rowIndex) { for (int i = 0; i < row.getChildCount(); i++) { View view = row.getChildAt(i); - view.setShadowColor(shadowColorFor(view)); + //view.setBackground(new MyHackyBackground()); + view.setOutlineSpotShadowColor(shadowColorFor(view)); + view.setOutlineAmbientShadowColor(shadowColorFor(view)); view.setElevation(6.0f * (rowIndex + 1)); } } @@ -44,12 +49,27 @@ public class ColoredShadowsActivity extends Activity { private int shadowColorFor(View view) { switch (view.getId()) { case R.id.grey: return 0xFF3C4043; - case R.id.blue: return 0xFF185ABC; - case R.id.red: return 0xFFB31412; - case R.id.yellow: return 0xFFEA8600; - case R.id.green: return 0xFF137333; + case R.id.blue: return Color.BLUE; + case R.id.red: return 0xFFEA4335; + case R.id.yellow: return 0xFFFBBC04; + case R.id.green: return 0xFF34A853; default: return 0xFF000000; } } + private static class MyHackyBackground extends ColorDrawable { + MyHackyBackground() { + super(0); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public int getAlpha() { + return 254; + } + } } diff --git a/tests/UsbTests/Android.mk b/tests/UsbTests/Android.mk new file mode 100644 index 000000000000..a04f32a6d714 --- /dev/null +++ b/tests/UsbTests/Android.mk @@ -0,0 +1,44 @@ +# +# 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. +# + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + frameworks-base-testutils \ + android-support-test \ + mockito-target-inline-minus-junit4 \ + platform-test-annotations \ + services.core \ + services.net \ + services.usb \ + truth-prebuilt \ + +LOCAL_JNI_SHARED_LIBRARIES := \ + libdexmakerjvmtiagent \ + +LOCAL_CERTIFICATE := platform + +LOCAL_PACKAGE_NAME := UsbTests + +LOCAL_COMPATIBILITY_SUITE := device-tests + +include $(BUILD_PACKAGE) diff --git a/tests/UsbTests/AndroidManifest.xml b/tests/UsbTests/AndroidManifest.xml new file mode 100644 index 000000000000..5d606951bb5e --- /dev/null +++ b/tests/UsbTests/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.usb" > + + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> + <uses-permission android:name="android.permission.MANAGE_USERS" /> + + <application android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.usb" + android:label="UsbTests"/> +</manifest> diff --git a/tests/UsbTests/AndroidTest.xml b/tests/UsbTests/AndroidTest.xml new file mode 100644 index 000000000000..0b623fbf2015 --- /dev/null +++ b/tests/UsbTests/AndroidTest.xml @@ -0,0 +1,29 @@ +<!-- 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. +--> +<configuration description="Runs sample instrumentation test."> + <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="UsbTests.apk"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/> + <option name="test-suite-tag" value="apct"/> + <option name="test-tag" value="UsbTests"/> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.server.usb"/> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> + </test> +</configuration>
\ No newline at end of file diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java new file mode 100644 index 000000000000..c491b4658999 --- /dev/null +++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java @@ -0,0 +1,316 @@ +/* + * 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.usb; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.hardware.usb.UsbManager; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.provider.Settings; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.server.FgThread; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** + * Tests for UsbHandler state changes. + */ +@RunWith(AndroidJUnit4.class) +public class UsbHandlerTest { + private static final String TAG = UsbHandlerTest.class.getSimpleName(); + + @Mock + private UsbDeviceManager mUsbDeviceManager; + @Mock + private UsbDebuggingManager mUsbDebuggingManager; + @Mock + private UsbAlsaManager mUsbAlsaManager; + @Mock + private UsbSettingsManager mUsbSettingsManager; + @Mock + private SharedPreferences mSharedPreferences; + @Mock + private SharedPreferences.Editor mEditor; + + private MockUsbHandler mUsbHandler; + + private static final int MSG_UPDATE_STATE = 0; + private static final int MSG_ENABLE_ADB = 1; + private static final int MSG_SET_CURRENT_FUNCTIONS = 2; + private static final int MSG_SYSTEM_READY = 3; + private static final int MSG_BOOT_COMPLETED = 4; + private static final int MSG_USER_SWITCHED = 5; + private static final int MSG_UPDATE_USER_RESTRICTIONS = 6; + private static final int MSG_SET_SCREEN_UNLOCKED_FUNCTIONS = 12; + private static final int MSG_UPDATE_SCREEN_LOCK = 13; + + private Map<String, String> mMockProperties; + private Map<String, Integer> mMockGlobalSettings; + + private class MockUsbHandler extends UsbDeviceManager.UsbHandler { + boolean mIsUsbTransferAllowed; + Intent mBroadcastedIntent; + + MockUsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager, + UsbDebuggingManager debuggingManager, UsbAlsaManager alsaManager, + UsbSettingsManager settingsManager) { + super(looper, context, deviceManager, debuggingManager, alsaManager, settingsManager); + mUseUsbNotification = false; + mIsUsbTransferAllowed = true; + mCurrentUsbFunctionsReceived = true; + } + + @Override + protected void setEnabledFunctions(long functions, boolean force) { + mCurrentFunctions = functions; + } + + @Override + protected void setSystemProperty(String property, String value) { + mMockProperties.put(property, value); + } + + @Override + protected void putGlobalSettings(ContentResolver resolver, String setting, int val) { + mMockGlobalSettings.put(setting, val); + } + + @Override + protected String getSystemProperty(String property, String def) { + if (mMockProperties.containsKey(property)) { + return mMockProperties.get(property); + } + return def; + } + + @Override + protected boolean isUsbTransferAllowed() { + return mIsUsbTransferAllowed; + } + + @Override + protected SharedPreferences getPinnedSharedPrefs(Context context) { + return mSharedPreferences; + } + + @Override + protected void sendStickyBroadcast(Intent intent) { + mBroadcastedIntent = intent; + } + } + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + mMockProperties = new HashMap<>(); + mMockGlobalSettings = new HashMap<>(); + when(mSharedPreferences.edit()).thenReturn(mEditor); + + mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(), + InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager, + mUsbAlsaManager, mUsbSettingsManager); + } + + @SmallTest + public void setFunctionsMtp() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_MTP)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + } + + @SmallTest + public void setFunctionsPtp() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_PTP)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_PTP, 0); + } + + @SmallTest + public void setFunctionsMidi() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_MIDI)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MIDI, 0); + } + + @SmallTest + public void setFunctionsRndis() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_RNDIS)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_RNDIS, 0); + } + + @SmallTest + public void enableAdb() { + sendBootCompleteMessages(mUsbHandler); + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_ENABLE_ADB, 1)); + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + assertTrue(mUsbHandler.mAdbEnabled); + assertEquals(mMockProperties.get(UsbDeviceManager.UsbHandler + .USB_PERSISTENT_CONFIG_PROPERTY), UsbManager.USB_FUNCTION_ADB); + verify(mUsbDebuggingManager).setAdbEnabled(true); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 1, 1)); + + assertTrue(mUsbHandler.mBroadcastedIntent.getBooleanExtra(UsbManager.USB_CONNECTED, false)); + assertTrue(mUsbHandler.mBroadcastedIntent + .getBooleanExtra(UsbManager.USB_CONFIGURED, false)); + assertTrue(mUsbHandler.mBroadcastedIntent + .getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false)); + } + + @SmallTest + public void disableAdb() { + mMockProperties.put(UsbDeviceManager.UsbHandler.USB_PERSISTENT_CONFIG_PROPERTY, + UsbManager.USB_FUNCTION_ADB); + mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(), + InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager, + mUsbAlsaManager, mUsbSettingsManager); + + sendBootCompleteMessages(mUsbHandler); + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_ENABLE_ADB, 0)); + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + assertFalse(mUsbHandler.mAdbEnabled); + assertEquals(mMockProperties.get(UsbDeviceManager.UsbHandler + .USB_PERSISTENT_CONFIG_PROPERTY), ""); + verify(mUsbDebuggingManager).setAdbEnabled(false); + } + + @SmallTest + public void bootCompletedCharging() { + sendBootCompleteMessages(mUsbHandler); + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + } + + @Test + @SmallTest + public void bootCompletedAdbEnabled() { + mMockProperties.put(UsbDeviceManager.UsbHandler.USB_PERSISTENT_CONFIG_PROPERTY, "adb"); + mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(), + InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbDebuggingManager, + mUsbAlsaManager, mUsbSettingsManager); + + sendBootCompleteMessages(mUsbHandler); + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + assertEquals(mMockGlobalSettings.get(Settings.Global.ADB_ENABLED).intValue(), 1); + assertTrue(mUsbHandler.mAdbEnabled); + verify(mUsbDebuggingManager).setAdbEnabled(true); + } + + @SmallTest + public void userSwitchedDisablesMtp() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_MTP)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_USER_SWITCHED, + UserHandle.getCallingUserId() + 1)); + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + } + + @SmallTest + public void changedRestrictionsDisablesMtp() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_MTP)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + + mUsbHandler.mIsUsbTransferAllowed = false; + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_USER_RESTRICTIONS)); + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + } + + @SmallTest + public void disconnectResetsCharging() { + sendBootCompleteMessages(mUsbHandler); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_MTP)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 0, 0)); + + assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE); + } + + @SmallTest + public void configuredSendsBroadcast() { + sendBootCompleteMessages(mUsbHandler); + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_MTP)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 1, 1)); + + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + assertTrue(mUsbHandler.mBroadcastedIntent.getBooleanExtra(UsbManager.USB_CONNECTED, false)); + assertTrue(mUsbHandler.mBroadcastedIntent + .getBooleanExtra(UsbManager.USB_CONFIGURED, false)); + assertTrue(mUsbHandler.mBroadcastedIntent + .getBooleanExtra(UsbManager.USB_FUNCTION_MTP, false)); + } + + @SmallTest + public void setScreenUnlockedFunctions() { + sendBootCompleteMessages(mUsbHandler); + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_SCREEN_LOCK, 0)); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, + UsbManager.FUNCTION_MTP)); + assertNotEquals(mUsbHandler.getScreenUnlockedFunctions() & UsbManager.FUNCTION_MTP, 0); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + verify(mEditor).putString(String.format(Locale.ENGLISH, + UsbDeviceManager.UNLOCKED_CONFIG_PREF, mUsbHandler.mCurrentUser), + UsbManager.USB_FUNCTION_MTP); + } + + @SmallTest + public void unlockScreen() { + when(mSharedPreferences.getString(String.format(Locale.ENGLISH, + UsbDeviceManager.UNLOCKED_CONFIG_PREF, mUsbHandler.mCurrentUser), "")) + .thenReturn(UsbManager.USB_FUNCTION_MTP); + sendBootCompleteMessages(mUsbHandler); + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_SCREEN_LOCK, 0)); + + assertNotEquals(mUsbHandler.getScreenUnlockedFunctions() & UsbManager.FUNCTION_MTP, 0); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_MTP, 0); + } + + private static void sendBootCompleteMessages(Handler handler) { + handler.handleMessage(handler.obtainMessage(MSG_BOOT_COMPLETED)); + handler.handleMessage(handler.obtainMessage(MSG_SYSTEM_READY)); + } +}
\ No newline at end of file diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index 099cfd457160..e692652c7ea4 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -311,7 +311,7 @@ public class TetheringTest { // Emulate pressing the USB tethering button in Settings UI. mTethering.startTethering(TETHERING_USB, null, false); mLooper.dispatchAll(); - verify(mUsbManager, times(1)).setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false); + verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); // Pretend we receive a USB connected broadcast. Here we also pretend // that the RNDIS function is somehow enabled, so that we see if we diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java index dc5ba0cc83b5..fe63aa1bf79b 100644 --- a/wifi/java/android/net/wifi/RttManager.java +++ b/wifi/java/android/net/wifi/RttManager.java @@ -7,22 +7,21 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; +import android.content.pm.PackageManager; +import android.net.wifi.rtt.RangingRequest; +import android.net.wifi.rtt.RangingResult; +import android.net.wifi.rtt.RangingResultCallback; +import android.net.wifi.rtt.WifiRttManager; import android.os.Looper; -import android.os.Message; -import android.os.Messenger; import android.os.Parcel; import android.os.Parcelable; -import android.os.RemoteException; import android.util.Log; -import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; +import java.util.List; + /** @hide */ @SystemApi @SystemService(Context.WIFI_RTT_SERVICE) @@ -175,7 +174,8 @@ public class RttManager { @Deprecated @SuppressLint("Doclava125") public Capabilities getCapabilities() { - return new Capabilities(); + throw new UnsupportedOperationException( + "getCapabilities is not supported in the adaptation layer"); } /** @@ -316,16 +316,7 @@ public class RttManager { @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) public RttCapabilities getRttCapabilities() { - synchronized (mCapabilitiesLock) { - if (mRttCapabilities == null) { - try { - mRttCapabilities = mService.getRttCapabilities(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - return mRttCapabilities; - } + return mRttCapabilities; } /** specifies parameters for RTT request */ @@ -972,69 +963,6 @@ public class RttManager { } } - private boolean rttParamSanity(RttParams params, int index) { - if (mRttCapabilities == null) { - if(getRttCapabilities() == null) { - Log.e(TAG, "Can not get RTT capabilities"); - throw new IllegalStateException("RTT chip is not working"); - } - } - - if (params.deviceType != RTT_PEER_TYPE_AP) { - return false; - } else if (params.requestType != RTT_TYPE_ONE_SIDED && params.requestType != - RTT_TYPE_TWO_SIDED) { - Log.e(TAG, "Request " + index + ": Illegal Request Type: " + params.requestType); - return false; - } else if (params.requestType == RTT_TYPE_ONE_SIDED && - !mRttCapabilities.oneSidedRttSupported) { - Log.e(TAG, "Request " + index + ": One side RTT is not supported"); - return false; - } else if (params.requestType == RTT_TYPE_TWO_SIDED && - !mRttCapabilities.twoSided11McRttSupported) { - Log.e(TAG, "Request " + index + ": two side RTT is not supported"); - return false; - } else if(params.bssid == null || params.bssid.isEmpty()) { - Log.e(TAG,"No BSSID in params"); - return false; - } else if ( params.numberBurst != 0 ) { - Log.e(TAG, "Request " + index + ": Illegal number of burst: " + params.numberBurst); - return false; - } else if (params.numSamplesPerBurst <= 0 || params.numSamplesPerBurst > 31) { - Log.e(TAG, "Request " + index + ": Illegal sample number per burst: " + - params.numSamplesPerBurst); - return false; - } else if (params.numRetriesPerMeasurementFrame < 0 || - params.numRetriesPerMeasurementFrame > 3) { - Log.e(TAG, "Request " + index + ": Illegal measurement frame retry number:" + - params.numRetriesPerMeasurementFrame); - return false; - } else if(params.numRetriesPerFTMR < 0 || - params.numRetriesPerFTMR > 3) { - Log.e(TAG, "Request " + index + ": Illegal FTMR frame retry number:" + - params.numRetriesPerFTMR); - return false; - } else if (params.LCIRequest && !mRttCapabilities.lciSupported) { - Log.e(TAG, "Request " + index + ": LCI is not supported"); - return false; - } else if (params.LCRRequest && !mRttCapabilities.lcrSupported) { - Log.e(TAG, "Request " + index + ": LCR is not supported"); - return false; - } else if (params.burstTimeout < 1 || - (params.burstTimeout > 11 && params.burstTimeout != 15)){ - Log.e(TAG, "Request " + index + ": Illegal burst timeout: " + params.burstTimeout); - return false; - } else if ((params.preamble & mRttCapabilities.preambleSupported) == 0) { - Log.e(TAG, "Request " + index + ": Do not support this preamble: " + params.preamble); - return false; - } else if ((params.bandwidth & mRttCapabilities.bwSupported) == 0) { - Log.e(TAG, "Request " + index + ": Do not support this bandwidth: " + params.bandwidth); - return false; - } - - return true; - } - /** * Request to start an RTT ranging * @@ -1045,24 +973,72 @@ public class RttManager { */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startRanging(RttParams[] params, RttListener listener) { - int index = 0; - for(RttParams rttParam : params) { - if (!rttParamSanity(rttParam, index)) { - throw new IllegalArgumentException("RTT Request Parameter Illegal"); + Log.i(TAG, "Send RTT request to RTT Service"); + + if (!mNewService.isAvailable()) { + listener.onFailure(REASON_NOT_AVAILABLE, ""); + return; + } + + RangingRequest.Builder builder = new RangingRequest.Builder(); + for (RttParams rttParams : params) { + if (rttParams.deviceType != RTT_PEER_TYPE_AP) { + listener.onFailure(REASON_INVALID_REQUEST, "Only AP peers are supported"); + return; + } + + ScanResult reconstructed = new ScanResult(); + reconstructed.BSSID = rttParams.bssid; + if (rttParams.requestType == RTT_TYPE_TWO_SIDED) { + reconstructed.setFlag(ScanResult.FLAG_80211mc_RESPONDER); } - index++; + reconstructed.channelWidth = rttParams.channelWidth; + reconstructed.frequency = rttParams.frequency; + reconstructed.centerFreq0 = rttParams.centerFreq0; + reconstructed.centerFreq1 = rttParams.centerFreq1; + builder.addResponder( + android.net.wifi.rtt.ResponderConfig.fromScanResult(reconstructed)); + } + try { + mNewService.startRanging(builder.build(), new RangingResultCallback() { + @Override + public void onRangingFailure(int code) { + int localCode = REASON_UNSPECIFIED; + if (code == STATUS_CODE_FAIL_RTT_NOT_AVAILABLE) { + localCode = REASON_NOT_AVAILABLE; + } + listener.onFailure(localCode, ""); + } + + @Override + public void onRangingResults(List<RangingResult> results) { + RttResult[] legacyResults = new RttResult[results.size()]; + int i = 0; + for (RangingResult result : results) { + legacyResults[i] = new RttResult(); + legacyResults[i].status = result.getStatus(); + legacyResults[i].bssid = result.getMacAddress().toString(); + legacyResults[i].distance = result.getDistanceMm() / 10; + legacyResults[i].distanceStandardDeviation = + result.getDistanceStdDevMm() / 10; + legacyResults[i].rssi = result.getRssi(); + legacyResults[i].ts = result.getRangingTimestampUs(); + } + listener.onSuccess(legacyResults); + } + }, null); + } catch (IllegalArgumentException e) { + Log.e(TAG, "startRanging: invalid arguments - " + e); + listener.onFailure(REASON_INVALID_REQUEST, e.getMessage()); + } catch (SecurityException e) { + Log.e(TAG, "startRanging: security exception - " + e); + listener.onFailure(REASON_PERMISSION_DENIED, e.getMessage()); } - validateChannel(); - ParcelableRttParams parcelableParams = new ParcelableRttParams(params); - Log.i(TAG, "Send RTT request to RTT Service"); - mAsyncChannel.sendMessage(CMD_OP_START_RANGING, - 0, putListener(listener), parcelableParams); } @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopRanging(RttListener listener) { - validateChannel(); - mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener)); + Log.e(TAG, "stopRanging: unsupported operation - nop"); } /** @@ -1095,12 +1071,8 @@ public class RttManager { */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void enableResponder(ResponderCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - validateChannel(); - int key = putListenerIfAbsent(callback); - mAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key); + throw new UnsupportedOperationException( + "enableResponder is not supported in the adaptation layer"); } /** @@ -1115,16 +1087,8 @@ public class RttManager { */ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void disableResponder(ResponderCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - validateChannel(); - int key = removeListener(callback); - if (key == INVALID_KEY) { - Log.e(TAG, "responder not enabled yet"); - return; - } - mAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key); + throw new UnsupportedOperationException( + "disableResponder is not supported in the adaptation layer"); } /** @@ -1238,17 +1202,9 @@ public class RttManager { /** @hide */ public static final int CMD_OP_REG_BINDER = BASE + 9; - private static final int INVALID_KEY = 0; - private final Context mContext; - private final IRttManager mService; - private final SparseArray mListenerMap = new SparseArray(); - private final Object mListenerMapLock = new Object(); - private final Object mCapabilitiesLock = new Object(); - + private final WifiRttManager mNewService; private RttCapabilities mRttCapabilities; - private int mListenerKey = 1; - private AsyncChannel mAsyncChannel; /** * Create a new WifiScanner instance. @@ -1263,170 +1219,20 @@ public class RttManager { */ public RttManager(Context context, IRttManager service, Looper looper) { mContext = context; - mService = service; - Messenger messenger = null; - int[] key = new int[1]; - try { - Log.d(TAG, "Get the messenger from " + mService); - messenger = mService.getMessenger(new Binder(), key); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - - if (messenger == null) { - throw new IllegalStateException("getMessenger() returned null! This is invalid."); - } - - mAsyncChannel = new AsyncChannel(); - - Handler handler = new ServiceHandler(looper); - mAsyncChannel.connectSync(mContext, handler, messenger); - // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message - // synchronously, which causes RttService to receive the wrong replyTo value. - mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION, - new RttClient(context.getPackageName())); - mAsyncChannel.sendMessage(CMD_OP_REG_BINDER, key[0]); + mNewService = (WifiRttManager) mContext.getSystemService(Context.WIFI_RTT_RANGING_SERVICE); + + boolean rttSupported = mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_RTT); + + mRttCapabilities = new RttCapabilities(); + mRttCapabilities.oneSidedRttSupported = rttSupported; + mRttCapabilities.twoSided11McRttSupported = rttSupported; + mRttCapabilities.lciSupported = false; + mRttCapabilities.lcrSupported = false; + mRttCapabilities.preambleSupported = PREAMBLE_HT | PREAMBLE_VHT; + mRttCapabilities.bwSupported = RTT_BW_40_SUPPORT | RTT_BW_80_SUPPORT; + mRttCapabilities.responderSupported = false; + mRttCapabilities.secureRttSupported = false; } - - private void validateChannel() { - if (mAsyncChannel == null) throw new IllegalStateException( - "No permission to access and change wifi or a bad initialization"); - } - - private int putListener(Object listener) { - if (listener == null) return INVALID_KEY; - int key; - synchronized (mListenerMapLock) { - do { - key = mListenerKey++; - } while (key == INVALID_KEY); - mListenerMap.put(key, listener); - } - return key; - } - - // Insert a listener if it doesn't exist in mListenerMap. Returns the key of the listener. - private int putListenerIfAbsent(Object listener) { - if (listener == null) return INVALID_KEY; - synchronized (mListenerMapLock) { - int key = getListenerKey(listener); - if (key != INVALID_KEY) { - return key; - } - do { - key = mListenerKey++; - } while (key == INVALID_KEY); - mListenerMap.put(key, listener); - return key; - } - - } - - private Object getListener(int key) { - if (key == INVALID_KEY) return null; - synchronized (mListenerMapLock) { - Object listener = mListenerMap.get(key); - return listener; - } - } - - private int getListenerKey(Object listener) { - if (listener == null) return INVALID_KEY; - synchronized (mListenerMapLock) { - int index = mListenerMap.indexOfValue(listener); - if (index == -1) { - return INVALID_KEY; - } else { - return mListenerMap.keyAt(index); - } - } - } - - private Object removeListener(int key) { - if (key == INVALID_KEY) return null; - synchronized (mListenerMapLock) { - Object listener = mListenerMap.get(key); - mListenerMap.remove(key); - return listener; - } - } - - private int removeListener(Object listener) { - int key = getListenerKey(listener); - if (key == INVALID_KEY) return key; - synchronized (mListenerMapLock) { - mListenerMap.remove(key); - return key; - } - } - - private class ServiceHandler extends Handler { - ServiceHandler(Looper looper) { - super(looper); - } - @Override - public void handleMessage(Message msg) { - Log.i(TAG, "RTT manager get message: " + msg.what); - switch (msg.what) { - case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: - return; - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - Log.e(TAG, "Channel connection lost"); - // This will cause all further async API calls on the WifiManager - // to fail and throw an exception - mAsyncChannel = null; - getLooper().quit(); - return; - } - - Object listener = getListener(msg.arg2); - if (listener == null) { - Log.e(TAG, "invalid listener key = " + msg.arg2 ); - return; - } else { - Log.i(TAG, "listener key = " + msg.arg2); - } - - switch (msg.what) { - /* ActionListeners grouped together */ - case CMD_OP_SUCCEEDED : - reportSuccess(listener, msg); - removeListener(msg.arg2); - break; - case CMD_OP_FAILED : - reportFailure(listener, msg); - removeListener(msg.arg2); - break; - case CMD_OP_ABORTED : - ((RttListener) listener).onAborted(); - removeListener(msg.arg2); - break; - case CMD_OP_ENALBE_RESPONDER_SUCCEEDED: - ResponderConfig config = (ResponderConfig) msg.obj; - ((ResponderCallback) (listener)).onResponderEnabled(config); - break; - case CMD_OP_ENALBE_RESPONDER_FAILED: - ((ResponderCallback) (listener)).onResponderEnableFailure(msg.arg1); - removeListener(msg.arg2); - break; - default: - if (DBG) Log.d(TAG, "Ignoring message " + msg.what); - return; - } - } - - void reportSuccess(Object listener, Message msg) { - RttListener rttListener = (RttListener) listener; - ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj; - ((RttListener) listener).onSuccess(parcelableResults.mResults); - } - - void reportFailure(Object listener, Message msg) { - RttListener rttListener = (RttListener) listener; - Bundle bundle = (Bundle) msg.obj; - ((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY)); - } - } - } |