summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/Android.bp16
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java6
-rw-r--r--apex/statsd/framework/java/android/util/StatsEvent.java48
-rw-r--r--apex/statsd/framework/test/src/android/util/StatsEventTest.java184
-rw-r--r--api/current.txt1
-rw-r--r--api/test-current.txt1
-rw-r--r--cmds/incidentd/src/FdBuffer.cpp22
-rw-r--r--cmds/incidentd/src/FdBuffer.h2
-rw-r--r--cmds/incidentd/src/PrivacyFilter.cpp20
-rw-r--r--cmds/incidentd/src/Reporter.cpp2
-rw-r--r--cmds/incidentd/src/Section.cpp115
-rw-r--r--cmds/incidentd/src/incidentd_util.cpp29
-rw-r--r--cmds/incidentd/src/incidentd_util.h30
-rw-r--r--core/java/android/app/ActivityThread.java5
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java4
-rw-r--r--core/java/android/content/pm/CrossProfileApps.java29
-rw-r--r--core/java/android/content/pm/ICrossProfileApps.aidl1
-rw-r--r--core/java/android/content/pm/PackageManager.java4
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java2
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java79
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java79
-rw-r--r--core/java/android/provider/Settings.java1
-rw-r--r--core/java/android/view/IWindowSession.aidl3
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java1
-rw-r--r--core/java/android/view/InsetsController.java32
-rw-r--r--core/java/android/view/InsetsState.java13
-rw-r--r--core/java/android/view/View.java10
-rw-r--r--core/java/android/view/ViewRootImpl.java152
-rw-r--r--core/java/android/view/WindowManagerImpl.java2
-rw-r--r--core/java/android/widget/Toast.java3
-rw-r--r--core/java/android/window/IDisplayAreaOrganizer.aidl28
-rw-r--r--core/java/android/window/IDisplayAreaOrganizerController.aidl26
-rw-r--r--core/java/android/window/ITaskOrganizer.aidl4
-rw-r--r--core/java/android/window/IWindowOrganizerController.aidl4
-rw-r--r--core/java/android/window/WindowOrganizer.java44
-rw-r--r--core/res/res/drawable/resolver_turn_on_work_button_ripple_background.xml10
-rw-r--r--core/res/res/layout/resolver_empty_states.xml4
-rw-r--r--core/res/res/values/strings.xml4
-rw-r--r--core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java2
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java2
-rw-r--r--core/tests/coretests/src/android/view/InsetsStateTest.java18
-rw-r--r--docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.pngbin0 -> 28504 bytes
-rw-r--r--docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.pngbin0 -> 25510 bytes
-rw-r--r--docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.pngbin0 -> 25561 bytes
-rw-r--r--libs/protoutil/include/android/util/ProtoOutputStream.h1
-rw-r--r--libs/protoutil/src/EncodedBuffer.cpp14
-rw-r--r--libs/protoutil/src/ProtoOutputStream.cpp8
-rw-r--r--packages/SystemUI/AndroidManifest.xml5
-rw-r--r--packages/SystemUI/res/layout/keyguard_media_header.xml153
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java266
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/ExpandHelper.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt120
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java77
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java2
-rw-r--r--services/core/java/com/android/server/PinnerService.java2
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java44
-rw-r--r--services/core/java/com/android/server/notification/BubbleExtractor.java39
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java1
-rw-r--r--services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java24
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java48
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java110
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java30
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java3
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java35
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java46
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskTile.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java33
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java48
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java84
-rw-r--r--services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java32
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java12
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java207
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java63
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java10
-rw-r--r--tests/BootImageProfileTest/AndroidTest.xml4
-rw-r--r--tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java4
-rw-r--r--tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java4
-rw-r--r--wifi/tests/AndroidTest.xml4
109 files changed, 2193 insertions, 936 deletions
diff --git a/apex/Android.bp b/apex/Android.bp
index 88c43f984847..cd34f98690d5 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -60,6 +60,10 @@ stubs_defaults {
removed_api_file: "api/removed.txt",
},
},
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/public/api",
+ },
}
stubs_defaults {
@@ -74,6 +78,10 @@ stubs_defaults {
removed_api_file: "api/system-removed.txt",
},
},
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/system/api",
+ },
}
// The defaults for module_libs comes in two parts - defaults for API checks
@@ -93,6 +101,10 @@ stubs_defaults {
removed_api_file: "api/module-lib-removed.txt",
},
},
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/module-lib/api",
+ },
}
stubs_defaults {
@@ -113,6 +125,10 @@ stubs_defaults {
removed_api_file: "api/removed.txt",
},
},
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/system-server/api",
+ },
}
// Empty for now, but a convenient place to add rules for all
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index cb87c6cefef5..3f254c043578 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -563,12 +563,10 @@ public class BlobStoreManager {
/**
* Return the {@link BlobHandle BlobHandles} corresponding to the data blobs that
- * the calling app has acquired a lease on using {@link #acquireLease(BlobHandle, int)} or
- * one of it's other variants.
+ * the calling app currently has a lease on.
*
- * @hide
+ * @return a list of {@link BlobHandle BlobHandles} that the caller has a lease on.
*/
- @TestApi
@NonNull
public List<BlobHandle> getLeasedBlobs() throws IOException {
try {
diff --git a/apex/statsd/framework/java/android/util/StatsEvent.java b/apex/statsd/framework/java/android/util/StatsEvent.java
index 1a45c4a5b7f6..8bd36a516b12 100644
--- a/apex/statsd/framework/java/android/util/StatsEvent.java
+++ b/apex/statsd/framework/java/android/util/StatsEvent.java
@@ -188,6 +188,12 @@ public final class StatsEvent {
@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 @@ public final class StatsEvent {
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 final class StatsEvent {
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 @@ public final class StatsEvent {
mCurrentAnnotationCount++;
writeAnnotationCount();
}
+
return this;
}
@@ -576,7 +596,7 @@ public final class StatsEvent {
**/
@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 @@ public final class StatsEvent {
mCurrentAnnotationCount++;
writeAnnotationCount();
}
+
return this;
}
@@ -619,19 +640,20 @@ public final class StatsEvent {
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 ac25e2734ac9..7b511553a26f 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 @@ public class StatsEventTest {
}
@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 @@ public class StatsEventTest {
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/api/current.txt b/api/current.txt
index c19a70c5c250..680b2248249a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7605,6 +7605,7 @@ package android.app.blob {
method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int) throws java.io.IOException;
method public void acquireLease(@NonNull android.app.blob.BlobHandle, @NonNull CharSequence) throws java.io.IOException;
method @IntRange(from=1) public long createSession(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
+ method @NonNull public java.util.List<android.app.blob.BlobHandle> getLeasedBlobs() throws java.io.IOException;
method @IntRange(from=0) public long getRemainingLeaseQuotaBytes();
method @NonNull public android.os.ParcelFileDescriptor openBlob(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
method @NonNull public android.app.blob.BlobStoreManager.Session openSession(@IntRange(from=1) long) throws java.io.IOException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 38cf6a16de56..0bd8a195edff 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -598,7 +598,6 @@ package android.app.blob {
public class BlobStoreManager {
method @Nullable public android.app.blob.LeaseInfo getLeaseInfo(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
- method @NonNull public java.util.List<android.app.blob.BlobHandle> getLeasedBlobs() throws java.io.IOException;
method public void waitForIdle(long) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException;
}
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index d295b84baf67..78c322edcccc 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -17,6 +17,7 @@
#include "Log.h"
#include "FdBuffer.h"
+#include "incidentd_util.h"
#include <log/log.h>
#include <utils/SystemClock.h>
@@ -31,17 +32,24 @@ namespace os {
namespace incidentd {
const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB
-const ssize_t MAX_BUFFER_COUNT = 6144; // 96 MB max
+const ssize_t MAX_BUFFER_SIZE = 96 * 1024 * 1024; // 96 MB
-FdBuffer::FdBuffer()
- :mBuffer(new EncodedBuffer(BUFFER_SIZE)),
+FdBuffer::FdBuffer(): FdBuffer(get_buffer_from_pool(), /* isBufferPooled= */ true) {
+}
+
+FdBuffer::FdBuffer(sp<EncodedBuffer> buffer, bool isBufferPooled)
+ :mBuffer(buffer),
mStartTime(-1),
mFinishTime(-1),
mTimedOut(false),
- mTruncated(false) {
+ mTruncated(false),
+ mIsBufferPooled(isBufferPooled) {
}
FdBuffer::~FdBuffer() {
+ if (mIsBufferPooled) {
+ return_buffer_to_pool(mBuffer);
+ }
}
status_t FdBuffer::read(int fd, int64_t timeout) {
@@ -51,7 +59,7 @@ status_t FdBuffer::read(int fd, int64_t timeout) {
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
while (true) {
- if (mBuffer->size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+ if (mBuffer->size() >= MAX_BUFFER_SIZE) {
mTruncated = true;
VLOG("Truncating data");
break;
@@ -106,7 +114,7 @@ status_t FdBuffer::readFully(int fd) {
mStartTime = uptimeMillis();
while (true) {
- if (mBuffer->size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+ if (mBuffer->size() >= MAX_BUFFER_SIZE) {
// Don't let it get too big.
mTruncated = true;
VLOG("Truncating data");
@@ -156,7 +164,7 @@ status_t FdBuffer::readProcessedDataInStream(int fd, unique_fd toFd, unique_fd f
// This is the buffer used to store processed data
while (true) {
- if (mBuffer->size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+ if (mBuffer->size() >= MAX_BUFFER_SIZE) {
VLOG("Truncating data");
mTruncated = true;
break;
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index a3493604f425..9b2794d165fb 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -35,6 +35,7 @@ using namespace android::util;
class FdBuffer {
public:
FdBuffer();
+ FdBuffer(sp<EncodedBuffer> buffer, bool isBufferPooled = false);
~FdBuffer();
/**
@@ -114,6 +115,7 @@ private:
int64_t mFinishTime;
bool mTimedOut;
bool mTruncated;
+ bool mIsBufferPooled;
};
} // namespace incidentd
diff --git a/cmds/incidentd/src/PrivacyFilter.cpp b/cmds/incidentd/src/PrivacyFilter.cpp
index d00ecdde5c63..0d427d1021a6 100644
--- a/cmds/incidentd/src/PrivacyFilter.cpp
+++ b/cmds/incidentd/src/PrivacyFilter.cpp
@@ -19,9 +19,6 @@
#include "incidentd_util.h"
#include "PrivacyFilter.h"
#include "proto_util.h"
-
-#include "incidentd_util.h"
-#include "proto_util.h"
#include "Section.h"
#include <android-base/file.h>
@@ -129,6 +126,8 @@ public:
FieldStripper(const Privacy* restrictions, const sp<ProtoReader>& data,
uint8_t bufferLevel);
+ ~FieldStripper();
+
/**
* Take the data that we have, and filter it down so that no fields
* are more sensitive than the given privacy policy.
@@ -167,6 +166,7 @@ private:
*/
uint8_t mCurrentLevel;
+ sp<EncodedBuffer> mEncodedBuffer;
};
FieldStripper::FieldStripper(const Privacy* restrictions, const sp<ProtoReader>& data,
@@ -174,19 +174,25 @@ FieldStripper::FieldStripper(const Privacy* restrictions, const sp<ProtoReader>&
:mRestrictions(restrictions),
mData(data),
mSize(data->size()),
- mCurrentLevel(bufferLevel) {
+ mCurrentLevel(bufferLevel),
+ mEncodedBuffer(get_buffer_from_pool()) {
if (mSize < 0) {
ALOGW("FieldStripper constructed with a ProtoReader that doesn't support size."
" Data will be missing.");
}
}
+FieldStripper::~FieldStripper() {
+ return_buffer_to_pool(mEncodedBuffer);
+}
+
status_t FieldStripper::strip(const uint8_t privacyPolicy) {
// If the current strip level is less (fewer fields retained) than what's already in the
// buffer, then we can skip it.
if (mCurrentLevel < privacyPolicy) {
PrivacySpec spec(privacyPolicy);
- ProtoOutputStream proto;
+ mEncodedBuffer->clear();
+ ProtoOutputStream proto(mEncodedBuffer);
// Optimization when no strip happens.
if (mRestrictions == NULL || spec.RequireAll()) {
@@ -267,7 +273,7 @@ status_t PrivacyFilter::writeData(const FdBuffer& buffer, uint8_t bufferLevel,
// Order the writes by privacy filter, with increasing levels of filtration,k
// so we can do the filter once, and then write many times.
sort(mOutputs.begin(), mOutputs.end(),
- [](const sp<FilterFd>& a, const sp<FilterFd>& b) -> bool {
+ [](const sp<FilterFd>& a, const sp<FilterFd>& b) -> bool {
return a->getPrivacyPolicy() < b->getPrivacyPolicy();
});
@@ -370,7 +376,7 @@ status_t filter_and_write_report(int to, int from, uint8_t bufferLevel,
write_field_or_skip(NULL, reader, fieldTag, true);
}
}
-
+ clear_buffer_pool();
err = reader->getError();
if (err != NO_ERROR) {
ALOGW("filter_and_write_report reader had an error: %s", strerror(-err));
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index ad253422452e..86a78f095f52 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -698,7 +698,7 @@ DONE:
listener->onReportFailed();
});
}
-
+ clear_buffer_pool();
ALOGI("Done taking incident report err=%s", strerror(-err));
}
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index c703c3ce0951..dec9cb0ad4ff 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -36,6 +36,7 @@
#include <log/log_read.h>
#include <log/logprint.h>
#include <private/android_logger.h>
+#include <sys/mman.h>
#include "FdBuffer.h"
#include "Privacy.h"
@@ -106,7 +107,6 @@ status_t FileSection::Execute(ReportWriter* writer) const {
return NO_ERROR;
}
- FdBuffer buffer;
Fpipe p2cPipe;
Fpipe c2pPipe;
// initiate pipes to pass data to/from incident_helper
@@ -122,6 +122,7 @@ status_t FileSection::Execute(ReportWriter* writer) const {
}
// parent process
+ FdBuffer buffer;
status_t readStatus = buffer.readProcessedDataInStream(fd.get(), std::move(p2cPipe.writeFd()),
std::move(c2pPipe.readFd()),
this->timeoutMs, mIsSysfs);
@@ -356,7 +357,6 @@ CommandSection::CommandSection(int id, const char* command, ...) : Section(id) {
CommandSection::~CommandSection() { free(mCommand); }
status_t CommandSection::Execute(ReportWriter* writer) const {
- FdBuffer buffer;
Fpipe cmdPipe;
Fpipe ihPipe;
@@ -377,6 +377,7 @@ status_t CommandSection::Execute(ReportWriter* writer) const {
}
cmdPipe.writeFd().reset();
+ FdBuffer buffer;
status_t readStatus = buffer.read(ihPipe.readFd().get(), this->timeoutMs);
writer->setSectionStats(buffer);
if (readStatus != NO_ERROR || buffer.timedOut()) {
@@ -574,6 +575,16 @@ static inline int32_t get4LE(uint8_t const* src) {
}
status_t LogSection::BlockingCall(unique_fd& pipeWriteFd) const {
+ // heap profile shows that liblog malloc & free significant amount of memory in this process.
+ // Hence forking a new process to prevent memory fragmentation.
+ pid_t pid = fork();
+ if (pid < 0) {
+ ALOGW("[%s] failed to fork", this->name.string());
+ return errno;
+ }
+ if (pid > 0) {
+ return wait_child(pid, this->timeoutMs);
+ }
// Open log buffer and getting logs since last retrieved time if any.
unique_ptr<logger_list, void (*)(logger_list*)> loggers(
gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end()
@@ -583,31 +594,31 @@ status_t LogSection::BlockingCall(unique_fd& pipeWriteFd) const {
if (android_logger_open(loggers.get(), mLogID) == NULL) {
ALOGE("[%s] Can't get logger.", this->name.string());
- return -1;
+ _exit(EXIT_FAILURE);
}
log_msg msg;
log_time lastTimestamp(0);
ProtoOutputStream proto;
+ status_t err = OK;
while (true) { // keeps reading until logd buffer is fully read.
- status_t err = android_logger_list_read(loggers.get(), &msg);
- // err = 0 - no content, unexpected connection drop or EOF.
- // err = +ive number - size of retrieved data from logger
- // err = -ive number, OS supplied error _except_ for -EAGAIN
- // err = -EAGAIN, graceful indication for ANDRODI_LOG_NONBLOCK that this is the end of data.
- if (err <= 0) {
- if (err != -EAGAIN) {
+ status_t status = android_logger_list_read(loggers.get(), &msg);
+ // status = 0 - no content, unexpected connection drop or EOF.
+ // status = +ive number - size of retrieved data from logger
+ // status = -ive number, OS supplied error _except_ for -EAGAIN
+ // status = -EAGAIN, graceful indication for ANDRODI_LOG_NONBLOCK that this is the end.
+ if (status <= 0) {
+ if (status != -EAGAIN) {
ALOGW("[%s] fails to read a log_msg.\n", this->name.string());
+ err = -status;
}
- // dump previous logs and don't consider this error a failure.
break;
}
if (mBinary) {
// remove the first uint32 which is tag's index in event log tags
android_log_context context = create_android_log_parser(msg.msg() + sizeof(uint32_t),
msg.len() - sizeof(uint32_t));
- ;
android_log_list_element elem;
lastTimestamp.tv_sec = msg.entry.sec;
@@ -667,9 +678,10 @@ status_t LogSection::BlockingCall(unique_fd& pipeWriteFd) const {
}
} else {
AndroidLogEntry entry;
- err = android_log_processLogBuffer(&msg.entry, &entry);
- if (err != NO_ERROR) {
+ status = android_log_processLogBuffer(&msg.entry, &entry);
+ if (status != OK) {
ALOGW("[%s] fails to process to an entry.\n", this->name.string());
+ err = status;
break;
}
lastTimestamp.tv_sec = entry.tv_sec;
@@ -688,17 +700,24 @@ status_t LogSection::BlockingCall(unique_fd& pipeWriteFd) const {
trimTail(entry.message, entry.messageLen));
proto.end(token);
}
+ if (!proto.flush(pipeWriteFd.get())) {
+ if (errno == EPIPE) {
+ ALOGW("[%s] wrote to a broken pipe\n", this->name.string());
+ }
+ err = errno;
+ break;
+ }
+ proto.clear();
}
gLastLogsRetrieved[mLogID] = lastTimestamp;
- if (!proto.flush(pipeWriteFd.get()) && errno == EPIPE) {
- ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
- return EPIPE;
- }
- return NO_ERROR;
+ _exit(err);
}
// ================================================================================
+const int LINK_NAME_LEN = 64;
+const int EXE_NAME_LEN = 1024;
+
TombstoneSection::TombstoneSection(int id, const char* type, const int64_t timeoutMs)
: WorkerThreadSection(id, timeoutMs), mType(type) {
name = "tombstone ";
@@ -716,25 +735,37 @@ status_t TombstoneSection::BlockingCall(unique_fd& pipeWriteFd) const {
const std::set<int> hal_pids = get_interesting_hal_pids();
- ProtoOutputStream proto;
+ auto pooledBuffer = get_buffer_from_pool();
+ ProtoOutputStream proto(pooledBuffer);
+ // dumpBufferSize should be a multiple of page size (4 KB) to reduce memory fragmentation
+ size_t dumpBufferSize = 64 * 1024; // 64 KB is enough for most tombstone dump
+ char* dumpBuffer = (char*)mmap(NULL, dumpBufferSize, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
struct dirent* d;
+ char link_name[LINK_NAME_LEN];
+ char exe_name[EXE_NAME_LEN];
status_t err = NO_ERROR;
while ((d = readdir(proc.get()))) {
int pid = atoi(d->d_name);
if (pid <= 0) {
continue;
}
-
- const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid);
- std::string exe;
- if (!android::base::Readlink(link_name, &exe)) {
- ALOGE("Section %s: Can't read '%s': %s\n", name.string(),
- link_name.c_str(), strerror(errno));
+ snprintf(link_name, LINK_NAME_LEN, "/proc/%d/exe", pid);
+ struct stat fileStat;
+ if (stat(link_name, &fileStat) != OK) {
continue;
}
+ size_t exe_name_len = readlink(link_name, exe_name, EXE_NAME_LEN);
+ if (exe_name_len < 0 || exe_name_len >= EXE_NAME_LEN) {
+ ALOGE("[%s] Can't read '%s': %s", name.string(), link_name, strerror(errno));
+ continue;
+ }
+ // readlink(2) does not put a null terminator at the end
+ exe_name[exe_name_len] = '\0';
bool is_java_process;
- if (exe == "/system/bin/app_process32" || exe == "/system/bin/app_process64") {
+ if (strncmp(exe_name, "/system/bin/app_process32", LINK_NAME_LEN) == 0 ||
+ strncmp(exe_name, "/system/bin/app_process64", LINK_NAME_LEN) == 0) {
if (mType != "java") continue;
// Don't bother dumping backtraces for the zygote.
if (IsZygote(pid)) {
@@ -743,7 +774,7 @@ status_t TombstoneSection::BlockingCall(unique_fd& pipeWriteFd) const {
}
is_java_process = true;
- } else if (should_dump_native_traces(exe.c_str())) {
+ } else if (should_dump_native_traces(exe_name)) {
if (mType != "native") continue;
is_java_process = false;
} else if (hal_pids.find(pid) != hal_pids.end()) {
@@ -799,29 +830,37 @@ status_t TombstoneSection::BlockingCall(unique_fd& pipeWriteFd) const {
ALOGE("[%s] child had an issue: %s\n", this->name.string(), strerror(-cStatus));
}
- auto dump = std::make_unique<char[]>(buffer.size());
+ // Resize dump buffer
+ if (dumpBufferSize < buffer.size()) {
+ munmap(dumpBuffer, dumpBufferSize);
+ while(dumpBufferSize < buffer.size()) dumpBufferSize = dumpBufferSize << 1;
+ dumpBuffer = (char*)mmap(NULL, dumpBufferSize, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ }
sp<ProtoReader> reader = buffer.data()->read();
int i = 0;
while (reader->hasNext()) {
- dump[i] = reader->next();
+ dumpBuffer[i] = reader->next();
i++;
}
uint64_t token = proto.start(android::os::BackTraceProto::TRACES);
proto.write(android::os::BackTraceProto::Stack::PID, pid);
- proto.write(android::os::BackTraceProto::Stack::DUMP, dump.get(), i);
+ proto.write(android::os::BackTraceProto::Stack::DUMP, dumpBuffer, i);
proto.write(android::os::BackTraceProto::Stack::DUMP_DURATION_NS,
static_cast<long long>(Nanotime() - start));
proto.end(token);
dumpPipe.readFd().reset();
- }
-
- if (!proto.flush(pipeWriteFd.get()) && errno == EPIPE) {
- ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
- if (err != NO_ERROR) {
- return EPIPE;
+ if (!proto.flush(pipeWriteFd.get())) {
+ if (errno == EPIPE) {
+ ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+ }
+ err = errno;
+ break;
}
+ proto.clear();
}
-
+ munmap(dumpBuffer, dumpBufferSize);
+ return_buffer_to_pool(pooledBuffer);
return err;
}
diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp
index 2649fb975792..150ab9991a2d 100644
--- a/cmds/incidentd/src/incidentd_util.cpp
+++ b/cmds/incidentd/src/incidentd_util.cpp
@@ -18,6 +18,7 @@
#include "incidentd_util.h"
+#include <android/util/EncodedBuffer.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include <wait.h>
@@ -28,8 +29,6 @@ namespace android {
namespace os {
namespace incidentd {
-using namespace android::base;
-
const Privacy* get_privacy_of_section(int id) {
int l = 0;
int r = PRIVACY_POLICY_COUNT - 1;
@@ -48,6 +47,30 @@ const Privacy* get_privacy_of_section(int id) {
return NULL;
}
+std::vector<sp<EncodedBuffer>> gBufferPool;
+std::mutex gBufferPoolLock;
+
+sp<EncodedBuffer> get_buffer_from_pool() {
+ std::scoped_lock<std::mutex> lock(gBufferPoolLock);
+ if (gBufferPool.size() == 0) {
+ return new EncodedBuffer();
+ }
+ sp<EncodedBuffer> buffer = gBufferPool.back();
+ gBufferPool.pop_back();
+ return buffer;
+}
+
+void return_buffer_to_pool(sp<EncodedBuffer> buffer) {
+ buffer->clear();
+ std::scoped_lock<std::mutex> lock(gBufferPoolLock);
+ gBufferPool.push_back(buffer);
+}
+
+void clear_buffer_pool() {
+ std::scoped_lock<std::mutex> lock(gBufferPoolLock);
+ gBufferPool.clear();
+}
+
// ================================================================================
Fpipe::Fpipe() : mRead(), mWrite() {}
@@ -84,7 +107,7 @@ pid_t fork_execute_cmd(char* const argv[], int in, int out, int* status) {
status = &dummy_status;
}
*status = 0;
- pid_t pid = fork();
+ pid_t pid = vfork();
if (pid < 0) {
*status = -errno;
return -1;
diff --git a/cmds/incidentd/src/incidentd_util.h b/cmds/incidentd/src/incidentd_util.h
index a54993fed42d..84998892e33c 100644
--- a/cmds/incidentd/src/incidentd_util.h
+++ b/cmds/incidentd/src/incidentd_util.h
@@ -19,18 +19,21 @@
#define INCIDENTD_UTIL_H
#include <stdarg.h>
-#include <unistd.h>
-
-#include <android-base/unique_fd.h>
#include <utils/Errors.h>
#include "Privacy.h"
namespace android {
+
+namespace util {
+class EncodedBuffer;
+}
+
namespace os {
namespace incidentd {
-using namespace android::base;
+using android::base::unique_fd;
+using android::util::EncodedBuffer;
/**
* Looks up Privacy of a section in the auto-gen PRIVACY_POLICY_LIST;
@@ -38,6 +41,25 @@ using namespace android::base;
const Privacy* get_privacy_of_section(int id);
/**
+ * Get an EncodedBuffer from an internal pool, or create and return a new one if the pool is empty.
+ * The EncodedBuffer should be returned after use.
+ * Thread safe.
+ */
+sp<EncodedBuffer> get_buffer_from_pool();
+
+/**
+ * Return the EncodedBuffer back to the pool for reuse.
+ * Thread safe.
+ */
+void return_buffer_to_pool(sp<EncodedBuffer> buffer);
+
+/**
+ * Clear the buffer pool to free memory, after taking an incident report.
+ * Thread safe.
+ */
+void clear_buffer_pool();
+
+/**
* This class wraps android::base::Pipe.
*/
class Fpipe {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a62f0a6807bb..bd3fee2d1fc7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -201,6 +201,7 @@ import java.lang.reflect.Method;
import java.net.InetAddress;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -7413,8 +7414,10 @@ public final class ActivityThread extends ClientTransactionHandler {
if (e.errno == OsConstants.EXDEV && oldPath.startsWith("/storage/")) {
Log.v(TAG, "Recovering failed rename " + oldPath + " to " + newPath);
try {
- Files.move(new File(oldPath).toPath(), new File(newPath).toPath());
+ Files.move(new File(oldPath).toPath(), new File(newPath).toPath(),
+ StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e2) {
+ Log.e(TAG, "Rename recovery failed ", e);
throw e;
}
} else {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 51cfa31ae4cf..c4458b30c2db 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2429,7 +2429,7 @@ public class DevicePolicyManager {
PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT
})
@Retention(RetentionPolicy.SOURCE)
- public @interface PersonalAppSuspensionReason {}
+ public @interface PersonalAppsSuspensionReason {}
/**
* Return true if the given administrator component is currently active (enabled) in the system.
@@ -11961,7 +11961,7 @@ public class DevicePolicyManager {
* {@link #PERSONAL_APPS_NOT_SUSPENDED} if apps are not suspended.
* @see #setPersonalAppsSuspended
*/
- public @PersonalAppSuspensionReason int getPersonalAppsSuspendedReasons(
+ public @PersonalAppsSuspensionReason int getPersonalAppsSuspendedReasons(
@NonNull ComponentName admin) {
throwIfParentInstance("getPersonalAppsSuspendedReasons");
if (mService != null) {
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 179fc5c661a5..6ba811e077ac 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -268,15 +268,17 @@ public class CrossProfileApps {
}
/**
- * Returns whether the calling package can request user consent to interact across profiles.
+ * Returns whether the calling package can request to navigate the user to
+ * the relevant settings page to request user consent to interact across profiles.
*
- * <p>If {@code true}, user consent can be obtained via {@link
+ * <p>If {@code true}, the navigation intent can be obtained via {@link
* #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link
* #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts.
*
* <p>Specifically, returns whether the following are all true:
* <ul>
- * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li>
+ * <li>{@code UserManager#getEnabledProfileIds(int)} ()} returns at least one other profile for
+ * the calling user.</li>
* <li>The calling app has requested</li>
* {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest.
* <li>The calling package has either been whitelisted by default by the OEM or has been
@@ -285,6 +287,10 @@ public class CrossProfileApps {
* </li>
* </ul>
*
+ * <p>Note that in order for the user to be able to grant the consent, the requesting package
+ * must be whitelisted by the admin or the OEM and installed in the other profile. If this is
+ * not the case the user will be shown a message explaining why they can't grant the consent.
+ *
* <p>Note that user consent could already be granted if given a return value of {@code true}.
* The package's current ability to interact across profiles can be checked with {@link
* #canInteractAcrossProfiles()}.
@@ -422,6 +428,23 @@ public class CrossProfileApps {
}
/**
+ * Returns {@code true} if the given package has requested
+ * {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} and the user has at least one
+ * other profile in the same profile group.
+ *
+ * <p>This differs from {@link #canConfigureInteractAcrossProfiles(String)} since it will
+ * not return {@code false} if the app is not whitelisted or not installed in the other profile.
+ *
+ * @hide
+ */
+ public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) {
+ try {
+ return mService.canUserAttemptToConfigureInteractAcrossProfiles(packageName);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ /**
* For each of the packages defined in {@code previousCrossProfilePackages} but not included in
* {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission
* #INTERACT_ACROSS_PROFILES} back to its default value if it can no longer be configured by
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index 4cecb30990e6..9b0dae221538 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -38,5 +38,6 @@ interface ICrossProfileApps {
boolean canRequestInteractAcrossProfiles(in String callingPackage);
void setInteractAcrossProfilesAppOp(in String packageName, int newMode);
boolean canConfigureInteractAcrossProfiles(in String packageName);
+ boolean canUserAttemptToConfigureInteractAcrossProfiles(in String packageName);
void resetInteractAcrossProfilesAppOps(in List<String> packageNames);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9f151cf073bd..370469ebe840 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -7088,7 +7088,7 @@ public abstract class PackageManager {
* Returns any packages in a given set of packages that cannot be suspended via a call to {@link
* #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
* SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical
- * packages to keep the device in a functioning state, e.g. the default dialer.
+ * packages to keep the device in a functioning state, e.g. the default dialer and launcher.
* Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this API.
*
* <p>
@@ -7106,7 +7106,7 @@ public abstract class PackageManager {
@RequiresPermission(Manifest.permission.SUSPEND_APPS)
@NonNull
public String[] getUnsuspendablePackages(@NonNull String[] packageNames) {
- throw new UnsupportedOperationException("canSuspendPackages not implemented");
+ throw new UnsupportedOperationException("getUnsuspendablePackages not implemented");
}
/**
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 34a40aef147f..eb6901f6650e 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1214,7 +1214,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <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 &gt;= 1.0 &gt;= 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 0ee748287fa2..6905f83104cd 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2180,27 +2180,66 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* {@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>
+ * </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>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
+ * <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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* 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 096aa0cd27b3..be03502eb943 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2410,27 +2410,66 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* {@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>
+ * </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>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
+ * <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 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* @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 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* 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/provider/Settings.java b/core/java/android/provider/Settings.java
index ccc3132c535d..60f10cd884ca 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10339,6 +10339,7 @@ public final class Settings {
/**
* Value to specify if wifi settings migration is complete or not.
+ * Note: This should only be used from within {@link android.net.wifi.WifiMigration} class.
*
* Type: int (0 for false, 1 for true)
* @hide
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index dfe89a37a229..45e51f756489 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -318,8 +318,7 @@ interface IWindowSession {
* Called when the client has changed the local insets state, and now the server should reflect
* that new state.
*/
- void insetsModified(IWindow window, in InsetsState state);
-
+ oneway void insetsModified(IWindow window, in InsetsState state);
/**
* Called when the system gesture exclusion has changed.
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index b70072877c66..07b6cc1c22d2 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -258,7 +258,6 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
return state.calculateInsets(frame, null /* ignoringVisibilityState */,
false /* isScreenRound */,
false /* alwaysConsumeSystemBars */, null /* displayCutout */,
- null /* legacyContentInsets */, null /* legacyStableInsets */,
LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/,
0 /* legacySystemUiFlags */, typeSideMap)
.getInsets(mTypes);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 048c0e26cdb8..43f80f1490ad 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -363,9 +363,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private final Runnable mAnimCallback;
- private final Rect mLastLegacyContentInsets = new Rect();
- private final Rect mLastLegacyStableInsets = new Rect();
-
/** Pending control request that is waiting on IME to be ready to be shown */
private PendingControlRequest mPendingImeControlRequest;
@@ -435,8 +432,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/,
mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(),
- mLastDisplayCutout, mLastLegacyContentInsets, mLastLegacyStableInsets,
- mLastLegacySoftInputMode, mLastLegacySystemUiFlags, null /* typeSideMap */);
+ mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacySystemUiFlags,
+ null /* typeSideMap */);
mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets,
mUnmodifiableTmpRunningAnims);
@@ -466,13 +463,16 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@VisibleForTesting
public boolean onStateChanged(InsetsState state) {
- if (mState.equals(state) && mLastDispachedState.equals(state)) {
+ boolean localStateChanged = !mState.equals(state);
+ if (!localStateChanged && mLastDispachedState.equals(state)) {
return false;
}
mState.set(state);
mLastDispachedState.set(state, true /* copySources */);
applyLocalVisibilityOverride();
- mViewRoot.notifyInsetsChanged();
+ if (localStateChanged) {
+ mViewRoot.notifyInsetsChanged();
+ }
if (!mState.equals(mLastDispachedState)) {
sendStateToWindowManager();
}
@@ -484,26 +484,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
*/
@VisibleForTesting
public WindowInsets calculateInsets(boolean isScreenRound,
- boolean alwaysConsumeSystemBars, DisplayCutout cutout, Rect legacyContentInsets,
- Rect legacyStableInsets, int legacySoftInputMode, int legacySystemUiFlags) {
- mLastLegacyContentInsets.set(legacyContentInsets);
- mLastLegacyStableInsets.set(legacyStableInsets);
+ boolean alwaysConsumeSystemBars, DisplayCutout cutout,
+ int legacySoftInputMode, int legacySystemUiFlags) {
mLastLegacySoftInputMode = legacySoftInputMode;
mLastLegacySystemUiFlags = legacySystemUiFlags;
mLastDisplayCutout = cutout;
mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/,
- isScreenRound, alwaysConsumeSystemBars, cutout, legacyContentInsets,
- legacyStableInsets, legacySoftInputMode, legacySystemUiFlags,
+ isScreenRound, alwaysConsumeSystemBars, cutout,
+ legacySoftInputMode, legacySystemUiFlags,
null /* typeSideMap */);
return mLastInsets;
}
/**
- * @see InsetsState#calculateVisibleInsets(Rect, Rect, int)
+ * @see InsetsState#calculateVisibleInsets(Rect, int)
*/
- public Rect calculateVisibleInsets(Rect legacyVisibleInsets,
- @SoftInputModeFlags int softInputMode) {
- return mState.calculateVisibleInsets(mFrame, legacyVisibleInsets, softInputMode);
+ public Rect calculateVisibleInsets(@SoftInputModeFlags int softInputMode) {
+ return mState.calculateVisibleInsets(mFrame, softInputMode);
}
/**
@@ -954,7 +951,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
}
- // TODO: Put this on a dispatcher thread.
try {
mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, tmpState);
} catch (RemoteException e) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index c2ad74a566e9..40e6f57f2286 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -160,7 +160,6 @@ public class InsetsState implements Parcelable {
*/
public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState,
boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout,
- @Nullable Rect legacyContentInsets, @Nullable Rect legacyStableInsets,
int legacySoftInputMode, int legacySystemUiFlags,
@Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
Insets[] typeInsetsMap = new Insets[Type.SIZE];
@@ -168,11 +167,6 @@ public class InsetsState implements Parcelable {
boolean[] typeVisibilityMap = new boolean[SIZE];
final Rect relativeFrame = new Rect(frame);
final Rect relativeFrameMax = new Rect(frame);
- if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
- && legacyContentInsets != null && legacyStableInsets != null) {
- WindowInsets.assignCompatInsets(typeInsetsMap, legacyContentInsets);
- WindowInsets.assignCompatInsets(typeMaxInsetsMap, legacyStableInsets);
- }
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
InsetsSource source = mSources.get(type);
if (source == null) {
@@ -217,12 +211,7 @@ public class InsetsState implements Parcelable {
&& (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
}
- public Rect calculateVisibleInsets(Rect frame, Rect legacyVisibleInsets,
- @SoftInputModeFlags int softInputMode) {
- if (sNewInsetsMode == NEW_INSETS_MODE_NONE) {
- return legacyVisibleInsets;
- }
-
+ public Rect calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
Insets insets = Insets.NONE;
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
InsetsSource source = mSources.get(type);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index fcab9d11f3d6..d69357bc503d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -80,6 +80,7 @@ import android.graphics.drawable.GradientDrawable;
import android.hardware.display.DisplayManagerGlobal;
import android.net.Uri;
import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -28745,7 +28746,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* of the screen decorations, these are the current insets for the
* content of the window.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.Q,
+ publicAlternatives = "Use {@link WindowInsets#getInsets(int)}")
final Rect mContentInsets = new Rect();
/**
@@ -28753,7 +28755,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* of the screen decorations, these are the current insets for the
* actual visible parts of the window.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.Q,
+ publicAlternatives = "Use {@link WindowInsets#getInsets(int)}")
final Rect mVisibleInsets = new Rect();
/**
@@ -28761,7 +28764,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* of the screen decorations, these are the current insets for the
* stable system windows.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.Q,
+ publicAlternatives = "Use {@link WindowInsets#getInsets(int)}")
final Rect mStableInsets = new Rect();
final DisplayCutout.ParcelableWrapper mDisplayCutout =
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 315c8777ca8f..50202aed36d2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -547,13 +547,11 @@ public final class ViewRootImpl implements ViewParent,
boolean mAddedTouchMode;
final Rect mTmpFrame = new Rect();
+ final Rect mTmpRect = new Rect();
// These are accessed by multiple threads.
final Rect mWinFrame; // frame given by window manager.
- final Rect mPendingVisibleInsets = new Rect();
- final Rect mPendingStableInsets = new Rect();
- final Rect mPendingContentInsets = new Rect();
final Rect mPendingBackDropFrame = new Rect();
final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
@@ -562,10 +560,6 @@ public final class ViewRootImpl implements ViewParent,
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
- final Rect mDispatchContentInsets = new Rect();
- final Rect mDispatchStableInsets = new Rect();
- DisplayCutout mDispatchDisplayCutout = DisplayCutout.NO_CUTOUT;
-
private WindowInsets mLastWindowInsets;
// Insets types hidden by legacy window flags or system UI flags.
@@ -1019,10 +1013,7 @@ public final class ViewRootImpl implements ViewParent,
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
}
- mPendingContentInsets.set(mAttachInfo.mContentInsets);
- mPendingStableInsets.set(mAttachInfo.mStableInsets);
mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout);
- mPendingVisibleInsets.set(0, 0, 0, 0);
mAttachInfo.mAlwaysConsumeSystemBars =
(res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) != 0;
mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars;
@@ -2200,45 +2191,20 @@ public final class ViewRootImpl implements ViewParent,
/* package */ WindowInsets getWindowInsets(boolean forceConstruct) {
if (mLastWindowInsets == null || forceConstruct) {
- mDispatchContentInsets.set(mAttachInfo.mContentInsets);
- mDispatchStableInsets.set(mAttachInfo.mStableInsets);
- mDispatchDisplayCutout = mAttachInfo.mDisplayCutout.get();
-
- Rect contentInsets = mDispatchContentInsets;
- Rect stableInsets = mDispatchStableInsets;
- DisplayCutout displayCutout = mDispatchDisplayCutout;
- // For dispatch we preserve old logic, but for direct requests from Views we allow to
- // immediately use pending insets. This is such that getRootWindowInsets returns the
- // result from the layout hint before we ran a traversal shortly after adding a window.
- if (!forceConstruct
- && (!mPendingContentInsets.equals(contentInsets) ||
- !mPendingStableInsets.equals(stableInsets) ||
- !mPendingDisplayCutout.get().equals(displayCutout))) {
- contentInsets = mPendingContentInsets;
- stableInsets = mPendingStableInsets;
- displayCutout = mPendingDisplayCutout.get();
- }
- contentInsets = ensureInsetsNonNegative(contentInsets, "content");
- stableInsets = ensureInsetsNonNegative(stableInsets, "stable");
mLastWindowInsets = mInsetsController.calculateInsets(
mContext.getResources().getConfiguration().isScreenRound(),
- mAttachInfo.mAlwaysConsumeSystemBars, displayCutout,
- contentInsets, stableInsets, mWindowAttributes.softInputMode,
- (mWindowAttributes.systemUiVisibility
+ mAttachInfo.mAlwaysConsumeSystemBars, mPendingDisplayCutout.get(),
+ mWindowAttributes.softInputMode, (mWindowAttributes.systemUiVisibility
| mWindowAttributes.subtreeSystemUiVisibility));
- }
- return mLastWindowInsets;
- }
- private Rect ensureInsetsNonNegative(Rect insets, String kind) {
- if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) {
- Log.wtf(mTag, "Negative " + kind + "Insets: " + insets + ", mFirst=" + mFirst);
- return new Rect(Math.max(0, insets.left),
- Math.max(0, insets.top),
- Math.max(0, insets.right),
- Math.max(0, insets.bottom));
+ Rect visibleInsets = mInsetsController.calculateVisibleInsets(
+ mWindowAttributes.softInputMode);
+
+ mAttachInfo.mVisibleInsets.set(visibleInsets);
+ mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect());
+ mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect());
}
- return insets;
+ return mLastWindowInsets;
}
public void dispatchApplyInsets(View host) {
@@ -2262,12 +2228,6 @@ public final class ViewRootImpl implements ViewParent,
== LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
}
- private void updateVisibleInsets() {
- Rect visibleInsets = mInsetsController.calculateVisibleInsets(mPendingVisibleInsets,
- mWindowAttributes.softInputMode);
- mAttachInfo.mVisibleInsets.set(visibleInsets);
- }
-
@VisibleForTesting
public InsetsController getInsetsController() {
return mInsetsController;
@@ -2403,7 +2363,7 @@ public final class ViewRootImpl implements ViewParent,
// Execute enqueued actions on every traversal in case a detached view enqueued an action
getRunQueue().executeActions(mAttachInfo.mHandler);
- boolean insetsChanged = false;
+ boolean cutoutChanged = false;
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
if (layoutRequested) {
@@ -2416,22 +2376,8 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mInTouchMode = !mAddedTouchMode;
ensureTouchModeLocally(mAddedTouchMode);
} else {
- if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
- insetsChanged = true;
- }
- if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
- insetsChanged = true;
- }
if (!mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout)) {
- insetsChanged = true;
- }
- if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
- updateVisibleInsets();
- if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
- + mAttachInfo.mVisibleInsets);
- }
- if (mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars) {
- insetsChanged = true;
+ cutoutChanged = true;
}
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
@@ -2494,7 +2440,6 @@ public final class ViewRootImpl implements ViewParent,
}
if (mApplyInsetsRequested) {
- updateVisibleInsets();
dispatchApplyInsets(host);
if (mLayoutRequested) {
// Short-circuit catching a new layout request here, so
@@ -2562,8 +2507,8 @@ public final class ViewRootImpl implements ViewParent,
controlInsetsForCompatibility(params);
}
- if (mFirst || windowShouldResize || insetsChanged ||
- viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
+ if (mFirst || windowShouldResize || viewVisibilityChanged || cutoutChanged || params != null
+ || mForceNextWindowRelayout) {
mForceNextWindowRelayout = false;
if (isViewVisible) {
@@ -2585,7 +2530,7 @@ public final class ViewRootImpl implements ViewParent,
}
boolean hwInitialized = false;
- boolean contentInsetsChanged = false;
+ boolean dispatchApplyInsets = false;
boolean hadSurface = mSurface.isValid();
try {
@@ -2608,9 +2553,6 @@ public final class ViewRootImpl implements ViewParent,
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
- + " content=" + mPendingContentInsets.toShortString()
- + " visible=" + mPendingVisibleInsets.toShortString()
- + " stable=" + mPendingStableInsets.toShortString()
+ " cutout=" + mPendingDisplayCutout.get().toString()
+ " surface=" + mSurface);
@@ -2627,14 +2569,7 @@ public final class ViewRootImpl implements ViewParent,
updatedConfiguration = true;
}
- contentInsetsChanged = !mPendingContentInsets.equals(
- mAttachInfo.mContentInsets);
- final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
- mAttachInfo.mVisibleInsets);
- final boolean stableInsetsChanged = !mPendingStableInsets.equals(
- mAttachInfo.mStableInsets);
- final boolean cutoutChanged = !mPendingDisplayCutout.equals(
- mAttachInfo.mDisplayCutout);
+ cutoutChanged = !mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout);
surfaceSizeChanged = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
final boolean alwaysConsumeSystemBarsChanged =
@@ -2645,42 +2580,25 @@ public final class ViewRootImpl implements ViewParent,
surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId())
&& mSurface.isValid();
- if (contentInsetsChanged) {
- mAttachInfo.mContentInsets.set(mPendingContentInsets);
- if (DEBUG_LAYOUT) Log.v(mTag, "Content insets changing to: "
- + mAttachInfo.mContentInsets);
- }
- if (stableInsetsChanged) {
- mAttachInfo.mStableInsets.set(mPendingStableInsets);
- if (DEBUG_LAYOUT) Log.v(mTag, "Decor insets changing to: "
- + mAttachInfo.mStableInsets);
- // Need to relayout with content insets.
- contentInsetsChanged = true;
- }
if (cutoutChanged) {
mAttachInfo.mDisplayCutout.set(mPendingDisplayCutout);
if (DEBUG_LAYOUT) {
Log.v(mTag, "DisplayCutout changing to: " + mAttachInfo.mDisplayCutout);
}
// Need to relayout with content insets.
- contentInsetsChanged = true;
+ dispatchApplyInsets = true;
}
if (alwaysConsumeSystemBarsChanged) {
mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars;
- contentInsetsChanged = true;
+ dispatchApplyInsets = true;
}
- if (contentInsetsChanged || mLastSystemUiVisibility !=
+ if (dispatchApplyInsets || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested) {
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
dispatchApplyInsets(host);
// We applied insets so force contentInsetsChanged to ensure the
// hierarchy is measured below.
- contentInsetsChanged = true;
- }
- if (visibleInsetsChanged) {
- updateVisibleInsets();
- if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
- + mAttachInfo.mVisibleInsets);
+ dispatchApplyInsets = true;
}
if (colorModeChanged && mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.setWideGamut(
@@ -2771,7 +2689,8 @@ public final class ViewRootImpl implements ViewParent,
&& mWinFrame.height() == mPendingBackDropFrame.height();
// TODO: Need cutout?
startDragResizing(mPendingBackDropFrame, !backdropSizeMatchesFrame,
- mPendingVisibleInsets, mPendingStableInsets, mResizeMode);
+ mLastWindowInsets.getSystemWindowInsets().toRect(),
+ mLastWindowInsets.getStableInsets().toRect(), mResizeMode);
} else {
// We shouldn't come here, but if we come we should end the resize.
endDragResizing();
@@ -2862,7 +2781,7 @@ public final class ViewRootImpl implements ViewParent,
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
- || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
+ || mHeight != host.getMeasuredHeight() || dispatchApplyInsets ||
updatedConfiguration) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
@@ -2871,7 +2790,7 @@ public final class ViewRootImpl implements ViewParent,
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
+ " mHeight=" + mHeight
+ " measuredHeight=" + host.getMeasuredHeight()
- + " coveredInsetsChanged=" + contentInsetsChanged);
+ + " dispatchApplyInsets=" + dispatchApplyInsets);
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
@@ -4917,12 +4836,9 @@ public final class ViewRootImpl implements ViewParent,
// Recycled in the fall through...
SomeArgs args = (SomeArgs) msg.obj;
if (mWinFrame.equals(args.arg1)
- && mPendingContentInsets.equals(args.arg2)
- && mPendingStableInsets.equals(args.arg6)
&& mPendingDisplayCutout.get().equals(args.arg9)
- && mPendingVisibleInsets.equals(args.arg3)
&& mPendingBackDropFrame.equals(args.arg8)
- && args.arg4 == null
+ && mLastReportedMergedConfiguration.equals(args.arg4)
&& args.argi1 == 0
&& mDisplay.getDisplayId() == args.argi3) {
break;
@@ -4950,16 +4866,10 @@ public final class ViewRootImpl implements ViewParent,
}
final boolean framesChanged = !mWinFrame.equals(args.arg1)
- || !mPendingContentInsets.equals(args.arg2)
- || !mPendingStableInsets.equals(args.arg6)
- || !mPendingDisplayCutout.get().equals(args.arg9)
- || !mPendingVisibleInsets.equals(args.arg3);
+ || !mPendingDisplayCutout.get().equals(args.arg9);
setFrame((Rect) args.arg1);
- mPendingContentInsets.set((Rect) args.arg2);
- mPendingStableInsets.set((Rect) args.arg6);
mPendingDisplayCutout.set((DisplayCutout) args.arg9);
- mPendingVisibleInsets.set((Rect) args.arg3);
mPendingBackDropFrame.set((Rect) args.arg8);
mForceNextWindowRelayout = args.argi1 != 0;
mPendingAlwaysConsumeSystemBars = args.argi2 != 0;
@@ -7413,10 +7323,9 @@ public final class ViewRootImpl implements ViewParent,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
- mTmpFrame, mPendingContentInsets, mPendingVisibleInsets,
- mPendingStableInsets, mPendingBackDropFrame, mPendingDisplayCutout,
- mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mSurfaceSize,
- mBlastSurfaceControl);
+ mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
+ mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
+ mSurfaceSize, mBlastSurfaceControl);
if (mSurfaceControl.isValid()) {
if (!mUseBLASTAdapter) {
mSurface.copyFrom(mSurfaceControl);
@@ -7437,9 +7346,6 @@ public final class ViewRootImpl implements ViewParent,
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame);
- mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
- mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
- mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
}
setFrame(mTmpFrame);
mInsetsController.onStateChanged(mTempInsets);
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 4050da1b5cb1..8bf1ade876ca 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -278,7 +278,7 @@ public final class WindowManagerImpl implements WindowManager {
if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
isScreenRound, alwaysConsumeSystemBars, displayCutout.get(),
- systemWindowInsets, stableInsets, SOFT_INPUT_ADJUST_NOTHING,
+ SOFT_INPUT_ADJUST_NOTHING,
SYSTEM_UI_FLAG_VISIBLE, null /* typeSideMap */);
} else {
return new WindowInsets.Builder()
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 7d97a91b7435..8943da4a2fc0 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -755,8 +755,9 @@ public class Toast {
/**
* Callback object to be called when the toast is shown or hidden.
*
- * Callback methods will be called on the looper thread provided on construction.
+ * <p>Callback methods will be called on the looper thread used for the {@link Toast} object.
*
+ * @see #makeText(Context, Looper, CharSequence, int)
* @see #addCallback(Callback)
*/
public abstract static class Callback {
diff --git a/core/java/android/window/IDisplayAreaOrganizer.aidl b/core/java/android/window/IDisplayAreaOrganizer.aidl
new file mode 100644
index 000000000000..1045ab2fb509
--- /dev/null
+++ b/core/java/android/window/IDisplayAreaOrganizer.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 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.window;
+
+import android.window.IWindowContainer;
+
+/**
+ * Interface for WindowManager to delegate control of display areas.
+ * {@hide}
+ */
+oneway interface IDisplayAreaOrganizer {
+ void onDisplayAreaAppeared(in IWindowContainer displayArea);
+ void onDisplayAreaVanished(in IWindowContainer displayArea);
+}
diff --git a/core/java/android/window/IDisplayAreaOrganizerController.aidl b/core/java/android/window/IDisplayAreaOrganizerController.aidl
new file mode 100644
index 000000000000..fc6fbef39ce2
--- /dev/null
+++ b/core/java/android/window/IDisplayAreaOrganizerController.aidl
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2020, 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.window;
+
+import android.window.IDisplayAreaOrganizer;
+
+/** @hide */
+interface IDisplayAreaOrganizerController {
+
+ /** Register a DisplayAreaOrganizer to manage display areas for a given feature. */
+ void registerOrganizer(in IDisplayAreaOrganizer organizer, int displayAreaFeature);
+}
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index 0a04462c4209..fcf4830fba60 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -25,8 +25,8 @@ import android.window.IWindowContainer;
* {@hide}
*/
oneway interface ITaskOrganizer {
- void taskAppeared(in ActivityManager.RunningTaskInfo taskInfo);
- void taskVanished(in ActivityManager.RunningTaskInfo taskInfo);
+ void onTaskAppeared(in ActivityManager.RunningTaskInfo taskInfo);
+ void onTaskVanished(in ActivityManager.RunningTaskInfo taskInfo);
/**
* Will fire when core attributes of a Task's info change. Relevant properties include the
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index 4b47924dc5a6..7f4b26dba479 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -16,6 +16,7 @@
package android.window;
+import android.window.IDisplayAreaOrganizerController;
import android.window.ITaskOrganizerController;
import android.window.IWindowContainerTransactionCallback;
import android.window.WindowContainerTransaction;
@@ -43,4 +44,7 @@ interface IWindowOrganizerController {
/** @return An interface enabling the management of task organizers. */
ITaskOrganizerController getTaskOrganizerController();
+
+ /** @return An interface enabling the management of display area organizers. */
+ IDisplayAreaOrganizerController getDisplayAreaOrganizerController();
}
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index b8969c162309..4bd5b29f98ac 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -152,6 +152,50 @@ public class WindowOrganizer {
}
}
};
+ }
+
+ /** Class for organizing display areas. */
+ public static class DisplayAreaOrganizer {
+
+ public static final int FEATURE_UNDEFINED = -1;
+ public static final int FEATURE_SYSTEM_FIRST = 0;
+ // The Root display area on a display
+ public static final int FEATURE_ROOT = FEATURE_SYSTEM_FIRST;
+ // Display area hosting the task container.
+ public static final int FEATURE_TASK_CONTAINER = FEATURE_SYSTEM_FIRST + 1;
+ // Display area hosting non-activity window tokens.
+ public static final int FEATURE_WINDOW_TOKENS = FEATURE_SYSTEM_FIRST + 2;
+
+ public static final int FEATURE_SYSTEM_LAST = 10_000;
+
+ // Vendor specific display area definition can start with this value.
+ public static final int FEATURE_VENDOR_FIRST = FEATURE_SYSTEM_LAST + 1;
+
+ /** @hide */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+ public static void registerOrganizer(
+ IDisplayAreaOrganizer organizer, int displayAreaFeature) throws RemoteException {
+ getController().registerOrganizer(organizer, displayAreaFeature);
+ }
+
+ /** @hide */
+ private static IDisplayAreaOrganizerController getController() {
+ return IDisplayAreaOrganizerControllerSingleton.get();
+ }
+
+ private static final Singleton<IDisplayAreaOrganizerController>
+ IDisplayAreaOrganizerControllerSingleton =
+ new Singleton<IDisplayAreaOrganizerController>() {
+ @Override
+ protected IDisplayAreaOrganizerController create() {
+ try {
+ return getWindowOrganizerController()
+ .getDisplayAreaOrganizerController();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+ };
}
}
diff --git a/core/res/res/drawable/resolver_turn_on_work_button_ripple_background.xml b/core/res/res/drawable/resolver_turn_on_work_button_ripple_background.xml
new file mode 100644
index 000000000000..786f5e676c36
--- /dev/null
+++ b/core/res/res/drawable/resolver_turn_on_work_button_ripple_background.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorAccent">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="?attr/colorControlHighlight" />
+ <corners android:radius="?attr/buttonCornerRadius" />
+ </shape>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index af803fcf09cc..210feaf092a7 100644
--- a/core/res/res/layout/resolver_empty_states.xml
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -57,11 +57,11 @@
android:text="@string/resolver_switch_on_work"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@null"
android:fontFamily="@string/config_headlineFontFamilyMedium"
android:textSize="14sp"
android:textColor="?attr/colorAccent"
- android:layout_centerHorizontal="true" />
+ android:layout_centerHorizontal="true"
+ android:background="@drawable/resolver_turn_on_work_button_ripple_background"/>
<ProgressBar
android:id="@+id/resolver_empty_state_progress"
style="@style/Widget.Material.Light.ProgressBar"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7ec7eccd7f8d..06f776013233 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -416,10 +416,10 @@
<!-- Content title for a notification. This notification indicates that the device owner has
changed the location settings. [CHAR LIMIT=NONE] -->
- <string name="location_changed_notification_title">Location settings changed by your admin</string>
+ <string name="location_changed_notification_title">Apps can access your location</string>
<!-- Content text for a notification. Tapping opens device location settings.
[CHAR LIMIT=NONE] -->
- <string name="location_changed_notification_text">Tap to see your location settings.</string>
+ <string name="location_changed_notification_text">Contact your IT admin to learn more</string>
<!-- Feature Id for Country Detector. [CHAR LIMIT=NONE]-->
<string name="country_detector">Country Detector</string>
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 7f0e0d2f54c7..03aba25bf9f7 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -74,7 +74,7 @@ public class ImeInsetsSourceConsumerTest {
false,
new DisplayCutout(
Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
- rect, rect, SOFT_INPUT_ADJUST_RESIZE, 0);
+ SOFT_INPUT_ADJUST_RESIZE, 0);
mImeConsumer = new ImeInsetsSourceConsumer(
new InsetsState(), Transaction::new, mController);
});
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index b449bb00a85d..34a1016f0ae9 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -162,7 +162,7 @@ public class InsetsControllerTest {
false,
new DisplayCutout(
Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
- rect, rect, SOFT_INPUT_ADJUST_RESIZE, 0);
+ SOFT_INPUT_ADJUST_RESIZE, 0);
mController.onFrameChanged(new Rect(0, 0, 100, 100));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 1d8e0a3186e8..721dc98ff4d1 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -73,7 +73,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setVisible(true);
SparseIntArray typeSideMap = new SparseIntArray();
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, 0, typeSideMap);
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, typeSideMap);
assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
assertEquals(ISIDE_TOP, typeSideMap.get(ITYPE_STATUS_BAR));
@@ -92,7 +92,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 100, 100, 300));
mState.getSource(ITYPE_IME).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, 0, null);
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, null);
assertEquals(100, insets.getStableInsetBottom());
assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(Type.systemBars()));
assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
@@ -111,7 +111,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, null, null, 0, 0, null);
+ false, DisplayCutout.NO_CUTOUT, 0, 0, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -127,7 +127,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
mState.getSource(ITYPE_IME).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING, 0, null);
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, null);
assertEquals(0, insets.getSystemWindowInsetBottom());
assertEquals(100, insets.getInsets(ime()).bottom);
assertTrue(insets.isVisible(ime()));
@@ -143,11 +143,11 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
mState.getSource(ITYPE_IME).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING,
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING,
SYSTEM_UI_FLAG_LAYOUT_STABLE, null);
assertEquals(100, insets.getSystemWindowInsetTop());
insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
- DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING,
+ DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING,
0 /* legacySystemUiFlags */, null);
assertEquals(0, insets.getSystemWindowInsetTop());
}
@@ -161,7 +161,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setVisible(true);
mState.removeSource(ITYPE_IME);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
- DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, 0, null);
+ DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, null);
assertEquals(0, insets.getSystemWindowInsetBottom());
}
@@ -255,7 +255,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
Rect visibleInsets = mState.calculateVisibleInsets(
- new Rect(0, 0, 100, 300), new Rect(), SOFT_INPUT_ADJUST_PAN);
+ new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_PAN);
assertEquals(new Rect(0, 100, 0, 100), visibleInsets);
}
}
@@ -273,7 +273,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
Rect visibleInsets = mState.calculateVisibleInsets(
- new Rect(0, 0, 100, 300), new Rect(), SOFT_INPUT_ADJUST_NOTHING);
+ new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_NOTHING);
assertEquals(new Rect(0, 100, 0, 0), visibleInsets);
}
}
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 000000000000..1acc59d167fc
--- /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 000000000000..4ab9ca4fa580
--- /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 000000000000..d74e673a9ce1
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
Binary files differ
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index 42bf03e6de05..f4a358de1c41 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -90,6 +90,7 @@ class ProtoOutputStream
{
public:
ProtoOutputStream();
+ ProtoOutputStream(sp<EncodedBuffer> buffer);
~ProtoOutputStream();
/**
diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp
index 7ffd8874a8fb..96b54c63a836 100644
--- a/libs/protoutil/src/EncodedBuffer.cpp
+++ b/libs/protoutil/src/EncodedBuffer.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "libprotoutil"
#include <stdlib.h>
+#include <sys/mman.h>
#include <android/util/EncodedBuffer.h>
#include <android/util/protobuf.h>
@@ -82,14 +83,16 @@ EncodedBuffer::Pointer::copy() const
}
// ===========================================================
-EncodedBuffer::EncodedBuffer() : EncodedBuffer(0)
+EncodedBuffer::EncodedBuffer() : EncodedBuffer(BUFFER_SIZE)
{
}
EncodedBuffer::EncodedBuffer(size_t chunkSize)
:mBuffers()
{
- mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
+ // Align chunkSize to memory page size
+ chunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
+ mChunkSize = (chunkSize / PAGE_SIZE + ((chunkSize % PAGE_SIZE == 0) ? 0 : 1)) * PAGE_SIZE;
mWp = Pointer(mChunkSize);
mEp = Pointer(mChunkSize);
}
@@ -98,7 +101,7 @@ EncodedBuffer::~EncodedBuffer()
{
for (size_t i=0; i<mBuffers.size(); i++) {
uint8_t* buf = mBuffers[i];
- free(buf);
+ munmap(buf, mChunkSize);
}
}
@@ -135,7 +138,10 @@ EncodedBuffer::writeBuffer()
if (mWp.index() > mBuffers.size()) return NULL;
uint8_t* buf = NULL;
if (mWp.index() == mBuffers.size()) {
- buf = (uint8_t*)malloc(mChunkSize);
+ // Use mmap instead of malloc to ensure memory alignment i.e. no fragmentation so that
+ // the mem region can be immediately reused by the allocator after calling munmap()
+ buf = (uint8_t*)mmap(NULL, mChunkSize, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if (buf == NULL) return NULL; // This indicates NO_MEMORY
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index ea9b79a0353f..fcf82eed4eb1 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -26,8 +26,12 @@
namespace android {
namespace util {
-ProtoOutputStream::ProtoOutputStream()
- :mBuffer(new EncodedBuffer()),
+ProtoOutputStream::ProtoOutputStream(): ProtoOutputStream(new EncodedBuffer())
+{
+}
+
+ProtoOutputStream::ProtoOutputStream(sp<EncodedBuffer> buffer)
+ :mBuffer(buffer),
mCopyBegin(0),
mCompact(false),
mDepth(0),
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index da93db7b37f6..f2d40cef83c7 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/res/layout/keyguard_media_header.xml b/packages/SystemUI/res/layout/keyguard_media_header.xml
new file mode 100644
index 000000000000..9c2d244cb8ec
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyguard_media_header.xml
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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
+ -->
+
+<!-- Layout for media controls on the lockscreen -->
+<com.android.systemui.statusbar.notification.stack.MediaHeaderView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="0dp"
+ android:paddingEnd="0dp"
+ android:focusable="true"
+ android:clickable="true"
+>
+
+ <!-- Background views required by ActivatableNotificationView. -->
+ <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+ android:id="@+id/backgroundNormal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
+ <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+ android:id="@+id/backgroundDimmed"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
+ <com.android.systemui.statusbar.notification.FakeShadowView
+ android:id="@+id/fake_shadow"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
+ <!-- Layout for media controls. -->
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_media_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_horizontal|fill_vertical"
+ android:padding="16dp"
+ >
+ <ImageView
+ android:id="@+id/album_art"
+ android:layout_width="@dimen/qs_media_album_size"
+ android:layout_height="@dimen/qs_media_album_size"
+ android:layout_marginRight="16dp"
+ android:layout_weight="0"
+ />
+
+ <!-- Media information -->
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_media_album_size"
+ android:layout_weight="1"
+ >
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ >
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/icon"
+ android:layout_width="16dp"
+ android:layout_height="16dp"
+ android:layout_marginEnd="5dp"
+ />
+ <TextView
+ android:id="@+id/app_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:singleLine="true"
+ />
+ </LinearLayout>
+
+ <!-- Song name -->
+ <TextView
+ android:id="@+id/header_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:textSize="18sp"
+ android:paddingBottom="6dp"
+ android:gravity="center"/>
+
+ <!-- Artist name -->
+ <TextView
+ android:id="@+id/header_artist"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:textSize="14sp"
+ android:singleLine="true"
+ />
+ </LinearLayout>
+
+ <!-- Controls -->
+ <LinearLayout
+ android:id="@+id/media_actions"
+ android:orientation="horizontal"
+ android:layoutDirection="ltr"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:layout_gravity="center"
+ >
+ <ImageButton
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:gravity="center"
+ android:visibility="gone"
+ android:id="@+id/action0"
+ />
+ <ImageButton
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:gravity="center"
+ android:visibility="gone"
+ android:id="@+id/action1"
+ />
+ <ImageButton
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:gravity="center"
+ android:visibility="gone"
+ android:id="@+id/action2"
+ />
+ </LinearLayout>
+ </LinearLayout>
+
+</com.android.systemui.statusbar.notification.stack.MediaHeaderView>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 38c931a9847f..c8c35c704297 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2693,7 +2693,7 @@
<string name="controls_structure_tooltip">Swipe to see more</string>
<!-- Message to tell the user to wait while systemui attempts to load a set of
- recommended controls [CHAR_LIMIT=30] -->
+ recommended controls [CHAR_LIMIT=60] -->
<string name="controls_seeding_in_progress">Loading recommendations</string>
<!-- Close the controls associated with a specific media session [CHAR_LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
new file mode 100644
index 000000000000..b0017269ff7e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2020 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.keyguard;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.media.MediaMetadata;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+import androidx.palette.graphics.Palette;
+
+import com.android.internal.util.ContrastColorUtil;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.stack.MediaHeaderView;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Media controls to display on the lockscreen
+ *
+ * TODO: Should extend MediaControlPanel to avoid code duplication.
+ * Unfortunately, it isn't currently possible because the ActivatableNotificationView background is
+ * different.
+ */
+@Singleton
+public class KeyguardMediaPlayer {
+
+ private static final String TAG = "KeyguardMediaPlayer";
+ // Buttons that can be displayed on lock screen media controls.
+ private static final int[] ACTION_IDS = {R.id.action0, R.id.action1, R.id.action2};
+
+ private final Context mContext;
+ private final Executor mBackgroundExecutor;
+ private float mAlbumArtRadius;
+ private int mAlbumArtSize;
+ private View mMediaNotifView;
+
+ @Inject
+ public KeyguardMediaPlayer(Context context, @Background Executor backgroundExecutor) {
+ mContext = context;
+ mBackgroundExecutor = backgroundExecutor;
+ loadDimens();
+ }
+
+ /** Binds media controls to a view hierarchy. */
+ public void bindView(View v) {
+ if (mMediaNotifView != null) {
+ throw new IllegalStateException("cannot bind views, already bound");
+ }
+ mMediaNotifView = v;
+ loadDimens();
+ }
+
+ /** Unbinds media controls. */
+ public void unbindView() {
+ if (mMediaNotifView == null) {
+ throw new IllegalStateException("cannot unbind views, nothing bound");
+ }
+ mMediaNotifView = null;
+ }
+
+ /** Clear the media controls because there isn't an active session. */
+ public void clearControls() {
+ if (mMediaNotifView != null) {
+ mMediaNotifView.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Update the media player
+ *
+ * TODO: consider registering a MediaLister instead of exposing this update method.
+ *
+ * @param entry Media notification that will be used to update the player
+ * @param appIcon Icon for the app playing the media
+ * @param mediaMetadata Media metadata that will be used to update the player
+ */
+ public void updateControls(NotificationEntry entry, Icon appIcon,
+ MediaMetadata mediaMetadata) {
+ if (mMediaNotifView == null) {
+ throw new IllegalStateException("cannot update controls, views not bound");
+ }
+ if (mediaMetadata == null) {
+ throw new IllegalArgumentException("media metadata was null");
+ }
+ mMediaNotifView.setVisibility(View.VISIBLE);
+
+ Notification notif = entry.getSbn().getNotification();
+
+ // Computed foreground and background color based on album art.
+ int fgColor = notif.color;
+ int bgColor = entry.getRow() == null ? -1 : entry.getRow().getCurrentBackgroundTint();
+ Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
+ if (artworkBitmap == null) {
+ artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+ }
+ if (artworkBitmap != null) {
+ // If we have art, get colors from that
+ Palette p = MediaNotificationProcessor.generateArtworkPaletteBuilder(artworkBitmap)
+ .generate();
+ Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(p);
+ bgColor = swatch.getRgb();
+ fgColor = MediaNotificationProcessor.selectForegroundColor(bgColor, p);
+ }
+ // Make sure colors will be legible
+ boolean isDark = !ContrastColorUtil.isColorLight(bgColor);
+ fgColor = ContrastColorUtil.resolveContrastColor(mContext, fgColor, bgColor,
+ isDark);
+ fgColor = ContrastColorUtil.ensureTextContrast(fgColor, bgColor, isDark);
+
+ // Album art
+ ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
+ if (albumView != null) {
+ // Resize art in a background thread
+ final Bitmap bm = artworkBitmap;
+ mBackgroundExecutor.execute(() -> processAlbumArt(bm, albumView));
+ }
+
+ // App icon
+ ImageView appIconView = mMediaNotifView.findViewById(R.id.icon);
+ if (appIconView != null) {
+ Drawable iconDrawable = appIcon.loadDrawable(mContext);
+ iconDrawable.setTint(fgColor);
+ appIconView.setImageDrawable(iconDrawable);
+ }
+
+ // App name
+ TextView appName = mMediaNotifView.findViewById(R.id.app_name);
+ if (appName != null) {
+ Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif);
+ String appNameString = builder.loadHeaderAppName();
+ appName.setText(appNameString);
+ appName.setTextColor(fgColor);
+ }
+
+ // Song name
+ TextView titleText = mMediaNotifView.findViewById(R.id.header_title);
+ if (titleText != null) {
+ String songName = mediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
+ titleText.setText(songName);
+ titleText.setTextColor(fgColor);
+ }
+
+ // Artist name
+ TextView artistText = mMediaNotifView.findViewById(R.id.header_artist);
+ if (artistText != null) {
+ String artistName = mediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
+ artistText.setText(artistName);
+ artistText.setTextColor(fgColor);
+ }
+
+ // Background color
+ if (mMediaNotifView instanceof MediaHeaderView) {
+ MediaHeaderView head = (MediaHeaderView) mMediaNotifView;
+ head.setBackgroundColor(bgColor);
+ }
+
+ // Control buttons
+ final List<Icon> icons = new ArrayList<>();
+ final List<PendingIntent> intents = new ArrayList<>();
+ Notification.Action[] actions = notif.actions;
+ final int[] actionsToShow = notif.extras.getIntArray(Notification.EXTRA_COMPACT_ACTIONS);
+
+ for (int i = 0; i < ACTION_IDS.length; i++) {
+ if (actionsToShow != null && actions != null && i < actionsToShow.length
+ && actionsToShow[i] < actions.length) {
+ final int idx = actionsToShow[i];
+ icons.add(actions[idx].getIcon());
+ intents.add(actions[idx].actionIntent);
+ } else {
+ icons.add(null);
+ intents.add(null);
+ }
+ }
+
+ Context packageContext = entry.getSbn().getPackageContext(mContext);
+ for (int i = 0; i < ACTION_IDS.length; i++) {
+ ImageButton button = mMediaNotifView.findViewById(ACTION_IDS[i]);
+ if (button == null) {
+ continue;
+ }
+ Icon icon = icons.get(i);
+ if (icon == null) {
+ button.setVisibility(View.GONE);
+ } else {
+ button.setVisibility(View.VISIBLE);
+ button.setImageDrawable(icon.loadDrawable(packageContext));
+ button.setImageTintList(ColorStateList.valueOf(fgColor));
+ final PendingIntent intent = intents.get(i);
+ if (intent != null) {
+ button.setOnClickListener(v -> {
+ try {
+ intent.send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.d(TAG, "failed to send action intent", e);
+ }
+ });
+ }
+ }
+ }
+ }
+
+ /**
+ * Process album art for layout
+ * @param albumArt bitmap to use for album art
+ * @param albumView view to hold the album art
+ */
+ private void processAlbumArt(Bitmap albumArt, ImageView albumView) {
+ RoundedBitmapDrawable roundedDrawable = null;
+ if (albumArt != null) {
+ Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
+ Bitmap scaled = Bitmap.createScaledBitmap(original, mAlbumArtSize, mAlbumArtSize,
+ false);
+ roundedDrawable = RoundedBitmapDrawableFactory.create(mContext.getResources(), scaled);
+ roundedDrawable.setCornerRadius(mAlbumArtRadius);
+ } else {
+ Log.e(TAG, "No album art available");
+ }
+
+ // Now that it's resized, update the UI
+ final RoundedBitmapDrawable result = roundedDrawable;
+ albumView.post(() -> {
+ albumView.setImageDrawable(result);
+ albumView.setVisibility(result == null ? View.GONE : View.VISIBLE);
+ });
+ }
+
+ private void loadDimens() {
+ mAlbumArtRadius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
+ mAlbumArtSize = (int) mContext.getResources().getDimension(
+ R.dimen.qs_media_album_size);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 18357a95714b..3afe19f926ec 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -966,17 +966,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
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 e2b12daf441e..59af458e2402 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -86,7 +86,8 @@ public class ExpandHelper implements Gefingerpoken {
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 @@ public class ExpandHelper implements Gefingerpoken {
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 @@ public class ExpandHelper implements Gefingerpoken {
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 @@ public class ExpandHelper implements Gefingerpoken {
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 @@ public class ExpandHelper implements Gefingerpoken {
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 02a452182d36..d17ca4041b31 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -69,6 +69,7 @@ public class SwipeHelper implements Gefingerpoken {
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 @@ public class SwipeHelper implements Gefingerpoken {
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() {
+
+ 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);
+ }
+ }
+ }
+ };
- final private int[] mTmpPos = new int[2];
private final int mFalsingThreshold;
private boolean mTouchAboveFalsingThreshold;
private boolean mDisableHwLayers;
@@ -102,7 +120,9 @@ public class SwipeHelper implements Gefingerpoken {
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 class SwipeHelper implements Gefingerpoken {
}
public void cancelLongPress() {
- if (mWatchLongPress != null) {
- mHandler.removeCallbacks(mWatchLongPress);
- mWatchLongPress = null;
- }
+ mHandler.removeCallbacks(mPerformLongPress);
}
@Override
@@ -287,27 +304,9 @@ public class SwipeHelper implements Gefingerpoken {
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 @@ public class SwipeHelper implements Gefingerpoken {
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 @@ public class SwipeHelper implements Gefingerpoken {
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/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
index 3707d61da37f..8cd89ddabe72 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
+import android.view.accessibility.AccessibilityManager;
import androidx.annotation.Nullable;
@@ -45,6 +46,8 @@ import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
+import dagger.Lazy;
+
/**
* A class for managing Assistant handle logic.
*
@@ -73,6 +76,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
private final Provider<AssistHandleViewController> mAssistHandleViewController;
private final DeviceConfigHelper mDeviceConfigHelper;
private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap;
+ private final Lazy<AccessibilityManager> mA11yManager;
private boolean mHandlesShowing = false;
private long mHandlesLastHiddenAt;
@@ -93,6 +97,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
DeviceConfigHelper deviceConfigHelper,
Map<AssistHandleBehavior, BehaviorController> behaviorMap,
NavigationModeController navigationModeController,
+ Lazy<AccessibilityManager> a11yManager,
DumpManager dumpManager) {
mContext = context;
mAssistUtils = assistUtils;
@@ -100,6 +105,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
mAssistHandleViewController = assistHandleViewController;
mDeviceConfigHelper = deviceConfigHelper;
mBehaviorMap = behaviorMap;
+ mA11yManager = a11yManager;
mInGesturalMode = QuickStepContract.isGesturalMode(
navigationModeController.addListener(this::handleNavigationModeChange));
@@ -211,9 +217,11 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
}
private long getShowAndGoDuration() {
- return mDeviceConfigHelper.getLong(
+ long configuredTime = mDeviceConfigHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS,
DEFAULT_SHOW_AND_GO_DURATION_MS);
+ return mA11yManager.get().getRecommendedTimeoutMillis(
+ (int) configuredTime, AccessibilityManager.FLAG_CONTENT_ICONS);
}
private String getBehaviorMode() {
@@ -291,7 +299,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
pw.println(" Phenotype Flags:");
pw.println(" "
- + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS
+ + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS + "(a11y modded)"
+ "="
+ getShowAndGoDuration());
pw.println(" "
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 9ceafc674d34..000000000000
--- 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 96939b010555..6f5a17dca432 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 java.util.Map;
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 @@ public abstract class AssistModule {
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/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index fcbd9121fcda..077ffd3729e5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -403,6 +403,9 @@ public class BubbleData {
}
private void doRemove(String key, @DismissReason int reason) {
+ if (DEBUG_BUBBLE_DATA) {
+ Log.d(TAG, "doRemove: " + key);
+ }
// If it was pending remove it
for (int i = 0; i < mPendingBubbles.size(); i++) {
if (mPendingBubbles.get(i).getKey().equals(key)) {
@@ -445,15 +448,14 @@ public class BubbleData {
if (reason == BubbleController.DISMISS_AGED
|| reason == BubbleController.DISMISS_USER_GESTURE) {
if (DEBUG_BUBBLE_DATA) {
- Log.d(TAG, "overflowing bubble: " + bubble);
+ Log.d(TAG, "Overflowing: " + bubble);
}
mOverflowBubbles.add(0, bubble);
bubble.stopInflation();
-
if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) {
// Remove oldest bubble.
if (DEBUG_BUBBLE_DATA) {
- Log.d(TAG, "Overflow full. Remove bubble: " + mOverflowBubbles.get(
+ Log.d(TAG, "Overflow full. Remove: " + mOverflowBubbles.get(
mOverflowBubbles.size() - 1));
}
mOverflowBubbles.remove(mOverflowBubbles.size() - 1);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index cff371f93f3d..541c8cf19943 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -940,7 +940,6 @@ public class BubbleStackView extends FrameLayout {
ViewClippingUtil.setClippingDeactivated(bubble.getIconView(), true, mClippingParameters);
animateInFlyoutForBubble(bubble);
updatePointerPosition();
- updateOverflowBtnVisibility( /*apply */ true);
requestUpdate();
logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
}
@@ -951,16 +950,18 @@ public class BubbleStackView extends FrameLayout {
Log.d(TAG, "removeBubble: " + bubble);
}
// Remove it from the views
- int removedIndex = mBubbleContainer.indexOfChild(bubble.getIconView());
- if (removedIndex >= 0) {
- mBubbleContainer.removeViewAt(removedIndex);
- bubble.cleanupExpandedState();
- bubble.setInflated(false);
- logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
- } else {
- Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble);
+ for (int i = 0; i < getBubbleCount(); i++) {
+ View v = mBubbleContainer.getChildAt(i);
+ if (v instanceof BadgedImageView
+ && ((BadgedImageView) v).getKey().equals(bubble.getKey())) {
+ mBubbleContainer.removeViewAt(i);
+ bubble.cleanupExpandedState();
+ bubble.setInflated(false);
+ logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+ return;
+ }
}
- updateOverflowBtnVisibility(/* apply */ true);
+ Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble);
}
private void updateOverflowBtnVisibility(boolean apply) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
index 3fd583fa7481..11181e56838e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
@@ -65,11 +65,18 @@ class AllModel(
override val elements: List<ElementWrapper> = createWrappers(controls)
override fun changeFavoriteStatus(controlId: String, favorite: Boolean) {
+ val toChange = elements.firstOrNull {
+ it is ControlWrapper && it.controlStatus.control.controlId == controlId
+ } as ControlWrapper?
+ if (favorite == toChange?.controlStatus?.favorite) return
if (favorite) {
favoriteIds.add(controlId)
} else {
favoriteIds.remove(controlId)
}
+ toChange?.let {
+ it.controlStatus.favorite = favorite
+ }
}
private fun createWrappers(list: List<ControlStatus>): List<ElementWrapper> {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index c28705575e86..902b5785c30a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -217,7 +217,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
}
@Override
- public void taskAppeared(ActivityManager.RunningTaskInfo info) {
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
Objects.requireNonNull(info, "Requires RunningTaskInfo");
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
getAspectRatioOrDefault(info.pictureInPictureParams),
@@ -250,7 +250,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
}
@Override
- public void taskVanished(ActivityManager.RunningTaskInfo info) {
+ public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
IWindowContainer token = info.token;
Objects.requireNonNull(token, "Requires valid IWindowContainer");
if (token.asBinder() != mToken.asBinder()) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 131f4e1c9d59..3879c164a84c 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -339,7 +339,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
insets = state.calculateInsets(state.getDisplayFrame(),
null /* ignoringVisibilityState */, insets.isRound(),
insets.shouldAlwaysConsumeSystemBars(), insets.getDisplayCutout(),
- null /* legacyContentInsets */, null /* legacyStableInsets */,
0 /* legacySystemUiFlags */,
SOFT_INPUT_ADJUST_NOTHING, null /* typeSideMap */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index 8bbb548b0ecf..c4089e5dd070 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -82,11 +82,11 @@ class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub {
}
@Override
- public void taskAppeared(RunningTaskInfo taskInfo) {
+ public void onTaskAppeared(RunningTaskInfo taskInfo) {
}
@Override
- public void taskVanished(RunningTaskInfo taskInfo) {
+ public void onTaskVanished(RunningTaskInfo taskInfo) {
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 5adee40613e6..4fa782269c2d 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 @@ public class DragDownHelper implements Gefingerpoken {
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 @@ public class DragDownHelper implements Gefingerpoken {
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 @@ public class DragDownHelper implements Gefingerpoken {
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/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index d8fdf928f6ad..e32d174d7c77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -46,6 +46,7 @@ import android.widget.ImageView;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.keyguard.KeyguardMediaPlayer;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.Interpolators;
@@ -65,6 +66,7 @@ import com.android.systemui.statusbar.phone.ScrimState;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.Utils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -111,6 +113,7 @@ public class NotificationMediaManager implements Dumpable {
private ScrimController mScrimController;
@Nullable
private LockscreenWallpaper mLockscreenWallpaper;
+ private final KeyguardMediaPlayer mMediaPlayer;
private final Executor mMainExecutor;
@@ -184,11 +187,13 @@ public class NotificationMediaManager implements Dumpable {
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
KeyguardBypassController keyguardBypassController,
+ KeyguardMediaPlayer keyguardMediaPlayer,
@Main Executor mainExecutor,
DeviceConfigProxy deviceConfig) {
mContext = context;
mMediaArtworkProcessor = mediaArtworkProcessor;
mKeyguardBypassController = keyguardBypassController;
+ mMediaPlayer = keyguardMediaPlayer;
mMediaListeners = new ArrayList<>();
// TODO: use MediaSessionManager.SessionListener to hook us up to future updates
// in session state
@@ -468,6 +473,7 @@ public class NotificationMediaManager implements Dumpable {
&& mBiometricUnlockController.isWakeAndUnlock();
if (mKeyguardStateController.isLaunchTransitionFadingAway() || wakeAndUnlock) {
mBackdrop.setVisibility(View.INVISIBLE);
+ mMediaPlayer.clearControls();
Trace.endSection();
return;
}
@@ -490,6 +496,14 @@ public class NotificationMediaManager implements Dumpable {
}
}
+ NotificationEntry entry = mEntryManager
+ .getActiveNotificationUnfiltered(mMediaNotificationKey);
+ if (entry != null) {
+ mMediaPlayer.updateControls(entry, getMediaIcon(), mediaMetadata);
+ } else {
+ mMediaPlayer.clearControls();
+ }
+
// Process artwork on a background thread and send the resulting bitmap to
// finishUpdateMediaMetaData.
if (metaDataChanged) {
@@ -498,7 +512,7 @@ public class NotificationMediaManager implements Dumpable {
}
mProcessArtworkTasks.clear();
}
- if (artworkBitmap != null) {
+ if (artworkBitmap != null && !Utils.useQsMediaPlayer(mContext)) {
mProcessArtworkTasks.add(new ProcessArtworkTask(this, metaDataChanged,
allowEnterAnimation).execute(artworkBitmap));
} else {
@@ -612,6 +626,7 @@ public class NotificationMediaManager implements Dumpable {
// We are unlocking directly - no animation!
mBackdrop.setVisibility(View.GONE);
mBackdropBack.setImageDrawable(null);
+ mMediaPlayer.clearControls();
if (windowController != null) {
windowController.setBackdropShowing(false);
}
@@ -628,6 +643,7 @@ public class NotificationMediaManager implements Dumpable {
mBackdrop.setVisibility(View.GONE);
mBackdropFront.animate().cancel();
mBackdropBack.setImageDrawable(null);
+ mMediaPlayer.clearControls();
mMainExecutor.execute(mHideBackdropFront);
});
if (mKeyguardStateController.isKeyguardFadingAway()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 4c99a90e7da0..e64b423aab60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.os.Handler;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.keyguard.KeyguardMediaPlayer;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -93,6 +94,7 @@ public interface StatusBarDependenciesModule {
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
KeyguardBypassController keyguardBypassController,
+ KeyguardMediaPlayer keyguardMediaPlayer,
@Main Executor mainExecutor,
DeviceConfigProxy deviceConfigProxy) {
return new NotificationMediaManager(
@@ -102,6 +104,7 @@ public interface StatusBarDependenciesModule {
notificationEntryManager,
mediaArtworkProcessor,
keyguardBypassController,
+ keyguardMediaPlayer,
mainExecutor,
deviceConfigProxy);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
index 5d1ab4fd9ad2..db5458664023 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
@@ -152,7 +152,13 @@ public class MediaNotificationProcessor {
}
}
- private int selectForegroundColor(int backgroundColor, Palette palette) {
+ /**
+ * Select a foreground color depending on whether the background color is dark or light
+ * @param backgroundColor Background color to coordinate with
+ * @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder}
+ * @return foreground color
+ */
+ public static int selectForegroundColor(int backgroundColor, Palette palette) {
if (ContrastColorUtil.isColorLight(backgroundColor)) {
return selectForegroundColorForSwatches(palette.getDarkVibrantSwatch(),
palette.getVibrantSwatch(),
@@ -170,7 +176,7 @@ public class MediaNotificationProcessor {
}
}
- private int selectForegroundColorForSwatches(Palette.Swatch moreVibrant,
+ private static int selectForegroundColorForSwatches(Palette.Swatch moreVibrant,
Palette.Swatch vibrant, Palette.Swatch moreMutedSwatch, Palette.Swatch mutedSwatch,
Palette.Swatch dominantSwatch, int fallbackColor) {
Palette.Swatch coloredCandidate = selectVibrantCandidate(moreVibrant, vibrant);
@@ -194,7 +200,7 @@ public class MediaNotificationProcessor {
}
}
- private Palette.Swatch selectMutedCandidate(Palette.Swatch first,
+ private static Palette.Swatch selectMutedCandidate(Palette.Swatch first,
Palette.Swatch second) {
boolean firstValid = hasEnoughPopulation(first);
boolean secondValid = hasEnoughPopulation(second);
@@ -215,7 +221,8 @@ public class MediaNotificationProcessor {
return null;
}
- private Palette.Swatch selectVibrantCandidate(Palette.Swatch first, Palette.Swatch second) {
+ private static Palette.Swatch selectVibrantCandidate(Palette.Swatch first,
+ Palette.Swatch second) {
boolean firstValid = hasEnoughPopulation(first);
boolean secondValid = hasEnoughPopulation(second);
if (firstValid && secondValid) {
@@ -235,7 +242,7 @@ public class MediaNotificationProcessor {
return null;
}
- private boolean hasEnoughPopulation(Palette.Swatch swatch) {
+ private static boolean hasEnoughPopulation(Palette.Swatch swatch) {
// We want a fraction that is at least 1% of the image
return swatch != null
&& (swatch.getPopulation() / (float) RESIZE_BITMAP_AREA > MINIMUM_IMAGE_FRACTION);
@@ -257,7 +264,7 @@ public class MediaNotificationProcessor {
* @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder}
* @return Swatch that should be used as the background of the media notification.
*/
- private static Palette.Swatch findBackgroundSwatch(Palette palette) {
+ public static Palette.Swatch findBackgroundSwatch(Palette palette) {
// by default we use the dominant palette
Palette.Swatch dominantSwatch = palette.getDominantSwatch();
if (dominantSwatch == null) {
@@ -301,7 +308,7 @@ public class MediaNotificationProcessor {
* @param artwork Media artwork
* @return Builder that generates the {@link Palette} for the media artwork.
*/
- private static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) {
+ public static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) {
// for the background we only take the left side of the image to ensure
// a smooth transition
return Palette.from(artwork)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index 48386dce5d3f..e2b01ffe59b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -23,9 +23,11 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_MEDIA_CONTROLS
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.Utils
import javax.inject.Inject
@@ -43,9 +45,18 @@ class NotificationSectionsFeatureManager @Inject constructor(
return usePeopleFiltering(proxy)
}
+ fun isMediaControlsEnabled(): Boolean {
+ return Utils.useQsMediaPlayer(context)
+ }
+
fun getNotificationBuckets(): IntArray {
return when {
- isFilteringEnabled() ->
+ isFilteringEnabled() && isMediaControlsEnabled() ->
+ intArrayOf(BUCKET_HEADS_UP, BUCKET_MEDIA_CONTROLS, BUCKET_PEOPLE, BUCKET_ALERTING,
+ BUCKET_SILENT)
+ !isFilteringEnabled() && isMediaControlsEnabled() ->
+ intArrayOf(BUCKET_HEADS_UP, BUCKET_MEDIA_CONTROLS, BUCKET_ALERTING, BUCKET_SILENT)
+ isFilteringEnabled() && !isMediaControlsEnabled() ->
intArrayOf(BUCKET_HEADS_UP, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT)
NotificationUtils.useNewInterruptionModel(context) ->
intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
new file mode 100644
index 000000000000..ab055e1bdc36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 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.notification.stack;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+
+/**
+ * Root view to insert Lock screen media controls into the notification stack.
+ */
+public class MediaHeaderView extends ActivatableNotificationView {
+
+ private View mContentView;
+
+ public MediaHeaderView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mContentView = findViewById(R.id.keyguard_media_view);
+ }
+
+ @Override
+ protected View getContentView() {
+ return mContentView;
+ }
+
+ /**
+ * Sets the background color, to be used when album art changes.
+ * @param color background
+ */
+ public void setBackgroundColor(int color) {
+ setTintColor(color);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index f6f836335c45..5fd11fd3ef14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -30,6 +30,7 @@ import android.view.LayoutInflater;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardMediaPlayer;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -71,6 +72,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
private final StatusBarStateController mStatusBarStateController;
private final ConfigurationController mConfigurationController;
private final PeopleHubViewAdapter mPeopleHubViewAdapter;
+ private final KeyguardMediaPlayer mKeyguardMediaPlayer;
private final NotificationSectionsFeatureManager mSectionsFeatureManager;
private final int mNumberOfSections;
@@ -110,17 +112,21 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
private boolean mPeopleHubVisible = false;
@Nullable private Subscription mPeopleHubSubscription;
+ private MediaHeaderView mMediaControlsView;
+
@Inject
NotificationSectionsManager(
ActivityStarter activityStarter,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
PeopleHubViewAdapter peopleHubViewAdapter,
+ KeyguardMediaPlayer keyguardMediaPlayer,
NotificationSectionsFeatureManager sectionsFeatureManager) {
mActivityStarter = activityStarter;
mStatusBarStateController = statusBarStateController;
mConfigurationController = configurationController;
mPeopleHubViewAdapter = peopleHubViewAdapter;
+ mKeyguardMediaPlayer = keyguardMediaPlayer;
mSectionsFeatureManager = sectionsFeatureManager;
mNumberOfSections = mSectionsFeatureManager.getNumberOfBuckets();
}
@@ -188,6 +194,13 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
mPeopleHubView = reinflateView(mPeopleHubView, layoutInflater, R.layout.people_strip);
mPeopleHubSubscription = mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary);
+
+ if (mMediaControlsView != null) {
+ mKeyguardMediaPlayer.unbindView();
+ }
+ mMediaControlsView = reinflateView(mMediaControlsView, layoutInflater,
+ R.layout.keyguard_media_header);
+ mKeyguardMediaPlayer.bindView(mMediaControlsView);
}
/** Listener for when the "clear all" button is clicked on the gentle notification header. */
@@ -198,6 +211,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
@Override
public boolean beginsSection(@NonNull View view, @Nullable View previous) {
return view == mGentleHeader
+ || view == mMediaControlsView
|| view == mPeopleHubView
|| view == mAlertingHeader
|| !Objects.equals(getBucket(view), getBucket(previous));
@@ -211,6 +225,8 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
private Integer getBucket(View view) {
if (view == mGentleHeader) {
return BUCKET_SILENT;
+ } else if (view == mMediaControlsView) {
+ return BUCKET_MEDIA_CONTROLS;
} else if (view == mPeopleHubView) {
return BUCKET_PEOPLE;
} else if (view == mAlertingHeader) {
@@ -238,9 +254,15 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
final boolean showHeaders = mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
final boolean usingPeopleFiltering = mSectionsFeatureManager.isFilteringEnabled();
+ final boolean isKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+ final boolean usingMediaControls = mSectionsFeatureManager.isMediaControlsEnabled();
boolean peopleNotifsPresent = false;
+ int currentMediaControlsIdx = -1;
+ // Currently, just putting media controls in the front and incrementing the position based
+ // on the number of heads-up notifs.
+ int mediaControlsTarget = isKeyguard && usingMediaControls ? 0 : -1;
int currentPeopleHeaderIdx = -1;
int peopleHeaderTarget = -1;
int currentAlertingHeaderIdx = -1;
@@ -255,6 +277,10 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
View child = mParent.getChildAt(i);
// Track the existing positions of the headers
+ if (child == mMediaControlsView) {
+ currentMediaControlsIdx = i;
+ continue;
+ }
if (child == mPeopleHubView) {
currentPeopleHeaderIdx = i;
continue;
@@ -276,6 +302,9 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
// Once we enter a new section, calculate the target position for the header.
switch (row.getEntry().getBucket()) {
case BUCKET_HEADS_UP:
+ if (mediaControlsTarget != -1) {
+ mediaControlsTarget++;
+ }
break;
case BUCKET_PEOPLE:
peopleNotifsPresent = true;
@@ -345,6 +374,8 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
alertingHeaderTarget, mAlertingHeader, currentAlertingHeaderIdx);
adjustHeaderVisibilityAndPosition(
peopleHeaderTarget, mPeopleHubView, currentPeopleHeaderIdx);
+ adjustViewPosition(
+ mediaControlsTarget, mMediaControlsView, currentMediaControlsIdx);
// Update headers to reflect state of section contents
mGentleHeader.setAreThereDismissableGentleNotifs(
@@ -378,6 +409,28 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
}
+ private void adjustViewPosition(int targetPosition, ExpandableView header,
+ int currentPosition) {
+ if (targetPosition == -1) {
+ if (currentPosition != -1) {
+ mParent.removeView(header);
+ }
+ } else {
+ if (currentPosition == -1) {
+ // If the header is animating away, it will still have a parent, so detach it first
+ // TODO: We should really cancel the active animations here. This will happen
+ // automatically when the view's intro animation starts, but it's a fragile link.
+ if (header.getTransientContainer() != null) {
+ header.getTransientContainer().removeTransientView(header);
+ header.setTransientContainer(null);
+ }
+ mParent.addView(header, targetPosition);
+ } else {
+ mParent.changeViewPosition(header, targetPosition);
+ }
+ }
+ }
+
/**
* Updates the boundaries (as tracked by their first and last views) of the priority sections.
*
@@ -463,6 +516,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
@VisibleForTesting
+ ExpandableView getMediaControlsView() {
+ return mMediaControlsView;
+ }
+
+ @VisibleForTesting
void setPeopleHubVisible(boolean visible) {
mPeopleHubVisible = visible;
}
@@ -501,13 +559,15 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
@Retention(SOURCE)
@IntDef(prefix = { "BUCKET_" }, value = {
BUCKET_HEADS_UP,
+ BUCKET_MEDIA_CONTROLS,
BUCKET_PEOPLE,
BUCKET_ALERTING,
BUCKET_SILENT
})
public @interface PriorityBucket {}
public static final int BUCKET_HEADS_UP = 0;
- public static final int BUCKET_PEOPLE = 1;
- public static final int BUCKET_ALERTING = 2;
- public static final int BUCKET_SILENT = 3;
+ public static final int BUCKET_MEDIA_CONTROLS = 1;
+ public static final int BUCKET_PEOPLE = 2;
+ public static final int BUCKET_ALERTING = 3;
+ public static final int BUCKET_SILENT = 4;
}
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 4d4a2ded08ca..be8af82f8baf 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 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
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 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
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 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
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 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
// 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 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
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 class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
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 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
// 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 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
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 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
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 f9726d2d77f9..5588c24f2fd6 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 @@ public class NotificationPanelViewController extends PanelViewController {
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 2719a3278d24..481401b3eb7b 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 @@ public abstract class PanelView extends FrameLayout {
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 30367ed026ac..83cc4e33e2db 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 @@ public abstract class PanelViewController {
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 @@ public abstract class PanelViewController {
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 @@ public abstract class PanelViewController {
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 @@ public abstract class PanelViewController {
// 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/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
new file mode 100644
index 000000000000..464a740c931c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 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.keyguard
+
+import android.graphics.drawable.Icon
+import android.media.MediaMetadata
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import android.widget.TextView
+import androidx.test.filters.SmallTest
+
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+public class KeyguardMediaPlayerTest : SysuiTestCase() {
+
+ private lateinit var keyguardMediaPlayer: KeyguardMediaPlayer
+ private lateinit var fakeExecutor: FakeExecutor
+ private lateinit var mediaMetadata: MediaMetadata.Builder
+ private lateinit var entry: NotificationEntryBuilder
+ @Mock private lateinit var mockView: View
+ private lateinit var textView: TextView
+ @Mock private lateinit var mockIcon: Icon
+
+ @Before
+ public fun setup() {
+ fakeExecutor = FakeExecutor(FakeSystemClock())
+ keyguardMediaPlayer = KeyguardMediaPlayer(context, fakeExecutor)
+ mockView = mock(View::class.java)
+ textView = TextView(context)
+ mockIcon = mock(Icon::class.java)
+ mediaMetadata = MediaMetadata.Builder()
+ entry = NotificationEntryBuilder()
+
+ keyguardMediaPlayer.bindView(mockView)
+ }
+
+ @After
+ public fun tearDown() {
+ keyguardMediaPlayer.unbindView()
+ }
+
+ @Test
+ public fun testBind() {
+ keyguardMediaPlayer.unbindView()
+ keyguardMediaPlayer.bindView(mockView)
+ }
+
+ @Test
+ public fun testUnboundClearControls() {
+ keyguardMediaPlayer.unbindView()
+ keyguardMediaPlayer.clearControls()
+ keyguardMediaPlayer.bindView(mockView)
+ }
+
+ @Test
+ public fun testUpdateControls() {
+ keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+ verify(mockView).setVisibility(View.VISIBLE)
+ }
+
+ @Test
+ public fun testClearControls() {
+ keyguardMediaPlayer.clearControls()
+ verify(mockView).setVisibility(View.GONE)
+ }
+
+ @Test
+ public fun testSongName() {
+ whenever<TextView>(mockView.findViewById(R.id.header_title)).thenReturn(textView)
+ val song: String = "Song"
+ mediaMetadata.putText(MediaMetadata.METADATA_KEY_TITLE, song)
+
+ keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+
+ assertThat(textView.getText()).isEqualTo(song)
+ }
+
+ @Test
+ public fun testArtistName() {
+ whenever<TextView>(mockView.findViewById(R.id.header_artist)).thenReturn(textView)
+ val artist: String = "Artist"
+ mediaMetadata.putText(MediaMetadata.METADATA_KEY_ARTIST, artist)
+
+ keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+
+ assertThat(textView.getText()).isEqualTo(artist)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
index 731101cb7329..afcd4414c667 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
@@ -33,6 +33,7 @@ import android.content.ComponentName;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
@@ -40,14 +41,13 @@ import com.android.internal.app.AssistUtils;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.phone.NavigationModeController;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -73,15 +73,16 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
@Mock private AssistHandleBehaviorController.BehaviorController mMockTestBehavior;
@Mock private NavigationModeController mMockNavigationModeController;
@Mock private AssistHandleViewController mMockAssistHandleViewController;
+ @Mock private AccessibilityManager mMockA11yManager;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mDependency.injectMockDependency(StatusBarStateController.class);
- mDependency.injectMockDependency(OverviewProxyService.class);
doAnswer(answerVoid(Runnable::run)).when(mMockHandler).post(any(Runnable.class));
doAnswer(answerVoid(Runnable::run)).when(mMockHandler)
.postDelayed(any(Runnable.class), anyLong());
+ doAnswer(invocation -> invocation.getArgument(0)).when(mMockA11yManager)
+ .getRecommendedTimeoutMillis(anyInt(), anyInt());
Map<AssistHandleBehavior, AssistHandleBehaviorController.BehaviorController> behaviorMap =
new EnumMap<>(AssistHandleBehavior.class);
@@ -99,6 +100,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
mMockDeviceConfigHelper,
behaviorMap,
mMockNavigationModeController,
+ () -> mMockA11yManager,
mock(DumpManager.class));
}
@@ -243,6 +245,28 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
}
@Test
+ public void showAndGo_usesA11yTimeout() {
+ // Arrange
+ when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
+ when(mMockDeviceConfigHelper.getLong(
+ eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS), anyLong()))
+ .thenReturn(12345L);
+ mAssistHandleBehaviorController.hide();
+ reset(mMockAssistHandleViewController, mMockA11yManager);
+ when(mMockA11yManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenReturn(54321);
+ ArgumentCaptor<Long> delay = ArgumentCaptor.forClass(Long.class);
+
+ // Act
+ mAssistHandleBehaviorController.showAndGo();
+
+ // Assert
+ verify(mMockA11yManager).getRecommendedTimeoutMillis(
+ eq(12345), eq(AccessibilityManager.FLAG_CONTENT_ICONS));
+ verify(mMockHandler).postDelayed(any(Runnable.class), delay.capture());
+ assert delay.getValue() == 54321L;
+ }
+
+ @Test
public void showAndGoDelayed_showsThenHidesHandlesWhenHiding() {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
index 9adab5d2369f..5e0d28f6f795 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
@@ -25,6 +25,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.controller.ControlInfo
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -156,12 +157,26 @@ class AllModelTest : SysuiTestCase() {
}
@Test
+ fun testAddFavorite_changesModelFlag() {
+ val indexToAdd = 6
+ val id = "$idPrefix$indexToAdd"
+ model.changeFavoriteStatus(id, true)
+ assertTrue(
+ (model.elements.first {
+ it is ControlWrapper && it.controlStatus.control.controlId == id
+ } as ControlWrapper)
+ .controlStatus.favorite
+ )
+ }
+
+ @Test
fun testAddFavorite_alreadyThere() {
val indexToAdd = 7
model.changeFavoriteStatus("$idPrefix$indexToAdd", true)
val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
+ assertEquals(expectedFavorites.size, model.favorites.size)
model.favorites.zip(expectedFavorites).forEach {
assertTrue(sameControl(it.first, it.second))
}
@@ -182,6 +197,19 @@ class AllModelTest : SysuiTestCase() {
}
@Test
+ fun testRemoveFavorite_changesModelFlag() {
+ val indexToRemove = 3
+ val id = "$idPrefix$indexToRemove"
+ model.changeFavoriteStatus(id, false)
+ assertFalse(
+ (model.elements.first {
+ it is ControlWrapper && it.controlStatus.control.controlId == id
+ } as ControlWrapper)
+ .controlStatus.favorite
+ )
+ }
+
+ @Test
fun testRemoveFavorite_notThere() {
val indexToRemove = 4
model.changeFavoriteStatus("$idPrefix$indexToRemove", false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
index 6388fe1a69c1..b501a2ebeb64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
@@ -27,6 +27,7 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.DeviceConfigProxyFake
+import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -38,6 +39,7 @@ import org.junit.runner.RunWith
class NotificationSectionsFeatureManagerTest : SysuiTestCase() {
var manager: NotificationSectionsFeatureManager? = null
val proxyFake = DeviceConfigProxyFake()
+ var originalQsMediaPlayer: Int = 0
@Before
public fun setup() {
@@ -45,6 +47,15 @@ class NotificationSectionsFeatureManagerTest : SysuiTestCase() {
NOTIFICATION_NEW_INTERRUPTION_MODEL, 1)
manager = NotificationSectionsFeatureManager(proxyFake, mContext)
manager!!.clearCache()
+ originalQsMediaPlayer = Settings.System.getInt(context.getContentResolver(),
+ "qs_media_player", 1)
+ Settings.System.putInt(context.getContentResolver(), "qs_media_player", 0)
+ }
+
+ @After
+ public fun teardown() {
+ Settings.System.putInt(context.getContentResolver(), "qs_media_player",
+ originalQsMediaPlayer)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index a263a7232352..646bc9699ff8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -42,6 +42,7 @@ import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
+import com.android.keyguard.KeyguardMediaPlayer;
import com.android.systemui.ActivityStarterDelegate;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -73,6 +74,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
@Mock private PeopleHubViewAdapter mPeopleHubAdapter;
+ @Mock private KeyguardMediaPlayer mKeyguardMediaPlayer;
@Mock private NotificationSectionsFeatureManager mSectionsFeatureManager;
@Mock private NotificationRowComponent mNotificationRowComponent;
@Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
@@ -91,6 +93,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mStatusBarStateController,
mConfigurationController,
mPeopleHubAdapter,
+ mKeyguardMediaPlayer,
mSectionsFeatureManager
);
// Required in order for the header inflation to work properly
@@ -333,13 +336,82 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
verify(mNssl).changeViewPosition(mSectionsManager.getPeopleHeaderView(), 0);
}
+ @Test
+ public void testMediaControls_AddWhenEnterKeyguard() {
+ enableMediaControls();
+
+ // GIVEN a stack that doesn't include media controls
+ setStackState(ChildType.ALERTING, ChildType.GENTLE_HEADER, ChildType.GENTLE);
+
+ // WHEN we go back to the keyguard
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+ mSectionsManager.updateSectionBoundaries();
+
+ // Then the media controls are added
+ verify(mNssl).addView(mSectionsManager.getMediaControlsView(), 0);
+ }
+
+ @Test
+ public void testMediaControls_AddWhenEnterKeyguardWithHeadsUp() {
+ enableMediaControls();
+
+ // GIVEN a stack that doesn't include media controls but includes HEADS_UP
+ setStackState(ChildType.HEADS_UP, ChildType.ALERTING, ChildType.GENTLE_HEADER,
+ ChildType.GENTLE);
+
+ // WHEN we go back to the keyguard
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+ mSectionsManager.updateSectionBoundaries();
+
+ // Then the media controls are added after HEADS_UP
+ verify(mNssl).addView(mSectionsManager.getMediaControlsView(), 1);
+ }
+
+ @Test
+ public void testMediaControls_RemoveWhenExitKeyguard() {
+ enableMediaControls();
+
+ // GIVEN a stack with media controls
+ setStackState(ChildType.MEDIA_CONTROLS, ChildType.ALERTING, ChildType.GENTLE_HEADER,
+ ChildType.GENTLE);
+
+ // WHEN we leave the keyguard
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
+ mSectionsManager.updateSectionBoundaries();
+
+ // Then the media controls is removed
+ verify(mNssl).removeView(mSectionsManager.getMediaControlsView());
+ }
+
+ @Test
+ public void testMediaControls_RemoveWhenPullDownShade() {
+ enableMediaControls();
+
+ // GIVEN a stack with media controls
+ setStackState(ChildType.MEDIA_CONTROLS, ChildType.ALERTING, ChildType.GENTLE_HEADER,
+ ChildType.GENTLE);
+
+ // WHEN we pull down the shade on the keyguard
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
+ mSectionsManager.updateSectionBoundaries();
+
+ // Then the media controls is removed
+ verify(mNssl).removeView(mSectionsManager.getMediaControlsView());
+ }
+
private void enablePeopleFiltering() {
when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true);
when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(4);
}
+ private void enableMediaControls() {
+ when(mSectionsFeatureManager.isMediaControlsEnabled()).thenReturn(true);
+ when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(4);
+ }
+
private enum ChildType {
- PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP, PERSON, ALERTING, GENTLE, OTHER
+ MEDIA_CONTROLS, PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP, PERSON, ALERTING,
+ GENTLE, OTHER
}
private void setStackState(ChildType... children) {
@@ -347,6 +419,9 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
for (int i = 0; i < children.length; i++) {
View child;
switch (children[i]) {
+ case MEDIA_CONTROLS:
+ child = mSectionsManager.getMediaControlsView();
+ break;
case PEOPLE_HEADER:
child = mSectionsManager.getPeopleHeaderView();
break;
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9b04e7931f7c..fe33fae98306 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1876,7 +1876,7 @@ class AlarmManagerService extends SystemService {
// 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 cea3251d26a1..3148a6205871 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -103,7 +103,7 @@ public final class PinnerService extends SystemService {
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/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 4b925ef7c245..9bbbc3b93fd7 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -155,13 +155,18 @@ class MediaRouter2ServiceImpl {
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
- final boolean trusted = mContext.checkCallingOrSelfPermission(
+ final boolean hasConfigureWifiDisplayPermission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
== PackageManager.PERMISSION_GRANTED;
+ final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ == PackageManager.PERMISSION_GRANTED;
+
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- registerRouter2Locked(router, uid, pid, packageName, userId, trusted);
+ registerRouter2Locked(router, uid, pid, packageName, userId,
+ hasConfigureWifiDisplayPermission, hasModifyAudioRoutingPermission);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -341,8 +346,6 @@ class MediaRouter2ServiceImpl {
throw new IllegalArgumentException("packageName must not be empty");
}
- final boolean trusted = true;
-
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
@@ -350,7 +353,7 @@ class MediaRouter2ServiceImpl {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- registerManagerLocked(manager, uid, pid, packageName, userId, trusted);
+ registerManagerLocked(manager, uid, pid, packageName, userId);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -532,7 +535,8 @@ class MediaRouter2ServiceImpl {
////////////////////////////////////////////////////////////////
private void registerRouter2Locked(@NonNull IMediaRouter2 router, int uid, int pid,
- @NonNull String packageName, int userId, boolean trusted) {
+ @NonNull String packageName, int userId, boolean hasConfigureWifiDisplayPermission,
+ boolean hasModifyAudioRoutingPermission) {
final IBinder binder = router.asBinder();
if (mAllRouterRecords.get(binder) != null) {
Slog.w(TAG, "Same router already exists. packageName=" + packageName);
@@ -540,8 +544,8 @@ class MediaRouter2ServiceImpl {
}
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
- RouterRecord routerRecord = new RouterRecord(
- userRecord, router, uid, pid, packageName, trusted);
+ RouterRecord routerRecord = new RouterRecord(userRecord, router, uid, pid, packageName,
+ hasConfigureWifiDisplayPermission, hasModifyAudioRoutingPermission);
try {
binder.linkToDeath(routerRecord, 0);
} catch (RemoteException ex) {
@@ -711,7 +715,7 @@ class MediaRouter2ServiceImpl {
}
private void registerManagerLocked(@NonNull IMediaRouter2Manager manager,
- int uid, int pid, @NonNull String packageName, int userId, boolean trusted) {
+ int uid, int pid, @NonNull String packageName, int userId) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -721,7 +725,7 @@ class MediaRouter2ServiceImpl {
}
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
- managerRecord = new ManagerRecord(userRecord, manager, uid, pid, packageName, trusted);
+ managerRecord = new ManagerRecord(userRecord, manager, uid, pid, packageName);
try {
binder.linkToDeath(managerRecord, 0);
} catch (RemoteException ex) {
@@ -778,7 +782,7 @@ class MediaRouter2ServiceImpl {
@NonNull IMediaRouter2Manager manager,
@NonNull String packageName, @NonNull MediaRoute2Info route) {
ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
- if (managerRecord == null || !managerRecord.mTrusted) {
+ if (managerRecord == null) {
return;
}
@@ -976,14 +980,16 @@ class MediaRouter2ServiceImpl {
public final IMediaRouter2 mRouter;
public final int mUid;
public final int mPid;
- public final boolean mTrusted;
+ public final boolean mHasConfigureWifiDisplayPermission;
+ public final boolean mHasModifyAudioRoutingPermission;
public final int mRouterId;
public RouteDiscoveryPreference mDiscoveryPreference;
public MediaRoute2Info mSelectedRoute;
- RouterRecord(UserRecord userRecord, IMediaRouter2 router,
- int uid, int pid, String packageName, boolean trusted) {
+ RouterRecord(UserRecord userRecord, IMediaRouter2 router, int uid, int pid,
+ String packageName, boolean hasConfigureWifiDisplayPermission,
+ boolean hasModifyAudioRoutingPermission) {
mUserRecord = userRecord;
mPackageName = packageName;
mSelectRouteSequenceNumbers = new ArrayList<>();
@@ -991,7 +997,8 @@ class MediaRouter2ServiceImpl {
mRouter = router;
mUid = uid;
mPid = pid;
- mTrusted = trusted;
+ mHasConfigureWifiDisplayPermission = hasConfigureWifiDisplayPermission;
+ mHasModifyAudioRoutingPermission = hasModifyAudioRoutingPermission;
mRouterId = mNextRouterOrManagerId.getAndIncrement();
}
@@ -1011,17 +1018,15 @@ class MediaRouter2ServiceImpl {
public final int mUid;
public final int mPid;
public final String mPackageName;
- public final boolean mTrusted;
public final int mManagerId;
ManagerRecord(UserRecord userRecord, IMediaRouter2Manager manager,
- int uid, int pid, String packageName, boolean trusted) {
+ int uid, int pid, String packageName) {
mUserRecord = userRecord;
mManager = manager;
mUid = uid;
mPid = pid;
mPackageName = packageName;
- mTrusted = trusted;
mManagerId = mNextRouterOrManagerId.getAndIncrement();
}
@@ -1036,9 +1041,6 @@ class MediaRouter2ServiceImpl {
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + this);
-
- final String indent = prefix + " ";
- pw.println(indent + "mTrusted=" + mTrusted);
}
@Override
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index e5cb5540b781..77921236e0d7 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 static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR
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 android.util.Slog;
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 @@ public class BubbleExtractor implements NotificationSignalExtractor {
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 f65f187eddc5..35dec5a59cec 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.ArrayUtils;
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/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 83da38195053..7069818e3894 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -249,16 +249,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
}
private boolean canRequestInteractAcrossProfilesUnchecked(String packageName) {
- List<UserHandle> targetUserProfiles =
- getTargetUserProfilesUnchecked(packageName, mInjector.getCallingUserId());
- if (targetUserProfiles.isEmpty()) {
- return false;
- }
- if (!hasRequestedAppOpPermission(
- AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) {
+ final int[] enabledProfileIds =
+ mInjector.getUserManager().getEnabledProfileIds(mInjector.getCallingUserId());
+ if (enabledProfileIds.length < 2) {
return false;
}
- return isCrossProfilePackageWhitelisted(packageName);
+ return hasRequestedAppOpPermission(
+ AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName);
}
private boolean hasRequestedAppOpPermission(String permission, String packageName) {
@@ -540,6 +537,17 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
return isCrossProfilePackageWhitelisted(packageName);
}
+ @Override
+ public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) {
+ final int[] profileIds = mInjector.getUserManager().getProfileIds(
+ mInjector.getCallingUserId(), /* enabledOnly= */ false);
+ if (profileIds.length < 2) {
+ return false;
+ }
+ return hasRequestedAppOpPermission(
+ AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName);
+ }
+
private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) {
return mInjector.withCleanCallingIdentity(() -> {
final int[] profileIds =
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index d9e41afa9141..8cac487f50b9 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -21,6 +21,9 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_ROOT;
+import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_WINDOW_TOKENS;
import static com.android.internal.util.Preconditions.checkState;
import static com.android.server.wm.DisplayAreaProto.NAME;
@@ -28,13 +31,16 @@ import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA;
+import android.graphics.Point;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
+import android.window.IDisplayAreaOrganizer;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.protolog.common.ProtoLog;
import java.util.Comparator;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -57,13 +63,24 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
protected final Type mType;
private final String mName;
+ final int mFeatureId;
+ private final DisplayAreaOrganizerController mOrganizerController;
+ IDisplayAreaOrganizer mOrganizer;
DisplayArea(WindowManagerService wms, Type type, String name) {
+ this(wms, type, name, FEATURE_UNDEFINED);
+ }
+
+ DisplayArea(WindowManagerService wms, Type type, String name, int featureId) {
super(wms);
// TODO(display-area): move this up to ConfigurationContainer
mOrientation = SCREEN_ORIENTATION_UNSET;
mType = type;
mName = name;
+ mFeatureId = featureId;
+ mRemoteToken = new RemoteToken(this);
+ mOrganizerController =
+ wms.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController;
}
@Override
@@ -116,6 +133,33 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
return DISPLAY_AREA;
}
+ void forAllDisplayAreas(Consumer<DisplayArea> callback) {
+ super.forAllDisplayAreas(callback);
+ callback.accept(this);
+ }
+
+ void setOrganizer(IDisplayAreaOrganizer organizer) {
+ if (mOrganizer == organizer) return;
+ sendDisplayAreaVanished();
+ mOrganizer = organizer;
+ sendDisplayAreaAppeared();
+ }
+
+ void sendDisplayAreaAppeared() {
+ if (mOrganizer == null) return;
+ mOrganizerController.onDisplayAreaAppeared(mOrganizer, this);
+ }
+
+ void sendDisplayAreaVanished() {
+ if (mOrganizer == null) return;
+ mOrganizerController.onDisplayAreaVanished(mOrganizer, this);
+ }
+
+ @Override
+ boolean isOrganized() {
+ return mOrganizer != null;
+ }
+
/**
* DisplayArea that contains WindowTokens, and orders them according to their type.
*/
@@ -152,7 +196,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
};
Tokens(WindowManagerService wms, Type type, String name) {
- super(wms, type, name);
+ super(wms, type, name, FEATURE_WINDOW_TOKENS);
}
void addChild(WindowToken token) {
@@ -191,7 +235,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
private final Rect mTmpDimBoundsRect = new Rect();
Root(WindowManagerService wms) {
- super(wms, Type.ANY, "DisplayArea.Root");
+ super(wms, Type.ANY, "DisplayArea.Root", FEATURE_ROOT);
}
@Override
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
new file mode 100644
index 000000000000..464b127a5e37
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.window.IDisplayAreaOrganizer;
+import android.window.IDisplayAreaOrganizerController;
+
+import java.util.HashMap;
+
+public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub {
+ private static final String TAG = "DisplayAreaOrganizerController";
+
+ final ActivityTaskManagerService mService;
+ private final WindowManagerGlobalLock mGlobalLock;
+ private final HashMap<Integer, IDisplayAreaOrganizer> mOrganizersByFeatureIds = new HashMap();
+
+ private class DeathRecipient implements IBinder.DeathRecipient {
+ int mFeature;
+ IDisplayAreaOrganizer mOrganizer;
+
+ DeathRecipient(IDisplayAreaOrganizer organizer, int feature) {
+ mOrganizer = organizer;
+ mFeature = feature;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mGlobalLock) {
+ mOrganizersByFeatureIds.remove(mFeature);
+ mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
+ if (da.mOrganizer != mOrganizer) return;
+ da.setOrganizer(null);
+ });
+ }
+ }
+ }
+
+ DisplayAreaOrganizerController(ActivityTaskManagerService atm) {
+ mService = atm;
+ mGlobalLock = atm.mGlobalLock;
+ }
+
+ private void enforceStackPermission(String func) {
+ mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func);
+ }
+
+ @Override
+ public void registerOrganizer(IDisplayAreaOrganizer organizer, int feature) {
+ enforceStackPermission("registerOrganizer()");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ if (mOrganizersByFeatureIds.get(feature) != null) {
+ throw new IllegalStateException(
+ "Replacing existing organizer currently unsupported");
+ }
+
+ final DeathRecipient dr = new DeathRecipient(organizer, feature);
+ try {
+ organizer.asBinder().linkToDeath(dr, 0);
+ } catch (RemoteException e) {
+ // Oh well...
+ }
+ mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
+ if (da.mFeatureId != feature) return;
+ da.setOrganizer(organizer);
+ });
+
+ mOrganizersByFeatureIds.put(feature, organizer);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) {
+ try {
+ organizer.onDisplayAreaAppeared(da.mRemoteToken);
+ } catch (RemoteException e) {
+ // Oh well...
+ }
+ }
+
+ void onDisplayAreaVanished(IDisplayAreaOrganizer organizer, DisplayArea da) {
+ try {
+ organizer.onDisplayAreaVanished(da.mRemoteToken);
+ } catch (RemoteException e) {
+ // Oh well...
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 885456a8488c..0c6e48314c48 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -73,16 +73,36 @@ class DisplayAreaPolicyBuilder {
*/
static class Feature {
private final String mName;
+ private final int mId;
private final boolean[] mWindowLayers;
- private Feature(String name, boolean[] windowLayers) {
+ private Feature(String name, int id, boolean[] windowLayers) {
mName = name;
+ mId = id;
mWindowLayers = windowLayers;
}
+ /**
+ * Returns the id of the feature.
+ *
+ * Must be unique among the features added to a {@link DisplayAreaPolicyBuilder}.
+ *
+ * @see android.window.WindowOrganizer.DisplayAreaOrganizer#FEATURE_SYSTEM_FIRST
+ * @see android.window.WindowOrganizer.DisplayAreaOrganizer#FEATURE_VENDOR_FIRST
+ */
+ public int getId() {
+ return mId;
+ }
+
+ @Override
+ public String toString() {
+ return "Feature(\"" + mName + "\", " + mId + '}';
+ }
+
static class Builder {
private final WindowManagerPolicy mPolicy;
private final String mName;
+ private final int mId;
private final boolean[] mLayers;
/**
@@ -96,10 +116,12 @@ class DisplayAreaPolicyBuilder {
* The builder starts out with the feature not applying to any types.
*
* @param name the name of the feature.
+ * @param id of the feature. {@see Feature#getId}
*/
- Builder(WindowManagerPolicy policy, String name) {
+ Builder(WindowManagerPolicy policy, String name, int id) {
mPolicy = policy;
mName = name;
+ mId = id;
mLayers = new boolean[mPolicy.getMaxWindowLayer()];
}
@@ -147,7 +169,7 @@ class DisplayAreaPolicyBuilder {
}
Feature build() {
- return new Feature(mName, mLayers.clone());
+ return new Feature(mName, mId, mLayers.clone());
}
private void set(int type, boolean value) {
@@ -364,7 +386,7 @@ class DisplayAreaPolicyBuilder {
return leaf;
} else {
return new DisplayArea(parent.mWmService, type, mFeature.mName + ":"
- + mMinLayer + ":" + mMaxLayer);
+ + mMinLayer + ":" + mMaxLayer, mFeature.mId);
}
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a20318dcd990..7df731b3efab 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -79,6 +79,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_TASK_CONTAINER;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
@@ -4294,7 +4295,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private ActivityStack mRootSplitScreenPrimaryTask = null;
TaskContainers(WindowManagerService service) {
- super(service, Type.ANY, "TaskContainers");
+ super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER);
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 34acabe76296..5e88fb0437c6 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -115,7 +115,6 @@ import static com.android.server.policy.WindowManagerPolicy.TRANSIT_HIDE;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_SHOW;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
-import static com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
@@ -178,7 +177,6 @@ import android.view.WindowManagerPolicyConstants;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.internal.policy.ScreenDecorationsUtils;
@@ -333,9 +331,6 @@ public class DisplayPolicy {
}
};
- @GuardedBy("mHandler")
- private SleepToken mDreamingSleepToken;
-
// The windows we were told about in focusChanged.
private WindowState mFocusedWindow;
private WindowState mLastFocusedWindow;
@@ -394,7 +389,6 @@ public class DisplayPolicy {
private boolean mShowingDream;
private boolean mLastShowingDream;
private boolean mDreamingLockscreen;
- private boolean mDreamingSleepTokenNeeded;
private boolean mAllowLockscreenWhenOn;
private InputConsumer mInputConsumer = null;
@@ -414,7 +408,6 @@ public class DisplayPolicy {
private RefreshRatePolicy mRefreshRatePolicy;
// -------- PolicyHandler --------
- private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 1;
private static final int MSG_REQUEST_TRANSIENT_BARS = 2;
private static final int MSG_DISPOSE_INPUT_CONSUMER = 3;
private static final int MSG_ENABLE_POINTER_LOCATION = 4;
@@ -434,9 +427,6 @@ public class DisplayPolicy {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_UPDATE_DREAMING_SLEEP_TOKEN:
- updateDreamingSleepToken(msg.arg1 != 0);
- break;
case MSG_REQUEST_TRANSIENT_BARS:
synchronized (mLock) {
WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
@@ -2638,15 +2628,6 @@ public class DisplayPolicy {
// while the dream is showing.
if (!mShowingDream) {
mDreamingLockscreen = mService.mPolicy.isKeyguardShowingAndNotOccluded();
- if (mDreamingSleepTokenNeeded) {
- mDreamingSleepTokenNeeded = false;
- mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 0, 1).sendToTarget();
- }
- } else {
- if (!mDreamingSleepTokenNeeded) {
- mDreamingSleepTokenNeeded = true;
- mHandler.obtainMessage(MSG_UPDATE_DREAMING_SLEEP_TOKEN, 1, 1).sendToTarget();
- }
}
if (mStatusBar != null) {
@@ -3160,21 +3141,6 @@ public class DisplayPolicy {
return !mShowingDream;
}
- private void updateDreamingSleepToken(boolean acquire) {
- if (acquire) {
- final int displayId = getDisplayId();
- if (mDreamingSleepToken == null) {
- mDreamingSleepToken = mService.mAtmInternal.acquireSleepToken(
- "DreamOnDisplay" + displayId, displayId);
- }
- } else {
- if (mDreamingSleepToken != null) {
- mDreamingSleepToken.release();
- mDreamingSleepToken = null;
- }
- }
- }
-
private void requestTransientBars(WindowState swipeTarget) {
if (!mService.mPolicy.isUserSetupComplete()) {
// Swipe-up for navigation bar is disabled during setup
@@ -3854,7 +3820,6 @@ public class DisplayPolicy {
}
pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
- pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken);
if (mStatusBar != null) {
pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar);
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index e2c7d52fa673..88cdd1781aee 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -301,7 +301,7 @@ final class InputMonitor {
* we may have some issues with modal-windows, but I guess we can
* cross that bridge when we come to implementing full-screen TaskOrg
*/
- if (child.getTask() != null && child.getTask().isControlledByTaskOrganizer()) {
+ if (child.getTask() != null && child.getTask().isOrganized()) {
inputWindowHandle.replaceTouchableRegionWithCrop(null /* Use this surfaces crop */);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index eca7d2e19288..55dc694bb611 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -509,22 +509,6 @@ class Task extends WindowContainer<WindowContainer> {
_voiceSession, _voiceInteractor, stack);
}
- class TaskToken extends RemoteToken {
- TaskToken(WindowContainer container) {
- super(container);
- }
-
- @Override
- public SurfaceControl getLeash() {
- // We need to copy the SurfaceControl instead of returning the original
- // because the Parcel FLAGS PARCELABLE_WRITE_RETURN_VALUE cause SurfaceControls
- // to release themselves.
- SurfaceControl sc = new SurfaceControl();
- sc.copyFrom(getSurfaceControl());
- return sc;
- }
- }
-
/** Don't use constructor directly. This is only used by XML parser. */
Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent, Intent _affinityIntent,
String _affinity, String _rootAffinity, ComponentName _realActivity,
@@ -550,7 +534,7 @@ class Task extends WindowContainer<WindowContainer> {
mTaskDescription = _lastTaskDescription;
// Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
setOrientation(SCREEN_ORIENTATION_UNSET);
- mRemoteToken = new TaskToken(this);
+ mRemoteToken = new RemoteToken(this);
affinityIntent = _affinityIntent;
affinity = _affinity;
rootAffinity = _rootAffinity;
@@ -3110,7 +3094,7 @@ class Task extends WindowContainer<WindowContainer> {
/**
* Animations are handled by the TaskOrganizer implementation.
*/
- if (isControlledByTaskOrganizer()) {
+ if (isOrganized()) {
return false;
}
// Don't animate while the task runs recents animation but only if we are in the mode
@@ -4014,7 +3998,8 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- boolean isControlledByTaskOrganizer() {
+ @Override
+ boolean isOrganized() {
final Task rootTask = getRootTask();
// if the rootTask is a "child" of a tile, then don't consider it a root task.
// TODO: remove this along with removing tile.
@@ -4030,7 +4015,7 @@ class Task extends WindowContainer<WindowContainer> {
* Avoid yanking back control from the TaskOrganizer, which has presumably reparented the
* Surface in to its own hierarchy.
*/
- if (isControlledByTaskOrganizer()) {
+ if (isOrganized()) {
return;
}
super.reparentSurfaceControl(t, newParent);
@@ -4117,27 +4102,6 @@ class Task extends WindowContainer<WindowContainer> {
sendTaskAppeared();
}
- @Override
- public void updateSurfacePosition() {
- // Avoid fighting with the TaskOrganizer over Surface position.
- if (isControlledByTaskOrganizer()) {
- return;
- } else {
- super.updateSurfacePosition();
- }
- }
-
- @Override
- void getRelativeDisplayedPosition(Point outPos) {
- // In addition to updateSurfacePosition, we keep other code that sets
- // position from fighting with the TaskOrganizer
- if (isControlledByTaskOrganizer()) {
- outPos.set(0, 0);
- return;
- }
- super.getRelativeDisplayedPosition(outPos);
- }
-
/**
* @return true if the task is currently focused.
*/
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index fc58ee785cf8..552367801678 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -111,7 +111,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
void addTask(Task t) {
mOrganizedTasks.add(t);
try {
- mOrganizer.taskAppeared(t.getTaskInfo());
+ mOrganizer.onTaskAppeared(t.getTaskInfo());
} catch (Exception e) {
Slog.e(TAG, "Exception sending taskAppeared callback" + e);
}
@@ -119,7 +119,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
void removeTask(Task t) {
try {
- mOrganizer.taskVanished(t.getTaskInfo());
+ mOrganizer.onTaskVanished(t.getTaskInfo());
} catch (Exception e) {
Slog.e(TAG, "Exception sending taskVanished callback" + e);
}
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
index 822f840005b7..51142b1d2eb1 100644
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ b/services/core/java/com/android/server/wm/TaskTile.java
@@ -218,7 +218,7 @@ public class TaskTile extends ActivityStack {
static TaskTile forToken(IBinder token) {
try {
- return (TaskTile) ((TaskToken) token).getContainer();
+ return (TaskTile) ((RemoteToken) token).getContainer();
} catch (ClassCastException e) {
Slog.w(TAG, "Bad tile token: " + token, e);
return null;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index a49cffb664fd..ae0b685cfac5 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1612,6 +1612,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return null;
}
+ void forAllDisplayAreas(Consumer<DisplayArea> callback) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ mChildren.get(i).forAllDisplayAreas(callback);
+ }
+ }
+
/**
* Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
* the input container in terms of z-order.
@@ -2325,6 +2331,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
void updateSurfacePosition() {
+ // Avoid fighting with the organizer over Surface position.
+ if (isOrganized()) return;
+
if (mSurfaceControl == null) {
return;
}
@@ -2374,6 +2383,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
void getRelativeDisplayedPosition(Point outPos) {
+ // In addition to updateSurfacePosition, we keep other code that sets
+ // position from fighting with the organizer
+ if (isOrganized()) {
+ outPos.set(0, 0);
+ return;
+ }
+
final Rect dispBounds = getDisplayedBounds();
outPos.set(dispBounds.left, dispBounds.top);
final WindowContainer parent = getParent();
@@ -2414,8 +2430,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return null;
}
- RemoteToken getRemoteToken() {
- return mRemoteToken;
+ /**
+ * @return {@code true} if window container is manage by a
+ * {@link android.window.WindowOrganizer}
+ */
+ boolean isOrganized() {
+ return false;
}
static WindowContainer fromBinder(IBinder binder) {
@@ -2439,7 +2459,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
@Override
public SurfaceControl getLeash() {
- throw new RuntimeException("Not implemented");
+ final WindowContainer wc = getContainer();
+ if (wc == null) return null;
+ // We need to copy the SurfaceControl instead of returning the original
+ // because the Parcel FLAGS PARCELABLE_WRITE_RETURN_VALUE cause SurfaceControls
+ // to release themselves.
+ SurfaceControl sc = new SurfaceControl();
+ sc.copyFrom(wc.getSurfaceControl());
+ return sc;
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index ebfe1096540e..e416e8073a75 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -33,6 +33,7 @@ import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Slog;
import android.view.SurfaceControl;
+import android.window.IDisplayAreaOrganizerController;
import android.window.ITaskOrganizerController;
import android.window.IWindowContainerTransactionCallback;
import android.window.IWindowOrganizerController;
@@ -76,11 +77,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mTransactionCallbacksByPendingSyncId = new HashMap();
final TaskOrganizerController mTaskOrganizerController;
+ final DisplayAreaOrganizerController mDisplayAreaOrganizerController;
WindowOrganizerController(ActivityTaskManagerService atm) {
mService = atm;
mGlobalLock = atm.mGlobalLock;
mTaskOrganizerController = new TaskOrganizerController(mService);
+ mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
}
@Override
@@ -318,6 +321,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return mTaskOrganizerController;
}
+ @Override
+ public IDisplayAreaOrganizerController getDisplayAreaOrganizerController() {
+ enforceStackPermission("getDisplayAreaOrganizerController()");
+ return mDisplayAreaOrganizerController;
+ }
+
int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
int id = mBLASTSyncEngine.startSyncSet(this);
mTransactionCallbacksByPendingSyncId.put(id, callback);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 740c5cb3e75a..023a1e8ede9f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -136,7 +136,7 @@ import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
-import android.app.admin.DevicePolicyManager.PersonalAppSuspensionReason;
+import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DeviceStateCache;
import android.app.admin.FactoryResetProtectionPolicy;
@@ -935,10 +935,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
handlePackagesChanged(null /* check all admins */, userHandle);
- } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
- || (Intent.ACTION_PACKAGE_ADDED.equals(action)
- && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false))) {
+ } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
+ } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
+ } else {
+ handleNewPackageInstalled(intent.getData().getSchemeSpecificPart(), userHandle);
+ }
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
&& !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
@@ -2028,6 +2032,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
+ private void handleNewPackageInstalled(String packageName, int userHandle) {
+ // If personal apps were suspended by the admin, suspend the newly installed one.
+ if (!getUserData(userHandle).mAppsSuspended) {
+ return;
+ }
+ final String[] packagesToSuspend = { packageName };
+ // Check if package is considered not suspendable?
+ if (mInjector.getPackageManager(userHandle)
+ .getUnsuspendablePackages(packagesToSuspend).length != 0) {
+ Slog.i(LOG_TAG, "Newly installed package is unsuspendable: " + packageName);
+ return;
+ }
+ try {
+ mIPackageManager.setPackagesSuspendedAsUser(packagesToSuspend, true /*suspend*/,
+ null, null, null, PLATFORM_PACKAGE_NAME, userHandle);
+ } catch (RemoteException ignored) {
+ // shouldn't happen.
+ }
+ }
+
/**
* Unit test will subclass it to inject mocks.
*/
@@ -2110,6 +2134,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mContext.getPackageManager();
}
+ PackageManager getPackageManager(int userId) {
+ return mContext
+ .createContextAsUser(UserHandle.of(userId), 0 /* flags */).getPackageManager();
+ }
+
PowerManagerInternal getPowerManagerInternal() {
return LocalServices.getService(PowerManagerInternal.class);
}
@@ -15650,7 +15679,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public @PersonalAppSuspensionReason int getPersonalAppsSuspendedReasons(ComponentName who) {
+ public @PersonalAppsSuspensionReason int getPersonalAppsSuspendedReasons(ComponentName who) {
synchronized (getLockObject()) {
final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER,
@@ -15669,7 +15698,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private @PersonalAppSuspensionReason int makeSuspensionReasons(
+ private @PersonalAppsSuspensionReason int makeSuspensionReasons(
boolean explicit, boolean timeout) {
int result = PERSONAL_APPS_NOT_SUSPENDED;
if (explicit) {
@@ -15793,7 +15822,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private void applyPersonalAppsSuspension(
- int profileUserId, @PersonalAppSuspensionReason int suspensionState) {
+ int profileUserId, @PersonalAppsSuspensionReason int suspensionState) {
final boolean suspended = getUserData(UserHandle.USER_SYSTEM).mAppsSuspended;
final boolean shouldSuspend = suspensionState != PERSONAL_APPS_NOT_SUSPENDED;
if (suspended != shouldSuspend) {
@@ -15813,8 +15842,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mInjector.binderWithCleanCallingIdentity(() -> {
try {
final String[] appsToSuspend =
- new PersonalAppsSuspensionHelper(mContext, mInjector.getPackageManager())
- .getPersonalAppsForSuspension(userId);
+ new PersonalAppsSuspensionHelper(
+ mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */))
+ .getPersonalAppsForSuspension();
final String[] failedPackages = mIPackageManager.setPackagesSuspendedAsUser(
appsToSuspend, suspended, null, null, null, PLATFORM_PACKAGE_NAME, userId);
if (!ArrayUtils.isEmpty(failedPackages)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index 180acc85e5f6..d9db17eba887 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -20,7 +20,6 @@ import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -32,7 +31,7 @@ import android.os.IBinder;
import android.os.ServiceManager;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.Log;
+import android.util.ArraySet;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
@@ -43,7 +42,6 @@ import com.android.server.inputmethod.InputMethodManagerInternal;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -56,18 +54,21 @@ public class PersonalAppsSuspensionHelper {
private final Context mContext;
private final PackageManager mPackageManager;
- public PersonalAppsSuspensionHelper(Context context, PackageManager packageManager) {
+ /**
+ * @param context Context for the user whose apps should to be suspended.
+ */
+ public PersonalAppsSuspensionHelper(Context context) {
mContext = context;
- mPackageManager = packageManager;
+ mPackageManager = context.getPackageManager();
}
/**
* @return List of packages that should be suspended to limit personal use.
*/
- String[] getPersonalAppsForSuspension(@UserIdInt int userId) {
+ String[] getPersonalAppsForSuspension() {
final List<PackageInfo> installedPackageInfos =
- mPackageManager.getInstalledPackagesAsUser(0 /* flags */, userId);
- final Set<String> result = new HashSet<>();
+ mPackageManager.getInstalledPackages(0 /* flags */);
+ final Set<String> result = new ArraySet<>();
for (final PackageInfo packageInfo : installedPackageInfos) {
final ApplicationInfo info = packageInfo.applicationInfo;
if ((!info.isSystemApp() && !info.isUpdatedSystemApp())
@@ -77,11 +78,15 @@ public class PersonalAppsSuspensionHelper {
}
result.removeAll(getCriticalPackages());
result.removeAll(getSystemLauncherPackages());
- result.removeAll(getAccessibilityServices(userId));
- result.removeAll(getInputMethodPackages(userId));
- result.remove(getActiveLauncherPackages(userId));
- result.remove(getDialerPackage(userId));
- result.remove(getSettingsPackageName(userId));
+ result.removeAll(getAccessibilityServices());
+ result.removeAll(getInputMethodPackages());
+ result.remove(getSettingsPackageName());
+
+ final String[] unsuspendablePackages =
+ mPackageManager.getUnsuspendablePackages(result.toArray(new String[0]));
+ for (final String pkg : unsuspendablePackages) {
+ result.remove(pkg);
+ }
Slog.i(LOG_TAG, "Packages subject to suspension: " + String.join(",", result));
return result.toArray(new String[0]);
@@ -104,7 +109,6 @@ public class PersonalAppsSuspensionHelper {
final ApplicationInfo applicationInfo =
mPackageManager.getApplicationInfo(packageName, 0);
if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) {
- Log.d(LOG_TAG, "Not suspending system launcher package: " + packageName);
result.add(packageName);
}
} catch (PackageManager.NameNotFoundException e) {
@@ -114,81 +118,53 @@ public class PersonalAppsSuspensionHelper {
return result;
}
- private List<String> getAccessibilityServices(int userId) {
+ private List<String> getAccessibilityServices() {
final List<AccessibilityServiceInfo> accessibilityServiceInfos =
- getAccessibilityManagerForUser(userId)
+ getAccessibilityManagerForUser(mContext.getUserId())
.getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK);
final List<String> result = new ArrayList<>();
for (final AccessibilityServiceInfo serviceInfo : accessibilityServiceInfos) {
final ComponentName componentName =
ComponentName.unflattenFromString(serviceInfo.getId());
if (componentName != null) {
- final String packageName = componentName.getPackageName();
- Slog.d(LOG_TAG, "Not suspending a11y service: " + packageName);
- result.add(packageName);
+ result.add(componentName.getPackageName());
}
}
return result;
}
- private List<String> getInputMethodPackages(int userId) {
- final List<InputMethodInfo> enabledImes =
- InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId);
+ private List<String> getInputMethodPackages() {
+ final List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get()
+ .getEnabledInputMethodListAsUser(mContext.getUserId());
final List<String> result = new ArrayList<>();
for (final InputMethodInfo info : enabledImes) {
- Slog.d(LOG_TAG, "Not suspending IME: " + info.getPackageName());
result.add(info.getPackageName());
}
return result;
}
@Nullable
- private String getActiveLauncherPackages(int userId) {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_HOME);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- return getPackageNameForIntent("active launcher", intent, userId);
- }
-
- @Nullable
- private String getSettingsPackageName(int userId) {
+ private String getSettingsPackageName() {
final Intent intent = new Intent(Settings.ACTION_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
- return getPackageNameForIntent("settings", intent, userId);
- }
-
- @Nullable
- private String getDialerPackage(int userId) {
- final Intent intent = new Intent(Intent.ACTION_DIAL);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- return getPackageNameForIntent("dialer", intent, userId);
- }
-
- @Nullable
- private String getPackageNameForIntent(String name, Intent intent, int userId) {
- final ResolveInfo resolveInfo =
- mPackageManager.resolveActivityAsUser(intent, /* flags= */ 0, userId);
+ final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, /* flags= */ 0);
if (resolveInfo != null) {
- final String packageName = resolveInfo.activityInfo.packageName;
- Slog.d(LOG_TAG, "Not suspending " + name + " package: " + packageName);
- return packageName;
+ return resolveInfo.activityInfo.packageName;
}
return null;
}
private List<String> getCriticalPackages() {
- final List<String> result = Arrays.asList(mContext.getResources()
+ return Arrays.asList(mContext.getResources()
.getStringArray(R.array.config_packagesExemptFromSuspension));
- Slog.d(LOG_TAG, "Not suspending critical packages: " + String.join(",", result));
- return result;
}
private boolean hasLauncherIntent(String packageName) {
final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
intentToResolve.setPackage(packageName);
- final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(
- intentToResolve, PackageManager.GET_UNINSTALLED_PACKAGES);
+ final List<ResolveInfo> resolveInfos =
+ mPackageManager.queryIntentActivities(intentToResolve, /* flags= */ 0);
return resolveInfos != null && !resolveInfos.isEmpty();
}
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index fa0febd7f20f..acdb68142178 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -419,6 +419,38 @@ public class CrossProfileAppsServiceImplRoboTest {
.isTrue();
}
+ @Test
+ public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsTrue() {
+ mockUninstallCrossProfileAppFromWorkProfile();
+ assertThat(mCrossProfileAppsServiceImpl
+ .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .isTrue();
+ }
+
+ @Test
+ public void canUserAttemptToConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse()
+ throws Exception {
+ mockCrossProfileAppDoesNotRequestInteractAcrossProfiles();
+ assertThat(mCrossProfileAppsServiceImpl
+ .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .isFalse();
+ }
+
+ @Test
+ public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsTrue() {
+ mockCrossProfileAppNotWhitelisted();
+ assertThat(mCrossProfileAppsServiceImpl
+ .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .isTrue();
+ }
+
+ @Test
+ public void canUserAttemptToConfigureInteractAcrossProfiles_returnsTrue() {
+ assertThat(mCrossProfileAppsServiceImpl
+ .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+ .isTrue();
+ }
+
private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) {
explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index bb149cf327b8..09af4421406d 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 @@ public class AlarmManagerServiceTest {
// 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 @@ public class AlarmManagerServiceTest {
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 @@ public class AlarmManagerServiceTest {
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 @@ public class AlarmManagerServiceTest {
}
}
// 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 @@ public class AlarmManagerServiceTest {
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 @@ public class AlarmManagerServiceTest {
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 9db7d5e3acdd..3c2944560435 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 @@ package com.android.server.notification;
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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@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/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 658ba1ba8a4a..6ec0f919ced7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1020,10 +1020,10 @@ public class ActivityStarterTests extends ActivityTestsBase {
mSecondary = TaskTile.forToken(secondary.asBinder());
}
@Override
- public void taskAppeared(ActivityManager.RunningTaskInfo info) {
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
}
@Override
- public void taskVanished(ActivityManager.RunningTaskInfo info) {
+ public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
}
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index cc9173ad12ad..12bdec6ec1e1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -78,11 +78,11 @@ public class DisplayAreaPolicyBuilderTest {
final Feature bar;
DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
- .addFeature(foo = new Feature.Builder(mPolicy, "Foo")
+ .addFeature(foo = new Feature.Builder(mPolicy, "Foo", 0)
.upTo(TYPE_STATUS_BAR)
.and(TYPE_NAVIGATION_BAR)
.build())
- .addFeature(bar = new Feature.Builder(mPolicy, "Bar")
+ .addFeature(bar = new Feature.Builder(mPolicy, "Bar", 1)
.all()
.except(TYPE_STATUS_BAR)
.build())
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 5359bd33e70f..a96f40143e0f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -60,7 +60,6 @@ import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.Rational;
import android.view.Display;
-import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.WindowContainerTransaction;
@@ -103,10 +102,10 @@ public class TaskOrganizerTests extends WindowTestsBase {
final ITaskOrganizer organizer = registerMockOrganizer();
task.setTaskOrganizer(organizer);
- verify(organizer).taskAppeared(any());
+ verify(organizer).onTaskAppeared(any());
task.removeImmediately();
- verify(organizer).taskVanished(any());
+ verify(organizer).onTaskVanished(any());
}
@Test
@@ -117,10 +116,10 @@ public class TaskOrganizerTests extends WindowTestsBase {
final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
task.setTaskOrganizer(organizer);
- verify(organizer).taskAppeared(any());
+ verify(organizer).onTaskAppeared(any());
task.setTaskOrganizer(organizer2);
- verify(organizer).taskVanished(any());
- verify(organizer2).taskAppeared(any());
+ verify(organizer).onTaskVanished(any());
+ verify(organizer2).onTaskAppeared(any());
}
@Test
@@ -131,10 +130,10 @@ public class TaskOrganizerTests extends WindowTestsBase {
final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- verify(organizer).taskAppeared(any());
+ verify(organizer).onTaskAppeared(any());
stack.setWindowingMode(WINDOWING_MODE_PINNED);
- verify(organizer).taskVanished(any());
- verify(organizer2).taskAppeared(any());
+ verify(organizer).onTaskVanished(any());
+ verify(organizer2).onTaskAppeared(any());
}
@Test
@@ -144,12 +143,12 @@ public class TaskOrganizerTests extends WindowTestsBase {
final ITaskOrganizer organizer = registerMockOrganizer();
stack.setTaskOrganizer(organizer);
- verify(organizer).taskAppeared(any());
- assertTrue(stack.isControlledByTaskOrganizer());
+ verify(organizer).onTaskAppeared(any());
+ assertTrue(stack.isOrganized());
stack.setTaskOrganizer(null);
- verify(organizer).taskVanished(any());
- assertFalse(stack.isControlledByTaskOrganizer());
+ verify(organizer).onTaskVanished(any());
+ assertFalse(stack.isOrganized());
}
@Test
@@ -159,12 +158,12 @@ public class TaskOrganizerTests extends WindowTestsBase {
final ITaskOrganizer organizer = registerMockOrganizer();
stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- verify(organizer).taskAppeared(any());
- assertTrue(stack.isControlledByTaskOrganizer());
+ verify(organizer).onTaskAppeared(any());
+ assertTrue(stack.isOrganized());
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
- verify(organizer).taskVanished(any());
- assertFalse(stack.isControlledByTaskOrganizer());
+ verify(organizer).onTaskVanished(any());
+ assertFalse(stack.isOrganized());
}
@Test
@@ -179,23 +178,23 @@ public class TaskOrganizerTests extends WindowTestsBase {
// First organizer is registered, verify a task appears when changing windowing mode
stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- verify(organizer, times(1)).taskAppeared(any());
- assertTrue(stack.isControlledByTaskOrganizer());
+ verify(organizer, times(1)).onTaskAppeared(any());
+ assertTrue(stack.isOrganized());
// Now we replace the registration and1 verify the new organizer receives tasks
// newly entering the windowing mode.
final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- verify(organizer2).taskAppeared(any());
- assertTrue(stack2.isControlledByTaskOrganizer());
+ verify(organizer2).onTaskAppeared(any());
+ assertTrue(stack2.isOrganized());
// Now we unregister the second one, the first one should automatically be reregistered
// so we verify that it's now seeing changes.
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2);
stack3.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- verify(organizer, times(2)).taskAppeared(any());
- assertTrue(stack3.isControlledByTaskOrganizer());
+ verify(organizer, times(2)).onTaskAppeared(any());
+ assertTrue(stack3.isOrganized());
}
@Test
@@ -206,10 +205,10 @@ public class TaskOrganizerTests extends WindowTestsBase {
final Task task = createTaskInStack(stack, 0 /* userId */);
final Task task2 = createTaskInStack(stack, 0 /* userId */);
stack.setWindowingMode(WINDOWING_MODE_PINNED);
- verify(organizer, times(1)).taskAppeared(any());
+ verify(organizer, times(1)).onTaskAppeared(any());
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- verify(organizer, times(1)).taskVanished(any());
+ verify(organizer, times(1)).onTaskVanished(any());
}
@Test
@@ -219,7 +218,7 @@ public class TaskOrganizerTests extends WindowTestsBase {
stack.setWindowingMode(WINDOWING_MODE_PINNED);
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
- verify(organizer, times(1)).taskAppeared(any());
+ verify(organizer, times(1)).onTaskAppeared(any());
}
@Test
@@ -396,10 +395,10 @@ public class TaskOrganizerTests extends WindowTestsBase {
final boolean[] called = {false};
ITaskOrganizer listener = new ITaskOrganizer.Stub() {
@Override
- public void taskAppeared(RunningTaskInfo taskInfo) { }
+ public void onTaskAppeared(RunningTaskInfo taskInfo) { }
@Override
- public void taskVanished(RunningTaskInfo container) { }
+ public void onTaskVanished(RunningTaskInfo container) { }
@Override
public void onTaskInfoChanged(RunningTaskInfo info) throws RemoteException {
@@ -447,10 +446,10 @@ public class TaskOrganizerTests extends WindowTestsBase {
final ArrayMap<IBinder, RunningTaskInfo> lastReportedTiles = new ArrayMap<>();
ITaskOrganizer listener = new ITaskOrganizer.Stub() {
@Override
- public void taskAppeared(RunningTaskInfo taskInfo) { }
+ public void onTaskAppeared(RunningTaskInfo taskInfo) { }
@Override
- public void taskVanished(RunningTaskInfo container) { }
+ public void onTaskVanished(RunningTaskInfo container) { }
@Override
public void onTaskInfoChanged(RunningTaskInfo info) {
@@ -667,11 +666,11 @@ public class TaskOrganizerTests extends WindowTestsBase {
RunningTaskInfo mInfo;
@Override
- public void taskAppeared(RunningTaskInfo info) {
+ public void onTaskAppeared(RunningTaskInfo info) {
mInfo = info;
}
@Override
- public void taskVanished(RunningTaskInfo info) {
+ public void onTaskVanished(RunningTaskInfo info) {
}
@Override
public void onTaskInfoChanged(RunningTaskInfo info) {
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index a6c5c3ba932b..35464340550b 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -449,13 +449,21 @@ public class SubscriptionInfo implements Parcelable {
}
/**
- * @return the number of this subscription.
+ * @return the number of this subscription if the calling app has been granted the
+ * READ_PHONE_NUMBERS permission, or an empty string otherwise
*/
public String getNumber() {
return mNumber;
}
/**
+ * @hide
+ */
+ public void clearNumber() {
+ mNumber = "";
+ }
+
+ /**
* @return the data roaming state for this subscription, either
* {@link SubscriptionManager#DATA_ROAMING_ENABLE} or {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
*/
diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml
index d7f820411f27..7e97fa3a8ff1 100644
--- a/tests/BootImageProfileTest/AndroidTest.xml
+++ b/tests/BootImageProfileTest/AndroidTest.xml
@@ -23,6 +23,10 @@
<option name="force-skip-system-props" value="true" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
+ <option name="cleanup-action" value="REBOOT" />
+ </target_preparer>
+
<test class="com.android.tradefed.testtype.HostTest" >
<option name="class" value="com.android.bootimageprofile.BootImageProfileTest" />
</test>
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
index f186ed3b75cf..5afd39ea9de1 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
@@ -150,7 +150,7 @@ public class TaskOrganizerMultiWindowTest extends Activity {
};
@Override
- public void taskAppeared(ActivityManager.RunningTaskInfo ti) {
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo ti) {
if (!gotFirstTask) {
mTaskView1.reparentTask(ti.token);
gotFirstTask = true;
@@ -158,7 +158,7 @@ public class TaskOrganizerMultiWindowTest extends Activity {
mTaskView2.reparentTask(ti.token);
}
}
- public void taskVanished(ActivityManager.RunningTaskInfo ti) {
+ public void onTaskVanished(ActivityManager.RunningTaskInfo ti) {
}
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
index a2f40dc0fd6f..520bc255499b 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
@@ -38,7 +38,7 @@ public class TaskOrganizerPipTest extends Service {
TaskView mTaskView;
class Organizer extends ITaskOrganizer.Stub {
- public void taskAppeared(ActivityManager.RunningTaskInfo ti) {
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo ti) {
mTaskView.reparentTask(ti.token);
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -48,7 +48,7 @@ public class TaskOrganizerPipTest extends Service {
} catch (Exception e) {
}
}
- public void taskVanished(ActivityManager.RunningTaskInfo ti) {
+ public void onTaskVanished(ActivityManager.RunningTaskInfo ti) {
}
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
}
diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml
index 34e2e3af9cda..ea5043ae5f65 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>