Merge "Clarify Toast.Callback javadoc" into rvc-dev
diff --git a/apex/statsd/framework/java/android/util/StatsEvent.java b/apex/statsd/framework/java/android/util/StatsEvent.java
index 1a45c4a..8bd36a5 100644
--- a/apex/statsd/framework/java/android/util/StatsEvent.java
+++ b/apex/statsd/framework/java/android/util/StatsEvent.java
@@ -188,6 +188,12 @@
@VisibleForTesting
public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x1000;
+ /**
+ * @hide
+ **/
+ @VisibleForTesting
+ public static final int ERROR_ATOM_ID_INVALID_POSITION = 0x2000;
+
// Size limits.
/**
@@ -350,19 +356,32 @@
mPos = 0;
writeTypeId(TYPE_OBJECT);
- // Set mPos to after atom id's location in the buffer.
- // First 2 elements in the buffer are event timestamp followed by the atom id.
- mPos = POS_ATOM_ID + Byte.BYTES + Integer.BYTES;
- mPosLastField = 0;
- mLastType = 0;
+ // Write timestamp.
+ mPos = POS_TIMESTAMP_NS;
+ writeLong(mTimestampNs);
}
/**
* Sets the atom id for this StatsEvent.
+ *
+ * This should be called immediately after StatsEvent.newBuilder()
+ * and should only be called once.
+ * Not calling setAtomId will result in ERROR_NO_ATOM_ID.
+ * Calling setAtomId out of order will result in ERROR_ATOM_ID_INVALID_POSITION.
**/
@NonNull
public Builder setAtomId(final int atomId) {
- mAtomId = atomId;
+ if (0 == mAtomId) {
+ mAtomId = atomId;
+
+ if (1 == mNumElements) { // Only timestamp is written so far.
+ writeInt(atomId);
+ } else {
+ // setAtomId called out of order.
+ mErrorMask |= ERROR_ATOM_ID_INVALID_POSITION;
+ }
+ }
+
return this;
}
@@ -557,7 +576,7 @@
public Builder addBooleanAnnotation(
final byte annotationId, final boolean value) {
// Ensure there's a field written to annotate.
- if (0 == mPosLastField) {
+ if (mNumElements < 2) {
mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
} else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) {
mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS;
@@ -568,6 +587,7 @@
mCurrentAnnotationCount++;
writeAnnotationCount();
}
+
return this;
}
@@ -576,7 +596,7 @@
**/
@NonNull
public Builder addIntAnnotation(final byte annotationId, final int value) {
- if (0 == mPosLastField) {
+ if (mNumElements < 2) {
mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
} else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) {
mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS;
@@ -587,6 +607,7 @@
mCurrentAnnotationCount++;
writeAnnotationCount();
}
+
return this;
}
@@ -619,19 +640,20 @@
mErrorMask |= ERROR_TOO_MANY_FIELDS;
}
- int size = mPos;
- mPos = POS_TIMESTAMP_NS;
- writeLong(mTimestampNs);
- writeInt(mAtomId);
if (0 == mErrorMask) {
mBuffer.putByte(POS_NUM_ELEMENTS, (byte) mNumElements);
} else {
+ // Write atom id and error mask. Overwrite any annotations for atom Id.
+ mPos = POS_ATOM_ID;
+ mPos += mBuffer.putByte(mPos, TYPE_INT);
+ mPos += mBuffer.putInt(mPos, mAtomId);
mPos += mBuffer.putByte(mPos, TYPE_ERRORS);
mPos += mBuffer.putInt(mPos, mErrorMask);
mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3);
- size = mPos;
}
+ final int size = mPos;
+
if (mUsePooledBuffer) {
return new StatsEvent(mAtomId, mBuffer, mBuffer.getBytes(), size);
} else {
diff --git a/apex/statsd/framework/test/src/android/util/StatsEventTest.java b/apex/statsd/framework/test/src/android/util/StatsEventTest.java
index ac25e27..7b51155 100644
--- a/apex/statsd/framework/test/src/android/util/StatsEventTest.java
+++ b/apex/statsd/framework/test/src/android/util/StatsEventTest.java
@@ -85,6 +85,45 @@
}
@Test
+ public void testOnlyAtomId() {
+ final int expectedAtomId = 109;
+
+ final long minTimestamp = SystemClock.elapsedRealtimeNanos();
+ final StatsEvent statsEvent = StatsEvent.newBuilder()
+ .setAtomId(expectedAtomId)
+ .usePooledBuffer()
+ .build();
+ final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
+
+ assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
+
+ final ByteBuffer buffer =
+ ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
+
+ assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
+
+ assertWithMessage("Incorrect number of elements in root object")
+ .that(buffer.get()).isEqualTo(2);
+
+ assertWithMessage("First element is not timestamp")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
+
+ assertWithMessage("Incorrect timestamp")
+ .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
+
+ assertWithMessage("Second element is not atom id")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
+
+ assertWithMessage("Incorrect atom id")
+ .that(buffer.getInt()).isEqualTo(expectedAtomId);
+
+ assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
+
+ statsEvent.release();
+ }
+
+ @Test
public void testIntBooleanIntInt() {
final int expectedAtomId = 109;
final int field1 = 1;
@@ -460,6 +499,151 @@
statsEvent.release();
}
+ @Test
+ public void testAtomIdAnnotations() {
+ final int expectedAtomId = 109;
+ final byte atomAnnotationId = 84;
+ final int atomAnnotationValue = 9;
+ final int field1 = 1;
+ final byte field1AnnotationId = 45;
+ final boolean field1AnnotationValue = false;
+ final boolean field2 = true;
+ final byte field2AnnotationId = 1;
+ final int field2AnnotationValue = 23;
+
+ final long minTimestamp = SystemClock.elapsedRealtimeNanos();
+ final StatsEvent statsEvent = StatsEvent.newBuilder()
+ .setAtomId(expectedAtomId)
+ .addIntAnnotation(atomAnnotationId, atomAnnotationValue)
+ .writeInt(field1)
+ .addBooleanAnnotation(field1AnnotationId, field1AnnotationValue)
+ .writeBoolean(field2)
+ .addIntAnnotation(field2AnnotationId, field2AnnotationValue)
+ .usePooledBuffer()
+ .build();
+ final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
+
+ assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
+
+ final ByteBuffer buffer =
+ ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
+
+ assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
+
+ assertWithMessage("Incorrect number of elements in root object")
+ .that(buffer.get()).isEqualTo(4);
+
+ assertWithMessage("First element is not timestamp")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
+
+ assertWithMessage("Incorrect timestamp")
+ .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
+
+ final byte atomIdHeader = buffer.get();
+ final int atomIdAnnotationValueCount = atomIdHeader >> 4;
+ final byte atomIdValueType = (byte) (atomIdHeader & 0x0F);
+ assertWithMessage("Second element is not atom id")
+ .that(atomIdValueType).isEqualTo(StatsEvent.TYPE_INT);
+ assertWithMessage("Atom id annotation count is wrong")
+ .that(atomIdAnnotationValueCount).isEqualTo(1);
+ assertWithMessage("Incorrect atom id")
+ .that(buffer.getInt()).isEqualTo(expectedAtomId);
+ assertWithMessage("Atom id's annotation id is wrong")
+ .that(buffer.get()).isEqualTo(atomAnnotationId);
+ assertWithMessage("Atom id's annotation type is wrong")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
+ assertWithMessage("Atom id's annotation value is wrong")
+ .that(buffer.getInt()).isEqualTo(atomAnnotationValue);
+
+ final byte field1Header = buffer.get();
+ final int field1AnnotationValueCount = field1Header >> 4;
+ final byte field1Type = (byte) (field1Header & 0x0F);
+ assertWithMessage("First field is not Int")
+ .that(field1Type).isEqualTo(StatsEvent.TYPE_INT);
+ assertWithMessage("First field annotation count is wrong")
+ .that(field1AnnotationValueCount).isEqualTo(1);
+ assertWithMessage("Incorrect field 1")
+ .that(buffer.getInt()).isEqualTo(field1);
+ assertWithMessage("First field's annotation id is wrong")
+ .that(buffer.get()).isEqualTo(field1AnnotationId);
+ assertWithMessage("First field's annotation type is wrong")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_BOOLEAN);
+ assertWithMessage("First field's annotation value is wrong")
+ .that(buffer.get()).isEqualTo(field1AnnotationValue ? 1 : 0);
+
+ final byte field2Header = buffer.get();
+ final int field2AnnotationValueCount = field2Header >> 4;
+ final byte field2Type = (byte) (field2Header & 0x0F);
+ assertWithMessage("Second field is not boolean")
+ .that(field2Type).isEqualTo(StatsEvent.TYPE_BOOLEAN);
+ assertWithMessage("Second field annotation count is wrong")
+ .that(field2AnnotationValueCount).isEqualTo(1);
+ assertWithMessage("Incorrect field 2")
+ .that(buffer.get()).isEqualTo(field2 ? 1 : 0);
+ assertWithMessage("Second field's annotation id is wrong")
+ .that(buffer.get()).isEqualTo(field2AnnotationId);
+ assertWithMessage("Second field's annotation type is wrong")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
+ assertWithMessage("Second field's annotation value is wrong")
+ .that(buffer.getInt()).isEqualTo(field2AnnotationValue);
+
+ assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
+
+ statsEvent.release();
+ }
+
+ @Test
+ public void testSetAtomIdNotCalledImmediately() {
+ final int expectedAtomId = 109;
+ final int field1 = 25;
+ final boolean field2 = true;
+
+ final long minTimestamp = SystemClock.elapsedRealtimeNanos();
+ final StatsEvent statsEvent = StatsEvent.newBuilder()
+ .writeInt(field1)
+ .setAtomId(expectedAtomId)
+ .writeBoolean(field2)
+ .usePooledBuffer()
+ .build();
+ final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
+
+ assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
+
+ final ByteBuffer buffer =
+ ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
+
+ assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
+
+ assertWithMessage("Incorrect number of elements in root object")
+ .that(buffer.get()).isEqualTo(3);
+
+ assertWithMessage("First element is not timestamp")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_LONG);
+
+ assertWithMessage("Incorrect timestamp")
+ .that(buffer.getLong()).isIn(Range.closed(minTimestamp, maxTimestamp));
+
+ assertWithMessage("Second element is not atom id")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_INT);
+
+ assertWithMessage("Incorrect atom id")
+ .that(buffer.getInt()).isEqualTo(expectedAtomId);
+
+ assertWithMessage("Third element is not errors type")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS);
+
+ final int errorMask = buffer.getInt();
+
+ assertWithMessage("ERROR_ATOM_ID_INVALID_POSITION should be the only error in the mask")
+ .that(errorMask).isEqualTo(StatsEvent.ERROR_ATOM_ID_INVALID_POSITION);
+
+ assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
+
+ statsEvent.release();
+ }
+
private static byte[] getByteArrayFromByteBuffer(final ByteBuffer buffer) {
final int numBytes = buffer.getInt();
byte[] bytes = new byte[numBytes];
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 34a40ae..eb6901f 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1214,7 +1214,7 @@
* <p>If the camera device supports zoom-out from 1x zoom, minZoom will be less than 1.0, and
* setting {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} to values less than 1.0 increases the camera's field
* of view.</p>
- * <p><b>Units</b>: A pair of zoom ratio in floating points: (minZoom, maxZoom)</p>
+ * <p><b>Units</b>: A pair of zoom ratio in floating-points: (minZoom, maxZoom)</p>
* <p><b>Range of valid values:</b><br></p>
* <p>maxZoom >= 1.0 >= minZoom</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 0ee7482..6905f83 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2180,27 +2180,66 @@
* {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} can still be used to specify the horizontal or vertical
* crop to achieve aspect ratios different than the native camera sensor.</p>
* <p>By using this control, the application gains a simpler way to control zoom, which can
- * be a combination of optical and digital zoom. More specifically, for a logical
- * multi-camera with more than one focal length, using a floating point zoom ratio offers
- * more zoom precision when a telephoto lens is used, as well as allowing zoom ratio of
- * less than 1.0 to zoom out to a wide field of view.</p>
- * <p>Note that the coordinate system of cropRegion, AE/AWB/AF regions, and faces now changes
- * to the effective after-zoom field-of-view represented by rectangle of (0, 0,
- * activeArrayWidth, activeArrayHeight).</p>
- * <p>For example, if {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} is 4032*3024, and the preview stream
- * is configured to the same 4:3 aspect ratio, the application can achieve 2.0x zoom in
- * one of two ways:</p>
+ * be a combination of optical and digital zoom. For example, a multi-camera system may
+ * contain more than one lens with different focal lengths, and the user can use optical
+ * zoom by switching between lenses. Using zoomRatio has benefits in the scenarios below:
+ * <em> Zooming in from a wide-angle lens to a telephoto lens: A floating-point ratio provides
+ * better precision compared to an integer value of {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}.
+ * </em> Zooming out from a wide lens to an ultrawide lens: zoomRatio supports zoom-out whereas
+ * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} doesn't.</p>
+ * <p>To illustrate, here are several scenarios of different zoom ratios, crop regions,
+ * and output streams, for a hypothetical camera device with an active array of size
+ * <code>(2000,1500)</code>.</p>
* <ul>
- * <li>zoomRatio = 2.0, scaler.cropRegion = (0, 0, 4032, 3024)</li>
- * <li>zoomRatio = 1.0 (default), scaler.cropRegion = (1008, 756, 3024, 2268)</li>
+ * <li>Camera Configuration:<ul>
+ * <li>Active array size: <code>2000x1500</code> (3 MP, 4:3 aspect ratio)</li>
+ * <li>Output stream #1: <code>640x480</code> (VGA, 4:3 aspect ratio)</li>
+ * <li>Output stream #2: <code>1280x720</code> (720p, 16:9 aspect ratio)</li>
* </ul>
- * <p>If the application intends to set aeRegions to be top-left quarter of the preview
- * field-of-view, the {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions} should be set to (0, 0, 2016, 1512) with
+ * </li>
+ * <li>Case #1: 4:3 crop region with 2.0x zoom ratio<ul>
+ * <li>Zoomed field of view: 1/4 of original field of view</li>
+ * <li>Crop region: <code>Rect(0, 0, 2000, 1500) // (left, top, right, bottom)</code> (post zoom)</li>
+ * </ul>
+ * </li>
+ * <li><img alt="4:3 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png" /><ul>
+ * <li><code>640x480</code> stream source area: <code>(0, 0, 2000, 1500)</code> (equal to crop region)</li>
+ * <li><code>1280x720</code> stream source area: <code>(0, 187, 2000, 1312)</code> (letterboxed)</li>
+ * </ul>
+ * </li>
+ * <li>Case #2: 16:9 crop region with 2.0x zoom.<ul>
+ * <li>Zoomed field of view: 1/4 of original field of view</li>
+ * <li>Crop region: <code>Rect(0, 187, 2000, 1312)</code></li>
+ * <li><img alt="16:9 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(250, 187, 1750, 1312)</code> (pillarboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(0, 187, 2000, 1312)</code> (equal to crop region)</li>
+ * </ul>
+ * </li>
+ * <li>Case #3: 1:1 crop region with 0.5x zoom out to ultrawide lens.<ul>
+ * <li>Zoomed field of view: 4x of original field of view (switched from wide lens to ultrawide lens)</li>
+ * <li>Crop region: <code>Rect(250, 0, 1750, 1500)</code></li>
+ * <li><img alt="1:1 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(250, 187, 1750, 1312)</code> (letterboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(250, 328, 1750, 1172)</code> (letterboxed)</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <p>As seen from the graphs above, the coordinate system of cropRegion now changes to the
+ * effective after-zoom field-of-view, and is represented by the rectangle of (0, 0,
+ * activeArrayWith, activeArrayHeight). The same applies to AE/AWB/AF regions, and faces.
+ * This coordinate system change isn't applicable to RAW capture and its related
+ * metadata such as intrinsicCalibration and lensShadingMap.</p>
+ * <p>Using the same hypothetical example above, and assuming output stream #1 (640x480) is
+ * the viewfinder stream, the application can achieve 2.0x zoom in one of two ways:</p>
+ * <ul>
+ * <li>zoomRatio = 2.0, scaler.cropRegion = (0, 0, 2000, 1500)</li>
+ * <li>zoomRatio = 1.0 (default), scaler.cropRegion = (500, 375, 1500, 1125)</li>
+ * </ul>
+ * <p>If the application intends to set aeRegions to be top-left quarter of the viewfinder
+ * field-of-view, the {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions} should be set to (0, 0, 1000, 750) with
* zoomRatio set to 2.0. Alternatively, the application can set aeRegions to the equivalent
- * region of (1008, 756, 2016, 1512) for zoomRatio of 1.0. If the application doesn't
+ * region of (500, 375, 1000, 750) for zoomRatio of 1.0. If the application doesn't
* explicitly set {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, its value defaults to 1.0.</p>
- * <p>This coordinate system change isn't applicable to RAW capture and its related metadata
- * such as intrinsicCalibration and lensShadingMap.</p>
* <p>One limitation of controlling zoom using zoomRatio is that the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}
* must only be used for letterboxing or pillarboxing of the sensor active array, and no
* FREEFORM cropping can be used with {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} other than 1.0.</p>
@@ -2216,7 +2255,6 @@
* @see CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CaptureRequest#SCALER_CROP_REGION
- * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
@PublicKey
@NonNull
@@ -2574,12 +2612,15 @@
* frames before the lens can change to the requested focal length.
* While the focal length is still changing, {@link CaptureResult#LENS_STATE android.lens.state} will
* be set to MOVING.</p>
- * <p>Optical zoom will not be supported on most devices.</p>
+ * <p>Optical zoom via this control will not be supported on most devices. Starting from API
+ * level 30, the camera device may combine optical and digital zoom through the
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} control.</p>
* <p><b>Units</b>: Millimeters</p>
* <p><b>Range of valid values:</b><br>
* {@link CameraCharacteristics#LENS_INFO_AVAILABLE_FOCAL_LENGTHS android.lens.info.availableFocalLengths}</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
* @see CaptureRequest#LENS_APERTURE
* @see CaptureRequest#LENS_FOCUS_DISTANCE
* @see CameraCharacteristics#LENS_INFO_AVAILABLE_FOCAL_LENGTHS
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 096aa0c..be03502 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2410,27 +2410,66 @@
* {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} can still be used to specify the horizontal or vertical
* crop to achieve aspect ratios different than the native camera sensor.</p>
* <p>By using this control, the application gains a simpler way to control zoom, which can
- * be a combination of optical and digital zoom. More specifically, for a logical
- * multi-camera with more than one focal length, using a floating point zoom ratio offers
- * more zoom precision when a telephoto lens is used, as well as allowing zoom ratio of
- * less than 1.0 to zoom out to a wide field of view.</p>
- * <p>Note that the coordinate system of cropRegion, AE/AWB/AF regions, and faces now changes
- * to the effective after-zoom field-of-view represented by rectangle of (0, 0,
- * activeArrayWidth, activeArrayHeight).</p>
- * <p>For example, if {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} is 4032*3024, and the preview stream
- * is configured to the same 4:3 aspect ratio, the application can achieve 2.0x zoom in
- * one of two ways:</p>
+ * be a combination of optical and digital zoom. For example, a multi-camera system may
+ * contain more than one lens with different focal lengths, and the user can use optical
+ * zoom by switching between lenses. Using zoomRatio has benefits in the scenarios below:
+ * <em> Zooming in from a wide-angle lens to a telephoto lens: A floating-point ratio provides
+ * better precision compared to an integer value of {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}.
+ * </em> Zooming out from a wide lens to an ultrawide lens: zoomRatio supports zoom-out whereas
+ * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} doesn't.</p>
+ * <p>To illustrate, here are several scenarios of different zoom ratios, crop regions,
+ * and output streams, for a hypothetical camera device with an active array of size
+ * <code>(2000,1500)</code>.</p>
* <ul>
- * <li>zoomRatio = 2.0, scaler.cropRegion = (0, 0, 4032, 3024)</li>
- * <li>zoomRatio = 1.0 (default), scaler.cropRegion = (1008, 756, 3024, 2268)</li>
+ * <li>Camera Configuration:<ul>
+ * <li>Active array size: <code>2000x1500</code> (3 MP, 4:3 aspect ratio)</li>
+ * <li>Output stream #1: <code>640x480</code> (VGA, 4:3 aspect ratio)</li>
+ * <li>Output stream #2: <code>1280x720</code> (720p, 16:9 aspect ratio)</li>
* </ul>
- * <p>If the application intends to set aeRegions to be top-left quarter of the preview
- * field-of-view, the {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions} should be set to (0, 0, 2016, 1512) with
+ * </li>
+ * <li>Case #1: 4:3 crop region with 2.0x zoom ratio<ul>
+ * <li>Zoomed field of view: 1/4 of original field of view</li>
+ * <li>Crop region: <code>Rect(0, 0, 2000, 1500) // (left, top, right, bottom)</code> (post zoom)</li>
+ * </ul>
+ * </li>
+ * <li><img alt="4:3 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png" /><ul>
+ * <li><code>640x480</code> stream source area: <code>(0, 0, 2000, 1500)</code> (equal to crop region)</li>
+ * <li><code>1280x720</code> stream source area: <code>(0, 187, 2000, 1312)</code> (letterboxed)</li>
+ * </ul>
+ * </li>
+ * <li>Case #2: 16:9 crop region with 2.0x zoom.<ul>
+ * <li>Zoomed field of view: 1/4 of original field of view</li>
+ * <li>Crop region: <code>Rect(0, 187, 2000, 1312)</code></li>
+ * <li><img alt="16:9 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(250, 187, 1750, 1312)</code> (pillarboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(0, 187, 2000, 1312)</code> (equal to crop region)</li>
+ * </ul>
+ * </li>
+ * <li>Case #3: 1:1 crop region with 0.5x zoom out to ultrawide lens.<ul>
+ * <li>Zoomed field of view: 4x of original field of view (switched from wide lens to ultrawide lens)</li>
+ * <li>Crop region: <code>Rect(250, 0, 1750, 1500)</code></li>
+ * <li><img alt="1:1 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png" /></li>
+ * <li><code>640x480</code> stream source area: <code>(250, 187, 1750, 1312)</code> (letterboxed)</li>
+ * <li><code>1280x720</code> stream source area: <code>(250, 328, 1750, 1172)</code> (letterboxed)</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * <p>As seen from the graphs above, the coordinate system of cropRegion now changes to the
+ * effective after-zoom field-of-view, and is represented by the rectangle of (0, 0,
+ * activeArrayWith, activeArrayHeight). The same applies to AE/AWB/AF regions, and faces.
+ * This coordinate system change isn't applicable to RAW capture and its related
+ * metadata such as intrinsicCalibration and lensShadingMap.</p>
+ * <p>Using the same hypothetical example above, and assuming output stream #1 (640x480) is
+ * the viewfinder stream, the application can achieve 2.0x zoom in one of two ways:</p>
+ * <ul>
+ * <li>zoomRatio = 2.0, scaler.cropRegion = (0, 0, 2000, 1500)</li>
+ * <li>zoomRatio = 1.0 (default), scaler.cropRegion = (500, 375, 1500, 1125)</li>
+ * </ul>
+ * <p>If the application intends to set aeRegions to be top-left quarter of the viewfinder
+ * field-of-view, the {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions} should be set to (0, 0, 1000, 750) with
* zoomRatio set to 2.0. Alternatively, the application can set aeRegions to the equivalent
- * region of (1008, 756, 2016, 1512) for zoomRatio of 1.0. If the application doesn't
+ * region of (500, 375, 1000, 750) for zoomRatio of 1.0. If the application doesn't
* explicitly set {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, its value defaults to 1.0.</p>
- * <p>This coordinate system change isn't applicable to RAW capture and its related metadata
- * such as intrinsicCalibration and lensShadingMap.</p>
* <p>One limitation of controlling zoom using zoomRatio is that the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}
* must only be used for letterboxing or pillarboxing of the sensor active array, and no
* FREEFORM cropping can be used with {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} other than 1.0.</p>
@@ -2446,7 +2485,6 @@
* @see CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CaptureRequest#SCALER_CROP_REGION
- * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
@PublicKey
@NonNull
@@ -2848,12 +2886,15 @@
* frames before the lens can change to the requested focal length.
* While the focal length is still changing, {@link CaptureResult#LENS_STATE android.lens.state} will
* be set to MOVING.</p>
- * <p>Optical zoom will not be supported on most devices.</p>
+ * <p>Optical zoom via this control will not be supported on most devices. Starting from API
+ * level 30, the camera device may combine optical and digital zoom through the
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} control.</p>
* <p><b>Units</b>: Millimeters</p>
* <p><b>Range of valid values:</b><br>
* {@link CameraCharacteristics#LENS_INFO_AVAILABLE_FOCAL_LENGTHS android.lens.info.availableFocalLengths}</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
* @see CaptureRequest#LENS_APERTURE
* @see CaptureRequest#LENS_FOCUS_DISTANCE
* @see CameraCharacteristics#LENS_INFO_AVAILABLE_FOCAL_LENGTHS
diff --git a/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
new file mode 100644
index 0000000..1acc59d
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
new file mode 100644
index 0000000..4ab9ca4
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
new file mode 100644
index 0000000..d74e673
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
Binary files differ
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index da93db7..f2d40ce 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -300,11 +300,6 @@
android:exported="false"
android:permission="com.android.systemui.permission.SELF" />
- <service android:name=".assist.AssistHandleService"
- android:exported="true"
- android:enabled="false"
- />
-
<!-- started from PhoneWindowManager
TODO: Should have an android:permission attribute -->
<service android:name=".screenshot.TakeScreenshotService"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 18357a9..3afe19f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -966,17 +966,20 @@
boolean changed = false;
if (enabled && (oldIntent == null)) {
- Intent intent = new Intent(
- DevicePolicyManager.ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE);
- ComponentName profileOwnerComponent =
- mDevicePolicyManager.getProfileOwnerAsUser(userId);
- intent.setComponent(profileOwnerComponent);
- ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, 0);
- if (resolveInfo != null) {
- Intent newIntent = new Intent();
- newIntent.setComponent(profileOwnerComponent);
- mSecondaryLockscreenRequirement.put(userId, newIntent);
- changed = true;
+ ComponentName poComponent = mDevicePolicyManager.getProfileOwnerAsUser(userId);
+ if (poComponent == null) {
+ Log.e(TAG, "No profile owner found for User " + userId);
+ } else {
+ Intent intent =
+ new Intent(DevicePolicyManager.ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE)
+ .setPackage(poComponent.getPackageName());
+ ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, 0);
+ if (resolveInfo != null && resolveInfo.serviceInfo != null) {
+ Intent launchIntent =
+ new Intent().setComponent(resolveInfo.serviceInfo.getComponentName());
+ mSecondaryLockscreenRequirement.put(userId, launchIntent);
+ changed = true;
+ }
}
} else if (!enabled && (oldIntent != null)) {
mSecondaryLockscreenRequirement.put(userId, null);
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index e2b12da..59af458 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -86,7 +86,8 @@
private float mInitialTouchSpan;
private float mLastFocusY;
private float mLastSpanY;
- private int mTouchSlop;
+ private final int mTouchSlop;
+ private final float mSlopMultiplier;
private float mLastMotionY;
private float mPullGestureMinXSpan;
private Callback mCallback;
@@ -177,6 +178,7 @@
final ViewConfiguration configuration = ViewConfiguration.get(mContext);
mTouchSlop = configuration.getScaledTouchSlop();
+ mSlopMultiplier = configuration.getAmbiguousGestureMultiplier();
mSGD = new ScaleGestureDetector(context, mScaleGestureListener);
mFlingAnimationUtils = new FlingAnimationUtils(mContext.getResources().getDisplayMetrics(),
@@ -258,6 +260,13 @@
mScrollAdapter = adapter;
}
+ private float getTouchSlop(MotionEvent event) {
+ // Adjust the touch slop if another gesture may be being performed.
+ return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+ ? mTouchSlop * mSlopMultiplier
+ : mTouchSlop;
+ }
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!isEnabled()) {
@@ -303,7 +312,7 @@
if (mWatchingForPull) {
final float yDiff = ev.getRawY() - mInitialTouchY;
final float xDiff = ev.getRawX() - mInitialTouchX;
- if (yDiff > mTouchSlop && yDiff > Math.abs(xDiff)) {
+ if (yDiff > getTouchSlop(ev) && yDiff > Math.abs(xDiff)) {
if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
mWatchingForPull = false;
if (mResizedView != null && !isFullyExpanded(mResizedView)) {
@@ -431,7 +440,7 @@
if (mWatchingForPull) {
final float yDiff = ev.getRawY() - mInitialTouchY;
final float xDiff = ev.getRawX() - mInitialTouchX;
- if (yDiff > mTouchSlop && yDiff > Math.abs(xDiff)) {
+ if (yDiff > getTouchSlop(ev) && yDiff > Math.abs(xDiff)) {
if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
mWatchingForPull = false;
if (mResizedView != null && !isFullyExpanded(mResizedView)) {
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 02a4521..d17ca404 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -69,6 +69,7 @@
private final FlingAnimationUtils mFlingAnimationUtils;
private float mPagingTouchSlop;
+ private final float mSlopMultiplier;
private final Callback mCallback;
private final int mSwipeDirection;
private final VelocityTracker mVelocityTracker;
@@ -84,11 +85,28 @@
private float mTranslation = 0;
private boolean mMenuRowIntercepting;
- private boolean mLongPressSent;
- private Runnable mWatchLongPress;
private final long mLongPressTimeout;
+ private boolean mLongPressSent;
+ private final float[] mDownLocation = new float[2];
+ private final Runnable mPerformLongPress = new Runnable() {
- final private int[] mTmpPos = new int[2];
+ private final int[] mViewOffset = new int[2];
+
+ @Override
+ public void run() {
+ if (mCurrView != null && !mLongPressSent) {
+ mLongPressSent = true;
+ if (mCurrView instanceof ExpandableNotificationRow) {
+ mCurrView.getLocationOnScreen(mViewOffset);
+ final int x = (int) mDownLocation[0] - mViewOffset[0];
+ final int y = (int) mDownLocation[1] - mViewOffset[1];
+ mCurrView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+ ((ExpandableNotificationRow) mCurrView).doLongClickCallback(x, y);
+ }
+ }
+ }
+ };
+
private final int mFalsingThreshold;
private boolean mTouchAboveFalsingThreshold;
private boolean mDisableHwLayers;
@@ -102,7 +120,9 @@
mHandler = new Handler();
mSwipeDirection = swipeDirection;
mVelocityTracker = VelocityTracker.obtain();
- mPagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
+ final ViewConfiguration configuration = ViewConfiguration.get(context);
+ mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
+ mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
// Extra long-press!
mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
@@ -255,10 +275,7 @@
}
public void cancelLongPress() {
- if (mWatchLongPress != null) {
- mHandler.removeCallbacks(mWatchLongPress);
- mWatchLongPress = null;
- }
+ mHandler.removeCallbacks(mPerformLongPress);
}
@Override
@@ -287,27 +304,9 @@
mInitialTouchPos = getPos(ev);
mPerpendicularInitialTouchPos = getPerpendicularPos(ev);
mTranslation = getTranslation(mCurrView);
- if (mWatchLongPress == null) {
- mWatchLongPress = new Runnable() {
- @Override
- public void run() {
- if (mCurrView != null && !mLongPressSent) {
- mLongPressSent = true;
- mCurrView.getLocationOnScreen(mTmpPos);
- final int x = (int) ev.getRawX() - mTmpPos[0];
- final int y = (int) ev.getRawY() - mTmpPos[1];
- if (mCurrView instanceof ExpandableNotificationRow) {
- mCurrView.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
- ExpandableNotificationRow currRow =
- (ExpandableNotificationRow) mCurrView;
- currRow.doLongClickCallback(x, y);
- }
- }
- }
- };
- }
- mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
+ mDownLocation[0] = ev.getRawX();
+ mDownLocation[1] = ev.getRawY();
+ mHandler.postDelayed(mPerformLongPress, mLongPressTimeout);
}
break;
@@ -318,7 +317,12 @@
float perpendicularPos = getPerpendicularPos(ev);
float delta = pos - mInitialTouchPos;
float deltaPerpendicular = perpendicularPos - mPerpendicularInitialTouchPos;
- if (Math.abs(delta) > mPagingTouchSlop
+ // Adjust the touch slop if another gesture may be being performed.
+ final float pagingTouchSlop =
+ ev.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+ ? mPagingTouchSlop * mSlopMultiplier
+ : mPagingTouchSlop;
+ if (Math.abs(delta) > pagingTouchSlop
&& Math.abs(delta) > Math.abs(deltaPerpendicular)) {
if (mCallback.canChildBeDragged(mCurrView)) {
mCallback.onBeginDrag(mCurrView);
@@ -327,6 +331,11 @@
mTranslation = getTranslation(mCurrView);
}
cancelLongPress();
+ } else if (ev.getClassification() == MotionEvent.CLASSIFICATION_DEEP_PRESS
+ && mHandler.hasCallbacks(mPerformLongPress)) {
+ // Accelerate the long press signal.
+ cancelLongPress();
+ mPerformLongPress.run();
}
}
break;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt
deleted file mode 100644
index 9ceafc6..0000000
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2019 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.assist
-
-import android.app.Service
-import android.content.Intent
-import android.os.IBinder
-import dagger.Lazy
-import javax.inject.Inject
-
-class AssistHandleService @Inject constructor(private val assistManager: Lazy<AssistManager>)
- : Service() {
-
- private val binder = object : IAssistHandleService.Stub() {
- override fun requestAssistHandles() {
- assistManager.get().requestAssistHandles()
- }
- }
-
- override fun onBind(intent: Intent?): IBinder? {
- return binder
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
index 96939b01..6f5a17d 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
@@ -16,7 +16,6 @@
package com.android.systemui.assist;
-import android.app.Service;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
@@ -34,11 +33,8 @@
import javax.inject.Named;
import javax.inject.Singleton;
-import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.ClassKey;
-import dagger.multibindings.IntoMap;
/** Module for dagger injections related to the Assistant. */
@Module
@@ -91,9 +87,4 @@
static Clock provideSystemClock() {
return SystemClock::uptimeMillis;
}
-
- @Binds
- @IntoMap
- @ClassKey(AssistHandleService.class)
- abstract Service bindAssistHandleService(AssistHandleService assistHandleService);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 5adee40..4fa7822 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -48,7 +48,8 @@
private float mInitialTouchX;
private float mInitialTouchY;
private boolean mDraggingDown;
- private float mTouchSlop;
+ private final float mTouchSlop;
+ private final float mSlopMultiplier;
private DragDownCallback mDragDownCallback;
private View mHost;
private final int[] mTemp2 = new int[2];
@@ -62,7 +63,9 @@
FalsingManager falsingManager) {
mMinDragDistance = context.getResources().getDimensionPixelSize(
R.dimen.keyguard_drag_down_min_distance);
- mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ final ViewConfiguration configuration = ViewConfiguration.get(context);
+ mTouchSlop = configuration.getScaledTouchSlop();
+ mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
mCallback = callback;
mDragDownCallback = dragDownCallback;
mHost = host;
@@ -85,7 +88,12 @@
case MotionEvent.ACTION_MOVE:
final float h = y - mInitialTouchY;
- if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) {
+ // Adjust the touch slop if another gesture may be being performed.
+ final float touchSlop =
+ event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+ ? mTouchSlop * mSlopMultiplier
+ : mTouchSlop;
+ if (h > touchSlop && h > Math.abs(x - mInitialTouchX)) {
mFalsingManager.onNotificatonStartDraggingDown();
mDraggingDown = true;
captureStartingChild(mInitialTouchX, mInitialTouchY);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 4d4a2ded..be8af82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -222,6 +222,7 @@
private boolean mIsScrollerBoundSet;
private Runnable mFinishScrollingCallback;
private int mTouchSlop;
+ private float mSlopMultiplier;
private int mMinimumVelocity;
private int mMaximumVelocity;
private int mOverflingDistance;
@@ -1022,6 +1023,7 @@
setClipChildren(false);
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
+ mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mOverflingDistance = configuration.getScaledOverflingDistance();
@@ -3744,15 +3746,23 @@
mLongPressListener = listener;
}
+ private float getTouchSlop(MotionEvent event) {
+ // Adjust the touch slop if another gesture may be being performed.
+ return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+ ? mTouchSlop * mSlopMultiplier
+ : mTouchSlop;
+ }
+
@Override
@ShadeViewRefactor(RefactorComponent.INPUT)
public boolean onTouchEvent(MotionEvent ev) {
+ NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
|| ev.getActionMasked() == MotionEvent.ACTION_UP;
handleEmptySpaceClick(ev);
boolean expandWantsIt = false;
boolean swipingInProgress = mSwipingInProgress;
- if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
+ if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion && guts == null) {
if (isCancelOrUp) {
mExpandHelper.onlyObserveMovements(false);
}
@@ -3778,7 +3788,6 @@
}
// Check if we need to clear any snooze leavebehinds
- NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
&& guts.getGutsContent() instanceof NotificationSnooze) {
NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
@@ -3891,12 +3900,13 @@
int deltaY = mLastMotionY - y;
final int xDiff = Math.abs(x - mDownX);
final int yDiff = Math.abs(deltaY);
- if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
+ final float touchSlop = getTouchSlop(ev);
+ if (!mIsBeingDragged && yDiff > touchSlop && yDiff > xDiff) {
setIsBeingDragged(true);
if (deltaY > 0) {
- deltaY -= mTouchSlop;
+ deltaY -= touchSlop;
} else {
- deltaY += mTouchSlop;
+ deltaY += touchSlop;
}
}
if (mIsBeingDragged) {
@@ -4046,9 +4056,11 @@
public boolean onInterceptTouchEvent(MotionEvent ev) {
initDownStates(ev);
handleEmptySpaceClick(ev);
+
+ NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
boolean expandWantsIt = false;
boolean swipingInProgress = mSwipingInProgress;
- if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
+ if (!swipingInProgress && !mOnlyScrollingInThisMotion && guts == null) {
expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
}
boolean scrollWantsIt = false;
@@ -4065,7 +4077,6 @@
}
// Check if we need to clear any snooze leavebehinds
boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
- NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
!expandWantsIt && !scrollWantsIt) {
mCheckForLeavebehind = false;
@@ -4083,8 +4094,9 @@
private void handleEmptySpaceClick(MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
- if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
- || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
+ final float touchSlop = getTouchSlop(ev);
+ if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > touchSlop
+ || Math.abs(ev.getX() - mInitialTouchX) > touchSlop)) {
mTouchIsClick = false;
}
break;
@@ -4168,7 +4180,7 @@
final int x = (int) ev.getX(pointerIndex);
final int yDiff = Math.abs(y - mLastMotionY);
final int xDiff = Math.abs(x - mDownX);
- if (yDiff > mTouchSlop && yDiff > xDiff) {
+ if (yDiff > getTouchSlop(ev) && yDiff > xDiff) {
setIsBeingDragged(true);
mLastMotionY = y;
mDownX = x;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index f9726d2..5588c24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1021,7 +1021,8 @@
trackMovement(event);
return true;
}
- if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
+ if (Math.abs(h) > getTouchSlop(event)
+ && Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
mQsTracking = true;
onQsExpansionStarted();
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 2719a32..481401b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -30,8 +30,6 @@
protected StatusBar mStatusBar;
protected HeadsUpManagerPhone mHeadsUpManager;
- protected int mTouchSlop;
-
protected KeyguardBottomAreaView mKeyguardBottomArea;
private OnConfigurationChangedListener mOnConfigurationChangedListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 30367ed..83cc4e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -91,7 +91,8 @@
protected boolean mTracking;
private boolean mTouchSlopExceeded;
private int mTrackingPointer;
- protected int mTouchSlop;
+ private int mTouchSlop;
+ private float mSlopMultiplier;
protected boolean mHintAnimationRunning;
private boolean mOverExpandedBeforeFling;
private boolean mTouchAboveFalsingThreshold;
@@ -260,11 +261,19 @@
protected void loadDimens() {
final ViewConfiguration configuration = ViewConfiguration.get(mView.getContext());
mTouchSlop = configuration.getScaledTouchSlop();
+ mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
mHintDistance = mResources.getDimension(R.dimen.hint_move_distance);
mUnlockFalsingThreshold = mResources.getDimensionPixelSize(
R.dimen.unlock_falsing_threshold);
}
+ protected float getTouchSlop(MotionEvent event) {
+ // Adjust the touch slop if another gesture may be being performed.
+ return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+ ? mTouchSlop * mSlopMultiplier
+ : mTouchSlop;
+ }
+
private void addMovement(MotionEvent event) {
// Add movement to velocity tracker using raw screen X and Y coordinates instead
// of window coordinates because the window frame may be moving at the same time.
@@ -1111,7 +1120,8 @@
addMovement(event);
if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
float hAbs = Math.abs(h);
- if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop))
+ float touchSlop = getTouchSlop(event);
+ if ((h < -touchSlop || (mAnimatingOnDown && hAbs > touchSlop))
&& hAbs > Math.abs(x - mInitialTouchX)) {
cancelHeightAnimator();
startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
@@ -1228,7 +1238,8 @@
// If the panel was collapsed when touching, we only need to check for the
// y-component of the gesture, as we have no conflicting horizontal gesture.
- if (Math.abs(h) > mTouchSlop && (Math.abs(h) > Math.abs(x - mInitialTouchX)
+ if (Math.abs(h) > getTouchSlop(event)
+ && (Math.abs(h) > Math.abs(x - mInitialTouchX)
|| mIgnoreXTouchSlop)) {
mTouchSlopExceeded = true;
if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9b04e79..fe33fae9 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1876,7 +1876,7 @@
// package was t(q) then the next delivery must be after t(q) + <window_size>
final long t = mAppWakeupHistory.getNthLastWakeupForPackage(
sourcePackage, sourceUserId, quotaForBucket);
- minElapsed = t + 1 + mConstants.APP_STANDBY_WINDOW;
+ minElapsed = t + mConstants.APP_STANDBY_WINDOW;
}
if (alarm.expectedWhenElapsed < minElapsed) {
alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index cea3251..3148a62 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -103,7 +103,7 @@
private static boolean PROP_PIN_CAMERA =
DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
"pin_camera",
- SystemProperties.getBoolean("pinner.pin_camera", true));
+ SystemProperties.getBoolean("pinner.pin_camera", false));
// Pin using pinlist.meta when pinning apps.
private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean(
"pinner.use_pinlist", true);
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index e5cb554..7792123 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -15,9 +15,7 @@
*/
package com.android.server.notification;
-import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.FLAG_BUBBLE;
-import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
@@ -25,7 +23,6 @@
import android.app.ActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
-import android.app.Person;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -34,8 +31,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-import java.util.ArrayList;
-
/**
* Determines whether a bubble can be shown for this notification
*/
@@ -152,41 +147,13 @@
return false;
}
- // At this point the bubble must fulfill communication policy
-
- // Communication always needs a person
- ArrayList<Person> peopleList = notification.extras != null
- ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
- : null;
- // Message style requires a person & it's not included in the list
boolean isMessageStyle = Notification.MessagingStyle.class.equals(
notification.getNotificationStyle());
- if (!isMessageStyle && (peopleList == null || peopleList.isEmpty())) {
- logBubbleError(r.getKey(), "Must have a person and be "
- + "Notification.MessageStyle or Notification.CATEGORY_CALL");
+ if (!isMessageStyle) {
+ logBubbleError(r.getKey(), "must be Notification.MessageStyle");
return false;
}
-
- // Communication is a message or a call
- boolean isCall = CATEGORY_CALL.equals(notification.category);
- boolean hasForegroundService = (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
- if (hasForegroundService && !isCall) {
- logBubbleError(r.getKey(),
- "foreground services must be Notification.CATEGORY_CALL to bubble");
- return false;
- }
- if (isMessageStyle) {
- return true;
- } else if (isCall) {
- if (hasForegroundService) {
- return true;
- }
- logBubbleError(r.getKey(), "calls require foreground service");
- return false;
- }
- logBubbleError(r.getKey(), "Must be "
- + "Notification.MessageStyle or Notification.CATEGORY_CALL");
- return false;
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f65f187..35dec5a 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -243,7 +243,6 @@
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.TriPredicate;
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index bb149cf..09af442 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -496,7 +496,7 @@
// This one should get deferred on set
setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
getNewMockPendingIntent());
- final long expectedNextTrigger = firstTrigger + 1 + mAppStandbyWindow;
+ final long expectedNextTrigger = firstTrigger + mAppStandbyWindow;
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
}
@@ -516,7 +516,7 @@
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
}
- final long expectedNextTrigger = firstTrigger + 1 + mAppStandbyWindow;
+ final long expectedNextTrigger = firstTrigger + mAppStandbyWindow;
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
}
@@ -676,7 +676,7 @@
final int rareQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_RARE);
// The last alarm should now be deferred.
final long expectedNextTrigger = (firstTrigger + workingQuota - 1 - rareQuota)
- + mAppStandbyWindow + 1;
+ + mAppStandbyWindow;
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
}
@@ -695,7 +695,7 @@
}
}
// The last alarm should be deferred due to exceeding the quota
- final long deferredTrigger = firstTrigger + 1 + mAppStandbyWindow;
+ final long deferredTrigger = firstTrigger + mAppStandbyWindow;
assertEquals(deferredTrigger, mTestTimer.getElapsed());
// Upgrading the bucket now
@@ -730,7 +730,7 @@
mTestTimer.expire();
}
// Any subsequent alarms in queue should all be deferred
- assertEquals(firstTrigger + mAppStandbyWindow + 1, mTestTimer.getElapsed());
+ assertEquals(firstTrigger + mAppStandbyWindow, mTestTimer.getElapsed());
// Paroling now
assertAndHandleParoleChanged(true);
@@ -744,7 +744,7 @@
assertAndHandleParoleChanged(false);
// Subsequent alarms should again get deferred
- final long expectedNextTrigger = (firstTrigger + 5) + 1 + mAppStandbyWindow;
+ final long expectedNextTrigger = (firstTrigger + 5) + mAppStandbyWindow;
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
}
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 9db7d5e..3c29445 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -18,7 +18,6 @@
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
-import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.FLAG_AUTO_CANCEL;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
@@ -5237,141 +5236,6 @@
}
@Test
- public void testFlagBubbleNotifs_flag_phonecall() throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
-
- // Give it bubble metadata
- Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
- // Give it a person
- Person person = new Person.Builder()
- .setName("bubblebot")
- .build();
- // Make it a phone call
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setCategory(CATEGORY_CALL)
- .addPerson(person)
- .setContentTitle("foo")
- .setBubbleMetadata(data)
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
- "testFlagBubbleNotifs_flag_phonecall", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- // Make sure it has foreground service
- sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
- nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
- waitForIdle();
-
- // yes phone call, yes person, yes foreground service, yes bubble
- assertTrue(mService.getNotificationRecord(
- sbn.getKey()).getNotification().isBubbleNotification());
- }
-
- @Test
- public void testFlagBubbleNotifs_noFlag_phonecall_noForegroundService() throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
-
- // Give it bubble metadata
- Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
- // Give it a person
- Person person = new Person.Builder()
- .setName("bubblebot")
- .build();
- // Make it a phone call
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setCategory(CATEGORY_CALL)
- .addPerson(person)
- .setContentTitle("foo")
- .setBubbleMetadata(data)
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
- nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
- waitForIdle();
-
- // yes phone call, yes person, NO foreground service, no bubble
- assertFalse(mService.getNotificationRecord(
- sbn.getKey()).getNotification().isBubbleNotification());
- }
-
- @Test
- public void testFlagBubbleNotifs_noFlag_phonecall_noPerson() throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
-
- // Give it bubble metadata
- Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
- // Make it a phone call
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setCategory(CATEGORY_CALL)
- .setContentTitle("foo")
- .setBubbleMetadata(data)
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
- "testFlagBubbleNotifs_noFlag_phonecall_noPerson", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- // Make sure it has foreground service
- sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
- nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
- waitForIdle();
-
- // yes phone call, yes foreground service, BUT NO person, no bubble
- assertFalse(mService.getNotificationRecord(
- sbn.getKey()).getNotification().isBubbleNotification());
- }
-
- @Test
- public void testFlagBubbleNotifs_noFlag_phonecall_noCategory() throws RemoteException {
- // Bubbles are allowed!
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
-
- // Give it bubble metadata
- Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
- // Give it a person
- Person person = new Person.Builder()
- .setName("bubblebot")
- .build();
- // No category
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .addPerson(person)
- .setContentTitle("foo")
- .setBubbleMetadata(data)
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
- "testFlagBubbleNotifs_noFlag_phonecall_noCategory", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- // Make sure it has foreground service
- sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
- nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
- waitForIdle();
-
- // yes person, yes foreground service, BUT NO call, no bubble
- assertFalse(mService.getNotificationRecord(
- sbn.getKey()).getNotification().isBubbleNotification());
- }
-
- @Test
public void testFlagBubbleNotifs_noFlag_messaging_appNotAllowed() throws RemoteException {
// Bubbles are NOT allowed!
setUpPrefsForBubbles(PKG, mUid, true /* global */, false /* app */, true /* channel */);
@@ -5432,77 +5296,6 @@
}
@Test
- public void testFlagBubbleNotifs_noFlag_phonecall_notAllowed() throws RemoteException {
- // Bubbles are not allowed!
- setUpPrefsForBubbles(PKG, mUid, false /* global */, true /* app */, true /* channel */);
-
- // Give it bubble metadata
- Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
- // Give it a person
- Person person = new Person.Builder()
- .setName("bubblebot")
- .build();
- // Make it a phone call
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setCategory(CATEGORY_CALL)
- .addPerson(person)
- .setContentTitle("foo")
- .setBubbleMetadata(data)
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
- "testFlagBubbleNotifs_noFlag_phonecall_notAllowed", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- // Make sure it has foreground service
- sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-
- mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
- sbn.getId(), sbn.getNotification(), sbn.getUserId());
- waitForIdle();
-
- // yes phone call, yes person, yes foreground service, but not allowed, no bubble
- assertFalse(mService.getNotificationRecord(
- sbn.getKey()).getNotification().isBubbleNotification());
- }
-
- @Test
- public void testFlagBubbleNotifs_noFlag_phonecall_channelNotAllowed() throws RemoteException {
- // Bubbles are allowed, but not on channel.
- setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, false /* channel */);
-
- // Give it bubble metadata
- Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
- // Give it a person
- Person person = new Person.Builder()
- .setName("bubblebot")
- .build();
- // Make it a phone call
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setCategory(CATEGORY_CALL)
- .addPerson(person)
- .setContentTitle("foo")
- .setBubbleMetadata(data)
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
- "testFlagBubbleNotifs_noFlag_phonecall_channelNotAllowed", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
- // Make sure it has foreground service
- sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
- mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
- nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
- waitForIdle();
-
- // yes phone call, yes person, yes foreground service, but channel not allowed, no bubble
- assertFalse(mService.getNotificationRecord(
- sbn.getKey()).getNotification().isBubbleNotification());
- }
-
- @Test
public void testCancelNotificationsFromApp_cancelsBubbles() throws Exception {
final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
nrBubble.getSbn().getNotification().flags |= FLAG_BUBBLE;
diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml
index 34e2e3a..ea5043a 100644
--- a/wifi/tests/AndroidTest.xml
+++ b/wifi/tests/AndroidTest.xml
@@ -30,5 +30,9 @@
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
<option name="mainline-module-package-name" value="com.google.android.wifi" />
+ <!-- TODO(b/151836001): com.android.wifi doesn't guarantee it is a Mainline module since
+ it could still be OEM customized. Workaround so that this test will still run on
+ AOSP builds. -->
+ <option name="mainline-module-package-name" value="com.android.wifi" />
</object>
</configuration>