diff options
344 files changed, 11416 insertions, 7598 deletions
diff --git a/Android.bp b/Android.bp index 3f06ffd7ecd8..b0e0b35a1f76 100644 --- a/Android.bp +++ b/Android.bp @@ -1133,16 +1133,6 @@ aidl_mapping { output: "framework-aidl-mappings.txt", } -genrule { - name: "framework-annotation-proc-index", - srcs: [":framework-annotation-proc"], - cmd: "unzip -qp $(in) unsupportedappusage/unsupportedappusage_index.csv > $(out)", - out: ["unsupportedappusage_index.csv"], - dist: { - targets: ["droidcore"], - }, -} - // Avoid including Parcelable classes as we don't want to have two copies of // Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony) // and TeleService app (packages/services/Telephony). 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/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java index e5a685f15df8..c8ca44b6ef74 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java @@ -116,7 +116,7 @@ class BlobMetadata { return mUserId; } - void addCommitter(@NonNull Committer committer) { + void addOrReplaceCommitter(@NonNull Committer committer) { synchronized (mMetadataLock) { // We need to override the committer data, so first remove any existing // committer before adding the new one. @@ -139,6 +139,12 @@ class BlobMetadata { } } + void removeCommitter(@NonNull Committer committer) { + synchronized (mMetadataLock) { + mCommitters.remove(committer); + } + } + void removeInvalidCommitters(SparseArray<String> packages) { synchronized (mMetadataLock) { mCommitters.removeIf(committer -> @@ -154,7 +160,7 @@ class BlobMetadata { } } - void addLeasee(String callingPackage, int callingUid, int descriptionResId, + void addOrReplaceLeasee(String callingPackage, int callingUid, int descriptionResId, CharSequence description, long leaseExpiryTimeMillis) { synchronized (mMetadataLock) { // We need to override the leasee data, so first remove any existing diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java index 65ccb997343b..6c48511a12cc 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java @@ -401,7 +401,7 @@ public class BlobStoreManagerService extends SystemService { throw new LimitExceededException("Total amount of data with an active lease" + " is exceeding the max limit"); } - blobMetadata.addLeasee(callingPackage, callingUid, + blobMetadata.addOrReplaceLeasee(callingPackage, callingUid, descriptionResId, description, leaseExpiryTimeMillis); if (LOGV) { Slog.v(TAG, "Acquired lease on " + blobHandle @@ -573,12 +573,16 @@ public class BlobStoreManagerService extends SystemService { final Committer newCommitter = new Committer(session.getOwnerPackageName(), session.getOwnerUid(), session.getBlobAccessMode()); final Committer existingCommitter = blob.getExistingCommitter(newCommitter); - blob.addCommitter(newCommitter); + blob.addOrReplaceCommitter(newCommitter); try { writeBlobsInfoLocked(); session.sendCommitCallbackResult(COMMIT_RESULT_SUCCESS); } catch (Exception e) { - blob.addCommitter(existingCommitter); + if (existingCommitter == null) { + blob.removeCommitter(newCommitter); + } else { + blob.addOrReplaceCommitter(existingCommitter); + } session.sendCommitCallbackResult(COMMIT_RESULT_ERROR); } getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid())) diff --git a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING index 8fbfb1daaf6f..d99830dc47c9 100644 --- a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING +++ b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING @@ -7,6 +7,7 @@ ], "options": [ {"include-filter": "com.android.server.DeviceIdleControllerTest"}, + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"} ] } diff --git a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING index bc7a7d3bef7d..b76c582cf287 100644 --- a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING +++ b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING @@ -4,6 +4,7 @@ "name": "FrameworksMockingServicesTests", "options": [ {"include-filter": "com.android.server.DeviceIdleControllerTest"}, + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"} ] } diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING index e2e118074aac..484fec31e594 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING +++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING @@ -3,6 +3,7 @@ { "name": "CtsJobSchedulerTestCases", "options": [ + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.LargeTest"} ] @@ -11,6 +12,7 @@ "name": "FrameworksMockingServicesTests", "options": [ {"include-filter": "com.android.server.job"}, + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"} ] }, @@ -18,6 +20,7 @@ "name": "FrameworksServicesTests", "options": [ {"include-filter": "com.android.server.job"}, + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"} ] } diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING index ba7572a5fb44..c5dc51cc9c24 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING +++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING @@ -4,6 +4,7 @@ "name": "CtsUsageStatsTestCases", "options": [ {"include-filter": "android.app.usage.cts.UsageStatsTest"}, + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"} ] }, @@ -11,6 +12,7 @@ "name": "FrameworksServicesTests", "options": [ {"include-filter": "com.android.server.usage"}, + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"} ] } diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index 821dd9e46c19..99e82e7a3367 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -132,19 +132,19 @@ droidstubs { java_library { name: "framework-media-stubs-publicapi", srcs: [":framework-media-stubs-srcs-publicapi"], - sdk_version: "current", + defaults: ["framework-module-stubs-lib-defaults-publicapi"], } java_library { name: "framework-media-stubs-systemapi", srcs: [":framework-media-stubs-srcs-systemapi"], - sdk_version: "system_current", + defaults: ["framework-module-stubs-lib-defaults-systemapi"], } java_library { name: "framework-media-stubs-module_libs_api", srcs: [":framework-media-stubs-srcs-module_libs_api"], - sdk_version: "system_current", + defaults: ["framework-module-stubs-lib-defaults-module_libs_api"], } java_library { diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index 02c55b7c8df9..5f86ed621084 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -52,6 +52,7 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.ParsableByteArray; +import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.ColorInfo; import java.io.EOFException; @@ -60,6 +61,7 @@ import java.io.InterruptedIOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; @@ -688,12 +690,83 @@ public final class MediaParser { * Returns an immutable list with the names of the parsers that are suitable for container * formats with the given {@link MediaFormat}. * - * <p>TODO: List which properties are taken into account. E.g. MimeType. + * <p>A parser supports a {@link MediaFormat} if the mime type associated with {@link + * MediaFormat#KEY_MIME} corresponds to the supported container format. + * + * @param mediaFormat The {@link MediaFormat} to check support for. + * @return The parser names that support the given {@code mediaFormat}, or the list of all + * parsers available if no container specific format information is provided. */ @NonNull @ParserName public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) { - throw new UnsupportedOperationException(); + String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME); + mimeType = mimeType == null ? null : Util.toLowerInvariant(mimeType.trim()); + if (TextUtils.isEmpty(mimeType)) { + // No MIME type provided. Return all. + return Collections.unmodifiableList( + new ArrayList<>(EXTRACTOR_FACTORIES_BY_NAME.keySet())); + } + ArrayList<String> result = new ArrayList<>(); + switch (mimeType) { + case "video/x-matroska": + case "audio/x-matroska": + case "video/x-webm": + case "audio/x-webm": + result.add(PARSER_NAME_MATROSKA); + break; + case "video/mp4": + case "audio/mp4": + case "application/mp4": + result.add(PARSER_NAME_MP4); + result.add(PARSER_NAME_FMP4); + break; + case "audio/mpeg": + result.add(PARSER_NAME_MP3); + break; + case "audio/aac": + result.add(PARSER_NAME_ADTS); + break; + case "audio/ac3": + result.add(PARSER_NAME_AC3); + break; + case "video/mp2t": + case "audio/mp2t": + result.add(PARSER_NAME_TS); + break; + case "video/x-flv": + result.add(PARSER_NAME_FLV); + break; + case "video/ogg": + case "audio/ogg": + case "application/ogg": + result.add(PARSER_NAME_OGG); + break; + case "video/mp2p": + case "video/mp1s": + result.add(PARSER_NAME_PS); + break; + case "audio/vnd.wave": + case "audio/wav": + case "audio/wave": + case "audio/x-wav": + result.add(PARSER_NAME_WAV); + break; + case "audio/amr": + result.add(PARSER_NAME_AMR); + break; + case "audio/ac4": + result.add(PARSER_NAME_AC4); + break; + case "audio/flac": + case "audio/x-flac": + result.add(PARSER_NAME_FLAC); + break; + default: + // No parsers support the given mime type. Do nothing. + break; + } + return Collections.unmodifiableList(result); } // Private fields. diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp index 86f4ab7c1128..707113b9672c 100644 --- a/apex/sdkextensions/framework/Android.bp +++ b/apex/sdkextensions/framework/Android.bp @@ -86,7 +86,7 @@ droidstubs { java_library { name: "framework-sdkextensions-stubs-publicapi", srcs: [":framework-sdkextensions-stubs-srcs-publicapi"], - sdk_version: "current", + defaults: ["framework-module-stubs-lib-defaults-publicapi"], visibility: [ "//frameworks/base", // Framework "//frameworks/base/apex/sdkextensions", // sdkextensions SDK @@ -96,7 +96,7 @@ java_library { java_library { name: "framework-sdkextensions-stubs-systemapi", srcs: [":framework-sdkextensions-stubs-srcs-systemapi"], - sdk_version: "system_current", + defaults: ["framework-module-stubs-lib-defaults-systemapi"], visibility: [ "//frameworks/base", // Framework "//frameworks/base/apex/sdkextensions", // sdkextensions SDK @@ -106,7 +106,7 @@ java_library { java_library { name: "framework-sdkextensions-stubs-module_libs_api", srcs: [":framework-sdkextensions-stubs-srcs-module_libs_api"], - sdk_version: "system_current", + defaults: ["framework-module-stubs-lib-defaults-module_libs_api"], visibility: [ "//frameworks/base", // Framework "//frameworks/base/apex/sdkextensions", // sdkextensions SDK diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp index 8185bb036b22..804eb030e795 100644 --- a/apex/statsd/framework/Android.bp +++ b/apex/statsd/framework/Android.bp @@ -46,19 +46,11 @@ filegroup { "//frameworks/base/apex/statsd:__subpackages__", ], } - -java_defaults { - name: "framework-statsd-defaults", - sdk_version: "module_current", - libs: [ "framework-annotations-lib" ], -} - java_library { name: "framework-statsd", - defaults: [ - "framework-statsd-defaults", - ], installable: true, + sdk_version: "module_current", + libs: [ "framework-annotations-lib" ], srcs: [ ":framework-statsd-sources", @@ -129,39 +121,33 @@ droidstubs { java_library { name: "framework-statsd-stubs-publicapi", - defaults: [ - "framework-statsd-defaults", - ], + defaults: ["framework-module-stubs-lib-defaults-publicapi"], srcs: [ ":framework-statsd-stubs-srcs-publicapi" ], visibility: [ "//frameworks/base", // Framework "//frameworks/base/apex/statsd", // statsd apex - ] + ], } java_library { name: "framework-statsd-stubs-systemapi", - defaults: [ - "framework-statsd-defaults", - ], + defaults: ["framework-module-stubs-lib-defaults-systemapi"], srcs: [ ":framework-statsd-stubs-srcs-systemapi" ], visibility: [ "//frameworks/base", // Framework "//frameworks/base/apex/statsd", // statsd apex - ] + ], } java_library { name: "framework-statsd-stubs-module_libs_api", - defaults: [ - "framework-statsd-defaults", - ], + defaults: ["framework-module-stubs-lib-defaults-module_libs_api"], srcs: [ ":framework-statsd-stubs-srcs-module_libs_api" ], visibility: [ "//frameworks/base", // Framework "//frameworks/base/apex/statsd", // statsd apex "//frameworks/opt/net/wifi/service" // wifi service - ] + ], } android_test { 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..ff74ce8b43dc 100644 --- a/api/current.txt +++ b/api/current.txt @@ -278,7 +278,7 @@ package android { field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8 field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9 - field public static final int actor = 16844313; // 0x1010619 + field public static final int actor = 16844312; // 0x1010618 field public static final int addPrintersActivity = 16843750; // 0x10103e6 field public static final int addStatesFromChildren = 16842992; // 0x10100f0 field public static final int adjustViewBounds = 16843038; // 0x101011e @@ -289,7 +289,6 @@ package android { field public static final int alignmentMode = 16843642; // 0x101037a field public static final int allContactsName = 16843468; // 0x10102cc field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601 - field public static final int allowAutoRevokePermissionsExemption = 16844309; // 0x1010615 field public static final int allowBackup = 16843392; // 0x1010280 field public static final int allowClearUserData = 16842757; // 0x1010005 field public static final int allowEmbedded = 16843765; // 0x10103f5 @@ -329,6 +328,7 @@ package android { field public static final int autoLink = 16842928; // 0x10100b0 field public static final int autoMirrored = 16843754; // 0x10103ea field public static final int autoRemoveFromRecents = 16843847; // 0x1010447 + field public static final int autoRevokePermissions = 16844308; // 0x1010614 field public static final int autoSizeMaxTextSize = 16844102; // 0x1010546 field public static final int autoSizeMinTextSize = 16844088; // 0x1010538 field public static final int autoSizePresetSizes = 16844087; // 0x1010537 @@ -710,7 +710,7 @@ package android { field public static final int gravity = 16842927; // 0x10100af field public static final int gridViewStyle = 16842865; // 0x1010071 field public static final int groupIndicator = 16843019; // 0x101010b - field public static final int gwpAsanMode = 16844312; // 0x1010618 + field public static final int gwpAsanMode = 16844311; // 0x1010617 field public static final int hand_hour = 16843011; // 0x1010103 field public static final int hand_minute = 16843012; // 0x1010104 field public static final int handle = 16843354; // 0x101025a @@ -955,7 +955,7 @@ package android { field public static final int mediaRouteButtonStyle = 16843693; // 0x10103ad field public static final int mediaRouteTypes = 16843694; // 0x10103ae field public static final int menuCategory = 16843230; // 0x10101de - field public static final int mimeGroup = 16844311; // 0x1010617 + field public static final int mimeGroup = 16844310; // 0x1010616 field public static final int mimeType = 16842790; // 0x1010026 field public static final int min = 16844089; // 0x1010539 field public static final int minAspectRatio = 16844187; // 0x101059b @@ -1084,7 +1084,7 @@ package android { field public static final int preferenceScreenStyle = 16842891; // 0x101008b field public static final int preferenceStyle = 16842894; // 0x101008e field public static final int presentationTheme = 16843712; // 0x10103c0 - field public static final int preserveLegacyExternalStorage = 16844310; // 0x1010616 + field public static final int preserveLegacyExternalStorage = 16844309; // 0x1010615 field public static final int previewImage = 16843482; // 0x10102da field public static final int primaryContentAlpha = 16844114; // 0x1010552 field public static final int priority = 16842780; // 0x101001c @@ -1141,7 +1141,6 @@ package android { field public static final int reqKeyboardType = 16843304; // 0x1010228 field public static final int reqNavigation = 16843306; // 0x101022a field public static final int reqTouchScreen = 16843303; // 0x1010227 - field public static final int requestAutoRevokePermissionsExemption = 16844308; // 0x1010614 field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603 field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e @@ -7605,6 +7604,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; @@ -25340,11 +25340,12 @@ package android.media { public final class MediaCodec.QueueRequest { method public void queue(); method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer); + method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @NonNull android.media.MediaCodec.CryptoInfo); method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int); method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float); method @NonNull public android.media.MediaCodec.QueueRequest setHardwareBuffer(@NonNull android.hardware.HardwareBuffer); method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int); - method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @Nullable android.media.MediaCodec.CryptoInfo); + method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int); method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long); method @NonNull public android.media.MediaCodec.QueueRequest setPresentationTimeUs(long); method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String); @@ -45922,13 +45923,9 @@ package android.telecom { method public void onConference(android.telecom.Connection, android.telecom.Connection); method public void onConnectionServiceFocusGained(); method public void onConnectionServiceFocusLost(); - method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); - method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); - method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); - method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); diff --git a/api/system-current.txt b/api/system-current.txt index a1fd0dbd8df0..7e25382f06d9 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -6061,7 +6061,7 @@ package android.net { method public void release(); } - public class InvalidPacketException extends java.lang.Exception { + public final class InvalidPacketException extends java.lang.Exception { ctor public InvalidPacketException(int); method public int getError(); field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb @@ -6255,7 +6255,7 @@ package android.net { field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18 } - public static class NetworkCapabilities.Builder { + public static final class NetworkCapabilities.Builder { ctor public NetworkCapabilities.Builder(); ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities); method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int); diff --git a/api/test-current.txt b/api/test-current.txt index 38cf6a16de56..ba6feedc65ba 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -535,6 +535,7 @@ package android.app { method public void setWindowingMode(int); method public void writeToParcel(android.os.Parcel, int); field public static final int ACTIVITY_TYPE_ASSISTANT = 4; // 0x4 + field public static final int ACTIVITY_TYPE_DREAM = 5; // 0x5 field public static final int ACTIVITY_TYPE_HOME = 2; // 0x2 field public static final int ACTIVITY_TYPE_RECENTS = 3; // 0x3 field public static final int ACTIVITY_TYPE_STANDARD = 1; // 0x1 @@ -598,7 +599,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; } @@ -1834,7 +1834,7 @@ package android.net { field public static final int TRANSPORT_TEST = 7; // 0x7 } - public static class NetworkCapabilities.Builder { + public static final class NetworkCapabilities.Builder { ctor public NetworkCapabilities.Builder(); ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities); method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int); diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java index 59544a971e5f..ca1d598ee7d7 100644 --- a/cmds/content/src/com/android/commands/content/Content.java +++ b/cmds/content/src/com/android/commands/content/Content.java @@ -32,8 +32,11 @@ import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.UserHandle; import android.text.TextUtils; +import android.util.Pair; import java.io.FileDescriptor; +import java.util.ArrayList; +import java.util.List; /** * This class is a command line utility for manipulating content. A client @@ -72,7 +75,7 @@ public class Content { "usage: adb shell content [subcommand] [options]\n" + "\n" + "usage: adb shell content insert --uri <URI> [--user <USER_ID>]" - + " --bind <BINDING> [--bind <BINDING>...]\n" + + " --bind <BINDING> [--bind <BINDING>...] [--extra <BINDING>...]\n" + " <URI> a content provider URI.\n" + " <BINDING> binds a typed value to a column and is formatted:\n" + " <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n" @@ -84,7 +87,8 @@ public class Content { + " adb shell content insert --uri content://settings/secure --bind name:s:new_setting" + " --bind value:s:new_value\n" + "\n" - + "usage: adb shell content update --uri <URI> [--user <USER_ID>] [--where <WHERE>]\n" + + "usage: adb shell content update --uri <URI> [--user <USER_ID>]" + + " [--where <WHERE>] [--extra <BINDING>...]\n" + " <WHERE> is a SQL style where clause in quotes (You have to escape single quotes" + " - see example below).\n" + " Example:\n" @@ -93,14 +97,15 @@ public class Content { + " value:s:newer_value --where \"name=\'new_setting\'\"\n" + "\n" + "usage: adb shell content delete --uri <URI> [--user <USER_ID>] --bind <BINDING>" - + " [--bind <BINDING>...] [--where <WHERE>]\n" + + " [--bind <BINDING>...] [--where <WHERE>] [--extra <BINDING>...]\n" + " Example:\n" + " # Remove \"new_setting\" secure setting.\n" + " adb shell content delete --uri content://settings/secure " + "--where \"name=\'new_setting\'\"\n" + "\n" + "usage: adb shell content query --uri <URI> [--user <USER_ID>]" - + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]\n" + + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]" + + " [--extra <BINDING>...]\n" + " <PROJECTION> is a list of colon separated column names and is formatted:\n" + " <COLUMN_NAME>[:<COLUMN_NAME>...]\n" + " <SORT_ORDER> is the order in which rows in the result should be sorted.\n" @@ -196,6 +201,7 @@ public class Content { Uri uri = null; int userId = UserHandle.USER_SYSTEM; ContentValues values = new ContentValues(); + Bundle extras = new Bundle(); for (String argument; (argument = mTokenizer.nextArg()) != null;) { if (ARGUMENT_URI.equals(argument)) { uri = Uri.parse(argumentValueRequired(argument)); @@ -203,6 +209,8 @@ public class Content { userId = Integer.parseInt(argumentValueRequired(argument)); } else if (ARGUMENT_BIND.equals(argument)) { parseBindValue(values); + } else if (ARGUMENT_EXTRA.equals(argument)) { + parseBindValue(extras); } else { throw new IllegalArgumentException("Unsupported argument: " + argument); } @@ -215,20 +223,23 @@ public class Content { throw new IllegalArgumentException("Bindings not specified." + " Did you specify --bind argument(s)?"); } - return new InsertCommand(uri, userId, values); + return new InsertCommand(uri, userId, values, extras); } private DeleteCommand parseDeleteCommand() { Uri uri = null; int userId = UserHandle.USER_SYSTEM; - String where = null; + Bundle extras = new Bundle(); for (String argument; (argument = mTokenizer.nextArg())!= null;) { if (ARGUMENT_URI.equals(argument)) { uri = Uri.parse(argumentValueRequired(argument)); } else if (ARGUMENT_USER.equals(argument)) { userId = Integer.parseInt(argumentValueRequired(argument)); } else if (ARGUMENT_WHERE.equals(argument)) { - where = argumentValueRequired(argument); + extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, + argumentValueRequired(argument)); + } else if (ARGUMENT_EXTRA.equals(argument)) { + parseBindValue(extras); } else { throw new IllegalArgumentException("Unsupported argument: " + argument); } @@ -237,23 +248,26 @@ public class Content { throw new IllegalArgumentException("Content provider URI not specified." + " Did you specify --uri argument?"); } - return new DeleteCommand(uri, userId, where); + return new DeleteCommand(uri, userId, extras); } private UpdateCommand parseUpdateCommand() { Uri uri = null; int userId = UserHandle.USER_SYSTEM; - String where = null; ContentValues values = new ContentValues(); + Bundle extras = new Bundle(); for (String argument; (argument = mTokenizer.nextArg())!= null;) { if (ARGUMENT_URI.equals(argument)) { uri = Uri.parse(argumentValueRequired(argument)); } else if (ARGUMENT_USER.equals(argument)) { userId = Integer.parseInt(argumentValueRequired(argument)); } else if (ARGUMENT_WHERE.equals(argument)) { - where = argumentValueRequired(argument); + extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, + argumentValueRequired(argument)); } else if (ARGUMENT_BIND.equals(argument)) { parseBindValue(values); + } else if (ARGUMENT_EXTRA.equals(argument)) { + parseBindValue(extras); } else { throw new IllegalArgumentException("Unsupported argument: " + argument); } @@ -266,7 +280,7 @@ public class Content { throw new IllegalArgumentException("Bindings not specified." + " Did you specify --bind argument(s)?"); } - return new UpdateCommand(uri, userId, values, where); + return new UpdateCommand(uri, userId, values, extras); } public CallCommand parseCallCommand() { @@ -274,7 +288,7 @@ public class Content { int userId = UserHandle.USER_SYSTEM; String arg = null; Uri uri = null; - ContentValues values = new ContentValues(); + Bundle extras = new Bundle(); for (String argument; (argument = mTokenizer.nextArg())!= null;) { if (ARGUMENT_URI.equals(argument)) { uri = Uri.parse(argumentValueRequired(argument)); @@ -285,11 +299,10 @@ public class Content { } else if (ARGUMENT_ARG.equals(argument)) { arg = argumentValueRequired(argument); } else if (ARGUMENT_EXTRA.equals(argument)) { - parseBindValue(values); + parseBindValue(extras); } else { throw new IllegalArgumentException("Unsupported argument: " + argument); } - } if (uri == null) { throw new IllegalArgumentException("Content provider URI not specified." @@ -298,7 +311,7 @@ public class Content { if (method == null) { throw new IllegalArgumentException("Content provider method not specified."); } - return new CallCommand(uri, userId, method, arg, values); + return new CallCommand(uri, userId, method, arg, extras); } private GetTypeCommand parseGetTypeCommand() { @@ -363,19 +376,22 @@ public class Content { Uri uri = null; int userId = UserHandle.USER_SYSTEM; String[] projection = null; - String sort = null; - String where = null; + Bundle extras = new Bundle(); for (String argument; (argument = mTokenizer.nextArg())!= null;) { if (ARGUMENT_URI.equals(argument)) { uri = Uri.parse(argumentValueRequired(argument)); } else if (ARGUMENT_USER.equals(argument)) { userId = Integer.parseInt(argumentValueRequired(argument)); } else if (ARGUMENT_WHERE.equals(argument)) { - where = argumentValueRequired(argument); + extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, + argumentValueRequired(argument)); } else if (ARGUMENT_SORT.equals(argument)) { - sort = argumentValueRequired(argument); + extras.putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER, + argumentValueRequired(argument)); } else if (ARGUMENT_PROJECTION.equals(argument)) { projection = argumentValueRequired(argument).split("[\\s]*:[\\s]*"); + } else if (ARGUMENT_EXTRA.equals(argument)) { + parseBindValue(extras); } else { throw new IllegalArgumentException("Unsupported argument: " + argument); } @@ -384,40 +400,76 @@ public class Content { throw new IllegalArgumentException("Content provider URI not specified." + " Did you specify --uri argument?"); } - return new QueryCommand(uri, userId, projection, where, sort); + return new QueryCommand(uri, userId, projection, extras); + } + + private List<String> splitWithEscaping(String argument, char splitChar) { + final List<String> res = new ArrayList<>(); + final StringBuilder cur = new StringBuilder(); + for (int i = 0; i < argument.length(); i++) { + char c = argument.charAt(i); + if (c == '\\') { + if (++i == argument.length()) { + throw new IllegalArgumentException("Invalid escaping"); + } else { + // Skip escaping char and insert next + c = argument.charAt(i); + cur.append(c); + } + } else if (c == splitChar) { + // Splitting char means next string + res.add(cur.toString()); + cur.setLength(0); + } else { + // Copy non-escaping and non-splitting char + cur.append(c); + } + } + res.add(cur.toString()); + return res; } - private void parseBindValue(ContentValues values) { + private Pair<String, Object> parseBindValue() { String argument = mTokenizer.nextArg(); if (TextUtils.isEmpty(argument)) { throw new IllegalArgumentException("Binding not well formed: " + argument); } - final int firstColonIndex = argument.indexOf(COLON); - if (firstColonIndex < 0) { + final List<String> split = splitWithEscaping(argument, COLON.charAt(0)); + if (split.size() != 3) { throw new IllegalArgumentException("Binding not well formed: " + argument); } - final int secondColonIndex = argument.indexOf(COLON, firstColonIndex + 1); - if (secondColonIndex < 0) { - throw new IllegalArgumentException("Binding not well formed: " + argument); - } - String column = argument.substring(0, firstColonIndex); - String type = argument.substring(firstColonIndex + 1, secondColonIndex); - String value = argument.substring(secondColonIndex + 1); + String column = split.get(0); + String type = split.get(1); + String value = split.get(2); if (TYPE_STRING.equals(type)) { - values.put(column, value); + return Pair.create(column, value); } else if (TYPE_BOOLEAN.equalsIgnoreCase(type)) { - values.put(column, Boolean.parseBoolean(value)); - } else if (TYPE_INTEGER.equalsIgnoreCase(type) || TYPE_LONG.equalsIgnoreCase(type)) { - values.put(column, Long.parseLong(value)); - } else if (TYPE_FLOAT.equalsIgnoreCase(type) || TYPE_DOUBLE.equalsIgnoreCase(type)) { - values.put(column, Double.parseDouble(value)); + return Pair.create(column, Boolean.parseBoolean(value)); + } else if (TYPE_INTEGER.equalsIgnoreCase(type)) { + return Pair.create(column, Integer.parseInt(value)); + } else if (TYPE_LONG.equalsIgnoreCase(type)) { + return Pair.create(column, Long.parseLong(value)); + } else if (TYPE_FLOAT.equalsIgnoreCase(type)) { + return Pair.create(column, Float.parseFloat(value)); + } else if (TYPE_DOUBLE.equalsIgnoreCase(type)) { + return Pair.create(column, Double.parseDouble(value)); } else if (TYPE_NULL.equalsIgnoreCase(type)) { - values.putNull(column); + return Pair.create(column, null); } else { throw new IllegalArgumentException("Unsupported type: " + type); } } + private void parseBindValue(ContentValues values) { + final Pair<String, Object> columnValue = parseBindValue(); + values.putObject(columnValue.first, columnValue.second); + } + + private void parseBindValue(Bundle extras) { + final Pair<String, Object> columnValue = parseBindValue(); + extras.putObject(columnValue.first, columnValue.second); + } + private String argumentValueRequired(String argument) { String value = mTokenizer.nextArg(); if (TextUtils.isEmpty(value) || value.startsWith(ARGUMENT_PREFIX)) { @@ -500,60 +552,43 @@ public class Content { private static class InsertCommand extends Command { final ContentValues mContentValues; + final Bundle mExtras; - public InsertCommand(Uri uri, int userId, ContentValues contentValues) { + public InsertCommand(Uri uri, int userId, ContentValues contentValues, Bundle extras) { super(uri, userId); mContentValues = contentValues; + mExtras = extras; } @Override public void onExecute(IContentProvider provider) throws Exception { - provider.insert(resolveCallingPackage(), null, mUri, mContentValues, null); + provider.insert(resolveCallingPackage(), null, mUri, mContentValues, mExtras); } } private static class DeleteCommand extends Command { - final String mWhere; + final Bundle mExtras; - public DeleteCommand(Uri uri, int userId, String where) { + public DeleteCommand(Uri uri, int userId, Bundle extras) { super(uri, userId); - mWhere = where; + mExtras = extras; } @Override public void onExecute(IContentProvider provider) throws Exception { - provider.delete(resolveCallingPackage(), null, mUri, - ContentResolver.createSqlQueryBundle(mWhere, null)); + provider.delete(resolveCallingPackage(), null, mUri, mExtras); } } private static class CallCommand extends Command { final String mMethod, mArg; - Bundle mExtras = null; + final Bundle mExtras; - public CallCommand(Uri uri, int userId, String method, String arg, ContentValues values) { + public CallCommand(Uri uri, int userId, String method, String arg, Bundle extras) { super(uri, userId); mMethod = method; mArg = arg; - if (values != null) { - mExtras = new Bundle(); - for (String key : values.keySet()) { - final Object val = values.get(key); - if (val instanceof String) { - mExtras.putString(key, (String) val); - } else if (val instanceof Float) { - mExtras.putFloat(key, (Float) val); - } else if (val instanceof Double) { - mExtras.putDouble(key, (Double) val); - } else if (val instanceof Boolean) { - mExtras.putBoolean(key, (Boolean) val); - } else if (val instanceof Integer) { - mExtras.putInt(key, (Integer) val); - } else if (val instanceof Long) { - mExtras.putLong(key, (Long) val); - } - } - } + mExtras = extras; } @Override @@ -604,21 +639,20 @@ public class Content { } } - private static class QueryCommand extends DeleteCommand { + private static class QueryCommand extends Command { final String[] mProjection; - final String mSortOrder; + final Bundle mExtras; - public QueryCommand( - Uri uri, int userId, String[] projection, String where, String sortOrder) { - super(uri, userId, where); + public QueryCommand(Uri uri, int userId, String[] projection, Bundle extras) { + super(uri, userId); mProjection = projection; - mSortOrder = sortOrder; + mExtras = extras; } @Override public void onExecute(IContentProvider provider) throws Exception { Cursor cursor = provider.query(resolveCallingPackage(), null, mUri, mProjection, - ContentResolver.createSqlQueryBundle(mWhere, null, mSortOrder), null); + mExtras, null); if (cursor == null) { System.out.println("No result found."); return; @@ -670,18 +704,19 @@ public class Content { } } - private static class UpdateCommand extends InsertCommand { - final String mWhere; + private static class UpdateCommand extends Command { + final ContentValues mValues; + final Bundle mExtras; - public UpdateCommand(Uri uri, int userId, ContentValues contentValues, String where) { - super(uri, userId, contentValues); - mWhere = where; + public UpdateCommand(Uri uri, int userId, ContentValues values, Bundle extras) { + super(uri, userId); + mValues = values; + mExtras = extras; } @Override public void onExecute(IContentProvider provider) throws Exception { - provider.update(resolveCallingPackage(), null, mUri, mContentValues, - ContentResolver.createSqlQueryBundle(mWhere, null)); + provider.update(resolveCallingPackage(), null, mUri, mValues, mExtras); } } 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/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 4529dff0dc5c..45f21ae8e3e1 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -97,6 +97,7 @@ cc_defaults { "src/stats_log_util.cpp", "src/statscompanion_util.cpp", "src/statsd_config.proto", + "src/statsd_metadata.proto", "src/StatsLogProcessor.cpp", "src/StatsService.cpp", "src/storage/StorageManager.cpp", @@ -378,55 +379,55 @@ cc_test { // statsd micro benchmark //############################# -//cc_benchmark { -// name: "statsd_benchmark", -// defaults: ["statsd_defaults"], -// -// srcs: [ -// // atom_field_options.proto needs field_options.proto, but that is -// // not included in libprotobuf-cpp-lite, so compile it here. -// ":libprotobuf-internal-protos", -// -// "benchmark/duration_metric_benchmark.cpp", -// "benchmark/filter_value_benchmark.cpp", -// "benchmark/get_dimensions_for_condition_benchmark.cpp", -// "benchmark/hello_world_benchmark.cpp", -// "benchmark/log_event_benchmark.cpp", -// "benchmark/main.cpp", -// "benchmark/metric_util.cpp", -// "benchmark/stats_write_benchmark.cpp", -// "src/atom_field_options.proto", -// "src/atoms.proto", -// "src/stats_log.proto", -// ], -// -// proto: { -// type: "lite", -// include_dirs: ["external/protobuf/src"], -// }, -// -// cflags: [ -// "-Wall", -// "-Werror", -// "-Wno-unused-parameter", -// "-Wno-unused-variable", -// "-Wno-unused-function", -// -// // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374 -// "-Wno-varargs", -// ], -// -// static_libs: [ -// "libplatformprotos", -// ], -// -// shared_libs: [ -// "libgtest_prod", -// "libprotobuf-cpp-lite", -// "libstatslog", -// "libstatssocket", -// ], -//} +cc_benchmark { + name: "statsd_benchmark", + defaults: ["statsd_defaults"], + + srcs: [ + // atom_field_options.proto needs field_options.proto, but that is + // not included in libprotobuf-cpp-lite, so compile it here. + ":libprotobuf-internal-protos", + + "benchmark/duration_metric_benchmark.cpp", + "benchmark/filter_value_benchmark.cpp", + "benchmark/get_dimensions_for_condition_benchmark.cpp", + "benchmark/hello_world_benchmark.cpp", + "benchmark/log_event_benchmark.cpp", + "benchmark/main.cpp", + "benchmark/metric_util.cpp", + "benchmark/stats_write_benchmark.cpp", + "src/atom_field_options.proto", + "src/atoms.proto", + "src/stats_log.proto", + ], + + proto: { + type: "lite", + include_dirs: ["external/protobuf/src"], + }, + + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + "-Wno-unused-variable", + "-Wno-unused-function", + + // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374 + "-Wno-varargs", + ], + + static_libs: [ + "libplatformprotos", + "libstatssocket_private", + ], + + shared_libs: [ + "libgtest_prod", + "libprotobuf-cpp-lite", + "libstatslog", + ], +} // ==== java proto device library (for test only) ============================== java_library { diff --git a/cmds/statsd/benchmark/duration_metric_benchmark.cpp b/cmds/statsd/benchmark/duration_metric_benchmark.cpp index 2631009c71f2..2d315d9395bc 100644 --- a/cmds/statsd/benchmark/duration_metric_benchmark.cpp +++ b/cmds/statsd/benchmark/duration_metric_benchmark.cpp @@ -137,77 +137,74 @@ static void BM_DurationMetricNoLink(benchmark::State& state) { int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - - std::vector<AttributionNodeInternal> attributions1 = { - CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; - - std::vector<AttributionNodeInternal> attributions2 = { - CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(555, "GMSCoreModule2")}; - std::vector<std::unique_ptr<LogEvent>> events; - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, - bucketStartTimeNs + 11)); - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, - bucketStartTimeNs + 40)); - - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, - bucketStartTimeNs + 102)); - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, - bucketStartTimeNs + 450)); - - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, - bucketStartTimeNs + 650)); - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, - bucketStartTimeNs + bucketSizeNs + 100)); - - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, - bucketStartTimeNs + bucketSizeNs + 640)); - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, - bucketStartTimeNs + bucketSizeNs + 650)); - - events.push_back(CreateStartScheduledJobEvent( - {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 2)); - events.push_back(CreateFinishScheduledJobEvent( - {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101)); - - events.push_back(CreateStartScheduledJobEvent( - {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201)); - events.push_back(CreateFinishScheduledJobEvent( - {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500)); - - events.push_back(CreateStartScheduledJobEvent( - {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600)); - events.push_back(CreateFinishScheduledJobEvent( - {CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850)); - - events.push_back(CreateStartScheduledJobEvent( - {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600)); - events.push_back(CreateFinishScheduledJobEvent( - {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900)); - - events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", - bucketStartTimeNs + 10)); - events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", - bucketStartTimeNs + 50)); - - events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", - bucketStartTimeNs + 200)); - events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 300)); - - events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", - bucketStartTimeNs + 400)); - events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc", - bucketStartTimeNs + bucketSizeNs - 1)); - - events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", - bucketStartTimeNs + 401)); - events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 700)); - + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 11, + android::view::DISPLAY_STATE_OFF)); + events.push_back( + CreateScreenStateChangedEvent(bucketStartTimeNs + 40, android::view::DISPLAY_STATE_ON)); + + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 102, + android::view::DISPLAY_STATE_OFF)); + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450, + android::view::DISPLAY_STATE_ON)); + + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 650, + android::view::DISPLAY_STATE_OFF)); + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100, + android::view::DISPLAY_STATE_ON)); + + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 640, + android::view::DISPLAY_STATE_OFF)); + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 650, + android::view::DISPLAY_STATE_ON)); + + vector<int> attributionUids1 = {9999}; + vector<string> attributionTags1 = {""}; + events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "job0")); + events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1, + attributionTags1, "job0")); + + events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids1, + attributionTags1, "job2")); + events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids1, + attributionTags1, "job2")); + + vector<int> attributionUids2 = {8888}; + events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2, + attributionTags1, "job2")); + events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850, + attributionUids2, attributionTags1, "job2")); + + events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 600, + attributionUids2, attributionTags1, "job1")); + events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900, + attributionUids2, attributionTags1, "job1")); + + vector<int> attributionUids3 = {111, 222, 222}; + vector<string> attributionTags3 = {"App1", "GMSCoreModule1", "GMSCoreModule2"}; + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 10, attributionUids3, + attributionTags3, "ReadEmail")); + events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 50, attributionUids3, attributionTags3, + "ReadEmail")); + + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200, attributionUids3, + attributionTags3, "ReadEmail")); + events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids3, + attributionTags3, "ReadEmail")); + + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids3, + attributionTags3, "ReadDoc")); + events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids3, + attributionTags3, "ReadDoc")); + + vector<int> attributionUids4 = {333, 222, 555}; + vector<string> attributionTags4 = {"App2", "GMSCoreModule1", "GMSCoreModule2"}; + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 401, attributionUids4, + attributionTags4, "ReadEmail")); + events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids4, + attributionTags4, "ReadEmail")); sortLogEventsByTimestamp(&events); while (state.KeepRunning()) { @@ -230,78 +227,75 @@ static void BM_DurationMetricLink(benchmark::State& state) { int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL; - std::vector<AttributionNodeInternal> attributions1 = { - CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(222, "GMSCoreModule2")}; - - std::vector<AttributionNodeInternal> attributions2 = { - CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(555, "GMSCoreModule2")}; - - std::vector<AttributionNodeInternal> attributions3 = { - CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"), - CreateAttribution(555, "GMSCoreModule2")}; - std::vector<std::unique_ptr<LogEvent>> events; - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, - bucketStartTimeNs + 55)); - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, - bucketStartTimeNs + 120)); - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, - bucketStartTimeNs + 121)); - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, - bucketStartTimeNs + 450)); - - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, - bucketStartTimeNs + 501)); - events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, - bucketStartTimeNs + bucketSizeNs + 100)); - - events.push_back(CreateStartScheduledJobEvent( - {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1)); - events.push_back(CreateFinishScheduledJobEvent( - {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101)); - - events.push_back(CreateStartScheduledJobEvent( - {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201)); - events.push_back(CreateFinishScheduledJobEvent( - {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500)); - events.push_back(CreateStartScheduledJobEvent( - {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600)); - events.push_back(CreateFinishScheduledJobEvent( - {CreateAttribution(333, "App2")}, "job2", - bucketStartTimeNs + bucketSizeNs + 850)); - - events.push_back( - CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3", - bucketStartTimeNs + bucketSizeNs - 2)); - events.push_back( - CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3", - bucketStartTimeNs + bucketSizeNs + 900)); - - events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", - bucketStartTimeNs + 50)); - events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail", - bucketStartTimeNs + 110)); - - events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", - bucketStartTimeNs + 300)); - events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail", - bucketStartTimeNs + bucketSizeNs + 700)); - events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc", - bucketStartTimeNs + 400)); - events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc", - bucketStartTimeNs + bucketSizeNs - 1)); - - events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc", - bucketStartTimeNs + 550)); - events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc", - bucketStartTimeNs + 800)); - events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc", - bucketStartTimeNs + bucketSizeNs - 1)); - events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc", - bucketStartTimeNs + bucketSizeNs + 700)); + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 55, + android::view::DISPLAY_STATE_OFF)); + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 120, + android::view::DISPLAY_STATE_ON)); + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 121, + android::view::DISPLAY_STATE_OFF)); + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450, + android::view::DISPLAY_STATE_ON)); + + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 501, + android::view::DISPLAY_STATE_OFF)); + events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100, + android::view::DISPLAY_STATE_ON)); + + vector<int> attributionUids1 = {111}; + vector<string> attributionTags1 = {"App1"}; + events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 1, attributionUids1, + attributionTags1, "job1")); + events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1, + attributionTags1, "job1")); + + vector<int> attributionUids2 = {333}; + vector<string> attributionTags2 = {"App2"}; + events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids2, + attributionTags2, "job2")); + events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids2, + attributionTags2, "job2")); + events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2, + attributionTags2, "job2")); + events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850, + attributionUids2, attributionTags2, "job2")); + + vector<int> attributionUids3 = {444}; + vector<string> attributionTags3 = {"App3"}; + events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs - 2, + attributionUids3, attributionTags3, "job3")); + events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900, + attributionUids3, attributionTags3, "job3")); + + vector<int> attributionUids4 = {111, 222, 222}; + vector<string> attributionTags4 = {"App1", "GMSCoreModule1", "GMSCoreModule2"}; + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids4, + attributionTags4, "ReadEmail")); + events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 110, attributionUids4, attributionTags4, + "ReadEmail")); + + vector<int> attributionUids5 = {333, 222, 555}; + vector<string> attributionTags5 = {"App2", "GMSCoreModule1", "GMSCoreModule2"}; + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 300, attributionUids5, + attributionTags5, "ReadEmail")); + events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids5, + attributionTags5, "ReadEmail")); + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids5, + attributionTags5, "ReadDoc")); + events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids5, + attributionTags5, "ReadDoc")); + + vector<int> attributionUids6 = {444, 222, 555}; + vector<string> attributionTags6 = {"App3", "GMSCoreModule1", "GMSCoreModule2"}; + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 550, attributionUids6, + attributionTags6, "ReadDoc")); + events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 800, attributionUids6, attributionTags6, + "ReadDoc")); + events.push_back(CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids6, + attributionTags6, "ReadDoc")); + events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids6, + attributionTags6, "ReadDoc")); sortLogEventsByTimestamp(&events); while (state.KeepRunning()) { diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp index cfe477d7ca8f..28bf21ae52bf 100644 --- a/cmds/statsd/benchmark/filter_value_benchmark.cpp +++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp @@ -19,6 +19,7 @@ #include "HashableDimensionKey.h" #include "logd/LogEvent.h" #include "stats_log_util.h" +#include "stats_event.h" namespace android { namespace os { @@ -26,17 +27,31 @@ namespace statsd { using std::vector; -static void createLogEventAndMatcher(LogEvent* event, FieldMatcher *field_matcher) { - AttributionNodeInternal node; - node.set_uid(100); - node.set_tag("LOCATION"); +static void createLogEventAndMatcher(LogEvent* event, FieldMatcher* field_matcher) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, 1); + AStatsEvent_overwriteTimestamp(statsEvent, 100000); - std::vector<AttributionNodeInternal> nodes = {node, node}; - event->write(nodes); - event->write(3.2f); - event->write("LOCATION"); - event->write((int64_t)990); - event->init(); + std::vector<int> attributionUids = {100, 100}; + std::vector<string> attributionTags = {"LOCATION", "LOCATION"}; + + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); + AStatsEvent_writeFloat(statsEvent, 3.2f); + AStatsEvent_writeString(statsEvent, "LOCATION"); + AStatsEvent_writeInt64(statsEvent, 990); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + event->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); field_matcher->set_field(1); auto child = field_matcher->add_child(); @@ -46,7 +61,7 @@ static void createLogEventAndMatcher(LogEvent* event, FieldMatcher *field_matche } static void BM_FilterValue(benchmark::State& state) { - LogEvent event(1, 100000); + LogEvent event(/*uid=*/0, /*pid=*/0); FieldMatcher field_matcher; createLogEventAndMatcher(&event, &field_matcher); diff --git a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp index 2a4403ed8282..c7d01cc406fc 100644 --- a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp +++ b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp @@ -19,6 +19,7 @@ #include "HashableDimensionKey.h" #include "logd/LogEvent.h" #include "stats_log_util.h" +#include "stats_event.h" namespace android { namespace os { @@ -27,16 +28,30 @@ namespace statsd { using std::vector; static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) { - AttributionNodeInternal node; - node.set_uid(100); - node.set_tag("LOCATION"); + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, 1); + AStatsEvent_overwriteTimestamp(statsEvent, 100000); - std::vector<AttributionNodeInternal> nodes = {node, node}; - event->write(nodes); - event->write(3.2f); - event->write("LOCATION"); - event->write((int64_t)990); - event->init(); + std::vector<int> attributionUids = {100, 100}; + std::vector<string> attributionTags = {"LOCATION", "LOCATION"}; + + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); + AStatsEvent_writeFloat(statsEvent, 3.2f); + AStatsEvent_writeString(statsEvent, "LOCATION"); + AStatsEvent_writeInt64(statsEvent, 990); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + event->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); link->conditionId = 1; @@ -54,7 +69,7 @@ static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) { static void BM_GetDimensionInCondition(benchmark::State& state) { Metric2Condition link; - LogEvent event(1, 100000); + LogEvent event(/*uid=*/0, /*pid=*/0); createLogEventAndLink(&event, &link); while (state.KeepRunning()) { diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp index 8b687438ca27..057e00bde202 100644 --- a/cmds/statsd/benchmark/log_event_benchmark.cpp +++ b/cmds/statsd/benchmark/log_event_benchmark.cpp @@ -39,7 +39,8 @@ static void BM_LogEventCreation(benchmark::State& state) { uint8_t msg[LOGGER_ENTRY_MAX_PAYLOAD]; size_t size = createAndParseStatsEvent(msg); while (state.KeepRunning()) { - benchmark::DoNotOptimize(LogEvent(msg, size, /*uid=*/ 1000, /*pid=*/ 1001)); + LogEvent event(/*uid=*/ 1000, /*pid=*/ 1001); + benchmark::DoNotOptimize(event.parseBuffer(msg, size)); } } BENCHMARK(BM_LogEventCreation); diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp index cca6d525ea16..4bce89fd7f9c 100644 --- a/cmds/statsd/benchmark/metric_util.cpp +++ b/cmds/statsd/benchmark/metric_util.cpp @@ -14,6 +14,8 @@ #include "metric_util.h" +#include "stats_event.h" + namespace android { namespace os { namespace statsd { @@ -246,117 +248,112 @@ FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) } std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( - const android::view::DisplayStateEnum state, uint64_t timestampNs) { - auto event = std::make_unique<LogEvent>(android::util::SCREEN_STATE_CHANGED, timestampNs); - event->write(state); - event->init(); - return event; -} + uint64_t timestampNs, const android::view::DisplayStateEnum state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); -std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent( - int level, uint64_t timestampNs) { - auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs); - (event->write(level)); - event->init(); - return event; + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; } std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& jobName, - const ScheduledJobStateChanged::State state, uint64_t timestampNs) { - auto event = std::make_unique<LogEvent>(android::util::SCHEDULED_JOB_STATE_CHANGED, timestampNs); - event->write(attributions); - event->write(jobName); - event->write(state); - event->init(); - return event; + const vector<int>& attributionUids, const vector<string>& attributionTags, + const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); + AStatsEvent_writeString(statsEvent, jobName.c_str()); + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; } -std::unique_ptr<LogEvent> CreateStartScheduledJobEvent( - const std::vector<AttributionNodeInternal>& attributions, - const string& name, uint64_t timestampNs) { - return CreateScheduledJobStateChangedEvent( - attributions, name, ScheduledJobStateChanged::STARTED, timestampNs); +std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& jobName) { + return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, + ScheduledJobStateChanged::STARTED, timestampNs); } // Create log event when scheduled job finishes. -std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent( - const std::vector<AttributionNodeInternal>& attributions, - const string& name, uint64_t timestampNs) { - return CreateScheduledJobStateChangedEvent( - attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs); -} - -std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, - const WakelockStateChanged::State state, uint64_t timestampNs) { - auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs); - event->write(attributions); - event->write(android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK); - event->write(wakelockName); - event->write(state); - event->init(); - return event; -} - -std::unique_ptr<LogEvent> CreateAcquireWakelockEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, - uint64_t timestampNs) { - return CreateWakelockStateChangedEvent( - attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs); -} - -std::unique_ptr<LogEvent> CreateReleaseWakelockEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, - uint64_t timestampNs) { - return CreateWakelockStateChangedEvent( - attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs); -} - -std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent( - const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) { - auto event = std::make_unique<LogEvent>( - android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs); - event->write(uid); - event->write("pkg_name"); - event->write("class_name"); - event->write(state); - event->init(); - return event; -} - -std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) { - return CreateActivityForegroundStateChangedEvent( - uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs); -} - -std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) { - return CreateActivityForegroundStateChangedEvent( - uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs); -} - -std::unique_ptr<LogEvent> CreateSyncStateChangedEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& name, - const SyncStateChanged::State state, uint64_t timestampNs) { - auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs); - event->write(attributions); - event->write(name); - event->write(state); - event->init(); - return event; -} - -std::unique_ptr<LogEvent> CreateSyncStartEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& name, - uint64_t timestampNs) { - return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs); -} - -std::unique_ptr<LogEvent> CreateSyncEndEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& name, - uint64_t timestampNs) { - return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs); +std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& jobName) { + return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, + ScheduledJobStateChanged::FINISHED, timestampNs); +} + +std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& name, + const SyncStateChanged::State state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); + AStatsEvent_writeString(statsEvent, name.c_str()); + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& name) { + return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name, + SyncStateChanged::ON); +} + +std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& name) { + return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name, + SyncStateChanged::OFF); } sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config, diff --git a/cmds/statsd/benchmark/metric_util.h b/cmds/statsd/benchmark/metric_util.h index 9b28d60b38c0..6199fa9dc7a1 100644 --- a/cmds/statsd/benchmark/metric_util.h +++ b/cmds/statsd/benchmark/metric_util.h @@ -94,55 +94,31 @@ FieldMatcher CreateAttributionUidDimensions(const int atomId, // Create log event for screen state changed. std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( - const android::view::DisplayStateEnum state, uint64_t timestampNs); - -// Create log event for screen brightness state changed. -std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent( - int level, uint64_t timestampNs); + uint64_t timestampNs, const android::view::DisplayStateEnum state); // Create log event when scheduled job starts. -std::unique_ptr<LogEvent> CreateStartScheduledJobEvent( - const std::vector<AttributionNodeInternal>& attributions, - const string& name, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& jobName); // Create log event when scheduled job finishes. -std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent( - const std::vector<AttributionNodeInternal>& attributions, - const string& name, uint64_t timestampNs); - -// Create log event for app moving to background. -std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs); - -// Create log event for app moving to foreground. -std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& jobName); // Create log event when the app sync starts. -std::unique_ptr<LogEvent> CreateSyncStartEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& name, - uint64_t timestampNs); - -// Create log event when the app sync ends. -std::unique_ptr<LogEvent> CreateSyncEndEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& name, - uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& name); // Create log event when the app sync ends. -std::unique_ptr<LogEvent> CreateAppCrashEvent( - const int uid, uint64_t timestampNs); - -// Create log event for acquiring wakelock. -std::unique_ptr<LogEvent> CreateAcquireWakelockEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, - uint64_t timestampNs); - -// Create log event for releasing wakelock. -std::unique_ptr<LogEvent> CreateReleaseWakelockEvent( - const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName, - uint64_t timestampNs); - -// Create log event for releasing wakelock. -std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent( - int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& name); // Helper function to create an AttributionNodeInternal proto. AttributionNodeInternal CreateAttribution(const int& uid, const string& tag); @@ -158,4 +134,4 @@ int64_t StringToId(const string& str); } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto index 40a24dc52e52..afee79d7506b 100644 --- a/cmds/statsd/src/atom_field_options.proto +++ b/cmds/statsd/src/atom_field_options.proto @@ -117,4 +117,6 @@ extend google.protobuf.FieldOptions { optional bool allow_from_any_uid = 50003 [default = false]; repeated string module = 50004; + + optional bool truncate_timestamp = 50005 [default = false]; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 5f2f546388a0..a14c012b2da3 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -78,7 +78,8 @@ message Atom { // Pushed atoms start at 2. oneof pushed { // For StatsLog reasons, 1 is illegal and will not work. Must start at 2. - BleScanStateChanged ble_scan_state_changed = 2 [(module) = "bluetooth"]; + BleScanStateChanged ble_scan_state_changed = 2 + [(module) = "bluetooth", (module) = "statsdtest"]; ProcessStateChanged process_state_changed = 3 [(module) = "framework"]; BleScanResultReceived ble_scan_result_received = 4 [(module) = "bluetooth"]; SensorStateChanged sensor_state_changed = @@ -93,7 +94,8 @@ message Atom { 10 [(module) = "framework", (module) = "statsdtest"]; LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 11 [(module) = "framework"]; - MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12 [(module) = "framework"]; + MobileRadioPowerStateChanged mobile_radio_power_state_changed = + 12 [(module) = "framework", (truncate_timestamp) = true]; WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13 [(module) = "framework"]; ActivityManagerSleepStateChanged activity_manager_sleep_state_changed = 14 [(module) = "framework"]; @@ -106,7 +108,8 @@ message Atom { 20 [(module) = "framework", (module) = "statsdtest"]; DeviceIdleModeStateChanged device_idle_mode_state_changed = 21 [(module) = "framework"]; DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22 [(module) = "framework"]; - AudioStateChanged audio_state_changed = 23 [(module) = "framework"]; + AudioStateChanged audio_state_changed = + 23 [(module) = "framework", (truncate_timestamp) = true]; MediaCodecStateChanged media_codec_state_changed = 24 [(module) = "framework"]; CameraStateChanged camera_state_changed = 25 [(module) = "framework"]; FlashlightStateChanged flashlight_state_changed = 26 [(module) = "framework"]; @@ -127,7 +130,8 @@ message Atom { WifiLockStateChanged wifi_lock_state_changed = 37 [(module) = "wifi"]; WifiSignalStrengthChanged wifi_signal_strength_changed = 38 [(module) = "wifi"]; WifiScanStateChanged wifi_scan_state_changed = 39 [(module) = "wifi"]; - PhoneSignalStrengthChanged phone_signal_strength_changed = 40 [(module) = "framework"]; + PhoneSignalStrengthChanged phone_signal_strength_changed = + 40 [(module) = "framework", (truncate_timestamp) = true]; SettingChanged setting_changed = 41 [(module) = "framework"]; ActivityForegroundStateChanged activity_foreground_state_changed = 42 [(module) = "framework", (module) = "statsdtest"]; @@ -153,7 +157,8 @@ message Atom { 59 [(module) = "framework", (module) = "statsdtest"]; ForegroundServiceStateChanged foreground_service_state_changed = 60 [(module) = "framework"]; - CallStateChanged call_state_changed = 61 [(module) = "telecom"]; + CallStateChanged call_state_changed = + 61 [(module) = "telecom", (truncate_timestamp) = true]; KeyguardStateChanged keyguard_state_changed = 62 [(module) = "sysui"]; KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63 [(module) = "sysui"]; KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64 [(module) = "sysui"]; @@ -419,8 +424,10 @@ message Atom { oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"]; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"]; - MobileBytesTransfer mobile_bytes_transfer = 10002 [(module) = "framework"]; - MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg = 10003 [(module) = "framework"]; + MobileBytesTransfer mobile_bytes_transfer = + 10002 [(module) = "framework", (truncate_timestamp) = true]; + MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg = + 10003 [(module) = "framework", (truncate_timestamp) = true]; BluetoothBytesTransfer bluetooth_bytes_transfer = 10006 [(module) = "framework"]; KernelWakelock kernel_wakelock = 10004 [(module) = "framework"]; SubsystemSleepState subsystem_sleep_state = 10005 [(module) = "statsdtest"]; @@ -434,7 +441,7 @@ message Atom { ProcessMemoryState process_memory_state = 10013 [(module) = "framework"]; SystemElapsedRealtime system_elapsed_realtime = 10014 [(module) = "framework"]; SystemUptime system_uptime = 10015 [(module) = "framework", (module) = "statsdtest"]; - CpuActiveTime cpu_active_time = 10016 [(module) = "framework"]; + CpuActiveTime cpu_active_time = 10016 [(module) = "framework", (module) = "statsdtest"]; CpuClusterTime cpu_cluster_time = 10017 [(module) = "framework", (module) = "statsdtest"]; DiskSpace disk_space = 10018 [deprecated=true, (module) = "statsdtest"]; RemainingBatteryCapacity remaining_battery_capacity = 10019 [(module) = "framework"]; @@ -503,6 +510,7 @@ message Atom { VoiceCallRatUsage voice_call_rat_usage = 10077 [(module) = "telephony"]; SimSlotState sim_slot_state = 10078 [(module) = "telephony"]; SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"]; + SettingSnapshot setting_snapshot = 10080 [(module) = "framework"]; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -8992,3 +9000,37 @@ message RankingSelected { // Which of the ranked targets got picked, default starting position 0. optional int32 position_picked = 4; } + +/** + * Logs settings provider values. + * + * Use DeviceConfig.getProperties to get a list Setting key, query the data from content provider, + * then write the value to proto. + * + */ +message SettingSnapshot { + + // Setting key + optional string name = 1; + + enum SettingsValueType { + NOTASSIGNED = 0; + ASSIGNED_BOOL_TYPE = 1; + ASSIGNED_INT_TYPE = 2; + ASSIGNED_FLOAT_TYPE = 3; + ASSIGNED_STRING_TYPE = 4; + }; + // Setting value type + optional SettingsValueType type = 2; + + optional bool bool_value = 3; + + optional int32 int_value = 4; + + optional float float_value = 5; + + optional string str_value = 6; + + // Android user index. 0 for primary user, 10, 11 for secondary or profile user + optional int32 user_id = 7; +} diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 6f54ea7d86c2..fca48f96f56d 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -80,7 +80,7 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap, - mMetricIndexesWithActivation, mNoReportMetricIds); + mAlertTrackerMap, mMetricIndexesWithActivation, mNoReportMetricIds); mHashStringsInReport = config.hash_strings_in_metric_report(); mVersionStringsInReport = config.version_strings_in_metric_report(); diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 6d20822fd54c..7500ec91ce30 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -230,6 +230,10 @@ private: // Maps deactivation triggering event to MetricProducers. std::unordered_map<int, std::vector<int>> mDeactivationAtomTrackerToMetricMap; + // Maps AlertIds to the index of the corresponding AnomalyTracker stored in mAllAnomalyTrackers. + // The map is used in LoadMetadata to more efficiently lookup AnomalyTrackers from an AlertId. + std::unordered_map<int64_t, int> mAlertTrackerMap; + std::vector<int> mMetricIndexesWithActivation; void initLogSourceWhiteList(); diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 40a313a14eab..e5fe87acc720 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -830,10 +830,10 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t bool initAlerts(const StatsdConfig& config, const unordered_map<int64_t, int>& metricProducerMap, + unordered_map<int64_t, int>& alertTrackerMap, const sp<AlarmMonitor>& anomalyAlarmMonitor, vector<sp<MetricProducer>>& allMetricProducers, vector<sp<AnomalyTracker>>& allAnomalyTrackers) { - unordered_map<int64_t, int> anomalyTrackerMap; for (int i = 0; i < config.alert_size(); i++) { const Alert& alert = config.alert(i); const auto& itr = metricProducerMap.find(alert.metric_id()); @@ -858,7 +858,7 @@ bool initAlerts(const StatsdConfig& config, // The ALOGW for this invalid alert was already displayed in addAnomalyTracker(). return false; } - anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size())); + alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size())); allAnomalyTrackers.push_back(anomalyTracker); } for (int i = 0; i < config.subscription_size(); ++i) { @@ -872,8 +872,8 @@ bool initAlerts(const StatsdConfig& config, (long long)subscription.id()); return false; } - const auto& itr = anomalyTrackerMap.find(subscription.rule_id()); - if (itr == anomalyTrackerMap.end()) { + const auto& itr = alertTrackerMap.find(subscription.rule_id()); + if (itr == alertTrackerMap.end()) { ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"", (long long)subscription.id(), (long long)subscription.rule_id()); return false; @@ -944,6 +944,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& unordered_map<int, std::vector<int>>& trackerToConditionMap, unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + unordered_map<int64_t, int>& alertTrackerMap, vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds) { unordered_map<int64_t, int> logTrackerMap; @@ -976,8 +977,8 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& ALOGE("initMetricProducers failed"); return false; } - if (!initAlerts(config, metricProducerMap, anomalyAlarmMonitor, allMetricProducers, - allAnomalyTrackers)) { + if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor, + allMetricProducers, allAnomalyTrackers)) { ALOGE("initAlerts failed"); return false; } diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index 5ebb232694a4..a8ccc6289b9a 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -128,6 +128,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& std::unordered_map<int, std::vector<int>>& trackerToConditionMap, unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::unordered_map<int64_t, int>& alertTrackerMap, vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds); diff --git a/cmds/statsd/src/statsd_metadata.proto b/cmds/statsd/src/statsd_metadata.proto new file mode 100644 index 000000000000..e00fe33655ca --- /dev/null +++ b/cmds/statsd/src/statsd_metadata.proto @@ -0,0 +1,63 @@ +/* + * 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. + */ + +syntax = "proto2"; + +package android.os.statsd.metadata; + +message ConfigKey { + optional int64 config_id = 1; + optional int32 uid = 2; +} + +message Field { + optional int32 tag = 1; + optional int32 field = 2; +} + +message FieldValue { + optional Field field = 1; + oneof value { + int32 value_int = 2; + int64 value_long = 3; + float value_float = 4; + double value_double = 5; + string value_str = 6; + bytes value_storage = 7; + } +} + +message MetricDimensionKey { + repeated FieldValue dimension_key_in_what = 1; + repeated FieldValue state_values_key = 2; +} + +message AlertMetadata { + optional int64 alert_id = 1; + // The earliest time the alert can be fired again in wall clock time. + optional int32 last_refractory_ends_sec = 2; + optional MetricDimensionKey dimension_key = 3; +} + +// All metadata for a config in statsd +message StatsMetadata { + optional ConfigKey config_key = 1; + repeated AlertMetadata alert_metadata = 2; +} + +message StatsMetadataList { + repeated StatsMetadata stats_metadata = 1; +}
\ No newline at end of file diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp index a5ff0674f2dd..0bf24f1d3606 100644 --- a/cmds/statsd/tests/FieldValue_test.cpp +++ b/cmds/statsd/tests/FieldValue_test.cpp @@ -14,13 +14,16 @@ * limitations under the License. */ #include <gtest/gtest.h> + #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "matchers/matcher_util.h" #include "src/logd/LogEvent.h" +#include "stats_event.h" #include "stats_log_util.h" #include "stats_util.h" #include "subscriber/SubscriberReporter.h" +#include "tests/statsd_test_util.h" #ifdef __ANDROID__ @@ -30,6 +33,58 @@ namespace android { namespace os { namespace statsd { +namespace { +void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, + const vector<int>& attributionUids, const vector<string>& attributionTags, + const string& name) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestamp); + + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); + AStatsEvent_writeString(statsEvent, name.c_str()); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + + AStatsEvent_release(statsEvent); +} + +void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, + const vector<int>& attributionUids, const vector<string>& attributionTags, + const int32_t value) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestamp); + + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); + AStatsEvent_writeInt32(statsEvent, value); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + + AStatsEvent_release(statsEvent); +} +} // anonymous namespace + TEST(AtomMatcherTest, TestFieldTranslation) { FieldMatcher matcher1; matcher1.set_field(10); @@ -72,66 +127,50 @@ TEST(AtomMatcherTest, TestFieldTranslation_ALL) { EXPECT_EQ((int32_t)0xff7f7f7f, matcher12.mMask); } -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(AtomMatcherTest, TestFilter_ALL) { -// FieldMatcher matcher1; -// matcher1.set_field(10); -// FieldMatcher* child = matcher1.add_child(); -// child->set_field(1); -// child->set_position(Position::ALL); -// -// child->add_child()->set_field(1); -// child->add_child()->set_field(2); -// -// child = matcher1.add_child(); -// child->set_field(2); -// -// vector<Matcher> matchers; -// translateFieldMatcher(matcher1, &matchers); -// -// AttributionNodeInternal attribution_node1; -// attribution_node1.set_uid(1111); -// attribution_node1.set_tag("location1"); -// -// AttributionNodeInternal attribution_node2; -// attribution_node2.set_uid(2222); -// attribution_node2.set_tag("location2"); -// -// AttributionNodeInternal attribution_node3; -// attribution_node3.set_uid(3333); -// attribution_node3.set_tag("location3"); -// std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2, -// attribution_node3}; -// -// // Set up the event -// LogEvent event(10, 12345); -// event.write(attribution_nodes); -// event.write("some value"); -// // Convert to a LogEvent -// event.init(); -// HashableDimensionKey output; -// -// filterValues(matchers, event.getValues(), &output); -// -// EXPECT_EQ((size_t)7, output.getValues().size()); -// EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField()); -// EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value); -// EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField()); -// EXPECT_EQ("location1", output.getValues()[1].mValue.str_value); -// -// EXPECT_EQ((int32_t)0x02010201, output.getValues()[2].mField.getField()); -// EXPECT_EQ((int32_t)2222, output.getValues()[2].mValue.int_value); -// EXPECT_EQ((int32_t)0x02010202, output.getValues()[3].mField.getField()); -// EXPECT_EQ("location2", output.getValues()[3].mValue.str_value); -// -// EXPECT_EQ((int32_t)0x02010301, output.getValues()[4].mField.getField()); -// EXPECT_EQ((int32_t)3333, output.getValues()[4].mValue.int_value); -// EXPECT_EQ((int32_t)0x02010302, output.getValues()[5].mField.getField()); -// EXPECT_EQ("location3", output.getValues()[5].mValue.str_value); -// -// EXPECT_EQ((int32_t)0x00020000, output.getValues()[6].mField.getField()); -// EXPECT_EQ("some value", output.getValues()[6].mValue.str_value); -//} +TEST(AtomMatcherTest, TestFilter_ALL) { + FieldMatcher matcher1; + matcher1.set_field(10); + FieldMatcher* child = matcher1.add_child(); + child->set_field(1); + child->set_position(Position::ALL); + + child->add_child()->set_field(1); + child->add_child()->set_field(2); + + child = matcher1.add_child(); + child->set_field(2); + + vector<Matcher> matchers; + translateFieldMatcher(matcher1, &matchers); + + std::vector<int> attributionUids = {1111, 2222, 3333}; + std::vector<string> attributionTags = {"location1", "location2", "location3"}; + + LogEvent event(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event, 10 /*atomId*/, 1012345, attributionUids, attributionTags, "some value"); + HashableDimensionKey output; + + filterValues(matchers, event.getValues(), &output); + + EXPECT_EQ((size_t)7, output.getValues().size()); + EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField()); + EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value); + EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField()); + EXPECT_EQ("location1", output.getValues()[1].mValue.str_value); + + EXPECT_EQ((int32_t)0x02010201, output.getValues()[2].mField.getField()); + EXPECT_EQ((int32_t)2222, output.getValues()[2].mValue.int_value); + EXPECT_EQ((int32_t)0x02010202, output.getValues()[3].mField.getField()); + EXPECT_EQ("location2", output.getValues()[3].mValue.str_value); + + EXPECT_EQ((int32_t)0x02010301, output.getValues()[4].mField.getField()); + EXPECT_EQ((int32_t)3333, output.getValues()[4].mValue.int_value); + EXPECT_EQ((int32_t)0x02010302, output.getValues()[5].mField.getField()); + EXPECT_EQ("location3", output.getValues()[5].mValue.str_value); + + EXPECT_EQ((int32_t)0x00020000, output.getValues()[6].mField.getField()); + EXPECT_EQ("some value", output.getValues()[6].mValue.str_value); +} TEST(AtomMatcherTest, TestSubDimension) { HashableDimensionKey dim; @@ -174,61 +213,45 @@ TEST(AtomMatcherTest, TestSubDimension) { EXPECT_TRUE(dim.contains(subDim4)); } -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(AtomMatcherTest, TestMetric2ConditionLink) { -// AttributionNodeInternal attribution_node1; -// attribution_node1.set_uid(1111); -// attribution_node1.set_tag("location1"); -// -// AttributionNodeInternal attribution_node2; -// attribution_node2.set_uid(2222); -// attribution_node2.set_tag("location2"); -// -// AttributionNodeInternal attribution_node3; -// attribution_node3.set_uid(3333); -// attribution_node3.set_tag("location3"); -// std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2, -// attribution_node3}; -// -// // Set up the event -// LogEvent event(10, 12345); -// event.write(attribution_nodes); -// event.write("some value"); -// // Convert to a LogEvent -// event.init(); -// -// FieldMatcher whatMatcher; -// whatMatcher.set_field(10); -// FieldMatcher* child11 = whatMatcher.add_child(); -// child11->set_field(1); -// child11->set_position(Position::ANY); -// child11 = child11->add_child(); -// child11->set_field(1); -// -// FieldMatcher conditionMatcher; -// conditionMatcher.set_field(27); -// FieldMatcher* child2 = conditionMatcher.add_child(); -// child2->set_field(2); -// child2->set_position(Position::LAST); -// -// child2 = child2->add_child(); -// child2->set_field(2); -// -// Metric2Condition link; -// -// translateFieldMatcher(whatMatcher, &link.metricFields); -// translateFieldMatcher(conditionMatcher, &link.conditionFields); -// -// EXPECT_EQ((size_t)1, link.metricFields.size()); -// EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField()); -// EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask); -// EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag()); -// -// EXPECT_EQ((size_t)1, link.conditionFields.size()); -// EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField()); -// EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask); -// EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag()); -//} +TEST(AtomMatcherTest, TestMetric2ConditionLink) { + std::vector<int> attributionUids = {1111, 2222, 3333}; + std::vector<string> attributionTags = {"location1", "location2", "location3"}; + + LogEvent event(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event, 10 /*atomId*/, 12345, attributionUids, attributionTags, "some value"); + + FieldMatcher whatMatcher; + whatMatcher.set_field(10); + FieldMatcher* child11 = whatMatcher.add_child(); + child11->set_field(1); + child11->set_position(Position::ANY); + child11 = child11->add_child(); + child11->set_field(1); + + FieldMatcher conditionMatcher; + conditionMatcher.set_field(27); + FieldMatcher* child2 = conditionMatcher.add_child(); + child2->set_field(2); + child2->set_position(Position::LAST); + + child2 = child2->add_child(); + child2->set_field(2); + + Metric2Condition link; + + translateFieldMatcher(whatMatcher, &link.metricFields); + translateFieldMatcher(conditionMatcher, &link.conditionFields); + + EXPECT_EQ((size_t)1, link.metricFields.size()); + EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField()); + EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask); + EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag()); + + EXPECT_EQ((size_t)1, link.conditionFields.size()); + EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField()); + EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask); + EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag()); +} TEST(AtomMatcherTest, TestWriteDimensionPath) { for (auto position : {Position::ANY, Position::ALL, Position::FIRST, Position::LAST}) { @@ -439,50 +462,38 @@ TEST(AtomMatcherTest, TestWriteDimensionLeafNodesToProto) { EXPECT_EQ(99999, dim4.value_long()); } -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(AtomMatcherTest, TestWriteAtomToProto) { -// AttributionNodeInternal attribution_node1; -// attribution_node1.set_uid(1111); -// attribution_node1.set_tag("location1"); -// -// AttributionNodeInternal attribution_node2; -// attribution_node2.set_uid(2222); -// attribution_node2.set_tag("location2"); -// -// std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2}; -// -// // Set up the event -// LogEvent event(4, 12345); -// event.write(attribution_nodes); -// event.write((int32_t)999); -// // Convert to a LogEvent -// event.init(); -// -// android::util::ProtoOutputStream protoOutput; -// writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput); -// -// vector<uint8_t> outData; -// outData.resize(protoOutput.size()); -// size_t pos = 0; -// sp<ProtoReader> reader = protoOutput.data(); -// while (reader->readBuffer() != NULL) { -// size_t toRead = reader->currentToRead(); -// std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); -// pos += toRead; -// reader->move(toRead); -// } -// -// Atom result; -// EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); -// EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case()); -// const auto& atom = result.ble_scan_result_received(); -// EXPECT_EQ(2, atom.attribution_node_size()); -// EXPECT_EQ(1111, atom.attribution_node(0).uid()); -// EXPECT_EQ("location1", atom.attribution_node(0).tag()); -// EXPECT_EQ(2222, atom.attribution_node(1).uid()); -// EXPECT_EQ("location2", atom.attribution_node(1).tag()); -// EXPECT_EQ(999, atom.num_results()); -//} +TEST(AtomMatcherTest, TestWriteAtomToProto) { + std::vector<int> attributionUids = {1111, 2222}; + std::vector<string> attributionTags = {"location1", "location2"}; + + LogEvent event(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event, 4 /*atomId*/, 12345, attributionUids, attributionTags, 999); + + android::util::ProtoOutputStream protoOutput; + writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput); + + vector<uint8_t> outData; + outData.resize(protoOutput.size()); + size_t pos = 0; + sp<ProtoReader> reader = protoOutput.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); + pos += toRead; + reader->move(toRead); + } + + Atom result; + EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size())); + EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case()); + const auto& atom = result.ble_scan_result_received(); + EXPECT_EQ(2, atom.attribution_node_size()); + EXPECT_EQ(1111, atom.attribution_node(0).uid()); + EXPECT_EQ("location1", atom.attribution_node(0).tag()); + EXPECT_EQ(2222, atom.attribution_node(1).uid()); + EXPECT_EQ("location2", atom.attribution_node(1).tag()); + EXPECT_EQ(999, atom.num_results()); +} /* * Test two Matchers is not a subset of one Matcher. diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp index 2de637714947..6f4c8e48eff2 100644 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp @@ -12,18 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "matchers/matcher_util.h" -#include "stats_log_util.h" -#include "stats_util.h" - #include <gtest/gtest.h> #include <log/log_event_list.h> #include <log/log_read.h> #include <log/logprint.h> - #include <stdio.h> +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "matchers/matcher_util.h" +#include "stats_event.h" +#include "stats_log_util.h" +#include "stats_util.h" +#include "statsd_test_util.h" + using namespace android::os::statsd; using std::unordered_map; using std::vector; @@ -39,646 +40,691 @@ const int ATTRIBUTION_TAG_FIELD_ID = 2; #ifdef __ANDROID__ -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(AtomMatcherTest, TestSimpleMatcher) { -// UidMap uidMap; -// -// // Set up the matcher -// AtomMatcher matcher; -// auto simpleMatcher = matcher.mutable_simple_atom_matcher(); -// simpleMatcher->set_atom_id(TAG_ID); -// -// LogEvent event(TAG_ID, 0); -// EXPECT_TRUE(event.write(11)); -// event.init(); -// -// // Test -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// // Wrong tag id. -// simpleMatcher->set_atom_id(TAG_ID + 1); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -//} -// -//TEST(AtomMatcherTest, TestAttributionMatcher) { -// UidMap uidMap; -// AttributionNodeInternal attribution_node1; -// attribution_node1.set_uid(1111); -// attribution_node1.set_tag("location1"); -// -// AttributionNodeInternal attribution_node2; -// attribution_node2.set_uid(2222); -// attribution_node2.set_tag("location2"); -// -// AttributionNodeInternal attribution_node3; -// attribution_node3.set_uid(3333); -// attribution_node3.set_tag("location3"); -// std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2, -// attribution_node3}; -// -// // Set up the event -// LogEvent event(TAG_ID, 0); -// event.write(attribution_nodes); -// event.write("some value"); -// // Convert to a LogEvent -// event.init(); -// -// // Set up the matcher -// AtomMatcher matcher; -// auto simpleMatcher = matcher.mutable_simple_atom_matcher(); -// simpleMatcher->set_atom_id(TAG_ID); -// -// // Match first node. -// auto attributionMatcher = simpleMatcher->add_field_value_matcher(); -// attributionMatcher->set_field(FIELD_ID_1); -// attributionMatcher->set_position(Position::FIRST); -// attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( -// ATTRIBUTION_TAG_FIELD_ID); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("tag"); -// -// auto fieldMatcher = simpleMatcher->add_field_value_matcher(); -// fieldMatcher->set_field(FIELD_ID_2); -// fieldMatcher->set_eq_string("some value"); -// -// // Tag not matched. -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("location3"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("location1"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// // Match last node. -// attributionMatcher->set_position(Position::LAST); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("location3"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// // Match any node. -// attributionMatcher->set_position(Position::ANY); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("location1"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("location2"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("location3"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("location4"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// // Attribution match but primitive field not match. -// attributionMatcher->set_position(Position::ANY); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("location2"); -// fieldMatcher->set_eq_string("wrong value"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// fieldMatcher->set_eq_string("some value"); -// -// // Uid match. -// attributionMatcher->set_position(Position::ANY); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field( -// ATTRIBUTION_UID_FIELD_ID); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("pkg0"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// uidMap.updateMap( -// 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, -// {android::String16("v1"), android::String16("v1"), android::String16("v2"), -// android::String16("v1"), android::String16("v2")}, -// {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), -// android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, -// {android::String16(""), android::String16(""), android::String16(""), -// android::String16(""), android::String16("")}); -// -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg3"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg2"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg1"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg0"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// attributionMatcher->set_position(Position::FIRST); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg0"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg3"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg2"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg1"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// attributionMatcher->set_position(Position::LAST); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg0"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg3"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg2"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg1"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// // Uid + tag. -// attributionMatcher->set_position(Position::ANY); -// attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( -// ATTRIBUTION_TAG_FIELD_ID); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg0"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location1"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg1"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location1"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg1"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location2"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg2"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location3"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg3"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location3"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg3"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location1"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// attributionMatcher->set_position(Position::FIRST); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg0"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location1"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg1"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location1"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg1"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location2"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg2"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location3"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg3"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location3"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg3"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location1"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// attributionMatcher->set_position(Position::LAST); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg0"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location1"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg1"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location1"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg1"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location2"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg2"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location3"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg3"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location3"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) -// ->set_eq_string("pkg3"); -// attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) -// ->set_eq_string("location1"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -//} -// -//TEST(AtomMatcherTest, TestUidFieldMatcher) { -// UidMap uidMap; -// uidMap.updateMap( -// 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, -// {android::String16("v1"), android::String16("v1"), android::String16("v2"), -// android::String16("v1"), android::String16("v2")}, -// {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), -// android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, -// {android::String16(""), android::String16(""), android::String16(""), -// android::String16(""), android::String16("")}); -// -// // Set up matcher -// AtomMatcher matcher; -// auto simpleMatcher = matcher.mutable_simple_atom_matcher(); -// simpleMatcher->set_atom_id(TAG_ID); -// simpleMatcher->add_field_value_matcher()->set_field(1); -// simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0"); -// -// // Set up the event -// LogEvent event(TAG_ID, 0); -// event.write(1111); -// event.init(); -// -// LogEvent event2(TAG_ID_2, 0); -// event2.write(1111); -// event2.write("some value"); -// event2.init(); -// -// // Tag not in kAtomsWithUidField -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// // Tag found in kAtomsWithUidField and has matching uid -// simpleMatcher->set_atom_id(TAG_ID_2); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2)); -// -// // Tag found in kAtomsWithUidField but has non-matching uid -// simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2)); -//} -// -//TEST(AtomMatcherTest, TestNeqAnyStringMatcher) { -// UidMap uidMap; -// uidMap.updateMap( -// 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, -// {android::String16("v1"), android::String16("v1"), android::String16("v2"), -// android::String16("v1"), android::String16("v2")}, -// {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), -// android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, -// {android::String16(""), android::String16(""), android::String16(""), -// android::String16(""), android::String16("")}); -// -// AttributionNodeInternal attribution_node1; -// attribution_node1.set_uid(1111); -// attribution_node1.set_tag("location1"); -// -// AttributionNodeInternal attribution_node2; -// attribution_node2.set_uid(2222); -// attribution_node2.set_tag("location2"); -// -// AttributionNodeInternal attribution_node3; -// attribution_node3.set_uid(3333); -// attribution_node3.set_tag("location3"); -// -// AttributionNodeInternal attribution_node4; -// attribution_node4.set_uid(1066); -// attribution_node4.set_tag("location3"); -// std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2, -// attribution_node3, attribution_node4}; -// -// // Set up the event -// LogEvent event(TAG_ID, 0); -// event.write(attribution_nodes); -// event.write("some value"); -// // Convert to a LogEvent -// event.init(); -// -// // Set up the matcher -// AtomMatcher matcher; -// auto simpleMatcher = matcher.mutable_simple_atom_matcher(); -// simpleMatcher->set_atom_id(TAG_ID); -// -// // Match first node. -// auto attributionMatcher = simpleMatcher->add_field_value_matcher(); -// attributionMatcher->set_field(FIELD_ID_1); -// attributionMatcher->set_position(Position::FIRST); -// attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( -// ATTRIBUTION_UID_FIELD_ID); -// auto neqStringList = attributionMatcher->mutable_matches_tuple() -// ->mutable_field_value_matcher(0) -// ->mutable_neq_any_string(); -// neqStringList->add_str_value("pkg2"); -// neqStringList->add_str_value("pkg3"); -// -// auto fieldMatcher = simpleMatcher->add_field_value_matcher(); -// fieldMatcher->set_field(FIELD_ID_2); -// fieldMatcher->set_eq_string("some value"); -// -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// neqStringList->Clear(); -// neqStringList->add_str_value("pkg1"); -// neqStringList->add_str_value("pkg3"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// attributionMatcher->set_position(Position::ANY); -// neqStringList->Clear(); -// neqStringList->add_str_value("maps.com"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// neqStringList->Clear(); -// neqStringList->add_str_value("PkG3"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// attributionMatcher->set_position(Position::LAST); -// neqStringList->Clear(); -// neqStringList->add_str_value("AID_STATSD"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -//} -// -//TEST(AtomMatcherTest, TestEqAnyStringMatcher) { -// UidMap uidMap; -// uidMap.updateMap( -// 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, -// {android::String16("v1"), android::String16("v1"), android::String16("v2"), -// android::String16("v1"), android::String16("v2")}, -// {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), -// android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, -// {android::String16(""), android::String16(""), android::String16(""), -// android::String16(""), android::String16("")}); -// -// AttributionNodeInternal attribution_node1; -// attribution_node1.set_uid(1067); -// attribution_node1.set_tag("location1"); -// -// AttributionNodeInternal attribution_node2; -// attribution_node2.set_uid(2222); -// attribution_node2.set_tag("location2"); -// -// AttributionNodeInternal attribution_node3; -// attribution_node3.set_uid(3333); -// attribution_node3.set_tag("location3"); -// -// AttributionNodeInternal attribution_node4; -// attribution_node4.set_uid(1066); -// attribution_node4.set_tag("location3"); -// std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2, -// attribution_node3, attribution_node4}; -// -// // Set up the event -// LogEvent event(TAG_ID, 0); -// event.write(attribution_nodes); -// event.write("some value"); -// // Convert to a LogEvent -// event.init(); -// -// // Set up the matcher -// AtomMatcher matcher; -// auto simpleMatcher = matcher.mutable_simple_atom_matcher(); -// simpleMatcher->set_atom_id(TAG_ID); -// -// // Match first node. -// auto attributionMatcher = simpleMatcher->add_field_value_matcher(); -// attributionMatcher->set_field(FIELD_ID_1); -// attributionMatcher->set_position(Position::FIRST); -// attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( -// ATTRIBUTION_UID_FIELD_ID); -// auto eqStringList = attributionMatcher->mutable_matches_tuple() -// ->mutable_field_value_matcher(0) -// ->mutable_eq_any_string(); -// eqStringList->add_str_value("AID_ROOT"); -// eqStringList->add_str_value("AID_INCIDENTD"); -// -// auto fieldMatcher = simpleMatcher->add_field_value_matcher(); -// fieldMatcher->set_field(FIELD_ID_2); -// fieldMatcher->set_eq_string("some value"); -// -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// attributionMatcher->set_position(Position::ANY); -// eqStringList->Clear(); -// eqStringList->add_str_value("AID_STATSD"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// eqStringList->Clear(); -// eqStringList->add_str_value("pkg1"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// auto normalStringField = fieldMatcher->mutable_eq_any_string(); -// normalStringField->add_str_value("some value123"); -// normalStringField->add_str_value("some value"); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// normalStringField->Clear(); -// normalStringField->add_str_value("AID_STATSD"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// eqStringList->Clear(); -// eqStringList->add_str_value("maps.com"); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -//} -// -//TEST(AtomMatcherTest, TestBoolMatcher) { -// UidMap uidMap; -// // Set up the matcher -// AtomMatcher matcher; -// auto simpleMatcher = matcher.mutable_simple_atom_matcher(); -// simpleMatcher->set_atom_id(TAG_ID); -// auto keyValue1 = simpleMatcher->add_field_value_matcher(); -// keyValue1->set_field(FIELD_ID_1); -// auto keyValue2 = simpleMatcher->add_field_value_matcher(); -// keyValue2->set_field(FIELD_ID_2); -// -// // Set up the event -// LogEvent event(TAG_ID, 0); -// EXPECT_TRUE(event.write(true)); -// EXPECT_TRUE(event.write(false)); -// // Convert to a LogEvent -// event.init(); -// -// // Test -// keyValue1->set_eq_bool(true); -// keyValue2->set_eq_bool(false); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// keyValue1->set_eq_bool(false); -// keyValue2->set_eq_bool(false); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// keyValue1->set_eq_bool(false); -// keyValue2->set_eq_bool(true); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// keyValue1->set_eq_bool(true); -// keyValue2->set_eq_bool(true); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -//} -// -//TEST(AtomMatcherTest, TestStringMatcher) { -// UidMap uidMap; -// // Set up the matcher -// AtomMatcher matcher; -// auto simpleMatcher = matcher.mutable_simple_atom_matcher(); -// simpleMatcher->set_atom_id(TAG_ID); -// auto keyValue = simpleMatcher->add_field_value_matcher(); -// keyValue->set_field(FIELD_ID_1); -// keyValue->set_eq_string("some value"); -// -// // Set up the event -// LogEvent event(TAG_ID, 0); -// event.write("some value"); -// // Convert to a LogEvent -// event.init(); -// -// // Test -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -//} -// -//TEST(AtomMatcherTest, TestMultiFieldsMatcher) { -// UidMap uidMap; -// // Set up the matcher -// AtomMatcher matcher; -// auto simpleMatcher = matcher.mutable_simple_atom_matcher(); -// simpleMatcher->set_atom_id(TAG_ID); -// auto keyValue1 = simpleMatcher->add_field_value_matcher(); -// keyValue1->set_field(FIELD_ID_1); -// auto keyValue2 = simpleMatcher->add_field_value_matcher(); -// keyValue2->set_field(FIELD_ID_2); -// -// // Set up the event -// LogEvent event(TAG_ID, 0); -// event.write(2); -// event.write(3); -// -// // Convert to a LogEvent -// event.init(); -// -// // Test -// keyValue1->set_eq_int(2); -// keyValue2->set_eq_int(3); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// keyValue1->set_eq_int(2); -// keyValue2->set_eq_int(4); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// keyValue1->set_eq_int(4); -// keyValue2->set_eq_int(3); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -//} -// -//TEST(AtomMatcherTest, TestIntComparisonMatcher) { -// UidMap uidMap; -// // Set up the matcher -// AtomMatcher matcher; -// auto simpleMatcher = matcher.mutable_simple_atom_matcher(); -// -// simpleMatcher->set_atom_id(TAG_ID); -// auto keyValue = simpleMatcher->add_field_value_matcher(); -// keyValue->set_field(FIELD_ID_1); -// -// // Set up the event -// LogEvent event(TAG_ID, 0); -// event.write(11); -// event.init(); -// -// // Test -// -// // eq_int -// keyValue->set_eq_int(10); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// keyValue->set_eq_int(11); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// keyValue->set_eq_int(12); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// // lt_int -// keyValue->set_lt_int(10); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// keyValue->set_lt_int(11); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// keyValue->set_lt_int(12); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// // lte_int -// keyValue->set_lte_int(10); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// keyValue->set_lte_int(11); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// keyValue->set_lte_int(12); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// // gt_int -// keyValue->set_gt_int(10); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// keyValue->set_gt_int(11); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// keyValue->set_gt_int(12); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -// -// // gte_int -// keyValue->set_gte_int(10); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// keyValue->set_gte_int(11); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); -// keyValue->set_gte_int(12); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); -//} -// -//TEST(AtomMatcherTest, TestFloatComparisonMatcher) { -// UidMap uidMap; -// // Set up the matcher -// AtomMatcher matcher; -// auto simpleMatcher = matcher.mutable_simple_atom_matcher(); -// simpleMatcher->set_atom_id(TAG_ID); -// -// auto keyValue = simpleMatcher->add_field_value_matcher(); -// keyValue->set_field(FIELD_ID_1); -// -// LogEvent event1(TAG_ID, 0); -// keyValue->set_lt_float(10.0); -// event1.write(10.1f); -// event1.init(); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1)); -// -// LogEvent event2(TAG_ID, 0); -// event2.write(9.9f); -// event2.init(); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2)); -// -// LogEvent event3(TAG_ID, 0); -// event3.write(10.1f); -// event3.init(); -// keyValue->set_gt_float(10.0); -// EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3)); -// -// LogEvent event4(TAG_ID, 0); -// event4.write(9.9f); -// event4.init(); -// EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4)); -//} + +namespace { + +void makeIntLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, + const int32_t value) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestamp); + + AStatsEvent_writeInt32(statsEvent, value); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + + AStatsEvent_release(statsEvent); +} + +void makeFloatLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, + const float floatValue) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestamp); + + AStatsEvent_writeFloat(statsEvent, floatValue); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + + AStatsEvent_release(statsEvent); +} + +void makeStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, + const string& name) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestamp); + + AStatsEvent_writeString(statsEvent, name.c_str()); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + + AStatsEvent_release(statsEvent); +} + +void makeIntStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, + const int32_t value, const string& name) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestamp); + + AStatsEvent_writeInt32(statsEvent, value); + AStatsEvent_writeString(statsEvent, name.c_str()); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + + AStatsEvent_release(statsEvent); +} + +void makeAttributionLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, + const vector<int>& attributionUids, + const vector<string>& attributionTags, const string& name) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestamp); + + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); + AStatsEvent_writeString(statsEvent, name.c_str()); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + + AStatsEvent_release(statsEvent); +} + +void makeBoolLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, + const bool bool1, const bool bool2) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestamp); + + AStatsEvent_writeBool(statsEvent, bool1); + AStatsEvent_writeBool(statsEvent, bool2); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + + AStatsEvent_release(statsEvent); +} + +} // anonymous namespace + +TEST(AtomMatcherTest, TestSimpleMatcher) { + UidMap uidMap; + + // Set up the matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + simpleMatcher->set_atom_id(TAG_ID); + + LogEvent event(/*uid=*/0, /*pid=*/0); + makeIntLogEvent(&event, TAG_ID, 0, 11); + + // Test + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + // Wrong tag id. + simpleMatcher->set_atom_id(TAG_ID + 1); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); +} + +TEST(AtomMatcherTest, TestAttributionMatcher) { + UidMap uidMap; + std::vector<int> attributionUids = {1111, 2222, 3333}; + std::vector<string> attributionTags = {"location1", "location2", "location3"}; + + // Set up the log event. + LogEvent event(/*uid=*/0, /*pid=*/0); + makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value"); + + // Set up the matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + simpleMatcher->set_atom_id(TAG_ID); + + // Match first node. + auto attributionMatcher = simpleMatcher->add_field_value_matcher(); + attributionMatcher->set_field(FIELD_ID_1); + attributionMatcher->set_position(Position::FIRST); + attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( + ATTRIBUTION_TAG_FIELD_ID); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "tag"); + + auto fieldMatcher = simpleMatcher->add_field_value_matcher(); + fieldMatcher->set_field(FIELD_ID_2); + fieldMatcher->set_eq_string("some value"); + + // Tag not matched. + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "location3"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "location1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + // Match last node. + attributionMatcher->set_position(Position::LAST); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "location3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + // Match any node. + attributionMatcher->set_position(Position::ANY); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "location1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "location2"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "location3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "location4"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + // Attribution match but primitive field not match. + attributionMatcher->set_position(Position::ANY); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "location2"); + fieldMatcher->set_eq_string("wrong value"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + fieldMatcher->set_eq_string("some value"); + + // Uid match. + attributionMatcher->set_position(Position::ANY); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field( + ATTRIBUTION_UID_FIELD_ID); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg0"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + uidMap.updateMap( + 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, + {android::String16("v1"), android::String16("v1"), android::String16("v2"), + android::String16("v1"), android::String16("v2")}, + {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), + android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, + {android::String16(""), android::String16(""), android::String16(""), + android::String16(""), android::String16("")}); + + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg2"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg0"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::FIRST); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg0"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg3"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg2"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::LAST); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg0"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg2"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg1"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + // Uid + tag. + attributionMatcher->set_position(Position::ANY); + attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( + ATTRIBUTION_TAG_FIELD_ID); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg0"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg1"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg1"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location2"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg2"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg3"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg3"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location1"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::FIRST); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg0"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg1"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg1"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location2"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg2"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location3"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg3"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location3"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg3"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location1"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::LAST); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg0"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location1"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg1"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location1"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg1"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location2"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg2"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg3"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string( + "pkg3"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string( + "location1"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); +} + +TEST(AtomMatcherTest, TestUidFieldMatcher) { + UidMap uidMap; + uidMap.updateMap( + 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, + {android::String16("v1"), android::String16("v1"), android::String16("v2"), + android::String16("v1"), android::String16("v2")}, + {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), + android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, + {android::String16(""), android::String16(""), android::String16(""), + android::String16(""), android::String16("")}); + + // Set up matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + simpleMatcher->set_atom_id(TAG_ID); + simpleMatcher->add_field_value_matcher()->set_field(1); + simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0"); + + // Set up the event + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeIntLogEvent(&event1, TAG_ID, 0, 1111); + + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeIntStringLogEvent(&event2, TAG_ID_2, 0, 1111, "some value"); + + // Tag not in kAtomsWithUidField + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1)); + + // Tag found in kAtomsWithUidField and has matching uid + simpleMatcher->set_atom_id(TAG_ID_2); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2)); + + // Tag found in kAtomsWithUidField but has non-matching uid + simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2)); +} + +TEST(AtomMatcherTest, TestNeqAnyStringMatcher) { + UidMap uidMap; + uidMap.updateMap( + 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, + {android::String16("v1"), android::String16("v1"), android::String16("v2"), + android::String16("v1"), android::String16("v2")}, + {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), + android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, + {android::String16(""), android::String16(""), android::String16(""), + android::String16(""), android::String16("")}); + + std::vector<int> attributionUids = {1111, 2222, 3333, 1066}; + std::vector<string> attributionTags = {"location1", "location2", "location3", "location3"}; + + // Set up the event + LogEvent event(/*uid=*/0, /*pid=*/0); + makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value"); + + // Set up the matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + simpleMatcher->set_atom_id(TAG_ID); + + // Match first node. + auto attributionMatcher = simpleMatcher->add_field_value_matcher(); + attributionMatcher->set_field(FIELD_ID_1); + attributionMatcher->set_position(Position::FIRST); + attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( + ATTRIBUTION_UID_FIELD_ID); + auto neqStringList = attributionMatcher->mutable_matches_tuple() + ->mutable_field_value_matcher(0) + ->mutable_neq_any_string(); + neqStringList->add_str_value("pkg2"); + neqStringList->add_str_value("pkg3"); + + auto fieldMatcher = simpleMatcher->add_field_value_matcher(); + fieldMatcher->set_field(FIELD_ID_2); + fieldMatcher->set_eq_string("some value"); + + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + neqStringList->Clear(); + neqStringList->add_str_value("pkg1"); + neqStringList->add_str_value("pkg3"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::ANY); + neqStringList->Clear(); + neqStringList->add_str_value("maps.com"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + neqStringList->Clear(); + neqStringList->add_str_value("PkG3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::LAST); + neqStringList->Clear(); + neqStringList->add_str_value("AID_STATSD"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); +} + +TEST(AtomMatcherTest, TestEqAnyStringMatcher) { + UidMap uidMap; + uidMap.updateMap( + 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */, + {android::String16("v1"), android::String16("v1"), android::String16("v2"), + android::String16("v1"), android::String16("v2")}, + {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"), + android::String16("Pkg2"), android::String16("PkG3")} /* package name list */, + {android::String16(""), android::String16(""), android::String16(""), + android::String16(""), android::String16("")}); + + std::vector<int> attributionUids = {1067, 2222, 3333, 1066}; + std::vector<string> attributionTags = {"location1", "location2", "location3", "location3"}; + + // Set up the event + LogEvent event(/*uid=*/0, /*pid=*/0); + makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value"); + + // Set up the matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + simpleMatcher->set_atom_id(TAG_ID); + + // Match first node. + auto attributionMatcher = simpleMatcher->add_field_value_matcher(); + attributionMatcher->set_field(FIELD_ID_1); + attributionMatcher->set_position(Position::FIRST); + attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( + ATTRIBUTION_UID_FIELD_ID); + auto eqStringList = attributionMatcher->mutable_matches_tuple() + ->mutable_field_value_matcher(0) + ->mutable_eq_any_string(); + eqStringList->add_str_value("AID_ROOT"); + eqStringList->add_str_value("AID_INCIDENTD"); + + auto fieldMatcher = simpleMatcher->add_field_value_matcher(); + fieldMatcher->set_field(FIELD_ID_2); + fieldMatcher->set_eq_string("some value"); + + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::ANY); + eqStringList->Clear(); + eqStringList->add_str_value("AID_STATSD"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + eqStringList->Clear(); + eqStringList->add_str_value("pkg1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + auto normalStringField = fieldMatcher->mutable_eq_any_string(); + normalStringField->add_str_value("some value123"); + normalStringField->add_str_value("some value"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + normalStringField->Clear(); + normalStringField->add_str_value("AID_STATSD"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + eqStringList->Clear(); + eqStringList->add_str_value("maps.com"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); +} + +TEST(AtomMatcherTest, TestBoolMatcher) { + UidMap uidMap; + // Set up the matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + simpleMatcher->set_atom_id(TAG_ID); + auto keyValue1 = simpleMatcher->add_field_value_matcher(); + keyValue1->set_field(FIELD_ID_1); + auto keyValue2 = simpleMatcher->add_field_value_matcher(); + keyValue2->set_field(FIELD_ID_2); + + // Set up the event + LogEvent event(/*uid=*/0, /*pid=*/0); + makeBoolLogEvent(&event, TAG_ID, 0, true, false); + + // Test + keyValue1->set_eq_bool(true); + keyValue2->set_eq_bool(false); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + keyValue1->set_eq_bool(false); + keyValue2->set_eq_bool(false); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + keyValue1->set_eq_bool(false); + keyValue2->set_eq_bool(true); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + keyValue1->set_eq_bool(true); + keyValue2->set_eq_bool(true); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); +} + +TEST(AtomMatcherTest, TestStringMatcher) { + UidMap uidMap; + // Set up the matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + simpleMatcher->set_atom_id(TAG_ID); + auto keyValue = simpleMatcher->add_field_value_matcher(); + keyValue->set_field(FIELD_ID_1); + keyValue->set_eq_string("some value"); + + // Set up the event + LogEvent event(/*uid=*/0, /*pid=*/0); + makeStringLogEvent(&event, TAG_ID, 0, "some value"); + + // Test + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); +} + +TEST(AtomMatcherTest, TestMultiFieldsMatcher) { + UidMap uidMap; + // Set up the matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + simpleMatcher->set_atom_id(TAG_ID); + auto keyValue1 = simpleMatcher->add_field_value_matcher(); + keyValue1->set_field(FIELD_ID_1); + auto keyValue2 = simpleMatcher->add_field_value_matcher(); + keyValue2->set_field(FIELD_ID_2); + + // Set up the event + LogEvent event(/*uid=*/0, /*pid=*/0); + CreateTwoValueLogEvent(&event, TAG_ID, 0, 2, 3); + + // Test + keyValue1->set_eq_int(2); + keyValue2->set_eq_int(3); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + keyValue1->set_eq_int(2); + keyValue2->set_eq_int(4); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + keyValue1->set_eq_int(4); + keyValue2->set_eq_int(3); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); +} + +TEST(AtomMatcherTest, TestIntComparisonMatcher) { + UidMap uidMap; + // Set up the matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + + simpleMatcher->set_atom_id(TAG_ID); + auto keyValue = simpleMatcher->add_field_value_matcher(); + keyValue->set_field(FIELD_ID_1); + + // Set up the event + LogEvent event(/*uid=*/0, /*pid=*/0); + makeIntLogEvent(&event, TAG_ID, 0, 11); + + // Test + + // eq_int + keyValue->set_eq_int(10); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + keyValue->set_eq_int(11); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + keyValue->set_eq_int(12); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + // lt_int + keyValue->set_lt_int(10); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + keyValue->set_lt_int(11); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + keyValue->set_lt_int(12); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + // lte_int + keyValue->set_lte_int(10); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + keyValue->set_lte_int(11); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + keyValue->set_lte_int(12); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + // gt_int + keyValue->set_gt_int(10); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + keyValue->set_gt_int(11); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + keyValue->set_gt_int(12); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + // gte_int + keyValue->set_gte_int(10); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + keyValue->set_gte_int(11); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + keyValue->set_gte_int(12); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); +} + +TEST(AtomMatcherTest, TestFloatComparisonMatcher) { + UidMap uidMap; + // Set up the matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + simpleMatcher->set_atom_id(TAG_ID); + + auto keyValue = simpleMatcher->add_field_value_matcher(); + keyValue->set_field(FIELD_ID_1); + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeFloatLogEvent(&event1, TAG_ID, 0, 10.1f); + keyValue->set_lt_float(10.0); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1)); + + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeFloatLogEvent(&event2, TAG_ID, 0, 9.9f); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2)); + + LogEvent event3(/*uid=*/0, /*pid=*/0); + makeFloatLogEvent(&event3, TAG_ID, 0, 10.1f); + keyValue->set_gt_float(10.0); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3)); + + LogEvent event4(/*uid=*/0, /*pid=*/0); + makeFloatLogEvent(&event4, TAG_ID, 0, 9.9f); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4)); +} // Helper for the composite matchers. void addSimpleMatcher(SimpleAtomMatcher* simpleMatcher, int tag, int key, int val) { diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index 71adc5789d92..356e40b9b99d 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -40,6 +40,7 @@ using android::os::statsd::Predicate; #ifdef __ANDROID__ const ConfigKey kConfigKey(0, 12345); +const long kAlertId = 3; const long timeBaseSec = 1000; @@ -85,7 +86,7 @@ StatsdConfig buildGoodConfig() { config.add_no_report_metric(3); auto alert = config.add_alert(); - alert->set_id(3); + alert->set_id(kAlertId); alert->set_metric_id(3); alert->set_num_buckets(10); alert->set_refractory_period_secs(100); @@ -218,7 +219,7 @@ StatsdConfig buildDimensionMetricsWithMultiTags() { metric->mutable_dimensions_in_what()->add_child()->set_field(1); auto alert = config.add_alert(); - alert->set_id(103); + alert->set_id(kAlertId); alert->set_metric_id(3); alert->set_num_buckets(10); alert->set_refractory_period_secs(100); @@ -284,6 +285,7 @@ TEST(MetricsManagerTest, TestGoodConfig) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; @@ -293,10 +295,14 @@ TEST(MetricsManagerTest, TestGoodConfig) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, + noReportMetricIds)); EXPECT_EQ(1u, allMetricProducers.size()); EXPECT_EQ(1u, allAnomalyTrackers.size()); EXPECT_EQ(1u, noReportMetricIds.size()); + EXPECT_EQ(1u, alertTrackerMap.size()); + EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end()); + EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0); } TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { @@ -316,6 +322,7 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; @@ -325,7 +332,8 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, + noReportMetricIds)); } TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { @@ -345,6 +353,7 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; @@ -354,7 +363,8 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, + noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingMatchers) { @@ -374,6 +384,7 @@ TEST(MetricsManagerTest, TestMissingMatchers) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, @@ -382,7 +393,8 @@ TEST(MetricsManagerTest, TestMissingMatchers) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, + noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingPredicate) { @@ -402,6 +414,7 @@ TEST(MetricsManagerTest, TestMissingPredicate) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, @@ -410,7 +423,7 @@ TEST(MetricsManagerTest, TestMissingPredicate) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, noReportMetricIds)); } TEST(MetricsManagerTest, TestCirclePredicateDependency) { @@ -430,6 +443,7 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; @@ -439,7 +453,8 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, + noReportMetricIds)); } TEST(MetricsManagerTest, testAlertWithUnknownMetric) { @@ -459,6 +474,7 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; @@ -468,7 +484,8 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, + noReportMetricIds)); } #else diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index d6498f4f1554..9e7b7c820e0d 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -253,1416 +253,1420 @@ TEST(StatsLogProcessorTest, TestReportIncludesSubConfig) { EXPECT_EQ(2, report.annotation(0).field_int32()); } -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) { -// // Setup a simple config. -// StatsdConfig config; -// config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); -// *config.add_atom_matcher() = wakelockAcquireMatcher; -// -// auto countMetric = config.add_count_metric(); -// countMetric->set_id(123456); -// countMetric->set_what(wakelockAcquireMatcher.id()); -// countMetric->set_bucket(FIVE_MINUTES); -// -// ConfigKey cfgKey; -// sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey); -// -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; -// auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 2); -// processor->OnLogEvent(event.get()); -// -// vector<uint8_t> bytes; -// ConfigMetricsReportList output; -// -// // Dump report WITHOUT erasing data. -// processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, FAST, &bytes); -// output.ParseFromArray(bytes.data(), bytes.size()); -// EXPECT_EQ(output.reports_size(), 1); -// EXPECT_EQ(output.reports(0).metrics_size(), 1); -// EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1); -// -// // Dump report WITH erasing data. There should be data since we didn't previously erase it. -// processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes); -// output.ParseFromArray(bytes.data(), bytes.size()); -// EXPECT_EQ(output.reports_size(), 1); -// EXPECT_EQ(output.reports(0).metrics_size(), 1); -// EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1); -// -// // Dump report again. There should be no data since we erased it. -// processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes); -// output.ParseFromArray(bytes.data(), bytes.size()); -// // We don't care whether statsd has a report, as long as it has no count metrics in it. -// bool noData = output.reports_size() == 0 -// || output.reports(0).metrics_size() == 0 -// || output.reports(0).metrics(0).count_metrics().data_size() == 0; -// EXPECT_TRUE(noData); -//} -// -//TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { -// int uid = 1111; -// -// // Setup a simple config, no activation -// StatsdConfig config1; -// int64_t cfgId1 = 12341; -// config1.set_id(cfgId1); -// config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); -// *config1.add_atom_matcher() = wakelockAcquireMatcher; -// -// long metricId1 = 1234561; -// long metricId2 = 1234562; -// auto countMetric1 = config1.add_count_metric(); -// countMetric1->set_id(metricId1); -// countMetric1->set_what(wakelockAcquireMatcher.id()); -// countMetric1->set_bucket(FIVE_MINUTES); -// -// auto countMetric2 = config1.add_count_metric(); -// countMetric2->set_id(metricId2); -// countMetric2->set_what(wakelockAcquireMatcher.id()); -// countMetric2->set_bucket(FIVE_MINUTES); -// -// ConfigKey cfgKey1(uid, cfgId1); -// -// // Add another config, with two metrics, one with activation -// StatsdConfig config2; -// int64_t cfgId2 = 12342; -// config2.set_id(cfgId2); -// config2.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// *config2.add_atom_matcher() = wakelockAcquireMatcher; -// -// long metricId3 = 1234561; -// long metricId4 = 1234562; -// -// auto countMetric3 = config2.add_count_metric(); -// countMetric3->set_id(metricId3); -// countMetric3->set_what(wakelockAcquireMatcher.id()); -// countMetric3->set_bucket(FIVE_MINUTES); -// -// auto countMetric4 = config2.add_count_metric(); -// countMetric4->set_id(metricId4); -// countMetric4->set_what(wakelockAcquireMatcher.id()); -// countMetric4->set_bucket(FIVE_MINUTES); -// -// auto metric3Activation = config2.add_metric_activation(); -// metric3Activation->set_metric_id(metricId3); -// metric3Activation->set_activation_type(ACTIVATE_IMMEDIATELY); -// auto metric3ActivationTrigger = metric3Activation->add_event_activation(); -// metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); -// metric3ActivationTrigger->set_ttl_seconds(100); -// -// ConfigKey cfgKey2(uid, cfgId2); -// -// // Add another config, with two metrics, both with activations -// StatsdConfig config3; -// int64_t cfgId3 = 12343; -// config3.set_id(cfgId3); -// config3.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// *config3.add_atom_matcher() = wakelockAcquireMatcher; -// -// long metricId5 = 1234565; -// long metricId6 = 1234566; -// auto countMetric5 = config3.add_count_metric(); -// countMetric5->set_id(metricId5); -// countMetric5->set_what(wakelockAcquireMatcher.id()); -// countMetric5->set_bucket(FIVE_MINUTES); -// -// auto countMetric6 = config3.add_count_metric(); -// countMetric6->set_id(metricId6); -// countMetric6->set_what(wakelockAcquireMatcher.id()); -// countMetric6->set_bucket(FIVE_MINUTES); -// -// auto metric5Activation = config3.add_metric_activation(); -// metric5Activation->set_metric_id(metricId5); -// metric5Activation->set_activation_type(ACTIVATE_IMMEDIATELY); -// auto metric5ActivationTrigger = metric5Activation->add_event_activation(); -// metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); -// metric5ActivationTrigger->set_ttl_seconds(100); -// -// auto metric6Activation = config3.add_metric_activation(); -// metric6Activation->set_metric_id(metricId6); -// metric6Activation->set_activation_type(ACTIVATE_IMMEDIATELY); -// auto metric6ActivationTrigger = metric6Activation->add_event_activation(); -// metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); -// metric6ActivationTrigger->set_ttl_seconds(200); -// -// ConfigKey cfgKey3(uid, cfgId3); -// -// sp<UidMap> m = new UidMap(); -// sp<StatsPullerManager> pullerManager = new StatsPullerManager(); -// sp<AlarmMonitor> anomalyAlarmMonitor; -// sp<AlarmMonitor> subscriberAlarmMonitor; -// vector<int64_t> activeConfigsBroadcast; -// -// long timeBase1 = 1; -// int broadcastCount = 0; -// StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, -// timeBase1, [](const ConfigKey& key) { return true; }, -// [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, -// const vector<int64_t>& activeConfigs) { -// broadcastCount++; -// EXPECT_EQ(broadcastUid, uid); -// activeConfigsBroadcast.clear(); -// activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), -// activeConfigs.begin(), activeConfigs.end()); -// return true; -// }); -// -// processor.OnConfigUpdated(1, cfgKey1, config1); -// processor.OnConfigUpdated(2, cfgKey2, config2); -// processor.OnConfigUpdated(3, cfgKey3, config3); -// -// EXPECT_EQ(3, processor.mMetricsManagers.size()); -// -// // Expect the first config and both metrics in it to be active. -// auto it = processor.mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor.mMetricsManagers.end()); -// auto& metricsManager1 = it->second; -// EXPECT_TRUE(metricsManager1->isActive()); -// -// auto metricIt = metricsManager1->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId1) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); -// auto& metricProducer1 = *metricIt; -// EXPECT_TRUE(metricProducer1->isActive()); -// -// metricIt = metricsManager1->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId2) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); -// auto& metricProducer2 = *metricIt; -// EXPECT_TRUE(metricProducer2->isActive()); -// -// // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active. -// it = processor.mMetricsManagers.find(cfgKey2); -// EXPECT_TRUE(it != processor.mMetricsManagers.end()); -// auto& metricsManager2 = it->second; -// EXPECT_TRUE(metricsManager2->isActive()); -// -// metricIt = metricsManager2->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId3) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end()); -// auto& metricProducer3 = *metricIt; -// EXPECT_FALSE(metricProducer3->isActive()); -// -// metricIt = metricsManager2->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId4) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end()); -// auto& metricProducer4 = *metricIt; -// EXPECT_TRUE(metricProducer4->isActive()); -// -// // Expect the third config and both metrics in it to be inactive. -// it = processor.mMetricsManagers.find(cfgKey3); -// EXPECT_TRUE(it != processor.mMetricsManagers.end()); -// auto& metricsManager3 = it->second; -// EXPECT_FALSE(metricsManager3->isActive()); -// -// metricIt = metricsManager3->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId5) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end()); -// auto& metricProducer5 = *metricIt; -// EXPECT_FALSE(metricProducer5->isActive()); -// -// metricIt = metricsManager3->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager3->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId6) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end()); -// auto& metricProducer6 = *metricIt; -// EXPECT_FALSE(metricProducer6->isActive()); -// -// // No broadcast for active configs should have happened yet. -// EXPECT_EQ(broadcastCount, 0); -// -// // Activate all 3 metrics that were not active. -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; -// auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1); -// processor.OnLogEvent(event.get()); -// -// // Assert that all 3 configs are active. -// EXPECT_TRUE(metricsManager1->isActive()); -// EXPECT_TRUE(metricsManager2->isActive()); -// EXPECT_TRUE(metricsManager3->isActive()); -// -// // A broadcast should have happened, and all 3 configs should be active in the broadcast. -// EXPECT_EQ(broadcastCount, 1); -// EXPECT_EQ(activeConfigsBroadcast.size(), 3); -// EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1) -// != activeConfigsBroadcast.end()); -// EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2) -// != activeConfigsBroadcast.end()); -// EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3) -// != activeConfigsBroadcast.end()); -// -// // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns. -// int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; -// processor.SaveActiveConfigsToDisk(shutDownTime); -// const int64_t ttl3 = event->GetElapsedTimestampNs() + -// metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; -// const int64_t ttl5 = event->GetElapsedTimestampNs() + -// metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; -// const int64_t ttl6 = event->GetElapsedTimestampNs() + -// metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; -// -// // Create a second StatsLogProcessor and push the same 3 configs. -// long timeBase2 = 1000; -// sp<StatsLogProcessor> processor2 = -// CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); -// processor2->OnConfigUpdated(timeBase2, cfgKey2, config2); -// processor2->OnConfigUpdated(timeBase2, cfgKey3, config3); -// -// EXPECT_EQ(3, processor2->mMetricsManagers.size()); -// -// // First config and both metrics are active. -// it = processor2->mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor2->mMetricsManagers.end()); -// auto& metricsManager1001 = it->second; -// EXPECT_TRUE(metricsManager1001->isActive()); -// -// metricIt = metricsManager1001->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId1) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); -// auto& metricProducer1001 = *metricIt; -// EXPECT_TRUE(metricProducer1001->isActive()); -// -// metricIt = metricsManager1001->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId2) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); -// auto& metricProducer1002 = *metricIt; -// EXPECT_TRUE(metricProducer1002->isActive()); -// -// // Second config is active. Metric 3 is inactive, metric 4 is active. -// it = processor2->mMetricsManagers.find(cfgKey2); -// EXPECT_TRUE(it != processor2->mMetricsManagers.end()); -// auto& metricsManager1002 = it->second; -// EXPECT_TRUE(metricsManager1002->isActive()); -// -// metricIt = metricsManager1002->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId3) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end()); -// auto& metricProducer1003 = *metricIt; -// EXPECT_FALSE(metricProducer1003->isActive()); -// -// metricIt = metricsManager1002->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId4) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end()); -// auto& metricProducer1004 = *metricIt; -// EXPECT_TRUE(metricProducer1004->isActive()); -// -// // Config 3 is inactive. both metrics are inactive. -// it = processor2->mMetricsManagers.find(cfgKey3); -// EXPECT_TRUE(it != processor2->mMetricsManagers.end()); -// auto& metricsManager1003 = it->second; -// EXPECT_FALSE(metricsManager1003->isActive()); -// EXPECT_EQ(2, metricsManager1003->mAllMetricProducers.size()); -// -// metricIt = metricsManager1003->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId5) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end()); -// auto& metricProducer1005 = *metricIt; -// EXPECT_FALSE(metricProducer1005->isActive()); -// -// metricIt = metricsManager1003->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1003->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId6) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end()); -// auto& metricProducer1006 = *metricIt; -// EXPECT_FALSE(metricProducer1006->isActive()); -// -// // Assert that all 3 metrics with activation are inactive and that the ttls were properly set. -// EXPECT_FALSE(metricProducer1003->isActive()); -// const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second; -// EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns); -// EXPECT_EQ(0, activation1003->start_ns); -// EXPECT_FALSE(metricProducer1005->isActive()); -// const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second; -// EXPECT_EQ(100 * NS_PER_SEC, activation1005->ttl_ns); -// EXPECT_EQ(0, activation1005->start_ns); -// EXPECT_FALSE(metricProducer1006->isActive()); -// const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second; -// EXPECT_EQ(200 * NS_PER_SEC, activation1006->ttl_ns); -// EXPECT_EQ(0, activation1006->start_ns); -// -// processor2->LoadActiveConfigsFromDisk(); -// -// // After loading activations from disk, assert that all 3 metrics are active. -// EXPECT_TRUE(metricProducer1003->isActive()); -// EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->start_ns); -// EXPECT_TRUE(metricProducer1005->isActive()); -// EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->start_ns); -// EXPECT_TRUE(metricProducer1006->isActive()); -// EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->start_ns); -// -// // Make sure no more broadcasts have happened. -// EXPECT_EQ(broadcastCount, 1); -//} -// -//TEST(StatsLogProcessorTest, TestActivationOnBoot) { -// int uid = 1111; -// -// StatsdConfig config1; -// config1.set_id(12341); -// config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); -// *config1.add_atom_matcher() = wakelockAcquireMatcher; -// -// long metricId1 = 1234561; -// long metricId2 = 1234562; -// auto countMetric1 = config1.add_count_metric(); -// countMetric1->set_id(metricId1); -// countMetric1->set_what(wakelockAcquireMatcher.id()); -// countMetric1->set_bucket(FIVE_MINUTES); -// -// auto countMetric2 = config1.add_count_metric(); -// countMetric2->set_id(metricId2); -// countMetric2->set_what(wakelockAcquireMatcher.id()); -// countMetric2->set_bucket(FIVE_MINUTES); -// -// auto metric1Activation = config1.add_metric_activation(); -// metric1Activation->set_metric_id(metricId1); -// metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); -// auto metric1ActivationTrigger = metric1Activation->add_event_activation(); -// metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); -// metric1ActivationTrigger->set_ttl_seconds(100); -// -// ConfigKey cfgKey1(uid, 12341); -// long timeBase1 = 1; -// sp<StatsLogProcessor> processor = -// CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); -// -// EXPECT_EQ(1, processor->mMetricsManagers.size()); -// auto it = processor->mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor->mMetricsManagers.end()); -// auto& metricsManager1 = it->second; -// EXPECT_TRUE(metricsManager1->isActive()); -// -// auto metricIt = metricsManager1->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId1) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); -// auto& metricProducer1 = *metricIt; -// EXPECT_FALSE(metricProducer1->isActive()); -// -// metricIt = metricsManager1->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId2) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); -// auto& metricProducer2 = *metricIt; -// EXPECT_TRUE(metricProducer2->isActive()); -// -// const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second; -// EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); -// EXPECT_EQ(0, activation1->start_ns); -// EXPECT_EQ(kNotActive, activation1->state); -// -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; -// auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1); -// processor->OnLogEvent(event.get()); -// -// EXPECT_FALSE(metricProducer1->isActive()); -// EXPECT_EQ(0, activation1->start_ns); -// EXPECT_EQ(kActiveOnBoot, activation1->state); -// -// int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; -// processor->SaveActiveConfigsToDisk(shutDownTime); -// EXPECT_FALSE(metricProducer1->isActive()); -// const int64_t ttl1 = metric1ActivationTrigger->ttl_seconds() * NS_PER_SEC; -// -// long timeBase2 = 1000; -// sp<StatsLogProcessor> processor2 = -// CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); -// -// EXPECT_EQ(1, processor2->mMetricsManagers.size()); -// it = processor2->mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor2->mMetricsManagers.end()); -// auto& metricsManager1001 = it->second; -// EXPECT_TRUE(metricsManager1001->isActive()); -// -// metricIt = metricsManager1001->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId1) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); -// auto& metricProducer1001 = *metricIt; -// EXPECT_FALSE(metricProducer1001->isActive()); -// -// metricIt = metricsManager1001->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId2) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); -// auto& metricProducer1002 = *metricIt; -// EXPECT_TRUE(metricProducer1002->isActive()); -// -// const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second; -// EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns); -// EXPECT_EQ(0, activation1001->start_ns); -// EXPECT_EQ(kNotActive, activation1001->state); -// -// processor2->LoadActiveConfigsFromDisk(); -// -// EXPECT_TRUE(metricProducer1001->isActive()); -// EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->start_ns); -// EXPECT_EQ(kActive, activation1001->state); -//} -// -//TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { -// int uid = 1111; -// -// // Create config with 2 metrics: -// // Metric 1: Activate on boot with 2 activations -// // Metric 2: Always active -// StatsdConfig config1; -// config1.set_id(12341); -// config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// *config1.add_atom_matcher() = wakelockAcquireMatcher; -// *config1.add_atom_matcher() = screenOnMatcher; -// -// long metricId1 = 1234561; -// long metricId2 = 1234562; -// -// auto countMetric1 = config1.add_count_metric(); -// countMetric1->set_id(metricId1); -// countMetric1->set_what(wakelockAcquireMatcher.id()); -// countMetric1->set_bucket(FIVE_MINUTES); -// -// auto countMetric2 = config1.add_count_metric(); -// countMetric2->set_id(metricId2); -// countMetric2->set_what(wakelockAcquireMatcher.id()); -// countMetric2->set_bucket(FIVE_MINUTES); -// -// auto metric1Activation = config1.add_metric_activation(); -// metric1Activation->set_metric_id(metricId1); -// metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); -// auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); -// metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); -// metric1ActivationTrigger1->set_ttl_seconds(100); -// auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); -// metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); -// metric1ActivationTrigger2->set_ttl_seconds(200); -// -// ConfigKey cfgKey1(uid, 12341); -// long timeBase1 = 1; -// sp<StatsLogProcessor> processor = -// CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); -// -// // Metric 1 is not active. -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_EQ(1, processor->mMetricsManagers.size()); -// auto it = processor->mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor->mMetricsManagers.end()); -// auto& metricsManager1 = it->second; -// EXPECT_TRUE(metricsManager1->isActive()); -// -// auto metricIt = metricsManager1->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId1) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); -// auto& metricProducer1 = *metricIt; -// EXPECT_FALSE(metricProducer1->isActive()); -// -// metricIt = metricsManager1->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId2) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); -// auto& metricProducer2 = *metricIt; -// EXPECT_TRUE(metricProducer2->isActive()); -// -// int i = 0; -// for (; i < metricsManager1->mAllAtomMatchers.size(); i++) { -// if (metricsManager1->mAllAtomMatchers[i]->getId() == -// metric1ActivationTrigger1->atom_matcher_id()) { -// break; -// } -// } -// const auto& activation1 = metricProducer1->mEventActivationMap.at(i); -// EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); -// EXPECT_EQ(0, activation1->start_ns); -// EXPECT_EQ(kNotActive, activation1->state); -// -// i = 0; -// for (; i < metricsManager1->mAllAtomMatchers.size(); i++) { -// if (metricsManager1->mAllAtomMatchers[i]->getId() == -// metric1ActivationTrigger2->atom_matcher_id()) { -// break; -// } -// } -// const auto& activation2 = metricProducer1->mEventActivationMap.at(i); -// EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); -// EXPECT_EQ(0, activation2->start_ns); -// EXPECT_EQ(kNotActive, activation2->state); -// // }}}------------------------------------------------------------------------------ -// -// // Trigger Activation 1 for Metric 1 -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; -// auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1); -// processor->OnLogEvent(event.get()); -// -// // Metric 1 is not active; Activation 1 set to kActiveOnBoot -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_FALSE(metricProducer1->isActive()); -// EXPECT_EQ(0, activation1->start_ns); -// EXPECT_EQ(kActiveOnBoot, activation1->state); -// EXPECT_EQ(0, activation2->start_ns); -// EXPECT_EQ(kNotActive, activation2->state); -// -// EXPECT_TRUE(metricProducer2->isActive()); -// // }}}----------------------------------------------------------------------------- -// -// // Simulate shutdown by saving state to disk -// int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; -// processor->SaveActiveConfigsToDisk(shutDownTime); -// EXPECT_FALSE(metricProducer1->isActive()); -// int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC; -// -// // Simulate device restarted state by creating new instance of StatsLogProcessor with the -// // same config. -// long timeBase2 = 1000; -// sp<StatsLogProcessor> processor2 = -// CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); -// -// // Metric 1 is not active. -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_EQ(1, processor2->mMetricsManagers.size()); -// it = processor2->mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor2->mMetricsManagers.end()); -// auto& metricsManager1001 = it->second; -// EXPECT_TRUE(metricsManager1001->isActive()); -// -// metricIt = metricsManager1001->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId1) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); -// auto& metricProducer1001 = *metricIt; -// EXPECT_FALSE(metricProducer1001->isActive()); -// -// metricIt = metricsManager1001->mAllMetricProducers.begin(); -// for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId2) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); -// auto& metricProducer1002 = *metricIt; -// EXPECT_TRUE(metricProducer1002->isActive()); -// -// i = 0; -// for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { -// if (metricsManager1001->mAllAtomMatchers[i]->getId() == -// metric1ActivationTrigger1->atom_matcher_id()) { -// break; -// } -// } -// const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i); -// EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns); -// EXPECT_EQ(0, activation1001_1->start_ns); -// EXPECT_EQ(kNotActive, activation1001_1->state); -// -// i = 0; -// for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { -// if (metricsManager1001->mAllAtomMatchers[i]->getId() == -// metric1ActivationTrigger2->atom_matcher_id()) { -// break; -// } -// } -// -// const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i); -// EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns); -// EXPECT_EQ(0, activation1001_2->start_ns); -// EXPECT_EQ(kNotActive, activation1001_2->state); -// // }}}----------------------------------------------------------------------------------- -// -// // Load saved state from disk. -// processor2->LoadActiveConfigsFromDisk(); -// -// // Metric 1 active; Activation 1 is active, Activation 2 is not active -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_TRUE(metricProducer1001->isActive()); -// EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns); -// EXPECT_EQ(kActive, activation1001_1->state); -// EXPECT_EQ(0, activation1001_2->start_ns); -// EXPECT_EQ(kNotActive, activation1001_2->state); -// -// EXPECT_TRUE(metricProducer1002->isActive()); -// // }}}-------------------------------------------------------------------------------- -// -// // Trigger Activation 2 for Metric 1. -// auto screenOnEvent = CreateScreenStateChangedEvent( -// android::view::DISPLAY_STATE_ON, -// timeBase2 + 200 -// ); -// processor2->OnLogEvent(screenOnEvent.get()); -// -// // Metric 1 active; Activation 1 is active, Activation 2 is set to kActiveOnBoot -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_TRUE(metricProducer1001->isActive()); -// EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns); -// EXPECT_EQ(kActive, activation1001_1->state); -// EXPECT_EQ(0, activation1001_2->start_ns); -// EXPECT_EQ(kActiveOnBoot, activation1001_2->state); -// -// EXPECT_TRUE(metricProducer1002->isActive()); -// // }}}--------------------------------------------------------------------------- -// -// // Simulate shutdown by saving state to disk -// shutDownTime = timeBase2 + 50 * NS_PER_SEC; -// processor2->SaveActiveConfigsToDisk(shutDownTime); -// EXPECT_TRUE(metricProducer1001->isActive()); -// EXPECT_TRUE(metricProducer1002->isActive()); -// ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime; -// int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC; -// -// // Simulate device restarted state by creating new instance of StatsLogProcessor with the -// // same config. -// long timeBase3 = timeBase2 + 120 * NS_PER_SEC; -// sp<StatsLogProcessor> processor3 = -// CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1); -// -// // Metric 1 is not active. -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_EQ(1, processor3->mMetricsManagers.size()); -// it = processor3->mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor3->mMetricsManagers.end()); -// auto& metricsManagerTimeBase3 = it->second; -// EXPECT_TRUE(metricsManagerTimeBase3->isActive()); -// -// metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin(); -// for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId1) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end()); -// auto& metricProducerTimeBase3_1 = *metricIt; -// EXPECT_FALSE(metricProducerTimeBase3_1->isActive()); -// -// metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin(); -// for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId2) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end()); -// auto& metricProducerTimeBase3_2 = *metricIt; -// EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); -// -// i = 0; -// for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { -// if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == -// metric1ActivationTrigger1->atom_matcher_id()) { -// break; -// } -// } -// const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i); -// EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns); -// EXPECT_EQ(0, activationTimeBase3_1->start_ns); -// EXPECT_EQ(kNotActive, activationTimeBase3_1->state); -// -// i = 0; -// for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { -// if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == -// metric1ActivationTrigger2->atom_matcher_id()) { -// break; -// } -// } -// -// const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i); -// EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns); -// EXPECT_EQ(0, activationTimeBase3_2->start_ns); -// EXPECT_EQ(kNotActive, activationTimeBase3_2->state); -// -// EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); -// // }}}---------------------------------------------------------------------------------- -// -// // Load saved state from disk. -// processor3->LoadActiveConfigsFromDisk(); -// -// // Metric 1 active: Activation 1 is active, Activation 2 is active -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_TRUE(metricProducerTimeBase3_1->isActive()); -// EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns); -// EXPECT_EQ(kActive, activationTimeBase3_1->state); -// EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns); -// EXPECT_EQ(kActive, activationTimeBase3_2->state); -// -// EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); -// // }}}------------------------------------------------------------------------------- -// -// // Trigger Activation 2 for Metric 1 again. -// screenOnEvent = CreateScreenStateChangedEvent( -// android::view::DISPLAY_STATE_ON, -// timeBase3 + 100 * NS_PER_SEC -// ); -// processor3->OnLogEvent(screenOnEvent.get()); -// -// // Metric 1 active; Activation 1 is not active, Activation 2 is set to active -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_TRUE(metricProducerTimeBase3_1->isActive()); -// EXPECT_EQ(kNotActive, activationTimeBase3_1->state); -// EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns); -// EXPECT_EQ(kActive, activationTimeBase3_2->state); -// -// EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); -// // }}}--------------------------------------------------------------------------- -// -// // Simulate shutdown by saving state to disk. -// shutDownTime = timeBase3 + 500 * NS_PER_SEC; -// processor3->SaveActiveConfigsToDisk(shutDownTime); -// EXPECT_TRUE(metricProducer1001->isActive()); -// EXPECT_TRUE(metricProducer1002->isActive()); -// ttl1 = timeBase3 + ttl1 - shutDownTime; -// ttl2 = timeBase3 + metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime; -// -// // Simulate device restarted state by creating new instance of StatsLogProcessor with the -// // same config. -// long timeBase4 = timeBase3 + 600 * NS_PER_SEC; -// sp<StatsLogProcessor> processor4 = -// CreateStatsLogProcessor(timeBase4, timeBase4, config1, cfgKey1); -// -// // Metric 1 is not active. -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_EQ(1, processor4->mMetricsManagers.size()); -// it = processor4->mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor4->mMetricsManagers.end()); -// auto& metricsManagerTimeBase4 = it->second; -// EXPECT_TRUE(metricsManagerTimeBase4->isActive()); -// -// metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin(); -// for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId1) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end()); -// auto& metricProducerTimeBase4_1 = *metricIt; -// EXPECT_FALSE(metricProducerTimeBase4_1->isActive()); -// -// metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin(); -// for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) { -// if ((*metricIt)->getMetricId() == metricId2) { -// break; -// } -// } -// EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end()); -// auto& metricProducerTimeBase4_2 = *metricIt; -// EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); -// -// i = 0; -// for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) { -// if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() == -// metric1ActivationTrigger1->atom_matcher_id()) { -// break; -// } -// } -// const auto& activationTimeBase4_1 = metricProducerTimeBase4_1->mEventActivationMap.at(i); -// EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase4_1->ttl_ns); -// EXPECT_EQ(0, activationTimeBase4_1->start_ns); -// EXPECT_EQ(kNotActive, activationTimeBase4_1->state); -// -// i = 0; -// for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) { -// if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() == -// metric1ActivationTrigger2->atom_matcher_id()) { -// break; -// } -// } -// -// const auto& activationTimeBase4_2 = metricProducerTimeBase4_1->mEventActivationMap.at(i); -// EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase4_2->ttl_ns); -// EXPECT_EQ(0, activationTimeBase4_2->start_ns); -// EXPECT_EQ(kNotActive, activationTimeBase4_2->state); -// -// EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); -// // }}}---------------------------------------------------------------------------------- -// -// // Load saved state from disk. -// processor4->LoadActiveConfigsFromDisk(); -// -// // Metric 1 active: Activation 1 is not active, Activation 2 is not active -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_FALSE(metricProducerTimeBase4_1->isActive()); -// EXPECT_EQ(kNotActive, activationTimeBase4_1->state); -// EXPECT_EQ(kNotActive, activationTimeBase4_2->state); -// -// EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); -// // }}}------------------------------------------------------------------------------- -//} -// -//TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes) { -// int uid = 1111; -// -// // Create config with 2 metrics: -// // Metric 1: Activate on boot with 2 activations -// // Metric 2: Always active -// StatsdConfig config1; -// config1.set_id(12341); -// config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// *config1.add_atom_matcher() = wakelockAcquireMatcher; -// *config1.add_atom_matcher() = screenOnMatcher; -// -// long metricId1 = 1234561; -// long metricId2 = 1234562; -// -// auto countMetric1 = config1.add_count_metric(); -// countMetric1->set_id(metricId1); -// countMetric1->set_what(wakelockAcquireMatcher.id()); -// countMetric1->set_bucket(FIVE_MINUTES); -// -// auto countMetric2 = config1.add_count_metric(); -// countMetric2->set_id(metricId2); -// countMetric2->set_what(wakelockAcquireMatcher.id()); -// countMetric2->set_bucket(FIVE_MINUTES); -// -// auto metric1Activation = config1.add_metric_activation(); -// metric1Activation->set_metric_id(metricId1); -// metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); -// auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); -// metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); -// metric1ActivationTrigger1->set_ttl_seconds(100); -// auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); -// metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); -// metric1ActivationTrigger2->set_ttl_seconds(200); -// metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); -// -// ConfigKey cfgKey1(uid, 12341); -// long timeBase1 = 1; -// sp<StatsLogProcessor> processor1 = -// CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); -// -// // Metric 1 is not active. -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_EQ(1, processor1->mMetricsManagers.size()); -// auto it = processor1->mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor1->mMetricsManagers.end()); -// auto& metricsManager1 = it->second; -// EXPECT_TRUE(metricsManager1->isActive()); -// -// EXPECT_EQ(metricsManager1->mAllMetricProducers.size(), 2); -// // We assume that the index of a MetricProducer within the mAllMetricProducers -// // array follows the order in which metrics are added to the config. -// auto& metricProducer1_1 = metricsManager1->mAllMetricProducers[0]; -// EXPECT_EQ(metricProducer1_1->getMetricId(), metricId1); -// EXPECT_FALSE(metricProducer1_1->isActive()); // inactive due to associated MetricActivation -// -// auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1]; -// EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2); -// EXPECT_TRUE(metricProducer1_2->isActive()); -// -// EXPECT_EQ(metricProducer1_1->mEventActivationMap.size(), 2); -// // The key in mEventActivationMap is the index of the associated atom matcher. We assume -// // that matchers are indexed in the order that they are added to the config. -// const auto& activation1_1_1 = metricProducer1_1->mEventActivationMap.at(0); -// EXPECT_EQ(100 * NS_PER_SEC, activation1_1_1->ttl_ns); -// EXPECT_EQ(0, activation1_1_1->start_ns); -// EXPECT_EQ(kNotActive, activation1_1_1->state); -// EXPECT_EQ(ACTIVATE_ON_BOOT, activation1_1_1->activationType); -// -// const auto& activation1_1_2 = metricProducer1_1->mEventActivationMap.at(1); -// EXPECT_EQ(200 * NS_PER_SEC, activation1_1_2->ttl_ns); -// EXPECT_EQ(0, activation1_1_2->start_ns); -// EXPECT_EQ(kNotActive, activation1_1_2->state); -// EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1_1_2->activationType); -// // }}}------------------------------------------------------------------------------ -// -// // Trigger Activation 1 for Metric 1 -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; -// auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1); -// processor1->OnLogEvent(event.get()); -// -// // Metric 1 is not active; Activation 1 set to kActiveOnBoot -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_FALSE(metricProducer1_1->isActive()); -// EXPECT_EQ(0, activation1_1_1->start_ns); -// EXPECT_EQ(kActiveOnBoot, activation1_1_1->state); -// EXPECT_EQ(0, activation1_1_2->start_ns); -// EXPECT_EQ(kNotActive, activation1_1_2->state); -// -// EXPECT_TRUE(metricProducer1_2->isActive()); -// // }}}----------------------------------------------------------------------------- -// -// // Simulate shutdown by saving state to disk -// int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; -// processor1->SaveActiveConfigsToDisk(shutDownTime); -// EXPECT_FALSE(metricProducer1_1->isActive()); -// -// // Simulate device restarted state by creating new instance of StatsLogProcessor with the -// // same config. -// long timeBase2 = 1000; -// sp<StatsLogProcessor> processor2 = -// CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); -// -// // Metric 1 is not active. -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_EQ(1, processor2->mMetricsManagers.size()); -// it = processor2->mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor2->mMetricsManagers.end()); -// auto& metricsManager2 = it->second; -// EXPECT_TRUE(metricsManager2->isActive()); -// -// EXPECT_EQ(metricsManager2->mAllMetricProducers.size(), 2); -// // We assume that the index of a MetricProducer within the mAllMetricProducers -// // array follows the order in which metrics are added to the config. -// auto& metricProducer2_1 = metricsManager2->mAllMetricProducers[0]; -// EXPECT_EQ(metricProducer2_1->getMetricId(), metricId1); -// EXPECT_FALSE(metricProducer2_1->isActive()); -// -// auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1]; -// EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2); -// EXPECT_TRUE(metricProducer2_2->isActive()); -// -// EXPECT_EQ(metricProducer2_1->mEventActivationMap.size(), 2); -// // The key in mEventActivationMap is the index of the associated atom matcher. We assume -// // that matchers are indexed in the order that they are added to the config. -// const auto& activation2_1_1 = metricProducer2_1->mEventActivationMap.at(0); -// EXPECT_EQ(100 * NS_PER_SEC, activation2_1_1->ttl_ns); -// EXPECT_EQ(0, activation2_1_1->start_ns); -// EXPECT_EQ(kNotActive, activation2_1_1->state); -// EXPECT_EQ(ACTIVATE_ON_BOOT, activation2_1_1->activationType); -// -// const auto& activation2_1_2 = metricProducer2_1->mEventActivationMap.at(1); -// EXPECT_EQ(200 * NS_PER_SEC, activation2_1_2->ttl_ns); -// EXPECT_EQ(0, activation2_1_2->start_ns); -// EXPECT_EQ(kNotActive, activation2_1_2->state); -// EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2_1_2->activationType); -// // }}}----------------------------------------------------------------------------------- -// -// // Load saved state from disk. -// processor2->LoadActiveConfigsFromDisk(); -// -// // Metric 1 active; Activation 1 is active, Activation 2 is not active -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_TRUE(metricProducer2_1->isActive()); -// int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC; -// EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns); -// EXPECT_EQ(kActive, activation2_1_1->state); -// EXPECT_EQ(0, activation2_1_2->start_ns); -// EXPECT_EQ(kNotActive, activation2_1_2->state); -// -// EXPECT_TRUE(metricProducer2_2->isActive()); -// // }}}-------------------------------------------------------------------------------- -// -// // Trigger Activation 2 for Metric 1. -// auto screenOnEvent = CreateScreenStateChangedEvent( -// android::view::DISPLAY_STATE_ON, -// timeBase2 + 200 -// ); -// processor2->OnLogEvent(screenOnEvent.get()); -// -// // Metric 1 active; Activation 1 is active, Activation 2 is active -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_TRUE(metricProducer2_1->isActive()); -// EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns); -// EXPECT_EQ(kActive, activation2_1_1->state); -// EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation2_1_2->start_ns); -// EXPECT_EQ(kActive, activation2_1_2->state); -// -// EXPECT_TRUE(metricProducer2_2->isActive()); -// // }}}--------------------------------------------------------------------------- -// -// // Simulate shutdown by saving state to disk -// shutDownTime = timeBase2 + 50 * NS_PER_SEC; -// processor2->SaveActiveConfigsToDisk(shutDownTime); -// EXPECT_TRUE(metricProducer2_1->isActive()); -// EXPECT_TRUE(metricProducer2_2->isActive()); -// ttl1 -= shutDownTime - timeBase2; -// int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC -// - (shutDownTime - screenOnEvent->GetElapsedTimestampNs()); -// -// // Simulate device restarted state by creating new instance of StatsLogProcessor with the -// // same config. -// long timeBase3 = timeBase2 + 120 * NS_PER_SEC; -// sp<StatsLogProcessor> processor3 = -// CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1); -// -// // Metric 1 is not active. -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_EQ(1, processor3->mMetricsManagers.size()); -// it = processor3->mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor3->mMetricsManagers.end()); -// auto& metricsManager3 = it->second; -// EXPECT_TRUE(metricsManager3->isActive()); -// -// EXPECT_EQ(metricsManager3->mAllMetricProducers.size(), 2); -// // We assume that the index of a MetricProducer within the mAllMetricProducers -// // array follows the order in which metrics are added to the config. -// auto& metricProducer3_1 = metricsManager3->mAllMetricProducers[0]; -// EXPECT_EQ(metricProducer3_1->getMetricId(), metricId1); -// EXPECT_FALSE(metricProducer3_1->isActive()); -// -// auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1]; -// EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2); -// EXPECT_TRUE(metricProducer3_2->isActive()); -// -// EXPECT_EQ(metricProducer3_1->mEventActivationMap.size(), 2); -// // The key in mEventActivationMap is the index of the associated atom matcher. We assume -// // that matchers are indexed in the order that they are added to the config. -// const auto& activation3_1_1 = metricProducer3_1->mEventActivationMap.at(0); -// EXPECT_EQ(100 * NS_PER_SEC, activation3_1_1->ttl_ns); -// EXPECT_EQ(0, activation3_1_1->start_ns); -// EXPECT_EQ(kNotActive, activation3_1_1->state); -// EXPECT_EQ(ACTIVATE_ON_BOOT, activation3_1_1->activationType); -// -// const auto& activation3_1_2 = metricProducer3_1->mEventActivationMap.at(1); -// EXPECT_EQ(200 * NS_PER_SEC, activation3_1_2->ttl_ns); -// EXPECT_EQ(0, activation3_1_2->start_ns); -// EXPECT_EQ(kNotActive, activation3_1_2->state); -// EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation3_1_2->activationType); -// // }}}---------------------------------------------------------------------------------- -// -// // Load saved state from disk. -// processor3->LoadActiveConfigsFromDisk(); -// -// // Metric 1 active: Activation 1 is active, Activation 2 is active -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_TRUE(metricProducer3_1->isActive()); -// EXPECT_EQ(timeBase3 + ttl1 - activation3_1_1->ttl_ns, activation3_1_1->start_ns); -// EXPECT_EQ(kActive, activation3_1_1->state); -// EXPECT_EQ(timeBase3 + ttl2 - activation3_1_2->ttl_ns, activation3_1_2->start_ns); -// EXPECT_EQ(kActive, activation3_1_2->state); -// -// EXPECT_TRUE(metricProducer3_2->isActive()); -// // }}}------------------------------------------------------------------------------- -// -// -// // Trigger Activation 2 for Metric 1 again. -// screenOnEvent = CreateScreenStateChangedEvent( -// android::view::DISPLAY_STATE_ON, -// timeBase3 + 100 * NS_PER_SEC -// ); -// processor3->OnLogEvent(screenOnEvent.get()); -// -// // Metric 1 active; Activation 1 is inactive (above screenOnEvent causes ttl1 to expire), -// // Activation 2 is set to active -// // Metric 2 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_TRUE(metricProducer3_1->isActive()); -// EXPECT_EQ(kNotActive, activation3_1_1->state); -// EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation3_1_2->start_ns); -// EXPECT_EQ(kActive, activation3_1_2->state); -// -// EXPECT_TRUE(metricProducer3_2->isActive()); -// // }}}--------------------------------------------------------------------------- -//} -// -//TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { -// int uid = 9876; -// long configId = 12341; -// -// // Create config with 3 metrics: -// // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate. -// // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate. -// // Metric 3: Always active -// StatsdConfig config1; -// config1.set_id(configId); -// config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. -// auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); -// auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); -// auto jobStartMatcher = CreateStartScheduledJobAtomMatcher(); -// auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher(); -// *config1.add_atom_matcher() = wakelockAcquireMatcher; -// *config1.add_atom_matcher() = screenOnMatcher; -// *config1.add_atom_matcher() = jobStartMatcher; -// *config1.add_atom_matcher() = jobFinishMatcher; -// -// long metricId1 = 1234561; -// long metricId2 = 1234562; -// long metricId3 = 1234563; -// -// auto countMetric1 = config1.add_count_metric(); -// countMetric1->set_id(metricId1); -// countMetric1->set_what(wakelockAcquireMatcher.id()); -// countMetric1->set_bucket(FIVE_MINUTES); -// -// auto countMetric2 = config1.add_count_metric(); -// countMetric2->set_id(metricId2); -// countMetric2->set_what(wakelockAcquireMatcher.id()); -// countMetric2->set_bucket(FIVE_MINUTES); -// -// auto countMetric3 = config1.add_count_metric(); -// countMetric3->set_id(metricId3); -// countMetric3->set_what(wakelockAcquireMatcher.id()); -// countMetric3->set_bucket(FIVE_MINUTES); -// -// // Metric 1 activates on boot for wakelock acquire, immediately for screen on. -// auto metric1Activation = config1.add_metric_activation(); -// metric1Activation->set_metric_id(metricId1); -// auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); -// metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); -// metric1ActivationTrigger1->set_ttl_seconds(100); -// metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT); -// auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); -// metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); -// metric1ActivationTrigger2->set_ttl_seconds(200); -// metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); -// -// // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish. -// auto metric2Activation = config1.add_metric_activation(); -// metric2Activation->set_metric_id(metricId2); -// auto metric2ActivationTrigger1 = metric2Activation->add_event_activation(); -// metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id()); -// metric2ActivationTrigger1->set_ttl_seconds(100); -// metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT); -// auto metric2ActivationTrigger2 = metric2Activation->add_event_activation(); -// metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id()); -// metric2ActivationTrigger2->set_ttl_seconds(200); -// metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); -// -// // Send the config. -// shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); -// string serialized = config1.SerializeAsString(); -// service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()}); -// -// // Make sure the config is stored on disk. Otherwise, we will not reset on system server death. -// StatsdConfig tmpConfig; -// ConfigKey cfgKey1(uid, configId); -// EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig)); -// -// // Metric 1 is not active. -// // Metric 2 is not active. -// // Metric 3 is active. -// // {{{--------------------------------------------------------------------------- -// sp<StatsLogProcessor> processor = service->mProcessor; -// EXPECT_EQ(1, processor->mMetricsManagers.size()); -// auto it = processor->mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor->mMetricsManagers.end()); -// auto& metricsManager1 = it->second; -// EXPECT_TRUE(metricsManager1->isActive()); -// EXPECT_EQ(3, metricsManager1->mAllMetricProducers.size()); -// -// auto& metricProducer1 = metricsManager1->mAllMetricProducers[0]; -// EXPECT_EQ(metricId1, metricProducer1->getMetricId()); -// EXPECT_FALSE(metricProducer1->isActive()); -// -// auto& metricProducer2 = metricsManager1->mAllMetricProducers[1]; -// EXPECT_EQ(metricId2, metricProducer2->getMetricId()); -// EXPECT_FALSE(metricProducer2->isActive()); -// -// auto& metricProducer3 = metricsManager1->mAllMetricProducers[2]; -// EXPECT_EQ(metricId3, metricProducer3->getMetricId()); -// EXPECT_TRUE(metricProducer3->isActive()); -// -// // Check event activations. -// EXPECT_EQ(metricsManager1->mAllAtomMatchers.size(), 4); -// EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(), -// metric1ActivationTrigger1->atom_matcher_id()); -// const auto& activation1 = metricProducer1->mEventActivationMap.at(0); -// EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); -// EXPECT_EQ(0, activation1->start_ns); -// EXPECT_EQ(kNotActive, activation1->state); -// EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType); -// -// EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(), -// metric1ActivationTrigger2->atom_matcher_id()); -// const auto& activation2 = metricProducer1->mEventActivationMap.at(1); -// EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); -// EXPECT_EQ(0, activation2->start_ns); -// EXPECT_EQ(kNotActive, activation2->state); -// EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType); -// -// EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(), -// metric2ActivationTrigger1->atom_matcher_id()); -// const auto& activation3 = metricProducer2->mEventActivationMap.at(2); -// EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns); -// EXPECT_EQ(0, activation3->start_ns); -// EXPECT_EQ(kNotActive, activation3->state); -// EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType); -// -// EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(), -// metric2ActivationTrigger2->atom_matcher_id()); -// const auto& activation4 = metricProducer2->mEventActivationMap.at(3); -// EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns); -// EXPECT_EQ(0, activation4->start_ns); -// EXPECT_EQ(kNotActive, activation4->state); -// EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType); -// // }}}------------------------------------------------------------------------------ -// -// // Trigger Activation 1 for Metric 1. Should activate on boot. -// // Trigger Activation 4 for Metric 2. Should activate immediately. -// long configAddedTimeNs = metricsManager1->mLastReportTimeNs; -// std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; -// auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 1 + configAddedTimeNs); -// processor->OnLogEvent(event.get()); -// -// event = CreateFinishScheduledJobEvent(attributions1, "finish1", 2 + configAddedTimeNs); -// processor->OnLogEvent(event.get()); -// -// // Metric 1 is not active; Activation 1 set to kActiveOnBoot -// // Metric 2 is active. Activation 4 set to kActive -// // Metric 3 is active. -// // {{{--------------------------------------------------------------------------- -// EXPECT_FALSE(metricProducer1->isActive()); -// EXPECT_EQ(0, activation1->start_ns); -// EXPECT_EQ(kActiveOnBoot, activation1->state); -// EXPECT_EQ(0, activation2->start_ns); -// EXPECT_EQ(kNotActive, activation2->state); -// -// EXPECT_TRUE(metricProducer2->isActive()); -// EXPECT_EQ(0, activation3->start_ns); -// EXPECT_EQ(kNotActive, activation3->state); -// EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns); -// EXPECT_EQ(kActive, activation4->state); -// -// EXPECT_TRUE(metricProducer3->isActive()); -// // }}}----------------------------------------------------------------------------- -// -// // Can't fake time with StatsService. -// // Lets get a time close to the system server death time and make sure it's sane. -// int64_t approximateSystemServerDeath = getElapsedRealtimeNs(); -// EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs); -// EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs); -// -// // System server dies. -// service->statsCompanionServiceDiedImpl(); -// -// // We should have a new metrics manager. Lets get it and ensure activation status is restored. -// // {{{--------------------------------------------------------------------------- -// EXPECT_EQ(1, processor->mMetricsManagers.size()); -// it = processor->mMetricsManagers.find(cfgKey1); -// EXPECT_TRUE(it != processor->mMetricsManagers.end()); -// auto& metricsManager2 = it->second; -// EXPECT_TRUE(metricsManager2->isActive()); -// EXPECT_EQ(3, metricsManager2->mAllMetricProducers.size()); -// -// auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0]; -// EXPECT_EQ(metricId1, metricProducer1001->getMetricId()); -// EXPECT_FALSE(metricProducer1001->isActive()); -// -// auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1]; -// EXPECT_EQ(metricId2, metricProducer1002->getMetricId()); -// EXPECT_TRUE(metricProducer1002->isActive()); -// -// auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2]; -// EXPECT_EQ(metricId3, metricProducer1003->getMetricId()); -// EXPECT_TRUE(metricProducer1003->isActive()); -// -// // Check event activations. -// // Activation 1 is kActiveOnBoot. -// // Activation 2 and 3 are not active. -// // Activation 4 is active. -// EXPECT_EQ(metricsManager2->mAllAtomMatchers.size(), 4); -// EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(), -// metric1ActivationTrigger1->atom_matcher_id()); -// const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0); -// EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns); -// EXPECT_EQ(0, activation1001->start_ns); -// EXPECT_EQ(kActiveOnBoot, activation1001->state); -// EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType); -// -// EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(), -// metric1ActivationTrigger2->atom_matcher_id()); -// const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1); -// EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns); -// EXPECT_EQ(0, activation1002->start_ns); -// EXPECT_EQ(kNotActive, activation1002->state); -// EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType); -// -// EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(), -// metric2ActivationTrigger1->atom_matcher_id()); -// const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2); -// EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns); -// EXPECT_EQ(0, activation1003->start_ns); -// EXPECT_EQ(kNotActive, activation1003->state); -// EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType); -// -// EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(), -// metric2ActivationTrigger2->atom_matcher_id()); -// const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3); -// EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns); -// EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns); -// EXPECT_EQ(kActive, activation1004->state); -// EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType); -// // }}}------------------------------------------------------------------------------ -// -// // Clear the data stored on disk as a result of the system server death. -// vector<uint8_t> buffer; -// processor->onDumpReport(cfgKey1, configAddedTimeNs + NS_PER_SEC, false, true, -// ADB_DUMP, FAST, &buffer); -//} +TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) { + // Setup a simple config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = wakelockAcquireMatcher; + + auto countMetric = config.add_count_metric(); + countMetric->set_id(123456); + countMetric->set_what(wakelockAcquireMatcher.id()); + countMetric->set_bucket(FIVE_MINUTES); + + ConfigKey cfgKey; + sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey); + + std::vector<int> attributionUids = {111}; + std::vector<string> attributionTags = {"App1"}; + std::unique_ptr<LogEvent> event = + CreateAcquireWakelockEvent(2 /*timestamp*/, attributionUids, attributionTags, "wl1"); + processor->OnLogEvent(event.get()); + + vector<uint8_t> bytes; + ConfigMetricsReportList output; + + // Dump report WITHOUT erasing data. + processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, FAST, + &bytes); + output.ParseFromArray(bytes.data(), bytes.size()); + EXPECT_EQ(output.reports_size(), 1); + EXPECT_EQ(output.reports(0).metrics_size(), 1); + EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1); + + // Dump report WITH erasing data. There should be data since we didn't previously erase it. + processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes); + output.ParseFromArray(bytes.data(), bytes.size()); + EXPECT_EQ(output.reports_size(), 1); + EXPECT_EQ(output.reports(0).metrics_size(), 1); + EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1); + + // Dump report again. There should be no data since we erased it. + processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes); + output.ParseFromArray(bytes.data(), bytes.size()); + // We don't care whether statsd has a report, as long as it has no count metrics in it. + bool noData = output.reports_size() == 0 || output.reports(0).metrics_size() == 0 || + output.reports(0).metrics(0).count_metrics().data_size() == 0; + EXPECT_TRUE(noData); +} + +TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { + int uid = 1111; + + // Setup a simple config, no activation + StatsdConfig config1; + int64_t cfgId1 = 12341; + config1.set_id(cfgId1); + config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + *config1.add_atom_matcher() = wakelockAcquireMatcher; + + long metricId1 = 1234561; + long metricId2 = 1234562; + auto countMetric1 = config1.add_count_metric(); + countMetric1->set_id(metricId1); + countMetric1->set_what(wakelockAcquireMatcher.id()); + countMetric1->set_bucket(FIVE_MINUTES); + + auto countMetric2 = config1.add_count_metric(); + countMetric2->set_id(metricId2); + countMetric2->set_what(wakelockAcquireMatcher.id()); + countMetric2->set_bucket(FIVE_MINUTES); + + ConfigKey cfgKey1(uid, cfgId1); + + // Add another config, with two metrics, one with activation + StatsdConfig config2; + int64_t cfgId2 = 12342; + config2.set_id(cfgId2); + config2.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + *config2.add_atom_matcher() = wakelockAcquireMatcher; + + long metricId3 = 1234561; + long metricId4 = 1234562; + + auto countMetric3 = config2.add_count_metric(); + countMetric3->set_id(metricId3); + countMetric3->set_what(wakelockAcquireMatcher.id()); + countMetric3->set_bucket(FIVE_MINUTES); + + auto countMetric4 = config2.add_count_metric(); + countMetric4->set_id(metricId4); + countMetric4->set_what(wakelockAcquireMatcher.id()); + countMetric4->set_bucket(FIVE_MINUTES); + + auto metric3Activation = config2.add_metric_activation(); + metric3Activation->set_metric_id(metricId3); + metric3Activation->set_activation_type(ACTIVATE_IMMEDIATELY); + auto metric3ActivationTrigger = metric3Activation->add_event_activation(); + metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric3ActivationTrigger->set_ttl_seconds(100); + + ConfigKey cfgKey2(uid, cfgId2); + + // Add another config, with two metrics, both with activations + StatsdConfig config3; + int64_t cfgId3 = 12343; + config3.set_id(cfgId3); + config3.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + *config3.add_atom_matcher() = wakelockAcquireMatcher; + + long metricId5 = 1234565; + long metricId6 = 1234566; + auto countMetric5 = config3.add_count_metric(); + countMetric5->set_id(metricId5); + countMetric5->set_what(wakelockAcquireMatcher.id()); + countMetric5->set_bucket(FIVE_MINUTES); + + auto countMetric6 = config3.add_count_metric(); + countMetric6->set_id(metricId6); + countMetric6->set_what(wakelockAcquireMatcher.id()); + countMetric6->set_bucket(FIVE_MINUTES); + + auto metric5Activation = config3.add_metric_activation(); + metric5Activation->set_metric_id(metricId5); + metric5Activation->set_activation_type(ACTIVATE_IMMEDIATELY); + auto metric5ActivationTrigger = metric5Activation->add_event_activation(); + metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric5ActivationTrigger->set_ttl_seconds(100); + + auto metric6Activation = config3.add_metric_activation(); + metric6Activation->set_metric_id(metricId6); + metric6Activation->set_activation_type(ACTIVATE_IMMEDIATELY); + auto metric6ActivationTrigger = metric6Activation->add_event_activation(); + metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric6ActivationTrigger->set_ttl_seconds(200); + + ConfigKey cfgKey3(uid, cfgId3); + + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + vector<int64_t> activeConfigsBroadcast; + + long timeBase1 = 1; + int broadcastCount = 0; + StatsLogProcessor processor( + m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, timeBase1, + [](const ConfigKey& key) { return true; }, + [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid, + const vector<int64_t>& activeConfigs) { + broadcastCount++; + EXPECT_EQ(broadcastUid, uid); + activeConfigsBroadcast.clear(); + activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(), + activeConfigs.end()); + return true; + }); + + processor.OnConfigUpdated(1, cfgKey1, config1); + processor.OnConfigUpdated(2, cfgKey2, config2); + processor.OnConfigUpdated(3, cfgKey3, config3); + + EXPECT_EQ(3, processor.mMetricsManagers.size()); + + // Expect the first config and both metrics in it to be active. + auto it = processor.mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor.mMetricsManagers.end()); + auto& metricsManager1 = it->second; + EXPECT_TRUE(metricsManager1->isActive()); + + auto metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer1 = *metricIt; + EXPECT_TRUE(metricProducer1->isActive()); + + metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer2 = *metricIt; + EXPECT_TRUE(metricProducer2->isActive()); + + // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active. + it = processor.mMetricsManagers.find(cfgKey2); + EXPECT_TRUE(it != processor.mMetricsManagers.end()); + auto& metricsManager2 = it->second; + EXPECT_TRUE(metricsManager2->isActive()); + + metricIt = metricsManager2->mAllMetricProducers.begin(); + for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId3) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end()); + auto& metricProducer3 = *metricIt; + EXPECT_FALSE(metricProducer3->isActive()); + + metricIt = metricsManager2->mAllMetricProducers.begin(); + for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId4) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end()); + auto& metricProducer4 = *metricIt; + EXPECT_TRUE(metricProducer4->isActive()); + + // Expect the third config and both metrics in it to be inactive. + it = processor.mMetricsManagers.find(cfgKey3); + EXPECT_TRUE(it != processor.mMetricsManagers.end()); + auto& metricsManager3 = it->second; + EXPECT_FALSE(metricsManager3->isActive()); + + metricIt = metricsManager3->mAllMetricProducers.begin(); + for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId5) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end()); + auto& metricProducer5 = *metricIt; + EXPECT_FALSE(metricProducer5->isActive()); + + metricIt = metricsManager3->mAllMetricProducers.begin(); + for (; metricIt != metricsManager3->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId6) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end()); + auto& metricProducer6 = *metricIt; + EXPECT_FALSE(metricProducer6->isActive()); + + // No broadcast for active configs should have happened yet. + EXPECT_EQ(broadcastCount, 0); + + // Activate all 3 metrics that were not active. + std::vector<int> attributionUids = {111}; + std::vector<string> attributionTags = {"App1"}; + std::unique_ptr<LogEvent> event = + CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1"); + processor.OnLogEvent(event.get()); + + // Assert that all 3 configs are active. + EXPECT_TRUE(metricsManager1->isActive()); + EXPECT_TRUE(metricsManager2->isActive()); + EXPECT_TRUE(metricsManager3->isActive()); + + // A broadcast should have happened, and all 3 configs should be active in the broadcast. + EXPECT_EQ(broadcastCount, 1); + EXPECT_EQ(activeConfigsBroadcast.size(), 3); + EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1) != + activeConfigsBroadcast.end()); + EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2) != + activeConfigsBroadcast.end()); + EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3) != + activeConfigsBroadcast.end()); + + // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns. + int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; + processor.SaveActiveConfigsToDisk(shutDownTime); + const int64_t ttl3 = event->GetElapsedTimestampNs() + + metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; + const int64_t ttl5 = event->GetElapsedTimestampNs() + + metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; + const int64_t ttl6 = event->GetElapsedTimestampNs() + + metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; + + // Create a second StatsLogProcessor and push the same 3 configs. + long timeBase2 = 1000; + sp<StatsLogProcessor> processor2 = + CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); + processor2->OnConfigUpdated(timeBase2, cfgKey2, config2); + processor2->OnConfigUpdated(timeBase2, cfgKey3, config3); + + EXPECT_EQ(3, processor2->mMetricsManagers.size()); + + // First config and both metrics are active. + it = processor2->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager1001 = it->second; + EXPECT_TRUE(metricsManager1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1001 = *metricIt; + EXPECT_TRUE(metricProducer1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1002 = *metricIt; + EXPECT_TRUE(metricProducer1002->isActive()); + + // Second config is active. Metric 3 is inactive, metric 4 is active. + it = processor2->mMetricsManagers.find(cfgKey2); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager1002 = it->second; + EXPECT_TRUE(metricsManager1002->isActive()); + + metricIt = metricsManager1002->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId3) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end()); + auto& metricProducer1003 = *metricIt; + EXPECT_FALSE(metricProducer1003->isActive()); + + metricIt = metricsManager1002->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId4) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end()); + auto& metricProducer1004 = *metricIt; + EXPECT_TRUE(metricProducer1004->isActive()); + + // Config 3 is inactive. both metrics are inactive. + it = processor2->mMetricsManagers.find(cfgKey3); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager1003 = it->second; + EXPECT_FALSE(metricsManager1003->isActive()); + EXPECT_EQ(2, metricsManager1003->mAllMetricProducers.size()); + + metricIt = metricsManager1003->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId5) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end()); + auto& metricProducer1005 = *metricIt; + EXPECT_FALSE(metricProducer1005->isActive()); + + metricIt = metricsManager1003->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1003->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId6) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end()); + auto& metricProducer1006 = *metricIt; + EXPECT_FALSE(metricProducer1006->isActive()); + + // Assert that all 3 metrics with activation are inactive and that the ttls were properly set. + EXPECT_FALSE(metricProducer1003->isActive()); + const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second; + EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns); + EXPECT_EQ(0, activation1003->start_ns); + EXPECT_FALSE(metricProducer1005->isActive()); + const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second; + EXPECT_EQ(100 * NS_PER_SEC, activation1005->ttl_ns); + EXPECT_EQ(0, activation1005->start_ns); + EXPECT_FALSE(metricProducer1006->isActive()); + const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second; + EXPECT_EQ(200 * NS_PER_SEC, activation1006->ttl_ns); + EXPECT_EQ(0, activation1006->start_ns); + + processor2->LoadActiveConfigsFromDisk(); + + // After loading activations from disk, assert that all 3 metrics are active. + EXPECT_TRUE(metricProducer1003->isActive()); + EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->start_ns); + EXPECT_TRUE(metricProducer1005->isActive()); + EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->start_ns); + EXPECT_TRUE(metricProducer1006->isActive()); + EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->start_ns); + + // Make sure no more broadcasts have happened. + EXPECT_EQ(broadcastCount, 1); +} + +TEST(StatsLogProcessorTest, TestActivationOnBoot) { + int uid = 1111; + + StatsdConfig config1; + config1.set_id(12341); + config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + *config1.add_atom_matcher() = wakelockAcquireMatcher; + + long metricId1 = 1234561; + long metricId2 = 1234562; + auto countMetric1 = config1.add_count_metric(); + countMetric1->set_id(metricId1); + countMetric1->set_what(wakelockAcquireMatcher.id()); + countMetric1->set_bucket(FIVE_MINUTES); + + auto countMetric2 = config1.add_count_metric(); + countMetric2->set_id(metricId2); + countMetric2->set_what(wakelockAcquireMatcher.id()); + countMetric2->set_bucket(FIVE_MINUTES); + + auto metric1Activation = config1.add_metric_activation(); + metric1Activation->set_metric_id(metricId1); + metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); + auto metric1ActivationTrigger = metric1Activation->add_event_activation(); + metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric1ActivationTrigger->set_ttl_seconds(100); + + ConfigKey cfgKey1(uid, 12341); + long timeBase1 = 1; + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); + + EXPECT_EQ(1, processor->mMetricsManagers.size()); + auto it = processor->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager1 = it->second; + EXPECT_TRUE(metricsManager1->isActive()); + + auto metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer1 = *metricIt; + EXPECT_FALSE(metricProducer1->isActive()); + + metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer2 = *metricIt; + EXPECT_TRUE(metricProducer2->isActive()); + + const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second; + EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kNotActive, activation1->state); + + std::vector<int> attributionUids = {111}; + std::vector<string> attributionTags = {"App1"}; + std::unique_ptr<LogEvent> event = + CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1"); + processor->OnLogEvent(event.get()); + + EXPECT_FALSE(metricProducer1->isActive()); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1->state); + + int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; + processor->SaveActiveConfigsToDisk(shutDownTime); + EXPECT_FALSE(metricProducer1->isActive()); + const int64_t ttl1 = metric1ActivationTrigger->ttl_seconds() * NS_PER_SEC; + + long timeBase2 = 1000; + sp<StatsLogProcessor> processor2 = + CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); + + EXPECT_EQ(1, processor2->mMetricsManagers.size()); + it = processor2->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager1001 = it->second; + EXPECT_TRUE(metricsManager1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1001 = *metricIt; + EXPECT_FALSE(metricProducer1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1002 = *metricIt; + EXPECT_TRUE(metricProducer1002->isActive()); + + const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second; + EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns); + EXPECT_EQ(0, activation1001->start_ns); + EXPECT_EQ(kNotActive, activation1001->state); + + processor2->LoadActiveConfigsFromDisk(); + + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->start_ns); + EXPECT_EQ(kActive, activation1001->state); +} + +TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { + int uid = 1111; + + // Create config with 2 metrics: + // Metric 1: Activate on boot with 2 activations + // Metric 2: Always active + StatsdConfig config1; + config1.set_id(12341); + config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + *config1.add_atom_matcher() = wakelockAcquireMatcher; + *config1.add_atom_matcher() = screenOnMatcher; + + long metricId1 = 1234561; + long metricId2 = 1234562; + + auto countMetric1 = config1.add_count_metric(); + countMetric1->set_id(metricId1); + countMetric1->set_what(wakelockAcquireMatcher.id()); + countMetric1->set_bucket(FIVE_MINUTES); + + auto countMetric2 = config1.add_count_metric(); + countMetric2->set_id(metricId2); + countMetric2->set_what(wakelockAcquireMatcher.id()); + countMetric2->set_bucket(FIVE_MINUTES); + + auto metric1Activation = config1.add_metric_activation(); + metric1Activation->set_metric_id(metricId1); + metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); + auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); + metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric1ActivationTrigger1->set_ttl_seconds(100); + auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); + metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); + metric1ActivationTrigger2->set_ttl_seconds(200); + + ConfigKey cfgKey1(uid, 12341); + long timeBase1 = 1; + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor->mMetricsManagers.size()); + auto it = processor->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager1 = it->second; + EXPECT_TRUE(metricsManager1->isActive()); + + auto metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer1 = *metricIt; + EXPECT_FALSE(metricProducer1->isActive()); + + metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer2 = *metricIt; + EXPECT_TRUE(metricProducer2->isActive()); + + int i = 0; + for (; i < metricsManager1->mAllAtomMatchers.size(); i++) { + if (metricsManager1->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger1->atom_matcher_id()) { + break; + } + } + const auto& activation1 = metricProducer1->mEventActivationMap.at(i); + EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kNotActive, activation1->state); + + i = 0; + for (; i < metricsManager1->mAllAtomMatchers.size(); i++) { + if (metricsManager1->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger2->atom_matcher_id()) { + break; + } + } + const auto& activation2 = metricProducer1->mEventActivationMap.at(i); + EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); + EXPECT_EQ(0, activation2->start_ns); + EXPECT_EQ(kNotActive, activation2->state); + // }}}------------------------------------------------------------------------------ + + // Trigger Activation 1 for Metric 1 + std::vector<int> attributionUids = {111}; + std::vector<string> attributionTags = {"App1"}; + std::unique_ptr<LogEvent> event = + CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1"); + processor->OnLogEvent(event.get()); + + // Metric 1 is not active; Activation 1 set to kActiveOnBoot + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_FALSE(metricProducer1->isActive()); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1->state); + EXPECT_EQ(0, activation2->start_ns); + EXPECT_EQ(kNotActive, activation2->state); + + EXPECT_TRUE(metricProducer2->isActive()); + // }}}----------------------------------------------------------------------------- + + // Simulate shutdown by saving state to disk + int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; + processor->SaveActiveConfigsToDisk(shutDownTime); + EXPECT_FALSE(metricProducer1->isActive()); + int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC; + + // Simulate device restarted state by creating new instance of StatsLogProcessor with the + // same config. + long timeBase2 = 1000; + sp<StatsLogProcessor> processor2 = + CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor2->mMetricsManagers.size()); + it = processor2->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager1001 = it->second; + EXPECT_TRUE(metricsManager1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1001 = *metricIt; + EXPECT_FALSE(metricProducer1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1002 = *metricIt; + EXPECT_TRUE(metricProducer1002->isActive()); + + i = 0; + for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { + if (metricsManager1001->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger1->atom_matcher_id()) { + break; + } + } + const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i); + EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns); + EXPECT_EQ(0, activation1001_1->start_ns); + EXPECT_EQ(kNotActive, activation1001_1->state); + + i = 0; + for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { + if (metricsManager1001->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger2->atom_matcher_id()) { + break; + } + } + + const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i); + EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns); + EXPECT_EQ(0, activation1001_2->start_ns); + EXPECT_EQ(kNotActive, activation1001_2->state); + // }}}----------------------------------------------------------------------------------- + + // Load saved state from disk. + processor2->LoadActiveConfigsFromDisk(); + + // Metric 1 active; Activation 1 is active, Activation 2 is not active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns); + EXPECT_EQ(kActive, activation1001_1->state); + EXPECT_EQ(0, activation1001_2->start_ns); + EXPECT_EQ(kNotActive, activation1001_2->state); + + EXPECT_TRUE(metricProducer1002->isActive()); + // }}}-------------------------------------------------------------------------------- + + // Trigger Activation 2 for Metric 1. + auto screenOnEvent = + CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON); + processor2->OnLogEvent(screenOnEvent.get()); + + // Metric 1 active; Activation 1 is active, Activation 2 is set to kActiveOnBoot + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns); + EXPECT_EQ(kActive, activation1001_1->state); + EXPECT_EQ(0, activation1001_2->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1001_2->state); + + EXPECT_TRUE(metricProducer1002->isActive()); + // }}}--------------------------------------------------------------------------- + + // Simulate shutdown by saving state to disk + shutDownTime = timeBase2 + 50 * NS_PER_SEC; + processor2->SaveActiveConfigsToDisk(shutDownTime); + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_TRUE(metricProducer1002->isActive()); + ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime; + int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC; + + // Simulate device restarted state by creating new instance of StatsLogProcessor with the + // same config. + long timeBase3 = timeBase2 + 120 * NS_PER_SEC; + sp<StatsLogProcessor> processor3 = + CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor3->mMetricsManagers.size()); + it = processor3->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor3->mMetricsManagers.end()); + auto& metricsManagerTimeBase3 = it->second; + EXPECT_TRUE(metricsManagerTimeBase3->isActive()); + + metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin(); + for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end()); + auto& metricProducerTimeBase3_1 = *metricIt; + EXPECT_FALSE(metricProducerTimeBase3_1->isActive()); + + metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin(); + for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end()); + auto& metricProducerTimeBase3_2 = *metricIt; + EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); + + i = 0; + for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { + if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger1->atom_matcher_id()) { + break; + } + } + const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i); + EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns); + EXPECT_EQ(0, activationTimeBase3_1->start_ns); + EXPECT_EQ(kNotActive, activationTimeBase3_1->state); + + i = 0; + for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { + if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger2->atom_matcher_id()) { + break; + } + } + + const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i); + EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns); + EXPECT_EQ(0, activationTimeBase3_2->start_ns); + EXPECT_EQ(kNotActive, activationTimeBase3_2->state); + + EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); + // }}}---------------------------------------------------------------------------------- + + // Load saved state from disk. + processor3->LoadActiveConfigsFromDisk(); + + // Metric 1 active: Activation 1 is active, Activation 2 is active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducerTimeBase3_1->isActive()); + EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns); + EXPECT_EQ(kActive, activationTimeBase3_1->state); + EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns); + EXPECT_EQ(kActive, activationTimeBase3_2->state); + + EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); + // }}}------------------------------------------------------------------------------- + + // Trigger Activation 2 for Metric 1 again. + screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC, + android::view::DISPLAY_STATE_ON); + processor3->OnLogEvent(screenOnEvent.get()); + + // Metric 1 active; Activation 1 is not active, Activation 2 is set to active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducerTimeBase3_1->isActive()); + EXPECT_EQ(kNotActive, activationTimeBase3_1->state); + EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns); + EXPECT_EQ(kActive, activationTimeBase3_2->state); + + EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); + // }}}--------------------------------------------------------------------------- + + // Simulate shutdown by saving state to disk. + shutDownTime = timeBase3 + 500 * NS_PER_SEC; + processor3->SaveActiveConfigsToDisk(shutDownTime); + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_TRUE(metricProducer1002->isActive()); + ttl1 = timeBase3 + ttl1 - shutDownTime; + ttl2 = timeBase3 + metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime; + + // Simulate device restarted state by creating new instance of StatsLogProcessor with the + // same config. + long timeBase4 = timeBase3 + 600 * NS_PER_SEC; + sp<StatsLogProcessor> processor4 = + CreateStatsLogProcessor(timeBase4, timeBase4, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor4->mMetricsManagers.size()); + it = processor4->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor4->mMetricsManagers.end()); + auto& metricsManagerTimeBase4 = it->second; + EXPECT_TRUE(metricsManagerTimeBase4->isActive()); + + metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin(); + for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end()); + auto& metricProducerTimeBase4_1 = *metricIt; + EXPECT_FALSE(metricProducerTimeBase4_1->isActive()); + + metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin(); + for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end()); + auto& metricProducerTimeBase4_2 = *metricIt; + EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); + + i = 0; + for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) { + if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger1->atom_matcher_id()) { + break; + } + } + const auto& activationTimeBase4_1 = metricProducerTimeBase4_1->mEventActivationMap.at(i); + EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase4_1->ttl_ns); + EXPECT_EQ(0, activationTimeBase4_1->start_ns); + EXPECT_EQ(kNotActive, activationTimeBase4_1->state); + + i = 0; + for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) { + if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger2->atom_matcher_id()) { + break; + } + } + + const auto& activationTimeBase4_2 = metricProducerTimeBase4_1->mEventActivationMap.at(i); + EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase4_2->ttl_ns); + EXPECT_EQ(0, activationTimeBase4_2->start_ns); + EXPECT_EQ(kNotActive, activationTimeBase4_2->state); + + EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); + // }}}---------------------------------------------------------------------------------- + + // Load saved state from disk. + processor4->LoadActiveConfigsFromDisk(); + + // Metric 1 active: Activation 1 is not active, Activation 2 is not active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_FALSE(metricProducerTimeBase4_1->isActive()); + EXPECT_EQ(kNotActive, activationTimeBase4_1->state); + EXPECT_EQ(kNotActive, activationTimeBase4_2->state); + + EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); + // }}}------------------------------------------------------------------------------- +} + +TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes) { + int uid = 1111; + + // Create config with 2 metrics: + // Metric 1: Activate on boot with 2 activations + // Metric 2: Always active + StatsdConfig config1; + config1.set_id(12341); + config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + *config1.add_atom_matcher() = wakelockAcquireMatcher; + *config1.add_atom_matcher() = screenOnMatcher; + + long metricId1 = 1234561; + long metricId2 = 1234562; + + auto countMetric1 = config1.add_count_metric(); + countMetric1->set_id(metricId1); + countMetric1->set_what(wakelockAcquireMatcher.id()); + countMetric1->set_bucket(FIVE_MINUTES); + + auto countMetric2 = config1.add_count_metric(); + countMetric2->set_id(metricId2); + countMetric2->set_what(wakelockAcquireMatcher.id()); + countMetric2->set_bucket(FIVE_MINUTES); + + auto metric1Activation = config1.add_metric_activation(); + metric1Activation->set_metric_id(metricId1); + metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); + auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); + metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric1ActivationTrigger1->set_ttl_seconds(100); + auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); + metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); + metric1ActivationTrigger2->set_ttl_seconds(200); + metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); + + ConfigKey cfgKey1(uid, 12341); + long timeBase1 = 1; + sp<StatsLogProcessor> processor1 = + CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor1->mMetricsManagers.size()); + auto it = processor1->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor1->mMetricsManagers.end()); + auto& metricsManager1 = it->second; + EXPECT_TRUE(metricsManager1->isActive()); + + EXPECT_EQ(metricsManager1->mAllMetricProducers.size(), 2); + // We assume that the index of a MetricProducer within the mAllMetricProducers + // array follows the order in which metrics are added to the config. + auto& metricProducer1_1 = metricsManager1->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer1_1->getMetricId(), metricId1); + EXPECT_FALSE(metricProducer1_1->isActive()); // inactive due to associated MetricActivation + + auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1]; + EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2); + EXPECT_TRUE(metricProducer1_2->isActive()); + + EXPECT_EQ(metricProducer1_1->mEventActivationMap.size(), 2); + // The key in mEventActivationMap is the index of the associated atom matcher. We assume + // that matchers are indexed in the order that they are added to the config. + const auto& activation1_1_1 = metricProducer1_1->mEventActivationMap.at(0); + EXPECT_EQ(100 * NS_PER_SEC, activation1_1_1->ttl_ns); + EXPECT_EQ(0, activation1_1_1->start_ns); + EXPECT_EQ(kNotActive, activation1_1_1->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation1_1_1->activationType); + + const auto& activation1_1_2 = metricProducer1_1->mEventActivationMap.at(1); + EXPECT_EQ(200 * NS_PER_SEC, activation1_1_2->ttl_ns); + EXPECT_EQ(0, activation1_1_2->start_ns); + EXPECT_EQ(kNotActive, activation1_1_2->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1_1_2->activationType); + // }}}------------------------------------------------------------------------------ + + // Trigger Activation 1 for Metric 1 + std::vector<int> attributionUids = {111}; + std::vector<string> attributionTags = {"App1"}; + std::unique_ptr<LogEvent> event = + CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1"); + processor1->OnLogEvent(event.get()); + + // Metric 1 is not active; Activation 1 set to kActiveOnBoot + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_FALSE(metricProducer1_1->isActive()); + EXPECT_EQ(0, activation1_1_1->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1_1_1->state); + EXPECT_EQ(0, activation1_1_2->start_ns); + EXPECT_EQ(kNotActive, activation1_1_2->state); + + EXPECT_TRUE(metricProducer1_2->isActive()); + // }}}----------------------------------------------------------------------------- + + // Simulate shutdown by saving state to disk + int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; + processor1->SaveActiveConfigsToDisk(shutDownTime); + EXPECT_FALSE(metricProducer1_1->isActive()); + + // Simulate device restarted state by creating new instance of StatsLogProcessor with the + // same config. + long timeBase2 = 1000; + sp<StatsLogProcessor> processor2 = + CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor2->mMetricsManagers.size()); + it = processor2->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager2 = it->second; + EXPECT_TRUE(metricsManager2->isActive()); + + EXPECT_EQ(metricsManager2->mAllMetricProducers.size(), 2); + // We assume that the index of a MetricProducer within the mAllMetricProducers + // array follows the order in which metrics are added to the config. + auto& metricProducer2_1 = metricsManager2->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer2_1->getMetricId(), metricId1); + EXPECT_FALSE(metricProducer2_1->isActive()); + + auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1]; + EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2); + EXPECT_TRUE(metricProducer2_2->isActive()); + + EXPECT_EQ(metricProducer2_1->mEventActivationMap.size(), 2); + // The key in mEventActivationMap is the index of the associated atom matcher. We assume + // that matchers are indexed in the order that they are added to the config. + const auto& activation2_1_1 = metricProducer2_1->mEventActivationMap.at(0); + EXPECT_EQ(100 * NS_PER_SEC, activation2_1_1->ttl_ns); + EXPECT_EQ(0, activation2_1_1->start_ns); + EXPECT_EQ(kNotActive, activation2_1_1->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation2_1_1->activationType); + + const auto& activation2_1_2 = metricProducer2_1->mEventActivationMap.at(1); + EXPECT_EQ(200 * NS_PER_SEC, activation2_1_2->ttl_ns); + EXPECT_EQ(0, activation2_1_2->start_ns); + EXPECT_EQ(kNotActive, activation2_1_2->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2_1_2->activationType); + // }}}----------------------------------------------------------------------------------- + + // Load saved state from disk. + processor2->LoadActiveConfigsFromDisk(); + + // Metric 1 active; Activation 1 is active, Activation 2 is not active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducer2_1->isActive()); + int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC; + EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns); + EXPECT_EQ(kActive, activation2_1_1->state); + EXPECT_EQ(0, activation2_1_2->start_ns); + EXPECT_EQ(kNotActive, activation2_1_2->state); + + EXPECT_TRUE(metricProducer2_2->isActive()); + // }}}-------------------------------------------------------------------------------- + + // Trigger Activation 2 for Metric 1. + auto screenOnEvent = + CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON); + processor2->OnLogEvent(screenOnEvent.get()); + + // Metric 1 active; Activation 1 is active, Activation 2 is active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducer2_1->isActive()); + EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns); + EXPECT_EQ(kActive, activation2_1_1->state); + EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation2_1_2->start_ns); + EXPECT_EQ(kActive, activation2_1_2->state); + + EXPECT_TRUE(metricProducer2_2->isActive()); + // }}}--------------------------------------------------------------------------- + + // Simulate shutdown by saving state to disk + shutDownTime = timeBase2 + 50 * NS_PER_SEC; + processor2->SaveActiveConfigsToDisk(shutDownTime); + EXPECT_TRUE(metricProducer2_1->isActive()); + EXPECT_TRUE(metricProducer2_2->isActive()); + ttl1 -= shutDownTime - timeBase2; + int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - + (shutDownTime - screenOnEvent->GetElapsedTimestampNs()); + + // Simulate device restarted state by creating new instance of StatsLogProcessor with the + // same config. + long timeBase3 = timeBase2 + 120 * NS_PER_SEC; + sp<StatsLogProcessor> processor3 = + CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor3->mMetricsManagers.size()); + it = processor3->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor3->mMetricsManagers.end()); + auto& metricsManager3 = it->second; + EXPECT_TRUE(metricsManager3->isActive()); + + EXPECT_EQ(metricsManager3->mAllMetricProducers.size(), 2); + // We assume that the index of a MetricProducer within the mAllMetricProducers + // array follows the order in which metrics are added to the config. + auto& metricProducer3_1 = metricsManager3->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer3_1->getMetricId(), metricId1); + EXPECT_FALSE(metricProducer3_1->isActive()); + + auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1]; + EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2); + EXPECT_TRUE(metricProducer3_2->isActive()); + + EXPECT_EQ(metricProducer3_1->mEventActivationMap.size(), 2); + // The key in mEventActivationMap is the index of the associated atom matcher. We assume + // that matchers are indexed in the order that they are added to the config. + const auto& activation3_1_1 = metricProducer3_1->mEventActivationMap.at(0); + EXPECT_EQ(100 * NS_PER_SEC, activation3_1_1->ttl_ns); + EXPECT_EQ(0, activation3_1_1->start_ns); + EXPECT_EQ(kNotActive, activation3_1_1->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation3_1_1->activationType); + + const auto& activation3_1_2 = metricProducer3_1->mEventActivationMap.at(1); + EXPECT_EQ(200 * NS_PER_SEC, activation3_1_2->ttl_ns); + EXPECT_EQ(0, activation3_1_2->start_ns); + EXPECT_EQ(kNotActive, activation3_1_2->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation3_1_2->activationType); + // }}}---------------------------------------------------------------------------------- + + // Load saved state from disk. + processor3->LoadActiveConfigsFromDisk(); + + // Metric 1 active: Activation 1 is active, Activation 2 is active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducer3_1->isActive()); + EXPECT_EQ(timeBase3 + ttl1 - activation3_1_1->ttl_ns, activation3_1_1->start_ns); + EXPECT_EQ(kActive, activation3_1_1->state); + EXPECT_EQ(timeBase3 + ttl2 - activation3_1_2->ttl_ns, activation3_1_2->start_ns); + EXPECT_EQ(kActive, activation3_1_2->state); + + EXPECT_TRUE(metricProducer3_2->isActive()); + // }}}------------------------------------------------------------------------------- + + // Trigger Activation 2 for Metric 1 again. + screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC, + android::view::DISPLAY_STATE_ON); + processor3->OnLogEvent(screenOnEvent.get()); + + // Metric 1 active; Activation 1 is inactive (above screenOnEvent causes ttl1 to expire), + // Activation 2 is set to active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducer3_1->isActive()); + EXPECT_EQ(kNotActive, activation3_1_1->state); + EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation3_1_2->start_ns); + EXPECT_EQ(kActive, activation3_1_2->state); + + EXPECT_TRUE(metricProducer3_2->isActive()); + // }}}--------------------------------------------------------------------------- +} + +TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { + int uid = 9876; + long configId = 12341; + + // Create config with 3 metrics: + // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate. + // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate. + // Metric 3: Always active + StatsdConfig config1; + config1.set_id(configId); + config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + auto jobStartMatcher = CreateStartScheduledJobAtomMatcher(); + auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher(); + *config1.add_atom_matcher() = wakelockAcquireMatcher; + *config1.add_atom_matcher() = screenOnMatcher; + *config1.add_atom_matcher() = jobStartMatcher; + *config1.add_atom_matcher() = jobFinishMatcher; + + long metricId1 = 1234561; + long metricId2 = 1234562; + long metricId3 = 1234563; + + auto countMetric1 = config1.add_count_metric(); + countMetric1->set_id(metricId1); + countMetric1->set_what(wakelockAcquireMatcher.id()); + countMetric1->set_bucket(FIVE_MINUTES); + + auto countMetric2 = config1.add_count_metric(); + countMetric2->set_id(metricId2); + countMetric2->set_what(wakelockAcquireMatcher.id()); + countMetric2->set_bucket(FIVE_MINUTES); + + auto countMetric3 = config1.add_count_metric(); + countMetric3->set_id(metricId3); + countMetric3->set_what(wakelockAcquireMatcher.id()); + countMetric3->set_bucket(FIVE_MINUTES); + + // Metric 1 activates on boot for wakelock acquire, immediately for screen on. + auto metric1Activation = config1.add_metric_activation(); + metric1Activation->set_metric_id(metricId1); + auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); + metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric1ActivationTrigger1->set_ttl_seconds(100); + metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT); + auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); + metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); + metric1ActivationTrigger2->set_ttl_seconds(200); + metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); + + // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish. + auto metric2Activation = config1.add_metric_activation(); + metric2Activation->set_metric_id(metricId2); + auto metric2ActivationTrigger1 = metric2Activation->add_event_activation(); + metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id()); + metric2ActivationTrigger1->set_ttl_seconds(100); + metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT); + auto metric2ActivationTrigger2 = metric2Activation->add_event_activation(); + metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id()); + metric2ActivationTrigger2->set_ttl_seconds(200); + metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); + + // Send the config. + shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + string serialized = config1.SerializeAsString(); + service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()}); + + // Make sure the config is stored on disk. Otherwise, we will not reset on system server death. + StatsdConfig tmpConfig; + ConfigKey cfgKey1(uid, configId); + EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig)); + + // Metric 1 is not active. + // Metric 2 is not active. + // Metric 3 is active. + // {{{--------------------------------------------------------------------------- + sp<StatsLogProcessor> processor = service->mProcessor; + EXPECT_EQ(1, processor->mMetricsManagers.size()); + auto it = processor->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager1 = it->second; + EXPECT_TRUE(metricsManager1->isActive()); + EXPECT_EQ(3, metricsManager1->mAllMetricProducers.size()); + + auto& metricProducer1 = metricsManager1->mAllMetricProducers[0]; + EXPECT_EQ(metricId1, metricProducer1->getMetricId()); + EXPECT_FALSE(metricProducer1->isActive()); + + auto& metricProducer2 = metricsManager1->mAllMetricProducers[1]; + EXPECT_EQ(metricId2, metricProducer2->getMetricId()); + EXPECT_FALSE(metricProducer2->isActive()); + + auto& metricProducer3 = metricsManager1->mAllMetricProducers[2]; + EXPECT_EQ(metricId3, metricProducer3->getMetricId()); + EXPECT_TRUE(metricProducer3->isActive()); + + // Check event activations. + EXPECT_EQ(metricsManager1->mAllAtomMatchers.size(), 4); + EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(), + metric1ActivationTrigger1->atom_matcher_id()); + const auto& activation1 = metricProducer1->mEventActivationMap.at(0); + EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kNotActive, activation1->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType); + + EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(), + metric1ActivationTrigger2->atom_matcher_id()); + const auto& activation2 = metricProducer1->mEventActivationMap.at(1); + EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); + EXPECT_EQ(0, activation2->start_ns); + EXPECT_EQ(kNotActive, activation2->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType); + + EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(), + metric2ActivationTrigger1->atom_matcher_id()); + const auto& activation3 = metricProducer2->mEventActivationMap.at(2); + EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns); + EXPECT_EQ(0, activation3->start_ns); + EXPECT_EQ(kNotActive, activation3->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType); + + EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(), + metric2ActivationTrigger2->atom_matcher_id()); + const auto& activation4 = metricProducer2->mEventActivationMap.at(3); + EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns); + EXPECT_EQ(0, activation4->start_ns); + EXPECT_EQ(kNotActive, activation4->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType); + // }}}------------------------------------------------------------------------------ + + // Trigger Activation 1 for Metric 1. Should activate on boot. + // Trigger Activation 4 for Metric 2. Should activate immediately. + long configAddedTimeNs = metricsManager1->mLastReportTimeNs; + std::vector<int> attributionUids = {111}; + std::vector<string> attributionTags = {"App1"}; + std::unique_ptr<LogEvent> event1 = CreateAcquireWakelockEvent( + 1 + configAddedTimeNs, attributionUids, attributionTags, "wl1"); + processor->OnLogEvent(event1.get()); + + std::unique_ptr<LogEvent> event2 = CreateFinishScheduledJobEvent( + 2 + configAddedTimeNs, attributionUids, attributionTags, "finish1"); + processor->OnLogEvent(event2.get()); + + // Metric 1 is not active; Activation 1 set to kActiveOnBoot + // Metric 2 is active. Activation 4 set to kActive + // Metric 3 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_FALSE(metricProducer1->isActive()); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1->state); + EXPECT_EQ(0, activation2->start_ns); + EXPECT_EQ(kNotActive, activation2->state); + + EXPECT_TRUE(metricProducer2->isActive()); + EXPECT_EQ(0, activation3->start_ns); + EXPECT_EQ(kNotActive, activation3->state); + EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns); + EXPECT_EQ(kActive, activation4->state); + + EXPECT_TRUE(metricProducer3->isActive()); + // }}}----------------------------------------------------------------------------- + + // Can't fake time with StatsService. + // Lets get a time close to the system server death time and make sure it's sane. + int64_t approximateSystemServerDeath = getElapsedRealtimeNs(); + EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs); + EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs); + + // System server dies. + service->statsCompanionServiceDiedImpl(); + + // We should have a new metrics manager. Lets get it and ensure activation status is restored. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor->mMetricsManagers.size()); + it = processor->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager2 = it->second; + EXPECT_TRUE(metricsManager2->isActive()); + EXPECT_EQ(3, metricsManager2->mAllMetricProducers.size()); + + auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0]; + EXPECT_EQ(metricId1, metricProducer1001->getMetricId()); + EXPECT_FALSE(metricProducer1001->isActive()); + + auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1]; + EXPECT_EQ(metricId2, metricProducer1002->getMetricId()); + EXPECT_TRUE(metricProducer1002->isActive()); + + auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2]; + EXPECT_EQ(metricId3, metricProducer1003->getMetricId()); + EXPECT_TRUE(metricProducer1003->isActive()); + + // Check event activations. + // Activation 1 is kActiveOnBoot. + // Activation 2 and 3 are not active. + // Activation 4 is active. + EXPECT_EQ(metricsManager2->mAllAtomMatchers.size(), 4); + EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(), + metric1ActivationTrigger1->atom_matcher_id()); + const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0); + EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns); + EXPECT_EQ(0, activation1001->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1001->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType); + + EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(), + metric1ActivationTrigger2->atom_matcher_id()); + const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1); + EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns); + EXPECT_EQ(0, activation1002->start_ns); + EXPECT_EQ(kNotActive, activation1002->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType); + + EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(), + metric2ActivationTrigger1->atom_matcher_id()); + const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2); + EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns); + EXPECT_EQ(0, activation1003->start_ns); + EXPECT_EQ(kNotActive, activation1003->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType); + + EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(), + metric2ActivationTrigger2->atom_matcher_id()); + const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3); + EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns); + EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns); + EXPECT_EQ(kActive, activation1004->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType); + // }}}------------------------------------------------------------------------------ + + // Clear the data stored on disk as a result of the system server death. + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey1, configAddedTimeNs + NS_PER_SEC, false, true, ADB_DUMP, FAST, + &buffer); +} #else GTEST_LOG_(INFO) << "This test does nothing.\n"; diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index a49c18fb9857..29005a2070fe 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -39,35 +39,28 @@ using android::util::ProtoReader; const string kApp1 = "app1.sharing.1"; const string kApp2 = "app2.sharing.1"; -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(UidMapTest, TestIsolatedUID) { -// sp<UidMap> m = new UidMap(); -// sp<StatsPullerManager> pullerManager = new StatsPullerManager(); -// sp<AlarmMonitor> anomalyAlarmMonitor; -// sp<AlarmMonitor> subscriberAlarmMonitor; -// // Construct the processor with a dummy sendBroadcast function that does nothing. -// StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, -// [](const ConfigKey& key) { return true; }, -// [](const int&, const vector<int64_t>&) {return true;}); -// LogEvent addEvent(util::ISOLATED_UID_CHANGED, 1); -// addEvent.write(100); // parent UID -// addEvent.write(101); // isolated UID -// addEvent.write(1); // Indicates creation. -// addEvent.init(); -// -// EXPECT_EQ(101, m->getHostUidOrSelf(101)); -// -// p.OnLogEvent(&addEvent); -// EXPECT_EQ(100, m->getHostUidOrSelf(101)); -// -// LogEvent removeEvent(util::ISOLATED_UID_CHANGED, 1); -// removeEvent.write(100); // parent UID -// removeEvent.write(101); // isolated UID -// removeEvent.write(0); // Indicates removal. -// removeEvent.init(); -// p.OnLogEvent(&removeEvent); -// EXPECT_EQ(101, m->getHostUidOrSelf(101)); -//} +TEST(UidMapTest, TestIsolatedUID) { + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + // Construct the processor with a dummy sendBroadcast function that does nothing. + StatsLogProcessor p( + m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, + [](const ConfigKey& key) { return true; }, + [](const int&, const vector<int64_t>&) { return true; }); + + std::unique_ptr<LogEvent> addEvent = CreateIsolatedUidChangedEvent( + 1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 1 /*is_create*/); + EXPECT_EQ(101, m->getHostUidOrSelf(101)); + p.OnLogEvent(addEvent.get()); + EXPECT_EQ(100, m->getHostUidOrSelf(101)); + + std::unique_ptr<LogEvent> removeEvent = CreateIsolatedUidChangedEvent( + 1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 0 /*is_create*/); + p.OnLogEvent(removeEvent.get()); + EXPECT_EQ(101, m->getHostUidOrSelf(101)); +} TEST(UidMapTest, TestMatching) { UidMap m; diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index ae6769ee72ee..c1d4693ce01c 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -1582,9 +1582,9 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - auto it = valueProducer->mCurrentSlicedBucket.begin(); - auto& interval1 = it->second[0]; - auto& baseInfo1 = + const auto& it = valueProducer->mCurrentSlicedBucket.begin(); + ValueMetricProducer::Interval& interval1 = it->second[0]; + ValueMetricProducer::BaseInfo& baseInfo1 = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0]; EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, baseInfo1.hasBase); @@ -1611,16 +1611,9 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { break; } } - // auto itBase = valueProducer->mCurrentBaseInfo.begin(); - // for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) { - // if (itBase != iterBase) { - // break; - // } - // } EXPECT_TRUE(it2 != it); - // EXPECT_TRUE(itBase != iterBase); - auto& interval2 = it2->second[0]; - auto& baseInfo2 = + ValueMetricProducer::Interval& interval2 = it2->second[0]; + ValueMetricProducer::BaseInfo& baseInfo2 = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0]; EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, baseInfo2.hasBase); @@ -1647,23 +1640,28 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - it = valueProducer->mCurrentSlicedBucket.begin(); - it2 = std::next(valueProducer->mCurrentSlicedBucket.begin()); - interval1 = it->second[0]; - interval2 = it2->second[0]; - baseInfo1 = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0]; - baseInfo2 = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0]; - - EXPECT_EQ(true, baseInfo1.hasBase); - EXPECT_EQ(5, baseInfo1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(5, interval1.value.long_value); + // Get new references now that entries have been deleted from the map + const auto& it3 = valueProducer->mCurrentSlicedBucket.begin(); + const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin()); + EXPECT_EQ(it3->second.size(), 1); + EXPECT_EQ(it4->second.size(), 1); + ValueMetricProducer::Interval& interval3 = it3->second[0]; + ValueMetricProducer::Interval& interval4 = it4->second[0]; + ValueMetricProducer::BaseInfo& baseInfo3 = + valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())->second[0]; + ValueMetricProducer::BaseInfo& baseInfo4 = + valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())->second[0]; + + EXPECT_EQ(true, baseInfo3.hasBase); + EXPECT_EQ(5, baseInfo3.base.long_value); + EXPECT_EQ(false, interval3.hasValue); + EXPECT_EQ(5, interval3.value.long_value); EXPECT_EQ(true, valueProducer->mHasGlobalBase); - EXPECT_EQ(true, baseInfo2.hasBase); - EXPECT_EQ(13, baseInfo2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_EQ(8, interval2.value.long_value); + EXPECT_EQ(true, baseInfo4.hasBase); + EXPECT_EQ(13, baseInfo4.base.long_value); + EXPECT_EQ(false, interval4.hasValue); + EXPECT_EQ(8, interval4.value.long_value); EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); } diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp index 4c55683d909c..5eef92e12ba9 100644 --- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp +++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp @@ -12,18 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include <gtest/gtest.h> +#include "src/shell/ShellSubscriber.h" +#include <gtest/gtest.h> +#include <stdio.h> #include <unistd.h> + +#include <vector> + #include "frameworks/base/cmds/statsd/src/atoms.pb.h" #include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h" #include "frameworks/base/cmds/statsd/src/shell/shell_data.pb.h" -#include "src/shell/ShellSubscriber.h" #include "stats_event.h" #include "tests/metrics/metrics_test_helper.h" - -#include <stdio.h> -#include <vector> +#include "tests/statsd_test_util.h" using namespace android::os::statsd; using android::sp; @@ -118,18 +120,9 @@ TEST(ShellSubscriberTest, testPushedSubscription) { vector<std::shared_ptr<LogEvent>> pushedList; // Create the LogEvent from an AStatsEvent - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, 29 /*screen_state_atom_id*/); - AStatsEvent_overwriteTimestamp(statsEvent, 1000); - AStatsEvent_writeInt32(statsEvent, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buffer = AStatsEvent_getBuffer(statsEvent, &size); - std::shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buffer, size); - AStatsEvent_release(statsEvent); - - pushedList.push_back(logEvent); + std::unique_ptr<LogEvent> logEvent = CreateScreenStateChangedEvent( + 1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON); + pushedList.push_back(std::move(logEvent)); // create a simple config to get screen events ShellSubscription config; diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp index b1633c63028d..a0e00954531f 100644 --- a/cmds/statsd/tests/state/StateTracker_test.cpp +++ b/cmds/statsd/tests/state/StateTracker_test.cpp @@ -13,11 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include <gtest/gtest.h> -#include "state/StateManager.h" #include "state/StateTracker.h" -#include "state/StateListener.h" +#include <gtest/gtest.h> + +#include "state/StateListener.h" +#include "state/StateManager.h" +#include "stats_event.h" #include "tests/statsd_test_util.h" #ifdef __ANDROID__ @@ -26,6 +28,8 @@ namespace android { namespace os { namespace statsd { +const int32_t timestampNs = 1000; + /** * Mock StateListener class for testing. * Stores primary key and state pairs. @@ -56,95 +60,49 @@ int getStateInt(StateManager& mgr, int atomId, const HashableDimensionKey& query return output.mValue.int_value; } -// TODO(b/149590301): Update these helpers to use new socket schema. -//// START: build event functions. -//// State with no primary fields - ScreenStateChanged -//std::shared_ptr<LogEvent> buildScreenEvent(int state) { -// std::shared_ptr<LogEvent> event = -// std::make_shared<LogEvent>(util::SCREEN_STATE_CHANGED, 1000 /*timestamp*/); -// event->write((int32_t)state); -// event->init(); -// return event; -//} -// -//// State with one primary field - UidProcessStateChanged -//std::shared_ptr<LogEvent> buildUidProcessEvent(int uid, int state) { -// std::shared_ptr<LogEvent> event = -// std::make_shared<LogEvent>(util::UID_PROCESS_STATE_CHANGED, 1000 /*timestamp*/); -// event->write((int32_t)uid); -// event->write((int32_t)state); -// event->init(); -// return event; -//} -// -//// State with first uid in attribution chain as primary field - WakelockStateChanged -//std::shared_ptr<LogEvent> buildPartialWakelockEvent(int uid, const std::string& tag, bool acquire) { -// std::vector<AttributionNodeInternal> chain; -// chain.push_back(AttributionNodeInternal()); -// AttributionNodeInternal& attr = chain.back(); -// attr.set_uid(uid); -// -// std::shared_ptr<LogEvent> event = -// std::make_shared<LogEvent>(util::WAKELOCK_STATE_CHANGED, 1000 /* timestamp */); -// event->write(chain); -// event->write((int32_t)1); // PARTIAL_WAKE_LOCK -// event->write(tag); -// event->write(acquire ? 1 : 0); -// event->init(); -// return event; -//} -// -//// State with multiple primary fields - OverlayStateChanged -//std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageName, int state) { -// std::shared_ptr<LogEvent> event = -// std::make_shared<LogEvent>(util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/); -// event->write((int32_t)uid); -// event->write(packageName); -// event->write(true); // using_alert_window -// event->write((int32_t)state); -// event->init(); -// return event; -//} -// -//// Incorrect event - missing fields -//std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) { -// std::shared_ptr<LogEvent> event = -// std::make_shared<LogEvent>(util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/); -// event->write((int32_t)uid); -// event->write(packageName); -// event->write((int32_t)state); -// event->init(); -// return event; -//} -// -//// Incorrect event - exclusive state has wrong type -//std::shared_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) { -// std::shared_ptr<LogEvent> event = -// std::make_shared<LogEvent>(util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/); -// event->write((int32_t)uid); -// event->write(packageName); -// event->write(true); -// event->write("string"); // exclusive state: string instead of int -// event->init(); -// return event; -//} -// -//std::shared_ptr<LogEvent> buildBleScanEvent(int uid, bool acquire, bool reset) { -// std::vector<AttributionNodeInternal> chain; -// chain.push_back(AttributionNodeInternal()); -// AttributionNodeInternal& attr = chain.back(); -// attr.set_uid(uid); -// -// std::shared_ptr<LogEvent> event = -// std::make_shared<LogEvent>(util::BLE_SCAN_STATE_CHANGED, 1000); -// event->write(chain); -// event->write(reset ? 2 : acquire ? 1 : 0); // PARTIAL_WAKE_LOCK -// event->write(0); // filtered -// event->write(0); // first match -// event->write(0); // opportunistic -// event->init(); -// return event; -//} +// START: build event functions. +// Incorrect event - missing fields +std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, + int state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, 1000); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeString(statsEvent, packageName.c_str()); + // Missing field 3 - using_alert_window. + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +// Incorrect event - exclusive state has wrong type +std::unique_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, 1000); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeString(statsEvent, packageName.c_str()); + AStatsEvent_writeInt32(statsEvent, true); // using_alert_window + AStatsEvent_writeString(statsEvent, "string"); // exclusive state: string instead of int + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} // END: build event functions. // START: get primary key functions @@ -293,302 +251,323 @@ TEST(StateTrackerTest, TestUnregisterListener) { EXPECT_EQ(0, mgr.getStateTrackersCount()); EXPECT_EQ(-1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED)); } -// TODO(b/149590301): Update these tests to use new socket schema. -///** -// * Test a binary state atom with nested counting. -// * -// * To go from an "ON" state to an "OFF" state with nested counting, we must see -// * an equal number of "OFF" events as "ON" events. -// * For example, ACQUIRE, ACQUIRE, RELEASE will still be in the ACQUIRE state. -// * ACQUIRE, ACQUIRE, RELEASE, RELEASE will be in the RELEASE state. -// */ -//TEST(StateTrackerTest, TestStateChangeNested) { -// sp<TestStateListener> listener = new TestStateListener(); -// StateManager mgr; -// mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener); -// -// std::shared_ptr<LogEvent> event1 = -// buildPartialWakelockEvent(1000 /* uid */, "tag", true /*acquire*/); -// mgr.onLogEvent(*event1); -// EXPECT_EQ(1, listener->updates.size()); -// EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); -// EXPECT_EQ(1, listener->updates[0].mState); -// listener->updates.clear(); -// -// std::shared_ptr<LogEvent> event2 = -// buildPartialWakelockEvent(1000 /* uid */, "tag", true /*acquire*/); -// mgr.onLogEvent(*event2); -// EXPECT_EQ(0, listener->updates.size()); -// -// std::shared_ptr<LogEvent> event3 = -// buildPartialWakelockEvent(1000 /* uid */, "tag", false /*release*/); -// mgr.onLogEvent(*event3); -// EXPECT_EQ(0, listener->updates.size()); -// -// std::shared_ptr<LogEvent> event4 = -// buildPartialWakelockEvent(1000 /* uid */, "tag", false /*release*/); -// mgr.onLogEvent(*event4); -// EXPECT_EQ(1, listener->updates.size()); -// EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); -// EXPECT_EQ(0, listener->updates[0].mState); -//} -// -///** -// * Test a state atom with a reset state. -// * -// * If the reset state value is seen, every state in the map is set to the default -// * state and every listener is notified. -// */ -//TEST(StateTrackerTest, TestStateChangeReset) { -// sp<TestStateListener> listener = new TestStateListener(); -// StateManager mgr; -// mgr.registerListener(util::BLE_SCAN_STATE_CHANGED, listener); -// -// std::shared_ptr<LogEvent> event1 = -// buildBleScanEvent(1000 /* uid */, true /*acquire*/, false /*reset*/); -// mgr.onLogEvent(*event1); -// EXPECT_EQ(1, listener->updates.size()); -// EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); -// EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState); -// listener->updates.clear(); -// -// std::shared_ptr<LogEvent> event2 = -// buildBleScanEvent(2000 /* uid */, true /*acquire*/, false /*reset*/); -// mgr.onLogEvent(*event2); -// EXPECT_EQ(1, listener->updates.size()); -// EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value); -// EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState); -// listener->updates.clear(); -// -// std::shared_ptr<LogEvent> event3 = -// buildBleScanEvent(2000 /* uid */, false /*acquire*/, true /*reset*/); -// mgr.onLogEvent(*event3); -// EXPECT_EQ(2, listener->updates.size()); -// EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[0].mState); -// EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[1].mState); -//} -// -///** -// * Test StateManager's onLogEvent and StateListener's onStateChanged correctly -// * updates listener for states without primary keys. -// */ -//TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) { -// sp<TestStateListener> listener1 = new TestStateListener(); -// StateManager mgr; -// mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); -// -// // log event -// std::shared_ptr<LogEvent> event = -// buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON); -// mgr.onLogEvent(*event); -// -// // check listener was updated -// EXPECT_EQ(1, listener1->updates.size()); -// EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey); -// EXPECT_EQ(2, listener1->updates[0].mState); -// -// // check StateTracker was updated by querying for state -// HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY; -// EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); -//} -// -///** -// * Test StateManager's onLogEvent and StateListener's onStateChanged correctly -// * updates listener for states with one primary key. -// */ -//TEST(StateTrackerTest, TestStateChangeOnePrimaryField) { -// sp<TestStateListener> listener1 = new TestStateListener(); -// StateManager mgr; -// mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener1); -// -// // log event -// std::shared_ptr<LogEvent> event = -// buildUidProcessEvent(1000 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP); -// mgr.onLogEvent(*event); -// -// // check listener was updated -// EXPECT_EQ(1, listener1->updates.size()); -// EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value); -// EXPECT_EQ(1002, listener1->updates[0].mState); -// -// // check StateTracker was updated by querying for state -// HashableDimensionKey queryKey; -// getUidProcessKey(1000 /* uid */, &queryKey); -// EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP, -// getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey)); -//} -// -//TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) { -// sp<TestStateListener> listener1 = new TestStateListener(); -// StateManager mgr; -// mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener1); -// -// // Log event. -// std::shared_ptr<LogEvent> event = -// buildPartialWakelockEvent(1001 /* uid */, "tag1", true /* acquire */); -// mgr.onLogEvent(*event); -// -// EXPECT_EQ(1, mgr.getStateTrackersCount()); -// EXPECT_EQ(1, mgr.getListenersCount(util::WAKELOCK_STATE_CHANGED)); -// -// // Check listener was updated. -// EXPECT_EQ(1, listener1->updates.size()); -// EXPECT_EQ(3, listener1->updates[0].mKey.getValues().size()); -// EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value); -// EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value); -// EXPECT_EQ("tag1", listener1->updates[0].mKey.getValues()[2].mValue.str_value); -// EXPECT_EQ(WakelockStateChanged::ACQUIRE, listener1->updates[0].mState); -// -// // Check StateTracker was updated by querying for state. -// HashableDimensionKey queryKey; -// getPartialWakelockKey(1001 /* uid */, "tag1", &queryKey); -// EXPECT_EQ(WakelockStateChanged::ACQUIRE, -// getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey)); -// -// // No state stored for this query key. -// HashableDimensionKey queryKey2; -// getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2); -// EXPECT_EQ(WakelockStateChanged::RELEASE, -// getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2)); -// -// // Partial query fails. -// HashableDimensionKey queryKey3; -// getPartialWakelockKey(1001 /* uid */, &queryKey3); -// EXPECT_EQ(WakelockStateChanged::RELEASE, -// getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3)); -//} -// -///** -// * Test StateManager's onLogEvent and StateListener's onStateChanged correctly -// * updates listener for states with multiple primary keys. -// */ -//TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) { -// sp<TestStateListener> listener1 = new TestStateListener(); -// StateManager mgr; -// mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1); -// -// // log event -// std::shared_ptr<LogEvent> event = -// buildOverlayEvent(1000 /* uid */, "package1", 1); // state: ENTERED -// mgr.onLogEvent(*event); -// -// // check listener was updated -// EXPECT_EQ(1, listener1->updates.size()); -// EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value); -// EXPECT_EQ(1, listener1->updates[0].mState); -// -// // check StateTracker was updated by querying for state -// HashableDimensionKey queryKey; -// getOverlayKey(1000 /* uid */, "package1", &queryKey); -// EXPECT_EQ(OverlayStateChanged::ENTERED, -// getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey)); -//} -// -///** -// * Test StateManager's onLogEvent and StateListener's onStateChanged -// * when there is an error extracting state from log event. Listener is not -// * updated of state change. -// */ -//TEST(StateTrackerTest, TestStateChangeEventError) { -// sp<TestStateListener> listener1 = new TestStateListener(); -// StateManager mgr; -// mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1); -// -// // log event -// std::shared_ptr<LogEvent> event1 = -// buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */); -// std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2"); -// -// // check listener was updated -// mgr.onLogEvent(*event1); -// EXPECT_EQ(0, listener1->updates.size()); -// mgr.onLogEvent(*event2); -// EXPECT_EQ(0, listener1->updates.size()); -//} -// -//TEST(StateTrackerTest, TestStateQuery) { -// sp<TestStateListener> listener1 = new TestStateListener(); -// sp<TestStateListener> listener2 = new TestStateListener(); -// sp<TestStateListener> listener3 = new TestStateListener(); -// sp<TestStateListener> listener4 = new TestStateListener(); -// StateManager mgr; -// mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); -// mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener2); -// mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener3); -// mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener4); -// -// std::shared_ptr<LogEvent> event1 = buildUidProcessEvent( -// 1000, -// android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 -// std::shared_ptr<LogEvent> event2 = buildUidProcessEvent( -// 1001, -// android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value: -// // 1003 -// std::shared_ptr<LogEvent> event3 = buildUidProcessEvent( -// 1002, -// android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000 -// std::shared_ptr<LogEvent> event4 = buildUidProcessEvent( -// 1001, -// android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 -// std::shared_ptr<LogEvent> event5 = -// buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON); -// std::shared_ptr<LogEvent> event6 = -// buildOverlayEvent(1000, "package1", OverlayStateChanged::ENTERED); -// std::shared_ptr<LogEvent> event7 = -// buildOverlayEvent(1000, "package2", OverlayStateChanged::EXITED); -// std::shared_ptr<LogEvent> event8 = buildPartialWakelockEvent(1005, "tag1", true); -// std::shared_ptr<LogEvent> event9 = buildPartialWakelockEvent(1005, "tag2", false); -// -// mgr.onLogEvent(*event1); -// mgr.onLogEvent(*event2); -// mgr.onLogEvent(*event3); -// mgr.onLogEvent(*event5); -// mgr.onLogEvent(*event5); -// mgr.onLogEvent(*event6); -// mgr.onLogEvent(*event7); -// mgr.onLogEvent(*event8); -// mgr.onLogEvent(*event9); -// -// // Query for UidProcessState of uid 1001 -// HashableDimensionKey queryKey1; -// getUidProcessKey(1001, &queryKey1); -// EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, -// getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1)); -// -// // Query for UidProcessState of uid 1004 - not in state map -// HashableDimensionKey queryKey2; -// getUidProcessKey(1004, &queryKey2); -// EXPECT_EQ(-1, getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, -// queryKey2)); // default state -// -// // Query for UidProcessState of uid 1001 - after change in state -// mgr.onLogEvent(*event4); -// EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP, -// getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1)); -// -// // Query for ScreenState -// EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, -// getStateInt(mgr, util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY)); -// -// // Query for OverlayState of uid 1000, package name "package2" -// HashableDimensionKey queryKey3; -// getOverlayKey(1000, "package2", &queryKey3); -// EXPECT_EQ(OverlayStateChanged::EXITED, -// getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey3)); -// -// // Query for WakelockState of uid 1005, tag 2 -// HashableDimensionKey queryKey4; -// getPartialWakelockKey(1005, "tag2", &queryKey4); -// EXPECT_EQ(WakelockStateChanged::RELEASE, -// getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey4)); -// -// // Query for WakelockState of uid 1005, tag 1 -// HashableDimensionKey queryKey5; -// getPartialWakelockKey(1005, "tag1", &queryKey5); -// EXPECT_EQ(WakelockStateChanged::ACQUIRE, -// getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey5)); -//} + +/** + * Test a binary state atom with nested counting. + * + * To go from an "ON" state to an "OFF" state with nested counting, we must see + * an equal number of "OFF" events as "ON" events. + * For example, ACQUIRE, ACQUIRE, RELEASE will still be in the ACQUIRE state. + * ACQUIRE, ACQUIRE, RELEASE, RELEASE will be in the RELEASE state. + */ +TEST(StateTrackerTest, TestStateChangeNested) { + sp<TestStateListener> listener = new TestStateListener(); + StateManager mgr; + mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener); + + std::vector<int> attributionUids1 = {1000}; + std::vector<string> attributionTags1 = {"tag"}; + + std::unique_ptr<LogEvent> event1 = CreateAcquireWakelockEvent(timestampNs, attributionUids1, + attributionTags1, "wakelockName"); + mgr.onLogEvent(*event1); + EXPECT_EQ(1, listener->updates.size()); + EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); + EXPECT_EQ(1, listener->updates[0].mState); + listener->updates.clear(); + + std::unique_ptr<LogEvent> event2 = CreateAcquireWakelockEvent( + timestampNs + 1000, attributionUids1, attributionTags1, "wakelockName"); + mgr.onLogEvent(*event2); + EXPECT_EQ(0, listener->updates.size()); + + std::unique_ptr<LogEvent> event3 = CreateReleaseWakelockEvent( + timestampNs + 2000, attributionUids1, attributionTags1, "wakelockName"); + mgr.onLogEvent(*event3); + EXPECT_EQ(0, listener->updates.size()); + + std::unique_ptr<LogEvent> event4 = CreateReleaseWakelockEvent( + timestampNs + 3000, attributionUids1, attributionTags1, "wakelockName"); + mgr.onLogEvent(*event4); + EXPECT_EQ(1, listener->updates.size()); + EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); + EXPECT_EQ(0, listener->updates[0].mState); +} + +/** + * Test a state atom with a reset state. + * + * If the reset state value is seen, every state in the map is set to the default + * state and every listener is notified. + */ +TEST(StateTrackerTest, TestStateChangeReset) { + sp<TestStateListener> listener = new TestStateListener(); + StateManager mgr; + mgr.registerListener(util::BLE_SCAN_STATE_CHANGED, listener); + + std::vector<int> attributionUids1 = {1000}; + std::vector<string> attributionTags1 = {"tag1"}; + std::vector<int> attributionUids2 = {2000}; + + std::unique_ptr<LogEvent> event1 = + CreateBleScanStateChangedEvent(timestampNs, attributionUids1, attributionTags1, + BleScanStateChanged::ON, false, false, false); + mgr.onLogEvent(*event1); + EXPECT_EQ(1, listener->updates.size()); + EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value); + EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState); + listener->updates.clear(); + + std::unique_ptr<LogEvent> event2 = + CreateBleScanStateChangedEvent(timestampNs + 1000, attributionUids2, attributionTags1, + BleScanStateChanged::ON, false, false, false); + mgr.onLogEvent(*event2); + EXPECT_EQ(1, listener->updates.size()); + EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value); + EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState); + listener->updates.clear(); + + std::unique_ptr<LogEvent> event3 = + CreateBleScanStateChangedEvent(timestampNs + 2000, attributionUids2, attributionTags1, + BleScanStateChanged::RESET, false, false, false); + mgr.onLogEvent(*event3); + EXPECT_EQ(2, listener->updates.size()); + EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[0].mState); + EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[1].mState); +} + +/** + * Test StateManager's onLogEvent and StateListener's onStateChanged correctly + * updates listener for states without primary keys. + */ +TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) { + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); + + // log event + std::unique_ptr<LogEvent> event = CreateScreenStateChangedEvent( + timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + mgr.onLogEvent(*event); + + // check listener was updated + EXPECT_EQ(1, listener1->updates.size()); + EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey); + EXPECT_EQ(2, listener1->updates[0].mState); + + // check StateTracker was updated by querying for state + HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY; + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey)); +} + +/** + * Test StateManager's onLogEvent and StateListener's onStateChanged correctly + * updates listener for states with one primary key. + */ +TEST(StateTrackerTest, TestStateChangeOnePrimaryField) { + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener1); + + // log event + std::unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent( + timestampNs, 1000 /*uid*/, android::app::ProcessStateEnum::PROCESS_STATE_TOP); + mgr.onLogEvent(*event); + + // check listener was updated + EXPECT_EQ(1, listener1->updates.size()); + EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value); + EXPECT_EQ(1002, listener1->updates[0].mState); + + // check StateTracker was updated by querying for state + HashableDimensionKey queryKey; + getUidProcessKey(1000 /* uid */, &queryKey); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP, + getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey)); +} + +TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) { + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener1); + + // Log event. + std::vector<int> attributionUids = {1001}; + std::vector<string> attributionTags = {"tag1"}; + + std::unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timestampNs, attributionUids, + attributionTags, "wakelockName"); + mgr.onLogEvent(*event); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, mgr.getListenersCount(util::WAKELOCK_STATE_CHANGED)); + + // Check listener was updated. + EXPECT_EQ(1, listener1->updates.size()); + EXPECT_EQ(3, listener1->updates[0].mKey.getValues().size()); + EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value); + EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value); + EXPECT_EQ("wakelockName", listener1->updates[0].mKey.getValues()[2].mValue.str_value); + EXPECT_EQ(WakelockStateChanged::ACQUIRE, listener1->updates[0].mState); + + // Check StateTracker was updated by querying for state. + HashableDimensionKey queryKey; + getPartialWakelockKey(1001 /* uid */, "wakelockName", &queryKey); + EXPECT_EQ(WakelockStateChanged::ACQUIRE, + getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey)); + + // No state stored for this query key. + HashableDimensionKey queryKey2; + getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2); + EXPECT_EQ(WakelockStateChanged::RELEASE, + getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2)); + + // Partial query fails. + HashableDimensionKey queryKey3; + getPartialWakelockKey(1001 /* uid */, &queryKey3); + EXPECT_EQ(WakelockStateChanged::RELEASE, + getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3)); +} + +/** + * Test StateManager's onLogEvent and StateListener's onStateChanged correctly + * updates listener for states with multiple primary keys. + */ +TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) { + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1); + + // log event + std::unique_ptr<LogEvent> event = CreateOverlayStateChangedEvent( + timestampNs, 1000 /* uid */, "package1", true /*using_alert_window*/, + OverlayStateChanged::ENTERED); + mgr.onLogEvent(*event); + + // check listener was updated + EXPECT_EQ(1, listener1->updates.size()); + EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value); + EXPECT_EQ(1, listener1->updates[0].mState); + + // check StateTracker was updated by querying for state + HashableDimensionKey queryKey; + getOverlayKey(1000 /* uid */, "package1", &queryKey); + EXPECT_EQ(OverlayStateChanged::ENTERED, + getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey)); +} + +/** + * Test StateManager's onLogEvent and StateListener's onStateChanged + * when there is an error extracting state from log event. Listener is not + * updated of state change. + */ +TEST(StateTrackerTest, TestStateChangeEventError) { + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1); + + // log event + std::shared_ptr<LogEvent> event1 = + buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */); + std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2"); + + // check listener was updated + mgr.onLogEvent(*event1); + EXPECT_EQ(0, listener1->updates.size()); + mgr.onLogEvent(*event2); + EXPECT_EQ(0, listener1->updates.size()); +} + +TEST(StateTrackerTest, TestStateQuery) { + sp<TestStateListener> listener1 = new TestStateListener(); + sp<TestStateListener> listener2 = new TestStateListener(); + sp<TestStateListener> listener3 = new TestStateListener(); + sp<TestStateListener> listener4 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1); + mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener2); + mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener3); + mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener4); + + std::unique_ptr<LogEvent> event1 = CreateUidProcessStateChangedEvent( + timestampNs, 1000 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 + std::unique_ptr<LogEvent> event2 = CreateUidProcessStateChangedEvent( + timestampNs + 1000, 1001 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value: + // 1003 + std::unique_ptr<LogEvent> event3 = CreateUidProcessStateChangedEvent( + timestampNs + 2000, 1002 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000 + std::unique_ptr<LogEvent> event4 = CreateUidProcessStateChangedEvent( + timestampNs + 3000, 1001 /*uid*/, + android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 + std::unique_ptr<LogEvent> event5 = CreateScreenStateChangedEvent( + timestampNs + 4000, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + std::unique_ptr<LogEvent> event6 = CreateOverlayStateChangedEvent( + timestampNs + 5000, 1000 /*uid*/, "package1", true /*using_alert_window*/, + OverlayStateChanged::ENTERED); + std::unique_ptr<LogEvent> event7 = CreateOverlayStateChangedEvent( + timestampNs + 6000, 1000 /*uid*/, "package2", true /*using_alert_window*/, + OverlayStateChanged::EXITED); + + std::vector<int> attributionUids = {1005}; + std::vector<string> attributionTags = {"tag"}; + + std::unique_ptr<LogEvent> event8 = CreateAcquireWakelockEvent( + timestampNs + 7000, attributionUids, attributionTags, "wakelock1"); + std::unique_ptr<LogEvent> event9 = CreateReleaseWakelockEvent( + timestampNs + 8000, attributionUids, attributionTags, "wakelock2"); + + mgr.onLogEvent(*event1); + mgr.onLogEvent(*event2); + mgr.onLogEvent(*event3); + mgr.onLogEvent(*event5); + mgr.onLogEvent(*event5); + mgr.onLogEvent(*event6); + mgr.onLogEvent(*event7); + mgr.onLogEvent(*event8); + mgr.onLogEvent(*event9); + + // Query for UidProcessState of uid 1001 + HashableDimensionKey queryKey1; + getUidProcessKey(1001, &queryKey1); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, + getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1)); + + // Query for UidProcessState of uid 1004 - not in state map + HashableDimensionKey queryKey2; + getUidProcessKey(1004, &queryKey2); + EXPECT_EQ(-1, getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, + queryKey2)); // default state + + // Query for UidProcessState of uid 1001 - after change in state + mgr.onLogEvent(*event4); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP, + getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1)); + + // Query for ScreenState + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + getStateInt(mgr, util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY)); + + // Query for OverlayState of uid 1000, package name "package2" + HashableDimensionKey queryKey3; + getOverlayKey(1000, "package2", &queryKey3); + EXPECT_EQ(OverlayStateChanged::EXITED, + getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey3)); + + // Query for WakelockState of uid 1005, tag 2 + HashableDimensionKey queryKey4; + getPartialWakelockKey(1005, "wakelock2", &queryKey4); + EXPECT_EQ(WakelockStateChanged::RELEASE, + getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey4)); + + // Query for WakelockState of uid 1005, tag 1 + HashableDimensionKey queryKey5; + getPartialWakelockKey(1005, "wakelock1", &queryKey5); + EXPECT_EQ(WakelockStateChanged::ACQUIRE, + getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey5)); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 050dbf8db7ad..c7838fcddb53 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -600,32 +600,51 @@ std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampN return logEvent; } -//std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent( -// const std::vector<AttributionNodeInternal>& attributions, const string& jobName, -// const ScheduledJobStateChanged::State state, uint64_t timestampNs) { -// auto event = std::make_unique<LogEvent>(util::SCHEDULED_JOB_STATE_CHANGED, timestampNs); -// event->write(attributions); -// event->write(jobName); -// event->write(state); -// event->init(); -// return event; -//} -// -//std::unique_ptr<LogEvent> CreateStartScheduledJobEvent( -// const std::vector<AttributionNodeInternal>& attributions, -// const string& name, uint64_t timestampNs) { -// return CreateScheduledJobStateChangedEvent( -// attributions, name, ScheduledJobStateChanged::STARTED, timestampNs); -//} -// -//// Create log event when scheduled job finishes. -//std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent( -// const std::vector<AttributionNodeInternal>& attributions, -// const string& name, uint64_t timestampNs) { -// return CreateScheduledJobStateChangedEvent( -// attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs); -//} -// +std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent( + const vector<int>& attributionUids, const vector<string>& attributionTags, + const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); + AStatsEvent_writeString(statsEvent, jobName.c_str()); + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& jobName) { + return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, + ScheduledJobStateChanged::STARTED, timestampNs); +} + +// Create log event when scheduled job finishes. +std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& jobName) { + return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName, + ScheduledJobStateChanged::FINISHED, timestampNs); +} + std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(uint64_t timestampNs, const vector<int>& attributionUids, const vector<string>& attributionTags, @@ -833,6 +852,62 @@ std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent( return logEvent; } +std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const BleScanStateChanged::State state, + const bool filtered, const bool firstMatch, + const bool opportunistic) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, util::BLE_SCAN_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_writeInt32(statsEvent, filtered); // filtered + AStatsEvent_writeInt32(statsEvent, firstMatch); // first match + AStatsEvent_writeInt32(statsEvent, opportunistic); // opportunistic + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid, + const string& packageName, + const bool usingAlertWindow, + const OverlayStateChanged::State state) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_writeString(statsEvent, packageName.c_str()); + AStatsEvent_writeInt32(statsEvent, usingAlertWindow); + AStatsEvent_writeInt32(statsEvent, state); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + + std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + return logEvent; +} + sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs, const StatsdConfig& config, const ConfigKey& key, const shared_ptr<IPullAtomCallback>& puller, diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index ead041c29f28..05e1572e3aa9 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -195,14 +195,16 @@ std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level); // Create log event when scheduled job starts. -std::unique_ptr<LogEvent> CreateStartScheduledJobEvent( - const std::vector<AttributionNodeInternal>& attributions, - const string& name, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& jobName); // Create log event when scheduled job finishes. -std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent( - const std::vector<AttributionNodeInternal>& attributions, - const string& name, uint64_t timestampNs); +std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const string& jobName); // Create log event when battery saver starts. std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs); @@ -247,6 +249,18 @@ std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(uint64_t timestampNs, in std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent( uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state); +std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs, + const vector<int>& attributionUids, + const vector<string>& attributionTags, + const BleScanStateChanged::State state, + const bool filtered, const bool firstMatch, + const bool opportunistic); + +std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid, + const string& packageName, + const bool usingAlertWindow, + const OverlayStateChanged::State state); + // Helper function to create an AttributionNodeInternal proto. AttributionNodeInternal CreateAttribution(const int& uid, const string& tag); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index a62f0a6807bb..21b56d3e337f 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; @@ -4973,10 +4974,8 @@ public final class ActivityThread extends ClientTransactionHandler { ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { ActivityClientRecord r = mActivities.get(token); - Class<? extends Activity> activityClass = null; if (localLOGV) Slog.v(TAG, "Performing finish of " + r); if (r != null) { - activityClass = r.activity.getClass(); r.activity.mConfigChangeFlags |= configChanges; if (finishing) { r.activity.mFinished = true; @@ -5029,7 +5028,6 @@ public final class ActivityThread extends ClientTransactionHandler { synchronized (mResourcesManager) { mActivities.remove(token); } - StrictMode.decrementExpectedActivityCount(activityClass); return r; } @@ -5049,6 +5047,7 @@ public final class ActivityThread extends ClientTransactionHandler { ActivityClientRecord r = performDestroyActivity(token, finishing, configChanges, getNonConfigInstance, reason); if (r != null) { + Class<? extends Activity> activityClass = r.activity.getClass(); cleanUpPendingRemoveWindows(r, finishing); WindowManager wm = r.activity.getWindowManager(); View v = r.activity.mDecor; @@ -5073,14 +5072,14 @@ public final class ActivityThread extends ClientTransactionHandler { } if (wtoken != null && r.mPendingRemoveWindow == null) { WindowManagerGlobal.getInstance().closeAll(wtoken, - r.activity.getClass().getName(), "Activity"); + activityClass.getName(), "Activity"); } else if (r.mPendingRemoveWindow != null) { // We're preserving only one window, others should be closed so app views // will be detached before the final tear down. It should be done now because // some components (e.g. WebView) rely on detach callbacks to perform receiver // unregister and other cleanup. WindowManagerGlobal.getInstance().closeAllExceptView(token, v, - r.activity.getClass().getName(), "Activity"); + activityClass.getName(), "Activity"); } r.activity.mDecor = null; } @@ -5092,18 +5091,23 @@ public final class ActivityThread extends ClientTransactionHandler { // about leaking windows, because that is a bug, so if they are // using this recreate facility then they get to live with leaks. WindowManagerGlobal.getInstance().closeAll(token, - r.activity.getClass().getName(), "Activity"); + activityClass.getName(), "Activity"); } // Mocked out contexts won't be participating in the normal // process lifecycle, but if we're running with a proper // ApplicationContext we need to have it tear down things // cleanly. - Context c = r.activity.getBaseContext(); - if (c instanceof ContextImpl) { - ((ContextImpl) c).scheduleFinalCleanup( - r.activity.getClass().getName(), "Activity"); + final ContextImpl impl = ContextImpl.getImpl(r.activity); + if (impl != null) { + impl.scheduleFinalCleanup(activityClass.getName(), "Activity"); } + + r.activity = null; + r.window = null; + r.hideForNow = false; + r.nextIdle = null; + StrictMode.decrementExpectedActivityCount(activityClass); } if (finishing) { try { @@ -5333,10 +5337,6 @@ public final class ActivityThread extends ClientTransactionHandler { handleDestroyActivity(r.token, false, configChanges, true, reason); - r.activity = null; - r.window = null; - r.hideForNow = false; - r.nextIdle = null; // Merge any pending results and pending intents; don't just replace them if (pendingResults != null) { if (r.pendingResults == null) { @@ -7413,8 +7413,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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 26db8f3ecb54..d4749bd8f330 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -8446,7 +8446,9 @@ public class AppOpsManager { /** * Pulls current AppOps access report and picks package and op to watch for next access report - * + * Returns null if no reports were collected since last call. There is no guarantee of report + * collection, hence this method should be called periodically even if no report was collected + * to pick different package and op to watch. * @hide */ @SystemApi diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index 61be01f9f6c0..0ecc003a33bd 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -499,9 +499,11 @@ public final class ApplicationExitInfo implements Parcelable { /** * Return the defining kernel user identifier, maybe different from {@link #getRealUid} and - * {@link #getPackageUid}, if an external service was bound with the flag - * {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here - * will be the kernel user identifier of the external service provider. + * {@link #getPackageUid}, if an external service has the + * {@link android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} set + * to <code>true<code> and was bound with the flag + * {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here will + * be the kernel user identifier of the external service provider. */ public int getDefiningUid() { return mDefiningUid; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 9ccfe8df14c2..17fd4efec9cc 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2414,8 +2414,7 @@ class ContextImpl extends Context { : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; final List<ResourcesLoader> loaders = mResources.getLoaders(); - // TODO(b/128338354): Rename to createTokenResources - return mResourcesManager.createBaseActivityResources(mToken, resDir, splitResDirs, + return mResourcesManager.createBaseTokenResources(mToken, resDir, splitResDirs, overlayDirs, libDirs, displayId, null /* overrideConfig */, compatInfo, mClassLoader, loaders); } @@ -2684,7 +2683,7 @@ class ContextImpl extends Context { // Create the base resources for which all configuration contexts for this Activity // will be rebased upon. - context.setResources(resourcesManager.createBaseActivityResources(activityToken, + context.setResources(resourcesManager.createBaseTokenResources(activityToken, packageInfo.getResDir(), splitDirs, packageInfo.getOverlayDirs(), diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index b8221b4efa2f..7fc10ed090c8 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -99,6 +99,7 @@ interface IActivityManager { void unregisterUidObserver(in IUidObserver observer); boolean isUidActive(int uid, String callingPackage); int getUidProcessState(int uid, in String callingPackage); + boolean isUidActiveOrForeground(int uid, String callingPackage); // =============== End of transactions used on native side as well ============================ // Special low-level communication with activity manager. diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 948546b18473..78d3581a7012 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -48,8 +48,6 @@ interface INotificationManager void clearData(String pkg, int uid, boolean fromApp); void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @nullable ITransientNotificationCallback callback); void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId); - // TODO(b/144152069): Remove this after assessing impact on dogfood. - void enqueueTextOrCustomToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId, boolean isCustom); void cancelToast(String pkg, IBinder token); void finishToken(String pkg, IBinder token); @@ -123,10 +121,14 @@ interface INotificationManager // INotificationListener method. @UnsupportedAppUsage StatusBarNotification[] getActiveNotifications(String callingPkg); + StatusBarNotification[] getActiveNotificationsWithAttribution(String callingPkg, + String callingAttributionTag); @UnsupportedAppUsage StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count, boolean includeSnoozed); + StatusBarNotification[] getHistoricalNotificationsWithAttribution(String callingPkg, + String callingAttributionTag, int count, boolean includeSnoozed); - NotificationHistory getNotificationHistory(String callingPkg); + NotificationHistory getNotificationHistory(String callingPkg, String callingAttributionTag); void registerListener(in INotificationListener listener, in ComponentName component, int userid); void unregisterListener(in INotificationListener listener, int userid); diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index d650801f39ea..10f7835b3d69 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -801,6 +801,11 @@ public final class LoadedApk { makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths); String libraryPermittedPath = mDataDir; + if (mActivityThread == null) { + // In a zygote context where mActivityThread is null we can't access the app data dir + // and including this in libraryPermittedPath would cause SELinux denials. + libraryPermittedPath = ""; + } if (isBundledApp) { // For bundled apps, add the base directory of the app (e.g., diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 811b9c082be2..0e97e3fe06ce 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -29,6 +29,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ParceledListSlice; +import android.content.pm.ShortcutInfo; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Build; @@ -841,7 +842,8 @@ public class NotificationManager { } /** - * Returns the notification channel settings for a given channel and conversation id. + * Returns the notification channel settings for a given channel and + * {@link ShortcutInfo#getId() conversation id}. * * <p>The channel must belong to your package, or to a package you are an approved notification * delegate for (see {@link #canNotifyAsPackage(String)}), or it will not be returned. To query diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 5f756033390b..392c05ac5949 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -695,26 +695,26 @@ public class ResourcesManager { } /** - * Creates base resources for an Activity. Calls to + * Creates base resources for a binder token. Calls to * {@link #getResources(IBinder, String, String[], String[], String[], int, Configuration, - * CompatibilityInfo, ClassLoader, List)} with the same activityToken will have their override + * CompatibilityInfo, ClassLoader, List)} with the same binder token will have their override * configurations merged with the one specified here. * - * @param activityToken Represents an Activity. + * @param token Represents an {@link Activity} or {@link WindowContext}. * @param resDir The base resource path. Can be null (only framework resources will be loaded). * @param splitResDirs An array of split resource paths. Can be null. * @param overlayDirs An array of overlay paths. Can be null. * @param libDirs An array of resource library paths. Can be null. * @param displayId The ID of the display for which to create the resources. * @param overrideConfig The configuration to apply on top of the base configuration. Can be - * null. This provides the base override for this Activity. + * {@code null}. This provides the base override for this token. * @param compatInfo The compatibility settings to use. Cannot be null. A default to use is * {@link CompatibilityInfo#DEFAULT_COMPATIBILITY_INFO}. * @param classLoader The class loader to use when inflating Resources. If null, the * {@link ClassLoader#getSystemClassLoader()} is used. * @return a Resources object from which to access resources. */ - public @Nullable Resources createBaseActivityResources(@NonNull IBinder activityToken, + public @Nullable Resources createBaseTokenResources(@NonNull IBinder token, @Nullable String resDir, @Nullable String[] splitResDirs, @Nullable String[] overlayDirs, @@ -739,24 +739,24 @@ public class ResourcesManager { classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader(); if (DEBUG) { - Slog.d(TAG, "createBaseActivityResources activity=" + activityToken + Slog.d(TAG, "createBaseActivityResources activity=" + token + " with key=" + key); } synchronized (this) { // Force the creation of an ActivityResourcesStruct. - getOrCreateActivityResourcesStructLocked(activityToken); + getOrCreateActivityResourcesStructLocked(token); } // Update any existing Activity Resources references. - updateResourcesForActivity(activityToken, overrideConfig, displayId, + updateResourcesForActivity(token, overrideConfig, displayId, false /* movedToDifferentDisplay */); - cleanupReferences(activityToken); - rebaseKeyForActivity(activityToken, key); + cleanupReferences(token); + rebaseKeyForActivity(token, key); synchronized (this) { - Resources resources = findResourcesForActivityLocked(activityToken, key, + Resources resources = findResourcesForActivityLocked(token, key, classLoader); if (resources != null) { return resources; @@ -764,7 +764,7 @@ public class ResourcesManager { } // Now request an actual Resources object. - return createResources(activityToken, key, classLoader); + return createResources(token, key, classLoader); } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index 6b40890e17c7..37e07de9809a 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -141,6 +141,8 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu public static final int ACTIVITY_TYPE_RECENTS = 3; /** Assistant activity type. */ public static final int ACTIVITY_TYPE_ASSISTANT = 4; + /** Dream activity type. */ + public static final int ACTIVITY_TYPE_DREAM = 5; /** @hide */ @IntDef(prefix = { "ACTIVITY_TYPE_" }, value = { @@ -149,6 +151,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_ASSISTANT, + ACTIVITY_TYPE_DREAM, }) public @interface ActivityType {} @@ -746,9 +749,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu * @hide */ public boolean isAlwaysOnTop() { - return mWindowingMode == WINDOWING_MODE_PINNED || (mAlwaysOnTop == ALWAYS_ON_TOP_ON - && (mWindowingMode == WINDOWING_MODE_FREEFORM - || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW)); + if (mWindowingMode == WINDOWING_MODE_PINNED) return true; + if (mActivityType == ACTIVITY_TYPE_DREAM) return true; + if (mAlwaysOnTop != ALWAYS_ON_TOP_ON) return false; + return mWindowingMode == WINDOWING_MODE_FREEFORM + || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW; } /** @@ -798,7 +803,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu /** @hide */ public static boolean supportSplitScreenWindowingMode(int activityType) { - return activityType != ACTIVITY_TYPE_ASSISTANT; + return activityType != ACTIVITY_TYPE_ASSISTANT && activityType != ACTIVITY_TYPE_DREAM; } /** @hide */ @@ -823,6 +828,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu case ACTIVITY_TYPE_HOME: return "home"; case ACTIVITY_TYPE_RECENTS: return "recents"; case ACTIVITY_TYPE_ASSISTANT: return "assistant"; + case ACTIVITY_TYPE_DREAM: return "dream"; } return String.valueOf(applicationType); } 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/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java index e0674d75974a..fa62a02499e0 100644 --- a/core/java/android/bluetooth/BluetoothHearingAid.java +++ b/core/java/android/bluetooth/BluetoothHearingAid.java @@ -379,6 +379,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + verifyDeviceNotNull(device, "setConnectionPolicy"); final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled() @@ -428,6 +429,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); + verifyDeviceNotNull(device, "getConnectionPolicy"); final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled() @@ -504,6 +506,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getHiSyncId(" + device + ")"); } + verifyDeviceNotNull(device, "getConnectionPolicy"); final IBluetoothHearingAid service = getService(); try { if (service == null) { @@ -577,6 +580,13 @@ public final class BluetoothHearingAid implements BluetoothProfile { return false; } + private void verifyDeviceNotNull(BluetoothDevice device, String methodName) { + if (device == null) { + Log.e(TAG, methodName + ": device param is null"); + throw new IllegalArgumentException("Device cannot be null"); + } + } + private boolean isValidDevice(BluetoothDevice device) { if (device == null) return false; diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 65eb642369c9..31c3232f4714 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -47,6 +47,7 @@ import android.os.Bundle; import android.os.CancellationSignal; import android.os.IBinder; import android.os.ICancellationSignal; +import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteCallback; @@ -303,7 +304,23 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall @Override public void getTypeAsync(Uri uri, RemoteCallback callback) { final Bundle result = new Bundle(); - result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri)); + try { + result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri)); + } catch (Exception e) { + Parcel parcel = Parcel.obtain(); + try { + try { + parcel.writeException(e); + } catch (Exception ex) { + // getType threw an unparcelable exception. Wrap the message into + // a parcelable exception type + parcel.writeException(new IllegalStateException(e.getMessage())); + } + result.putByteArray(ContentResolver.REMOTE_CALLBACK_ERROR, parcel.marshall()); + } finally { + parcel.recycle(); + } + } callback.sendResult(result); } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 7510ce73a59a..59862ae132a9 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -55,6 +55,7 @@ import android.os.DeadObjectException; import android.os.IBinder; import android.os.ICancellationSignal; import android.os.OperationCanceledException; +import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.RemoteCallback; import android.os.RemoteException; @@ -735,6 +736,9 @@ public abstract class ContentResolver implements ContentInterface { /** @hide */ public static final String REMOTE_CALLBACK_RESULT = "result"; + /** @hide */ + public static final String REMOTE_CALLBACK_ERROR = "error"; + /** * How long we wait for an attached process to publish its content providers * before we decide it must be hung. @@ -874,6 +878,9 @@ public abstract class ContentResolver implements ContentInterface { final StringResultListener resultListener = new StringResultListener(); provider.getTypeAsync(url, new RemoteCallback(resultListener)); resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS); + if (resultListener.exception != null) { + throw resultListener.exception; + } return resultListener.result; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity @@ -898,6 +905,9 @@ public abstract class ContentResolver implements ContentInterface { resolveUserId(url), new RemoteCallback(resultListener)); resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS); + if (resultListener.exception != null) { + throw resultListener.exception; + } return resultListener.result; } catch (RemoteException e) { // We just failed to send a oneway request to the System Server. Nothing to do. @@ -915,15 +925,41 @@ public abstract class ContentResolver implements ContentInterface { @GuardedBy("this") public T result; + @GuardedBy("this") + public RuntimeException exception; + @Override public void onResult(Bundle result) { synchronized (this) { - this.result = getResultFromBundle(result); + this.exception = getExceptionFromBundle(result); + if (this.exception == null) { + this.result = getResultFromBundle(result); + } done = true; notifyAll(); } } + private RuntimeException getExceptionFromBundle(Bundle result) { + byte[] bytes = result.getByteArray(REMOTE_CALLBACK_ERROR); + if (bytes == null) { + return null; + } + + Parcel parcel = Parcel.obtain(); + try { + parcel.unmarshall(bytes, 0, bytes.length); + parcel.setDataPosition(0); + parcel.readException(); + } catch (RuntimeException ex) { + return ex; + } finally { + parcel.recycle(); + } + + return null; + } + protected abstract T getResultFromBundle(Bundle result); public void waitForResult(long timeout) { @@ -1250,6 +1286,9 @@ public abstract class ContentResolver implements ContentInterface { provider.canonicalizeAsync(mPackageName, mAttributionTag, url, new RemoteCallback(resultListener)); resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS); + if (resultListener.exception != null) { + throw resultListener.exception; + } return resultListener.result; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 95385ee9b79c..b1d6c830d3b7 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -11215,6 +11215,17 @@ public class Intent implements Parcelable, Cloneable { && hasWebURI(); } + private boolean isImageCaptureIntent() { + return (MediaStore.ACTION_IMAGE_CAPTURE.equals(mAction) + || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(mAction) + || MediaStore.ACTION_VIDEO_CAPTURE.equals(mAction)); + } + + /** @hide */ + public boolean isImplicitImageCaptureIntent() { + return mPackage == null && mComponent == null && isImageCaptureIntent(); + } + /** * @hide */ @@ -11241,9 +11252,7 @@ public class Intent implements Parcelable, Cloneable { } putParcelableArrayListExtra(EXTRA_STREAM, newStreams); } - } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action) - || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action) - || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) { + } else if (isImageCaptureIntent()) { final Uri output = getParcelableExtra(MediaStore.EXTRA_OUTPUT); if (output != null) { putExtra(MediaStore.EXTRA_OUTPUT, maybeAddUserId(output, contentUserHint)); @@ -11349,9 +11358,7 @@ public class Intent implements Parcelable, Cloneable { } } catch (ClassCastException e) { } - } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action) - || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action) - || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) { + } else if (isImageCaptureIntent()) { final Uri output; try { output = getParcelableExtra(MediaStore.EXTRA_OUTPUT); diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 7d5fca44833c..b67060111785 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -752,6 +752,30 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public @interface ApplicationInfoPrivateFlags {} /** + * Constant corresponding to <code>allowed</code> in the + * {@link android.R.attr#autoRevokePermissions} attribute. + * + * @hide + */ + public static final int AUTO_REVOKE_ALLOWED = 0; + + /** + * Constant corresponding to <code>discouraged</code> in the + * {@link android.R.attr#autoRevokePermissions} attribute. + * + * @hide + */ + public static final int AUTO_REVOKE_DISCOURAGED = 1; + + /** + * Constant corresponding to <code>disallowed</code> in the + * {@link android.R.attr#autoRevokePermissions} attribute. + * + * @hide + */ + public static final int AUTO_REVOKE_DISALLOWED = 2; + + /** * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants. * @hide */ 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/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 39f285821581..4c9553249a0c 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -192,9 +192,7 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging); - ParsingPackage setDontAutoRevokePermissions(boolean dontAutoRevokePermissions); - - ParsingPackage setAllowDontAutoRevokePermissions(boolean allowDontAutoRevokePermissions); + ParsingPackage setAutoRevokePermissions(int autoRevokePermissions); ParsingPackage setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage); diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index f2ab60a5a602..079a47056c24 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -30,7 +30,6 @@ import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageParser; -import android.content.pm.ProcessInfo; import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedAttribution; import android.content.pm.parsing.component.ParsedComponent; @@ -405,8 +404,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { private boolean hasFragileUserData; private boolean cantSaveState; private boolean allowNativeHeapPointerTagging; - private boolean dontAutoRevokePermissions; - private boolean allowDontAutoRevokePermissions; + private int autoRevokePermissions; private boolean preserveLegacyExternalStorage; protected int gwpAsanMode; @@ -1089,8 +1087,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeBoolean(this.hasFragileUserData); dest.writeBoolean(this.cantSaveState); dest.writeBoolean(this.allowNativeHeapPointerTagging); - dest.writeBoolean(this.dontAutoRevokePermissions); - dest.writeBoolean(this.allowDontAutoRevokePermissions); + dest.writeInt(this.autoRevokePermissions); dest.writeBoolean(this.preserveLegacyExternalStorage); dest.writeArraySet(this.mimeGroups); dest.writeInt(this.gwpAsanMode); @@ -1249,8 +1246,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.hasFragileUserData = in.readBoolean(); this.cantSaveState = in.readBoolean(); this.allowNativeHeapPointerTagging = in.readBoolean(); - this.dontAutoRevokePermissions = in.readBoolean(); - this.allowDontAutoRevokePermissions = in.readBoolean(); + this.autoRevokePermissions = in.readInt(); this.preserveLegacyExternalStorage = in.readBoolean(); this.mimeGroups = (ArraySet<String>) in.readArraySet(boot); this.gwpAsanMode = in.readInt(); @@ -2026,13 +2022,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override - public boolean isDontAutoRevokePermmissions() { - return dontAutoRevokePermissions; - } - - @Override - public boolean isAllowDontAutoRevokePermmissions() { - return allowDontAutoRevokePermissions; + public int getAutoRevokePermissions() { + return autoRevokePermissions; } @Override @@ -2506,14 +2497,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override - public ParsingPackageImpl setDontAutoRevokePermissions(boolean value) { - dontAutoRevokePermissions = value; - return this; - } - - @Override - public ParsingPackageImpl setAllowDontAutoRevokePermissions(boolean value) { - allowDontAutoRevokePermissions = value; + public ParsingPackageImpl setAutoRevokePermissions(int value) { + autoRevokePermissions = value; return this; } diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java index e700c6a5fd81..687bc235cfa7 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -771,11 +771,7 @@ public interface ParsingPackageRead extends Parcelable { /** @see ApplicationInfo#PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING */ boolean isAllowNativeHeapPointerTagging(); - /** @see ApplicationInfo#PRIVATE_FLAG2_DONT_AUTO_REVOKE_PERMISSIONS */ - boolean isDontAutoRevokePermmissions(); - - /** @see ApplicationInfo#PRIVATE_FLAG2_ALLOW_DONT_AUTO_REVOKE_PERMISSIONS */ - boolean isAllowDontAutoRevokePermmissions(); + int getAutoRevokePermissions(); boolean hasPreserveLegacyExternalStorage(); diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 789904aae6f9..ec771286d980 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -1829,8 +1829,7 @@ public class ParsingPackageUtils { .setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa)) .setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa)) .setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa)) - .setDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_requestAutoRevokePermissionsExemption, sa)) - .setAllowDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_allowAutoRevokePermissionsExemption, sa)) + .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa)) // targetSdkVersion gated .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa)) .setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa)) diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl index 8bcaf828be97..e7219caf6cd8 100644 --- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl +++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl @@ -26,7 +26,9 @@ package android.hardware.biometrics; oneway interface IBiometricServiceReceiverInternal { // Notify BiometricService that authentication was successful. If user confirmation is required, // the auth token must be submitted into KeyStore. - void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token); + // TODO(b/151967372): Strength should be changed to authenticatorId + void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token, + boolean isStrongBiometric); // Notify BiometricService authentication was rejected. void onAuthenticationFailed(); // Notify BiometricService than an error has occured. Forward to the correct receiver depending 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 >= 1.0 >= minZoom</p> * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 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/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java index c31cb4e61b6e..9b3e8c9c137d 100644 --- a/core/java/android/inputmethodservice/InlineSuggestionSession.java +++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java @@ -166,9 +166,14 @@ class InlineSuggestionSession { } return; } - + // The IME doesn't have information about the virtual view id for the child views in the + // web view, so we are only comparing the parent view id here. This means that for cases + // where there are two input fields in the web view, they will have the same view id + // (although different virtual child id), and we will not be able to distinguish them. + final AutofillId imeClientFieldId = mClientAutofillIdSupplier.get(); if (!mComponentName.getPackageName().equals(mClientPackageNameSupplier.get()) - || !fieldId.equalsIgnoreSession(mClientAutofillIdSupplier.get())) { + || imeClientFieldId == null + || fieldId.getViewId() != imeClientFieldId.getViewId()) { if (DEBUG) { Log.d(TAG, "handleOnInlineSuggestionsResponse() called on the wrong package/field " diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 7c34ddcb9287..6daded4ee641 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1249,6 +1249,7 @@ public class InputMethodService extends AbstractInputMethodService { WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false); mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars()); mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM); + mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true); // IME layout should always be inset by navigation bar, no matter its current visibility, // unless automotive requests it, since automotive may hide the navigation bar. diff --git a/core/java/android/net/InvalidPacketException.java b/core/java/android/net/InvalidPacketException.java index b3b0f11a776b..1873d778c0f2 100644 --- a/core/java/android/net/InvalidPacketException.java +++ b/core/java/android/net/InvalidPacketException.java @@ -27,7 +27,7 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi -public class InvalidPacketException extends Exception { +public final class InvalidPacketException extends Exception { private final int mError; // Must match SocketKeepalive#ERROR_INVALID_IP_ADDRESS. diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index fcfcebdf0862..af9414c56654 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -415,6 +415,20 @@ public final class NetworkCapabilities implements Parcelable { | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY); /** + * Capabilities that are allowed for test networks. This list must be set so that it is safe + * for an unprivileged user to create a network with these capabilities via shell. As such, + * it must never contain capabilities that are generally useful to the system, such as + * INTERNET, IMS, SUPL, etc. + */ + private static final long TEST_NETWORKS_ALLOWED_CAPABILITIES = + (1 << NET_CAPABILITY_NOT_METERED) + | (1 << NET_CAPABILITY_NOT_RESTRICTED) + | (1 << NET_CAPABILITY_NOT_VPN) + | (1 << NET_CAPABILITY_NOT_ROAMING) + | (1 << NET_CAPABILITY_NOT_CONGESTED) + | (1 << NET_CAPABILITY_NOT_SUSPENDED); + + /** * Adds the given capability to this {@code NetworkCapability} instance. * Note that when searching for a network to satisfy a request, all capabilities * requested must be satisfied. @@ -646,6 +660,21 @@ public final class NetworkCapabilities implements Parcelable { } /** + * Test networks have strong restrictions on what capabilities they can have. Enforce these + * restrictions. + * @hide + */ + public void restrictCapabilitesForTestNetwork() { + final long originalCapabilities = mNetworkCapabilities; + final NetworkSpecifier originalSpecifier = mNetworkSpecifier; + clearAll(); + // Reset the transports to only contain TRANSPORT_TEST. + mTransportTypes = (1 << TRANSPORT_TEST); + mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES; + mNetworkSpecifier = originalSpecifier; + } + + /** * Representing the transport type. Apps should generally not care about transport. A * request for a fast internet connection could be satisfied by a number of different * transports. If any are specified here it will be satisfied a Network that matches @@ -2000,7 +2029,7 @@ public final class NetworkCapabilities implements Parcelable { */ @SystemApi @TestApi - public static class Builder { + public static final class Builder { private final NetworkCapabilities mCaps; /** diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 785b51d2dee9..cdc00195c169 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -2224,15 +2224,27 @@ public final class PowerManager { * Intent that is broadcast when the state of {@link #isPowerSaveMode()} is about to change. * This broadcast is only sent to registered receivers. * + * @deprecated This is sent at the same time as {@link #ACTION_POWER_SAVE_MODE_CHANGED} so it + * does not provide advanced warning. As such it will be removed in future Android versions. + * Use {@link #ACTION_POWER_SAVE_MODE_CHANGED} and {@link #isPowerSaveMode()} instead. + * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, + publicAlternatives = "Use {@link #ACTION_POWER_SAVE_MODE_CHANGED} instead.") @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + @Deprecated public static final String ACTION_POWER_SAVE_MODE_CHANGING = "android.os.action.POWER_SAVE_MODE_CHANGING"; - /** @hide */ - @UnsupportedAppUsage + /** + * @deprecated Use {@link #isPowerSaveMode()} instead. + * + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, + publicAlternatives = "Use {@link #isPowerSaveMode()} instead.") + @Deprecated public static final String EXTRA_POWER_SAVE_MODE = "mode"; /** diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java index 71f931da1a92..5cc73caa4f60 100644 --- a/core/java/android/os/incremental/V4Signature.java +++ b/core/java/android/os/incremental/V4Signature.java @@ -72,16 +72,16 @@ public class V4Signature { * V4 signature data. */ public static class SigningInfo { - public final byte[] v3Digest; // used to match with the corresponding APK + public final byte[] apkDigest; // used to match with the corresponding APK public final byte[] certificate; // ASN.1 DER form public final byte[] additionalData; // a free-form binary data blob public final byte[] publicKey; // ASN.1 DER, must match the certificate public final int signatureAlgorithmId; // see the APK v2 doc for the list public final byte[] signature; - SigningInfo(byte[] v3Digest, byte[] certificate, byte[] additionalData, + SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData, byte[] publicKey, int signatureAlgorithmId, byte[] signature) { - this.v3Digest = v3Digest; + this.apkDigest = apkDigest; this.certificate = certificate; this.additionalData = additionalData; this.publicKey = publicKey; @@ -94,13 +94,13 @@ public class V4Signature { */ public static SigningInfo fromByteArray(byte[] bytes) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); - byte[] v3Digest = readBytes(buffer); + byte[] apkDigest = readBytes(buffer); byte[] certificate = readBytes(buffer); byte[] additionalData = readBytes(buffer); byte[] publicKey = readBytes(buffer); int signatureAlgorithmId = buffer.getInt(); byte[] signature = readBytes(buffer); - return new SigningInfo(v3Digest, certificate, additionalData, publicKey, + return new SigningInfo(apkDigest, certificate, additionalData, publicKey, signatureAlgorithmId, signature); } } @@ -150,7 +150,7 @@ public class V4Signature { final int size = 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize( hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize( - signingInfo.v3Digest) + bytesSize(signingInfo.certificate) + bytesSize( + signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize( signingInfo.additionalData); ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); buffer.putInt(size); @@ -159,7 +159,7 @@ public class V4Signature { buffer.put(hashingInfo.log2BlockSize); writeBytes(buffer, hashingInfo.salt); writeBytes(buffer, hashingInfo.rawRootHash); - writeBytes(buffer, signingInfo.v3Digest); + writeBytes(buffer, signingInfo.apkDigest); writeBytes(buffer, signingInfo.certificate); writeBytes(buffer, signingInfo.additionalData); return buffer.array(); diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index bbc936d76e1c..99bdfd1fc103 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -194,4 +194,5 @@ interface IStorageManager { boolean needsCheckpoint() = 86; void abortChanges(in String message, boolean retry) = 87; void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88; + void fixupAppDir(in String path) = 89; } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 1454aac66d21..aee32ed769ac 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -162,7 +162,12 @@ public class StorageManager { /** {@hide} */ public static final String PROP_SETTINGS_FUSE = FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.SETTINGS_FUSE_FLAG; - + /** + * Property that determines whether {@link OP_LEGACY_STORAGE} is sticky for + * legacy apps. + * @hide + */ + public static final String PROP_LEGACY_OP_STICKY = "persist.sys.legacy_storage_sticky"; /** {@hide} */ public static final String UUID_PRIVATE_INTERNAL = null; @@ -2470,6 +2475,36 @@ public class StorageManager { } } + /** + * Asks StorageManager to fixup the permissions of an application-private directory. + * + * On devices without sdcardfs, filesystem permissions aren't magically fixed up. This + * is problematic mostly in application-private directories, which are owned by the + * application itself; if another process with elevated permissions creates a file + * in these directories, the UID will be wrong, and the owning package won't be able + * to access the files. + * + * This API can be used to recursively fix up the permissions on the passed in path. + * The default platform user of this API is the DownloadProvider, which can download + * things in application-private directories on their behalf. + * + * This API doesn't require any special permissions, because it merely changes the + * permissions of a directory to what they should anyway be. + * + * @param path the path for which we should fix up the permissions + * + * @hide + */ + public void fixupAppDir(@NonNull File path) { + try { + mStorageManager.fixupAppDir(path.getCanonicalPath()); + } catch (IOException e) { + Log.e(TAG, "Failed to get canonical path for " + path.getPath(), e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** {@hide} */ private static void setCacheBehavior(File path, String name, boolean enabled) throws IOException { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ccc3132c535d..bbcb9d9249af 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6564,13 +6564,6 @@ public final class Settings { * Setting specifying if the accessibility shortcut is enabled. * @hide */ - public static final String ACCESSIBILITY_SHORTCUT_ENABLED = - "accessibility_shortcut_enabled"; - - /** - * Setting specifying if the accessibility shortcut is enabled. - * @hide - */ public static final String ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN = "accessibility_shortcut_on_lock_screen"; @@ -10339,6 +10332,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/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java index a9addba375af..b6a8ced29f72 100644 --- a/core/java/android/service/autofill/InlinePresentation.java +++ b/core/java/android/service/autofill/InlinePresentation.java @@ -49,7 +49,8 @@ public final class InlinePresentation implements Parcelable { private final @NonNull InlinePresentationSpec mInlinePresentationSpec; /** - * Indicates whether the UI should be pinned, hence non-scrollable, in the host. + * Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the + * host. */ private final boolean mPinned; diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 7d070b1d9df6..f8265d6366ab 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -780,15 +780,16 @@ public class AlwaysOnHotwordDetector { audioCapabilities |= AUDIO_CAPABILITY_NOISE_SUPPRESSION; } - int code = STATUS_ERROR; + int code; try { code = mModelManagementService.startRecognition( mKeyphraseMetadata.id, mLocale.toLanguageTag(), mInternalCallback, new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers, recognitionExtra, null /* additional data */, audioCapabilities)); } catch (RemoteException e) { - Slog.w(TAG, "RemoteException in startRecognition!", e); + throw e.rethrowFromSystemServer(); } + if (code != STATUS_OK) { Slog.w(TAG, "startRecognition() failed with error code " + code); } @@ -796,12 +797,12 @@ public class AlwaysOnHotwordDetector { } private int stopRecognitionLocked() { - int code = STATUS_ERROR; + int code; try { code = mModelManagementService.stopRecognition(mKeyphraseMetadata.id, mInternalCallback); } catch (RemoteException e) { - Slog.w(TAG, "RemoteException in stopRecognition!", e); + throw e.rethrowFromSystemServer(); } if (code != STATUS_OK) { @@ -968,12 +969,12 @@ public class AlwaysOnHotwordDetector { } } - ModuleProperties dspModuleProperties = null; + ModuleProperties dspModuleProperties; try { dspModuleProperties = mModelManagementService.getDspModuleProperties(); } catch (RemoteException e) { - Slog.w(TAG, "RemoteException in getDspProperties!", e); + throw e.rethrowFromSystemServer(); } // No DSP available @@ -989,7 +990,7 @@ public class AlwaysOnHotwordDetector { mKeyphraseMetadata = mModelManagementService.getEnrolledKeyphraseMetadata( mText, mLocale.toLanguageTag()); } catch (RemoteException e) { - Slog.w(TAG, "RemoteException in internalUpdateEnrolledKeyphraseMetadata", e); + throw e.rethrowFromSystemServer(); } } } diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index b54e4d9b876d..45d3465fdae8 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -35,6 +35,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; import android.util.ArraySet; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceActionCheckCallback; @@ -47,6 +48,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.Set; /** @@ -63,6 +65,8 @@ import java.util.Set; * separate process from this one. */ public class VoiceInteractionService extends Service { + static final String TAG = VoiceInteractionService.class.getSimpleName(); + /** * The {@link Intent} that must be declared as handled by the service. * To be supported, the service must also require the @@ -240,9 +244,22 @@ public class VoiceInteractionService extends Service { public void onReady() { mSystemService = IVoiceInteractionManagerService.Stub.asInterface( ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); + Objects.requireNonNull(mSystemService); + try { + mSystemService.asBinder().linkToDeath(mDeathRecipient, 0); + } catch (RemoteException e) { + Log.wtf(TAG, "unable to link to death with system service"); + } mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager()); } + private IBinder.DeathRecipient mDeathRecipient = () -> { + Log.e(TAG, "system service binder died shutting down"); + Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage( + VoiceInteractionService::onShutdownInternal, VoiceInteractionService.this)); + }; + + private void onShutdownInternal() { onShutdown(); // Stop any active recognitions when shutting down. @@ -349,16 +366,24 @@ public class VoiceInteractionService extends Service { } private void safelyShutdownHotwordDetector() { - try { - synchronized (mLock) { - if (mHotwordDetector != null) { - mHotwordDetector.stopRecognition(); - mHotwordDetector.invalidate(); - mHotwordDetector = null; - } + synchronized (mLock) { + if (mHotwordDetector == null) { + return; + } + + try { + mHotwordDetector.stopRecognition(); + } catch (Exception ex) { + // Ignore. } - } catch (Exception ex) { - // Ignore. + + try { + mHotwordDetector.invalidate(); + } catch (Exception ex) { + // Ignore. + } + + mHotwordDetector = null; } } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index 04be71f2babc..346fe293d7ae 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -24,6 +24,7 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; +import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4; import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; import android.util.ArrayMap; @@ -117,7 +118,10 @@ public class ApkSignatureSchemeV2Verifier { return vSigner.certs; } - private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity) + /** + * Same as above returns the full signer object, containing additional info e.g. digest. + */ + public static VerifiedSigner verify(String apkFile, boolean verifyIntegrity) throws SignatureNotFoundException, SecurityException, IOException { try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { return verify(apk, verifyIntegrity); @@ -209,9 +213,11 @@ public class ApkSignatureSchemeV2Verifier { verityDigest, apk.length(), signatureInfo); } + byte[] digest = pickBestDigestForV4(contentDigests); + return new VerifiedSigner( signerCerts.toArray(new X509Certificate[signerCerts.size()][]), - verityRootHash); + verityRootHash, digest); } private static X509Certificate[] verifySigner( @@ -426,11 +432,14 @@ public class ApkSignatureSchemeV2Verifier { */ public static class VerifiedSigner { public final X509Certificate[][] certs; + public final byte[] verityRootHash; + public final byte[] digest; - public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) { + public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) { this.certs = certs; this.verityRootHash = verityRootHash; + this.digest = digest; } } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index 2437af26770b..4ab541b616ed 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -16,8 +16,6 @@ package android.util.apk; -import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256; -import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm; @@ -26,6 +24,7 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; +import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4; import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; import android.os.Build; @@ -213,24 +212,11 @@ public class ApkSignatureSchemeV3Verifier { verityDigest, apk.length(), signatureInfo); } - result.digest = pickBestV3DigestForV4(contentDigests); + result.digest = pickBestDigestForV4(contentDigests); return result; } - // Keep in sync with pickBestV3DigestForV4 in apksigner.V3SchemeVerifier. - private static byte[] pickBestV3DigestForV4(Map<Integer, byte[]> contentDigests) { - final int[] orderedContentDigestTypes = - {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256, - CONTENT_DIGEST_CHUNKED_SHA256}; - for (int contentDigestType : orderedContentDigestTypes) { - if (contentDigests.containsKey(contentDigestType)) { - return contentDigests.get(contentDigestType); - } - } - return null; - } - private static VerifiedSigner verifySigner( ByteBuffer signerBlock, Map<Integer, byte[]> contentDigests, diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java index 8c240d99f590..d40efce0b3b3 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java @@ -145,7 +145,7 @@ public class ApkSignatureSchemeV4Verifier { "Public key mismatch between certificate and signature record"); } - return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.v3Digest); + return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest); } /** @@ -155,11 +155,11 @@ public class ApkSignatureSchemeV4Verifier { */ public static class VerifiedSigner { public final Certificate[] certs; - public byte[] v3Digest; + public byte[] apkDigest; - public VerifiedSigner(Certificate[] certs, byte[] v3Digest) { + public VerifiedSigner(Certificate[] certs, byte[] apkDigest) { this.certs = certs; - this.v3Digest = v3Digest; + this.apkDigest = apkDigest; } } diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index c1cee48cc663..ab8f80d3d1a5 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -184,27 +184,45 @@ public class ApkSignatureVerifier { Signature[] signerSigs = convertToSignatures(signerCerts); if (verifyFull) { - // v4 is an add-on and requires v3 signature to validate against its certificates - ApkSignatureSchemeV3Verifier.VerifiedSigner nonstreaming = - ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath); - Certificate[][] nonstreamingCerts = new Certificate[][]{nonstreaming.certs}; - Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts); + byte[] nonstreamingDigest = null; + Certificate[][] nonstreamingCerts = null; + + try { + // v4 is an add-on and requires v2 or v3 signature to validate against its + // certificate and digest + ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer = + ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath); + nonstreamingDigest = v3Signer.digest; + nonstreamingCerts = new Certificate[][]{v3Signer.certs}; + } catch (SignatureNotFoundException e) { + try { + ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer = + ApkSignatureSchemeV2Verifier.verify(apkPath, false); + nonstreamingDigest = v2Signer.digest; + nonstreamingCerts = v2Signer.certs; + } catch (SignatureNotFoundException ee) { + throw new SecurityException( + "V4 verification failed to collect V2/V3 certificates from : " + + apkPath, ee); + } + } + Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts); if (nonstreamingSigs.length != signerSigs.length) { throw new SecurityException( - "Invalid number of certificates: " + nonstreaming.certs.length); + "Invalid number of certificates: " + nonstreamingSigs.length); } for (int i = 0, size = signerSigs.length; i < size; ++i) { if (!nonstreamingSigs[i].equals(signerSigs[i])) { - throw new SecurityException("V4 signature certificate does not match V3"); + throw new SecurityException( + "V4 signature certificate does not match V2/V3"); } } - // TODO(b/151240006): add support for v2 digest and make it mandatory. - if (!ArrayUtils.isEmpty(vSigner.v3Digest) && !ArrayUtils.equals(vSigner.v3Digest, - nonstreaming.digest, vSigner.v3Digest.length)) { - throw new SecurityException("V3 digest in V4 signature does not match V3"); + if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest, + vSigner.apkDigest.length)) { + throw new SecurityException("APK digest in V4 signature does not match V2/V3"); } } diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java index 4fe8515143a0..2a4b65d23e64 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -421,6 +421,10 @@ final class ApkSigningBlockUtils { static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2; static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3; + private static final int[] V4_CONTENT_DIGEST_ALGORITHMS = + {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256, + CONTENT_DIGEST_CHUNKED_SHA256}; + static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) { int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1); int digestAlgorithm2 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm2); @@ -572,6 +576,21 @@ final class ApkSigningBlockUtils { } /** + * Returns the best digest from the map of available digests. + * similarly to compareContentDigestAlgorithm. + * + * Keep in sync with pickBestDigestForV4 in apksigner's ApkSigningBlockUtils. + */ + static byte[] pickBestDigestForV4(Map<Integer, byte[]> contentDigests) { + for (int algo : V4_CONTENT_DIGEST_ALGORITHMS) { + if (contentDigests.containsKey(algo)) { + return contentDigests.get(algo); + } + } + return null; + } + + /** * Returns new byte buffer whose content is a shared subsequence of this buffer's content * between the specified start (inclusive) and end (exclusive) positions. As opposed to * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 43f80f1490ad..e6bd84391fef 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -17,6 +17,7 @@ package android.view; import static android.view.InsetsState.ITYPE_IME; +import static android.view.InsetsState.toInternalType; import static android.view.InsetsState.toPublicType; import static android.view.WindowInsets.Type.all; import static android.view.WindowInsets.Type.ime; @@ -26,8 +27,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONT import android.animation.AnimationHandler; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; import android.animation.TypeEvaluator; import android.animation.ValueAnimator; import android.annotation.IntDef; @@ -38,11 +37,9 @@ import android.graphics.Rect; import android.os.CancellationSignal; import android.os.Handler; import android.os.RemoteException; -import android.renderscript.Sampler.Value; import android.util.ArraySet; import android.util.Log; import android.util.Pair; -import android.util.Property; import android.util.SparseArray; import android.view.InsetsSourceConsumer.ShowResult; import android.view.InsetsState.InternalInsetsType; @@ -467,7 +464,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (!localStateChanged && mLastDispachedState.equals(state)) { return false; } - mState.set(state); + updateState(state); mLastDispachedState.set(state, true /* copySources */); applyLocalVisibilityOverride(); if (localStateChanged) { @@ -479,6 +476,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return true; } + private void updateState(InsetsState newState) { + mState.setDisplayFrame(newState.getDisplayFrame()); + for (int i = newState.getSourcesCount() - 1; i >= 0; i--) { + InsetsSource source = newState.sourceAt(i); + getSourceConsumer(source.getType()).updateSource(source); + } + for (int i = mState.getSourcesCount() - 1; i >= 0; i--) { + InsetsSource source = mState.sourceAt(i); + if (newState.peekSource(source.getType()) == null) { + mState.removeSource(source.getType()); + } + } + } + /** * @see InsetsState#calculateInsets */ @@ -858,8 +869,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation control.cancel(); } for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { - if (mRunningAnimations.get(i).runner == control) { + RunningAnimation runningAnimation = mRunningAnimations.get(i); + if (runningAnimation.runner == control) { mRunningAnimations.remove(i); + ArraySet<Integer> types = toInternalType(control.getTypes()); + for (int j = types.size() - 1; j >= 0; j--) { + if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) { + mViewRoot.notifyInsetsChanged(); + } + } break; } } diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 332573449e18..f36621c0a49e 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -16,11 +16,13 @@ package android.view; +import static android.view.InsetsController.ANIMATION_TYPE_NONE; import static android.view.InsetsController.AnimationType; import static android.view.InsetsState.toPublicType; import android.annotation.IntDef; import android.annotation.Nullable; +import android.graphics.Rect; import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl.Transaction; import android.view.WindowInsets.Type.InsetsType; @@ -64,6 +66,8 @@ public class InsetsSourceConsumer { private final Supplier<Transaction> mTransactionSupplier; private @Nullable InsetsSourceControl mSourceControl; private boolean mHasWindowFocus; + private Rect mPendingFrame; + private Rect mPendingVisibleFrame; public InsetsSourceConsumer(@InternalInsetsType int type, InsetsState state, Supplier<Transaction> transactionSupplier, InsetsController controller) { @@ -215,6 +219,38 @@ public class InsetsSourceConsumer { // no-op for types that always return ShowResult#SHOW_IMMEDIATELY. } + void updateSource(InsetsSource newSource) { + InsetsSource source = mState.peekSource(mType); + if (source == null || mController.getAnimationType(mType) == ANIMATION_TYPE_NONE + || source.getFrame().equals(newSource.getFrame())) { + mState.addSource(newSource); + return; + } + + // Frame is changing while animating. Keep note of the new frame but keep existing frame + // until animaition is finished. + newSource = new InsetsSource(newSource); + mPendingFrame = new Rect(newSource.getFrame()); + mPendingVisibleFrame = newSource.getVisibleFrame() != null + ? new Rect(newSource.getVisibleFrame()) + : null; + newSource.setFrame(source.getFrame()); + newSource.setVisibleFrame(source.getVisibleFrame()); + mState.addSource(newSource); + } + + boolean notifyAnimationFinished() { + if (mPendingFrame != null) { + InsetsSource source = mState.getSource(mType); + source.setFrame(mPendingFrame); + source.setVisibleFrame(mPendingVisibleFrame); + mPendingFrame = null; + mPendingVisibleFrame = null; + return true; + } + return false; + } + /** * Sets requested visibility from the client, regardless of whether we are able to control it at * the moment. diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index cd22ad6151b8..fe70ff7a1dbf 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -192,6 +192,7 @@ public class SurfaceControlViewHost { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); + lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; setView(view, lp); } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 59fc6e9b5ede..c89e0c9fc60e 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -176,6 +176,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall boolean mUseAlpha = false; float mSurfaceAlpha = 1f; boolean mClipSurfaceToBounds; + int mBackgroundColor = Color.BLACK; @UnsupportedAppUsage boolean mHaveFrame = false; @@ -828,6 +829,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } } + private Transaction updateBackgroundColor(Transaction t) { + final float[] colorComponents = new float[] { Color.red(mBackgroundColor) / 255.f, + Color.green(mBackgroundColor) / 255.f, Color.blue(mBackgroundColor) / 255.f }; + t.setColor(mBackgroundControl, colorComponents); + return t; + } private void releaseSurfaces() { mSurfaceAlpha = 1f; @@ -1000,6 +1007,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } updateBackgroundVisibility(mTmpTransaction); + updateBackgroundColor(mTmpTransaction); if (mUseAlpha) { mTmpTransaction.setAlpha(mSurfaceControl, alpha); mSurfaceAlpha = alpha; @@ -1399,10 +1407,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall return; } - final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f, - Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f }; - - mTmpTransaction.setColor(mBackgroundControl, colorComponents).apply(); + mBackgroundColor = bgColor; + updateBackgroundColor(mTmpTransaction).apply(); } @UnsupportedAppUsage diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 4aeea10eee68..62dd192a6d67 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -405,6 +405,13 @@ public class Editor { // The actual zoom value may changes based on this initial zoom value. private float mInitialZoom = 1f; + // For calculating the line change slops while moving cursor/selection. + // The slop max/min value include line height and the slop on the upper/lower line. + private static final int LINE_CHANGE_SLOP_MAX_DP = 45; + private static final int LINE_CHANGE_SLOP_MIN_DP = 12; + private int mLineChangeSlopMax; + private int mLineChangeSlopMin; + Editor(TextView textView) { mTextView = textView; // Synchronize the filter list, which places the undo input filter at the end. @@ -430,6 +437,14 @@ public class Editor { logCursor("Editor", "New magnifier is %s.", mNewMagnifierEnabled ? "enabled" : "disabled"); } + + mLineChangeSlopMax = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, LINE_CHANGE_SLOP_MAX_DP, + mTextView.getContext().getResources().getDisplayMetrics()); + mLineChangeSlopMin = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, LINE_CHANGE_SLOP_MIN_DP, + mTextView.getContext().getResources().getDisplayMetrics()); + } @VisibleForTesting @@ -6018,7 +6033,14 @@ public class Editor { } } - private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) { + @VisibleForTesting + public void setLineChangeSlopMinMaxForTesting(final int min, final int max) { + mLineChangeSlopMin = min; + mLineChangeSlopMax = max; + } + + @VisibleForTesting + public int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) { final int trueLine = mTextView.getLineAtCoordinate(y); if (layout == null || prevLine > layout.getLineCount() || layout.getLineCount() <= 0 || prevLine < 0) { @@ -6031,28 +6053,21 @@ public class Editor { return trueLine; } + final int lineHeight = layout.getLineBottom(prevLine) - layout.getLineTop(prevLine); + int slop = (int)(LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS + * (layout.getLineBottom(trueLine) - layout.getLineTop(trueLine))); + slop = Math.max(mLineChangeSlopMin, + Math.min(mLineChangeSlopMax, lineHeight + slop)) - lineHeight; + slop = Math.max(0, slop); + final float verticalOffset = mTextView.viewportToContentVerticalOffset(); - final int lineCount = layout.getLineCount(); - final float slop = mTextView.getLineHeight() * LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS; - - final float firstLineTop = layout.getLineTop(0) + verticalOffset; - final float prevLineTop = layout.getLineTop(prevLine) + verticalOffset; - final float yTopBound = Math.max(prevLineTop - slop, firstLineTop + slop); - - final float lastLineBottom = layout.getLineBottom(lineCount - 1) + verticalOffset; - final float prevLineBottom = layout.getLineBottom(prevLine) + verticalOffset; - final float yBottomBound = Math.min(prevLineBottom + slop, lastLineBottom - slop); - - // Determine if we've moved lines based on y position and previous line. - int currLine; - if (y <= yTopBound) { - currLine = Math.max(prevLine - 1, 0); - } else if (y >= yBottomBound) { - currLine = Math.min(prevLine + 1, lineCount - 1); - } else { - currLine = prevLine; + if (trueLine > prevLine && y >= layout.getLineBottom(prevLine) + slop + verticalOffset) { + return trueLine; + } + if (trueLine < prevLine && y <= layout.getLineTop(prevLine) - slop + verticalOffset) { + return trueLine; } - return currLine; + return prevLine; } /** diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index 7d97a91b7435..4f14539dd976 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -144,9 +144,6 @@ public class Toast { @Nullable private CharSequence mText; - // TODO(b/144152069): Remove this after assessing impact on dogfood. - private boolean mIsCustomToast; - /** * Construct an empty Toast object. You must call {@link #setView} before you * can call {@link #show}. @@ -214,8 +211,7 @@ public class Toast { service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback); } } else { - service.enqueueTextOrCustomToast(pkg, mToken, tn, mDuration, displayId, - mIsCustomToast); + service.enqueueToast(pkg, mToken, tn, mDuration, displayId); } } catch (RemoteException e) { // Empty @@ -253,7 +249,6 @@ public class Toast { */ @Deprecated public void setView(View view) { - mIsCustomToast = true; mNextView = view; } @@ -755,8 +750,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/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index 54ea57a6cae4..d64b5f1118dc 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -144,9 +144,6 @@ public class AccessibilityShortcutController { Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE), false, co, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED), - false, co, UserHandle.USER_ALL); - mContext.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN), false, co, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( @@ -174,8 +171,6 @@ public class AccessibilityShortcutController { public void onSettingsChanged() { final boolean hasShortcutTarget = hasShortcutTarget(); final ContentResolver cr = mContext.getContentResolver(); - final boolean enabled = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 1, mUserId) == 1; // Enable the shortcut from the lockscreen by default if the dialog has been shown final int dialogAlreadyShown = Settings.Secure.getIntForUser( cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, DialogStaus.NOT_SHOWN, @@ -183,7 +178,7 @@ public class AccessibilityShortcutController { mEnabledOnLockScreen = Settings.Secure.getIntForUser( cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, dialogAlreadyShown, mUserId) == 1; - mIsShortcutEnabled = enabled && hasShortcutTarget; + mIsShortcutEnabled = hasShortcutTarget; } /** diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index c83cab77dd13..31527e8dbe5d 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -227,7 +227,7 @@ public class ResolverDrawerLayout extends ViewGroup { } final int oldCollapsibleHeight = mCollapsibleHeight; - mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight()); + mCollapsibleHeight = Math.min(mCollapsibleHeight, getMaxCollapsedHeight()); if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) { return; diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index ed2c5b2b4930..ab57e3dcdc53 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2665,4 +2665,8 @@ enum PageId { // Open: Settings > Sound > Do Not Disturb > People > Messages // OS: R DND_MESSAGES = 1839; + + // Open: Settings > Sound > Do Not Disturb > Apps > <Choose App> + // OS: R + DND_APPS_BYPASSING = 1840; } diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index d6e200afc029..28eb98b07690 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1827,19 +1827,11 @@ <attr name="gwpAsanMode" /> - <!-- If {@code true} allow requesting that its permissions don't get automatically - revoked when the app is unused for an extended amount of time. - - The default value is {@code false}. --> - <attr name="requestAutoRevokePermissionsExemption" format="boolean" /> - - <!-- If {@code true} its permissions shouldn't get automatically - revoked when the app is unused for an extended amount of time. - - This implies {@code requestDontAutoRevokePermissions=true} - - The default value is {@code false}. --> - <attr name="allowAutoRevokePermissionsExemption" format="boolean" /> + <attr name="autoRevokePermissions"> + <enum name="allowed" value="0" /> + <enum name="discouraged" value="1" /> + <enum name="disallowed" value="2" /> + </attr> </declare-styleable> <!-- An attribution is a logical part of an app and is identified by a tag. diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index f02d54f48d29..e694e160009e 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3014,8 +3014,7 @@ <!-- @hide @SystemApi --> <public name="minExtensionVersion" /> <public name="allowNativeHeapPointerTagging" /> - <public name="requestAutoRevokePermissionsExemption" /> - <public name="allowAutoRevokePermissionsExemption" /> + <public name="autoRevokePermissions" /> <public name="preserveLegacyExternalStorage" /> <public name="mimeGroup" /> <public name="gwpAsanMode" /> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 2ef0c927cc61..88f9fc2199e5 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -702,7 +702,7 @@ please see themes_device_defaults.xml. </style> <style name="Theme.Dream"> - <item name="windowBackground">@null</item> + <item name="windowBackground">@color/black</item> <item name="windowDisablePreview">true</item> </style> diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java index 1737bd0fa20b..57e5dd8f8f20 100644 --- a/core/tests/coretests/src/android/content/ContentResolverTest.java +++ b/core/tests/coretests/src/android/content/ContentResolverTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -236,6 +237,16 @@ public class ContentResolverTest { } @Test + public void testGetType_providerException() { + try { + mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote/error")); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected + } + } + + @Test public void testCanonicalize() { Uri canonical = mResolver.canonicalize( Uri.parse("content://android.content.FakeProviderRemote/something")); diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java index 1d7ba5d9be46..a32009493094 100644 --- a/core/tests/coretests/src/android/content/FakeProviderRemote.java +++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java @@ -37,6 +37,9 @@ public class FakeProviderRemote extends ContentProvider { @Override public String getType(Uri uri) { + if (uri.getPath() != null && uri.getPath().contains("error")) { + throw new IllegalArgumentException("Expected exception"); + } return "fake/remote"; } diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java index df5c9d2df90b..4114b28a7252 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java @@ -184,7 +184,7 @@ public class ResourcesManagerTest extends TestCase { @SmallTest public void testThemesGetUpdatedWithNewImpl() { Binder activity1 = new Binder(); - Resources resources1 = mResourcesManager.createBaseActivityResources( + Resources resources1 = mResourcesManager.createBaseTokenResources( activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); @@ -217,7 +217,7 @@ public class ResourcesManagerTest extends TestCase { // Create a Resources for the Activity. Configuration config1 = new Configuration(); config1.densityDpi = 280; - Resources resources1 = mResourcesManager.createBaseActivityResources( + Resources resources1 = mResourcesManager.createBaseTokenResources( activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config1, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 34a1016f0ae9..efdb51dcdfa9 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -31,6 +31,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -638,6 +639,32 @@ public class InsetsControllerTest { }); } + @Test + public void testFrameUpdateDuringAnimation() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + + mController.onControlsChanged(createSingletonControl(ITYPE_IME)); + + // Pretend IME is calling + mController.show(ime(), true /* fromIme */); + + InsetsState copy = new InsetsState(mController.getState(), true /* copySources */); + copy.getSource(ITYPE_IME).setFrame(0, 1, 2, 3); + copy.getSource(ITYPE_IME).setVisibleFrame(new Rect(4, 5, 6, 7)); + mController.onStateChanged(copy); + assertNotEquals(new Rect(0, 1, 2, 3), + mController.getState().getSource(ITYPE_IME).getFrame()); + assertNotEquals(new Rect(4, 5, 6, 7), + mController.getState().getSource(ITYPE_IME).getVisibleFrame()); + mController.cancelExistingAnimation(); + assertEquals(new Rect(0, 1, 2, 3), + mController.getState().getSource(ITYPE_IME).getFrame()); + assertEquals(new Rect(4, 5, 6, 7), + mController.getState().getSource(ITYPE_IME).getVisibleFrame()); + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + private void waitUntilNextFrame() throws Exception { final CountDownLatch latch = new CountDownLatch(1); Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT, diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java index ba27fac05a99..eae1bbc930d4 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java @@ -15,11 +15,12 @@ */ package android.view.contentcapture; +import static org.mockito.Mockito.mock; import static org.testng.Assert.assertThrows; +import android.content.ContentCaptureOptions; import android.content.Context; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -37,16 +38,20 @@ public class ContentCaptureManagerTest { @Mock private Context mMockContext; - private ContentCaptureManager mManager; - - @Before - public void before() { - mManager = new ContentCaptureManager(mMockContext, /* service= */ null, - /* options= */ null); + @Test + public void testConstructor_invalidParametersThrowsException() { + assertThrows(NullPointerException.class, + () -> new ContentCaptureManager(mMockContext, /* service= */ null, /* options= */ + null)); } @Test - public void testRemoveData_invalid() { - assertThrows(NullPointerException.class, () -> mManager.removeData(null)); + public void testRemoveData_invalidParametersThrowsException() { + final IContentCaptureManager mockService = mock(IContentCaptureManager.class); + final ContentCaptureOptions options = new ContentCaptureOptions(null); + final ContentCaptureManager manager = + new ContentCaptureManager(mMockContext, mockService, options); + + assertThrows(NullPointerException.class, () -> manager.removeData(null)); } } diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java index f81964c9cdf9..1b5ce8fd4ce5 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java @@ -16,6 +16,7 @@ package android.widget; +import static android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE; import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; import static android.widget.espresso.TextViewActions.dragOnText; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex; @@ -37,6 +38,9 @@ import android.app.Activity; import android.app.Instrumentation; import android.graphics.Rect; import android.text.Layout; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.AbsoluteSizeSpan; import android.util.ArraySet; import android.util.Log; import android.view.InputDevice; @@ -489,6 +493,38 @@ public class EditorCursorDragTest { } @Test + public void testLineChangeSlop() throws Throwable { + TextView tv = mActivity.findViewById(R.id.textview); + Spannable s = new SpannableString("a\nb\nc"); + s.setSpan(new AbsoluteSizeSpan(10), 2, 4, SPAN_INCLUSIVE_EXCLUSIVE); + s.setSpan(new AbsoluteSizeSpan(32), 4, 5, SPAN_INCLUSIVE_EXCLUSIVE); + mInstrumentation.runOnMainSync(() -> tv.setText(s)); + + Layout layout = tv.getLayout(); + Editor editor = tv.getEditorForTesting(); + final float verticalOffset = tv.getExtendedPaddingTop(); + editor.setLineChangeSlopMinMaxForTesting(30, 65); + // Hit top part of upper line, jump to upper line. + assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 5 + verticalOffset)) + .isEqualTo(0); + // Hit bottom part of upper line, stay at current line. + assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 40 + verticalOffset)) + .isEqualTo(1); + // Hit current line, stay at current line. + assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 70 + verticalOffset)) + .isEqualTo(1); + // Hit top part of lower line, stay at current line. + assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 85 + verticalOffset)) + .isEqualTo(1); + // Hit bottom part of lower line, jump to lower line. + assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 110 + verticalOffset)) + .isEqualTo(2); + // Hit lower line of lower line, jump to target line. + assertThat(editor.getCurrentLineAdjustedForSlop(layout, 0, 110 + verticalOffset)) + .isEqualTo(2); + } + + @Test public void testCursorDrag_snapDistance() throws Throwable { String text = "line1: This is the 1st line: A\n" + "line2: This is the 2nd line: B\n" diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index 9af0ed0ab826..4a33da680585 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -17,7 +17,6 @@ package com.android.internal.accessibility; import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN; -import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED; import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; @@ -102,10 +101,8 @@ public class AccessibilityShortcutControllerTest { private static final long[] VIBRATOR_PATTERN_LONG = {VIBRATOR_PATTERN_1, VIBRATOR_PATTERN_2}; // Convenience values for enabling/disabling to make code more readable - private static final int DISABLED = 0; private static final int ENABLED_EXCEPT_LOCK_SCREEN = 1; private static final int ENABLED_INCLUDING_LOCK_SCREEN = 2; - private static final int DISABLED_BUT_LOCK_SCREEN_ON = 3; private @Mock Context mContext; private @Mock FrameworkObjectProvider mFrameworkObjectProvider; @@ -225,14 +222,6 @@ public class AccessibilityShortcutControllerTest { } @Test - public void testShortcutAvailable_disabledWithValidServiceWhenCreated_shouldReturnFalse() - throws Exception { - configureValidShortcutService(); - configureShortcutEnabled(DISABLED_BUT_LOCK_SCREEN_ON); - assertFalse(getController().isAccessibilityShortcutAvailable(false)); - } - - @Test public void testShortcutAvailable_onLockScreenButDisabledThere_shouldReturnFalse() throws Exception { configureValidShortcutService(); @@ -285,20 +274,8 @@ public class AccessibilityShortcutControllerTest { } @Test - public void testShortcutAvailable_whenShortcutBecomesDisabled_shouldReturnFalse() - throws Exception { - configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); - configureValidShortcutService(); - AccessibilityShortcutController accessibilityShortcutController = getController(); - configureShortcutEnabled(DISABLED); - accessibilityShortcutController.onSettingsChanged(); - assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable(false)); - } - - @Test public void testShortcutAvailable_whenShortcutBecomesEnabled_shouldReturnTrue() throws Exception { - configureShortcutEnabled(DISABLED); configureValidShortcutService(); AccessibilityShortcutController accessibilityShortcutController = getController(); configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); @@ -594,31 +571,19 @@ public class AccessibilityShortcutControllerTest { } private void configureShortcutEnabled(int enabledValue) { - final boolean enabled; final boolean lockscreen; switch (enabledValue) { - case DISABLED: - enabled = false; - lockscreen = false; - break; - case DISABLED_BUT_LOCK_SCREEN_ON: - enabled = false; - lockscreen = true; - break; case ENABLED_INCLUDING_LOCK_SCREEN: - enabled = true; lockscreen = true; break; case ENABLED_EXCEPT_LOCK_SCREEN: - enabled = true; lockscreen = false; break; default: throw new IllegalArgumentException(); } - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_ENABLED, enabled ? 1 : 0); Settings.Secure.putInt( mContentResolver, ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, lockscreen ? 1 : 0); } diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml index 5c89da05a68c..98f5824011ae 100644 --- a/data/etc/hiddenapi-package-whitelist.xml +++ b/data/etc/hiddenapi-package-whitelist.xml @@ -61,7 +61,6 @@ Do NOT include any apps that are updatable via Play Store! <hidden-api-whitelisted-app package="com.android.terminal" /> <hidden-api-whitelisted-app package="com.android.wallpaper" /> <hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" /> - <!-- STOPSHIP: Remove this when fixing all @hide usage for tethering.--> - <hidden-api-whitelisted-app package="com.android.networkstack.tethering" /> + <!-- TODO: Remove NetworkStack whitelisting --> <hidden-api-whitelisted-app package="com.android.networkstack" /> </config> 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 Binary files differnew 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 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 Binary files differnew file mode 100644 index 000000000000..4ab9ca4fa580 --- /dev/null +++ b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png 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 Binary files differnew file mode 100644 index 000000000000..d74e673a9ce1 --- /dev/null +++ b/docs/html/reference/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png 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/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 383202b20550..fffdd683f22c 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -4579,6 +4579,7 @@ public class AudioManager { * {@hide} */ @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setWiredDeviceConnectionState(int type, int state, String address, String name) { final IAudioService service = getService(); try { diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 8b973a177f90..0a56accd1636 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -708,6 +708,8 @@ public class AudioSystem DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET); } + public static final String LEGACY_REMOTE_SUBMIX_ADDRESS = "0"; + // device states, must match AudioSystem::device_connection_state @UnsupportedAppUsage public static final int DEVICE_STATE_UNAVAILABLE = 0; diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index c0461bc598ed..1d70a0dbc57c 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -46,6 +46,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -3048,16 +3049,47 @@ final public class MediaCodec { * @param block The linear block object * @param offset The byte offset into the input buffer at which the data starts. * @param size The number of bytes of valid input data. - * @param cryptoInfo Metadata describing the structure of the encrypted input sample. - * may be null for non-encrypted content. * @return this object * @throws IllegalStateException if a buffer is already set */ public @NonNull QueueRequest setLinearBlock( @NonNull LinearBlock block, int offset, + int size) { + if (!isAccessible()) { + throw new IllegalStateException("The request is stale"); + } + if (mLinearBlock != null || mHardwareBuffer != null) { + throw new IllegalStateException("Cannot set block twice"); + } + mLinearBlock = block; + mOffset = offset; + mSize = size; + mCryptoInfo = null; + return this; + } + + /** + * Set an encrypted linear block to this queue request. Exactly one buffer must be + * set for a queue request before calling {@link #queue}. It is possible + * to use the same {@link LinearBlock} object for multiple queue + * requests. The behavior is undefined if the range of the buffer + * overlaps for multiple requests, or the application writes into the + * region being processed by the codec. + * + * @param block The linear block object + * @param offset The byte offset into the input buffer at which the data starts. + * @param size The number of bytes of valid input data. + * @param cryptoInfo Metadata describing the structure of the encrypted input sample. + * @return this object + * @throws IllegalStateException if a buffer is already set + */ + public @NonNull QueueRequest setEncryptedLinearBlock( + @NonNull LinearBlock block, + int offset, int size, - @Nullable MediaCodec.CryptoInfo cryptoInfo) { + @NonNull MediaCodec.CryptoInfo cryptoInfo) { + Objects.requireNonNull(cryptoInfo); if (!isAccessible()) { throw new IllegalStateException("The request is stale"); } diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 29dfd730a84c..9310d38c3bd1 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -40,6 +40,7 @@ using ::android::hardware::tv::tuner::V1_0::DataFormat; using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxAlpLengthType; +using ::android::hardware::tv::tuner::V1_0::DemuxCapabilities; using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings; using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings; @@ -1382,6 +1383,42 @@ jobject JTuner::openDvr(DvrType type, jlong bufferSize) { return dvrObj; } +jobject JTuner::getDemuxCaps() { + DemuxCapabilities caps; + Result res; + mTuner->getDemuxCaps([&](Result r, const DemuxCapabilities& demuxCaps) { + caps = demuxCaps; + res = r; + }); + if (res != Result::SUCCESS) { + return NULL; + } + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass clazz = env->FindClass("android/media/tv/tuner/DemuxCapabilities"); + jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIIIIJI[IZ)V"); + + jint numDemux = caps.numDemux; + jint numRecord = caps.numRecord; + jint numPlayback = caps.numPlayback; + jint numTsFilter = caps.numTsFilter; + jint numSectionFilter = caps.numSectionFilter; + jint numAudioFilter = caps.numAudioFilter; + jint numVideoFilter = caps.numVideoFilter; + jint numPesFilter = caps.numPesFilter; + jint numPcrFilter = caps.numPcrFilter; + jlong numBytesInSectionFilter = caps.numBytesInSectionFilter; + jint filterCaps = static_cast<jint>(caps.filterCaps); + jboolean bTimeFilter = caps.bTimeFilter; + + jintArray linkCaps = env->NewIntArray(caps.linkCaps.size()); + env->SetIntArrayRegion( + linkCaps, 0, caps.linkCaps.size(), reinterpret_cast<jint*>(&caps.linkCaps[0])); + + return env->NewObject(clazz, capsInit, numDemux, numRecord, numPlayback, numTsFilter, + numSectionFilter, numAudioFilter, numVideoFilter, numPesFilter, numPcrFilter, + numBytesInSectionFilter, filterCaps, linkCaps, bTimeFilter); +} + } // namespace android //////////////////////////////////////////////////////////////////////////////// @@ -2739,8 +2776,9 @@ static jobject android_media_tv_Tuner_open_dvr_playback( return tuner->openDvr(DvrType::PLAYBACK, bufferSize); } -static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv*, jobject) { - return NULL; +static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv* env, jobject thiz) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->getDemuxCaps(); } static int android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) { diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 5d2bba6c8aa2..18aac285e7c0 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -185,6 +185,7 @@ struct JTuner : public RefBase { jobject openTimeFilter(); jobject openDescrambler(); jobject openDvr(DvrType type, jlong bufferSize); + jobject getDemuxCaps(); protected: Result openDemux(); diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java index 2b841967d4a8..59735f413f9d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java @@ -189,19 +189,6 @@ public class AccessibilityUtils { return context.getString(R.string.config_defaultAccessibilityService); } - /** - * Check if the accessibility shortcut is enabled for a user - * - * @param context A valid context - * @param userId The user of interest - * @return {@code true} if the shortcut is enabled for the user. {@code false} otherwise. - * Note that the shortcut may be enabled, but no action associated with it. - */ - public static boolean isShortcutEnabled(Context context, int userId) { - return Settings.Secure.getIntForUser(context.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 1, userId) == 1; - } - private static Set<ComponentName> getInstalledServices(Context context) { final Set<ComponentName> installedServices = new HashSet<>(); installedServices.clear(); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java index d17f242d5d63..a1fba4a018e2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java @@ -174,7 +174,7 @@ public class HearingAidProfile implements LocalBluetoothProfile { @Override public boolean isEnabled(BluetoothDevice device) { - if (mService == null) { + if (mService == null || device == null) { return false; } return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; @@ -182,7 +182,7 @@ public class HearingAidProfile implements LocalBluetoothProfile { @Override public int getConnectionPolicy(BluetoothDevice device) { - if (mService == null) { + if (mService == null || device == null) { return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); @@ -191,7 +191,7 @@ public class HearingAidProfile implements LocalBluetoothProfile { @Override public boolean setEnabled(BluetoothDevice device, boolean enabled) { boolean isEnabled = false; - if (mService == null) { + if (mService == null || device == null) { return false; } if (enabled) { @@ -213,7 +213,7 @@ public class HearingAidProfile implements LocalBluetoothProfile { } public long getHiSyncId(BluetoothDevice device) { - if (mService == null) { + if (mService == null || device == null) { return BluetoothHearingAid.HI_SYNC_ID_INVALID; } return mService.getHiSyncId(device); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index b1300a97a324..922caeb0a817 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -66,6 +66,7 @@ public class LocalMediaManager implements BluetoothCallback { private LocalBluetoothManager mLocalBluetoothManager; private InfoMediaManager mInfoMediaManager; private String mPackageName; + private MediaDevice mOnTransferBluetoothDevice; @VisibleForTesting List<MediaDevice> mMediaDevices = new ArrayList<>(); @@ -143,7 +144,7 @@ public class LocalMediaManager implements BluetoothCallback { final CachedBluetoothDevice cachedDevice = ((BluetoothMediaDevice) device).getCachedDevice(); if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) { - device.setState(MediaDeviceState.STATE_CONNECTING); + mOnTransferBluetoothDevice = connectDevice; cachedDevice.connect(); return; } @@ -389,6 +390,10 @@ public class LocalMediaManager implements BluetoothCallback { mCurrentConnectedDevice = infoMediaDevice != null ? infoMediaDevice : updateCurrentConnectedDevice(); dispatchDeviceListUpdate(); + if (mOnTransferBluetoothDevice != null && mOnTransferBluetoothDevice.isConnected()) { + connectDevice(mOnTransferBluetoothDevice); + mOnTransferBluetoothDevice = null; + } } private List<MediaDevice> buildDisconnectedBluetoothDevice() { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index 6b3a97f8c8de..4c61ef504090 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -107,8 +108,8 @@ public class LocalMediaManagerTest { when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile); - mInfoMediaDevice1 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1, - TEST_PACKAGE_NAME); + mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1, + TEST_PACKAGE_NAME)); mInfoMediaDevice2 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo2, TEST_PACKAGE_NAME); mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager, @@ -565,6 +566,34 @@ public class LocalMediaManagerTest { } @Test + public void onDeviceListAdded_transferToDisconnectedBluetooth_verifyConnectDevice() { + final List<MediaDevice> devices = new ArrayList<>(); + final MediaDevice currentDevice = mock(MediaDevice.class); + final MediaDevice device = mock(BluetoothMediaDevice.class); + final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); + mLocalMediaManager.mMediaDevices.add(device); + mLocalMediaManager.mMediaDevices.add(currentDevice); + + when(device.getId()).thenReturn(TEST_DEVICE_ID_1); + when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID); + when(((BluetoothMediaDevice) device).getCachedDevice()).thenReturn(cachedDevice); + when(cachedDevice.isConnected()).thenReturn(false); + when(cachedDevice.isBusy()).thenReturn(false); + + mLocalMediaManager.registerCallback(mCallback); + mLocalMediaManager.connectDevice(device); + + verify(cachedDevice).connect(); + when(device.isConnected()).thenReturn(true); + mLocalMediaManager.mCurrentConnectedDevice = currentDevice; + devices.add(mInfoMediaDevice1); + devices.add(currentDevice); + mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices); + + verify(mInfoMediaDevice1).connect(); + } + + @Test public void onRequestFailed_shouldDispatchOnRequestFailed() { mLocalMediaManager.registerCallback(mCallback); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index d350d9df37b8..d320df9c24ba 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -48,7 +48,6 @@ public class SecureSettings { Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, - Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 4d33b627f4e2..8801a9c32a36 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -25,10 +25,10 @@ import static android.provider.settings.validators.SettingsValidators.COMMA_SEPA import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.JSON_OBJECT_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR; +import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR; -import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.TILE_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR; @@ -82,7 +82,6 @@ public class SecureSettingsValidators { Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, BOOLEAN_VALIDATOR); - VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put( diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index af74121a11c9..b22caf0edc66 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1781,9 +1781,6 @@ class SettingsProtoDumpUtil { Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, SecureSettingsProto.Accessibility.LARGE_POINTER_ICON); dumpSetting(s, p, - Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, - SecureSettingsProto.Accessibility.SHORTCUT_ENABLED); - dumpSetting(s, p, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, SecureSettingsProto.Accessibility.SHORTCUT_ON_LOCK_SCREEN); dumpSetting(s, p, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 5a9d7497b641..2fde87c08ad5 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -3436,7 +3436,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 188; + private static final int SETTINGS_VERSION = 189; private final int mUserId; @@ -4759,6 +4759,23 @@ public class SettingsProvider extends ContentProvider { currentVersion = 188; } + if (currentVersion == 188) { + // Deprecate ACCESSIBILITY_SHORTCUT_ENABLED, and migrate it + // to ACCESSIBILITY_SHORTCUT_TARGET_SERVICE. + final SettingsState secureSettings = getSecureSettingsLocked(userId); + final Setting shortcutEnabled = secureSettings.getSettingLocked( + "accessibility_shortcut_enabled"); + if ("0".equals(shortcutEnabled.getValue())) { + // Clear shortcut key targets list setting. + secureSettings.insertSettingLocked( + Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, + "", null /* tag */, false /* makeDefault */, + SettingsState.SYSTEM_PACKAGE_NAME); + } + secureSettings.deleteSettingLocked("accessibility_shortcut_enabled"); + currentVersion = 189; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 64e52376effd..0ae00e1ac8b5 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -1019,8 +1019,11 @@ public class BugreportProgressService extends Service { * Wraps up bugreport generation and triggers a notification to share the bugreport. */ private void onBugreportFinished(BugreportInfo info) { + if (!TextUtils.isEmpty(info.shareTitle)) { + info.setTitle(info.shareTitle); + } Log.d(TAG, "Bugreport finished with title: " + info.getTitle() - + " and shareDescription: " + info.shareDescription); + + " and shareDescription: " + info.shareDescription); info.finished = new AtomicBoolean(true); synchronized (mLock) { @@ -1795,7 +1798,9 @@ public class BugreportProgressService extends Service { /** * User-provided, detailed description of the bugreport; when set, will be added to the body - * of the {@link Intent#ACTION_SEND_MULTIPLE} intent. + * of the {@link Intent#ACTION_SEND_MULTIPLE} intent. This is shown in the app where the + * bugreport is being shared as an attachment. This is not related/dependant on + * {@code shareDescription}. */ private String description; @@ -2130,7 +2135,6 @@ public class BugreportProgressService extends Service { return new BugreportInfo[size]; } }; - } @GuardedBy("mLock") 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/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml index a06f434d068c..65b04fd8fd99 100644 --- a/packages/SystemUI/res/layout/bubble_overflow_activity.xml +++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml @@ -19,6 +19,7 @@ android:id="@+id/bubble_overflow_container" android:layout_width="match_parent" android:layout_height="match_parent" + android:paddingTop="@dimen/bubble_overflow_padding" android:orientation="vertical" android:layout_gravity="center_horizontal"> diff --git a/packages/SystemUI/res/layout/bubble_overflow_view.xml b/packages/SystemUI/res/layout/bubble_overflow_view.xml new file mode 100644 index 000000000000..d67c81d67ada --- /dev/null +++ b/packages/SystemUI/res/layout/bubble_overflow_view.xml @@ -0,0 +1,41 @@ +<?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 + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/bubble_overflow_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <com.android.systemui.bubbles.BadgedImageView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/bubble_view" + android:layout_gravity="center" + android:layout_width="@dimen/individual_bubble_size" + android:layout_height="@dimen/individual_bubble_size"/> + + <TextView + android:id="@+id/bubble_view_name" + android:fontFamily="@*android:string/config_bodyFontFamily" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2" + android:textColor="?android:attr/textColorSecondary" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:maxLines="1" + android:layout_gravity="center" + android:gravity="center"/> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/controls_no_favorites.xml b/packages/SystemUI/res/layout/controls_no_favorites.xml index 74fc167c4632..41282301ab1c 100644 --- a/packages/SystemUI/res/layout/controls_no_favorites.xml +++ b/packages/SystemUI/res/layout/controls_no_favorites.xml @@ -50,7 +50,6 @@ <TextView style="@style/TextAppearance.ControlSetup.Subtitle" android:id="@+id/controls_subtitle" - android:visibility="gone" android:layout_marginTop="12dp" android:layout_width="wrap_content" android:layout_height="wrap_content" 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..de9ef218083b --- /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" + 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="wrap_content" + 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/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml index 9ef8c1dbbb95..fe8557bfcce4 100644 --- a/packages/SystemUI/res/layout/qs_media_panel.xml +++ b/packages/SystemUI/res/layout/qs_media_panel.xml @@ -56,7 +56,7 @@ <LinearLayout android:orientation="vertical" android:layout_width="0dp" - android:layout_height="@dimen/qs_media_album_size" + android:layout_height="wrap_content" android:layout_weight="1" > <LinearLayout diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index e45cbecd3aa1..432cd749abbd 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1150,8 +1150,8 @@ <dimen name="bubble_overflow_height">380dp</dimen> <!-- Bubble overflow padding when there are no bubbles --> <dimen name="bubble_overflow_empty_state_padding">16dp</dimen> - <!-- Margin of overflow bubbles --> - <dimen name="bubble_overflow_margin">16dp</dimen> + <!-- Padding of container for overflow bubbles --> + <dimen name="bubble_overflow_padding">5dp</dimen> <!-- Height of the triangle that points to the expanded bubble --> <dimen name="bubble_pointer_height">4dp</dimen> <!-- Width of the triangle that points to the expanded bubble --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index f7ad13425ef6..c8c35c704297 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2636,14 +2636,17 @@ <!-- Quick Controls strings --> <!-- Quick Controls empty state, title [CHAR LIMIT=30] --> - <string name="quick_controls_title">Quick Controls</string> + <string name="quick_controls_title">Quick controls</string> <!-- Quick Controls empty state, subtitle [CHAR LIMIT=100] --> <string name="quick_controls_subtitle">Add controls for your connected devices</string> - <!-- Controls management providers screen title [CHAR LIMIT=30]--> - <string name="controls_providers_title">Add Controls</string> - <!-- Controls management providers screen subtitle [CHAR LIMIT=NONE] --> - <string name="controls_providers_subtitle">Choose app to add controls</string> + <!-- Quick Controls setup, title [CHAR LIMIT=50] --> + <string name="quick_controls_setup_title">Set up quick controls</string> + <!-- Quick Controls setup, subtitle [CHAR LIMIT=100] --> + <string name="quick_controls_setup_subtitle">Hold the Power button to access your controls</string> + + <!-- Controls management providers screen title [CHAR LIMIT=60]--> + <string name="controls_providers_title">Choose app to add controls</string> <!-- Number of favorites for controls management screen [CHAR LIMIT=NONE]--> <plurals name="controls_number_of_favorites"> <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> control added.</item> @@ -2679,12 +2682,8 @@ <string name="controls_pin_verifying">Verifying\u2026</string> <!-- Controls PIN entry dialog, text hint [CHAR LIMIT=30] --> <string name="controls_pin_instructions">Enter PIN</string> - <!-- Controls passphrase entry dialog, text hint [CHAR LIMIT=30] --> - <string name="controls_passphrase_instructions">Enter passphrase</string> <!-- Controls PIN entry dialog, text hint, retry [CHAR LIMIT=30] --> <string name="controls_pin_instructions_retry">Try another PIN</string> - <!-- Controls passphrase entry dialog, text hint, retry [CHAR LIMIT=50] --> - <string name="controls_passphrase_instructions_retry">Try another passphrase</string> <!-- Controls confirmation dialog, waiting to confirm [CHAR LIMIT=30] --> <string name="controls_confirmation_confirming">Confirming\u2026</string> <!-- Controls confirmation dialog, user prompt [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 07bd3a0567e6..136935080824 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -73,14 +73,19 @@ public class QuickStepContract { public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED = 1 << 9; // The search feature is disabled (either by SUW/SysUI/device policy) public static final int SYSUI_STATE_SEARCH_DISABLED = 1 << 10; - // The notification panel is expanded and interactive (either locked or unlocked), and the - // quick settings is not expanded + // The notification panel is expanded and interactive (either locked or unlocked), and quick + // settings is expanded. public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11; // Winscope tracing is enabled public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12; // The Assistant gesture should be constrained. It is up to the launcher implementation to // decide how to constrain it public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13; + // The bubble stack is expanded. This means that the home gesture should be ignored, since a + // swipe up is an attempt to close the bubble stack, but that the back gesture should remain + // enabled (since it's used to navigate back within the bubbled app, or to collapse the bubble + // stack. + public static final int SYSUI_STATE_BUBBLES_EXPANDED = 1 << 14; @Retention(RetentionPolicy.SOURCE) @IntDef({SYSUI_STATE_SCREEN_PINNING, @@ -96,7 +101,8 @@ public class QuickStepContract { SYSUI_STATE_HOME_DISABLED, SYSUI_STATE_SEARCH_DISABLED, SYSUI_STATE_TRACING_ENABLED, - SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED + SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED, + SYSUI_STATE_BUBBLES_EXPANDED }) public @interface SystemUiStateFlags {} @@ -118,6 +124,7 @@ public class QuickStepContract { str.add((flags & SYSUI_STATE_TRACING_ENABLED) != 0 ? "tracing" : ""); str.add((flags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0 ? "asst_gesture_constrain" : ""); + str.add((flags & SYSUI_STATE_BUBBLES_EXPANDED) != 0 ? "bubbles_expanded" : ""); return str.toString(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMedia.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardMedia.kt new file mode 100644 index 000000000000..487c29573a14 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMedia.kt @@ -0,0 +1,33 @@ +/* + * 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.Drawable + +import java.util.List + +/** State for lock screen media controls. */ +data class KeyguardMedia( + val foregroundColor: Int, + val backgroundColor: Int, + val app: String?, + val appIcon: Drawable?, + val artist: String?, + val song: String?, + val artwork: Drawable?, + val actionIcons: List<Drawable> +) 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..d1544346a25a --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java @@ -0,0 +1,364 @@ +/* + * 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.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Observer; +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 final KeyguardMediaViewModel mViewModel; + private KeyguardMediaObserver mObserver; + + @Inject + public KeyguardMediaPlayer(Context context, @Background Executor backgroundExecutor) { + mContext = context; + mBackgroundExecutor = backgroundExecutor; + mViewModel = new KeyguardMediaViewModel(context); + } + + /** Binds media controls to a view hierarchy. */ + public void bindView(View v) { + if (mObserver != null) { + throw new IllegalStateException("cannot bind views, already bound"); + } + mViewModel.loadDimens(); + mObserver = new KeyguardMediaObserver(v); + // Control buttons + for (int i = 0; i < ACTION_IDS.length; i++) { + ImageButton button = v.findViewById(ACTION_IDS[i]); + if (button == null) { + continue; + } + final int index = i; + button.setOnClickListener(unused -> mViewModel.onActionClick(index)); + } + mViewModel.getKeyguardMedia().observeForever(mObserver); + } + + /** Unbinds media controls. */ + public void unbindView() { + if (mObserver == null) { + throw new IllegalStateException("cannot unbind views, nothing bound"); + } + mViewModel.getKeyguardMedia().removeObserver(mObserver); + mObserver = null; + } + + /** Clear the media controls because there isn't an active session. */ + public void clearControls() { + mBackgroundExecutor.execute(mViewModel::clearControls); + } + + /** + * 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 (mObserver == null) { + throw new IllegalStateException("cannot update controls, views not bound"); + } + if (mediaMetadata == null) { + Log.d(TAG, "media metadata was null, closing media controls"); + // Note that clearControls() executes on the same background executor, so there + // shouldn't be an issue with an outdated update running after clear. However, if stale + // controls are observed then consider removing any enqueued updates. + clearControls(); + return; + } + mBackgroundExecutor.execute(() -> mViewModel.updateControls(entry, appIcon, mediaMetadata)); + } + + /** ViewModel for KeyguardMediaControls. */ + private static final class KeyguardMediaViewModel { + + private final Context mContext; + private final MutableLiveData<KeyguardMedia> mMedia = new MutableLiveData<>(); + private final Object mActionsLock = new Object(); + private List<PendingIntent> mActions; + private float mAlbumArtRadius; + private int mAlbumArtSize; + + KeyguardMediaViewModel(Context context) { + mContext = context; + loadDimens(); + } + + /** Close the media player because there isn't an active session. */ + public void clearControls() { + synchronized (mActionsLock) { + mActions = null; + } + mMedia.postValue(null); + } + + /** Update the media player with information about the active session. */ + public void updateControls(NotificationEntry entry, Icon appIcon, + MediaMetadata mediaMetadata) { + + // Foreground and Background colors computed from album art + Notification notif = entry.getSbn().getNotification(); + 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 + RoundedBitmapDrawable artwork = null; + if (artworkBitmap != null) { + Bitmap original = artworkBitmap.copy(Bitmap.Config.ARGB_8888, true); + Bitmap scaled = Bitmap.createScaledBitmap(original, mAlbumArtSize, mAlbumArtSize, + false); + artwork = RoundedBitmapDrawableFactory.create(mContext.getResources(), scaled); + artwork.setCornerRadius(mAlbumArtRadius); + } + + // App name + Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif); + String app = builder.loadHeaderAppName(); + + // App Icon + Drawable appIconDrawable = appIcon.loadDrawable(mContext); + + // Song name + String song = mediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE); + + // Artist name + String artist = mediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST); + + // Control buttons + List<Drawable> actionIcons = new ArrayList<>(); + final List<PendingIntent> intents = new ArrayList<>(); + Notification.Action[] actions = notif.actions; + final int[] actionsToShow = notif.extras.getIntArray( + Notification.EXTRA_COMPACT_ACTIONS); + + Context packageContext = entry.getSbn().getPackageContext(mContext); + 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]; + actionIcons.add(actions[idx].getIcon().loadDrawable(packageContext)); + intents.add(actions[idx].actionIntent); + } else { + actionIcons.add(null); + intents.add(null); + } + } + synchronized (mActionsLock) { + mActions = intents; + } + + KeyguardMedia data = new KeyguardMedia(fgColor, bgColor, app, appIconDrawable, artist, + song, artwork, actionIcons); + mMedia.postValue(data); + } + + /** Gets state for the lock screen media controls. */ + public LiveData<KeyguardMedia> getKeyguardMedia() { + return mMedia; + } + + /** + * Handle user clicks on media control buttons (actions). + * + * @param index position of the button that was clicked. + */ + public void onActionClick(int index) { + PendingIntent intent = null; + // This might block the ui thread to wait for the lock. Currently, however, the + // lock is held by the bg thread to assign a member, which should be fast. An + // alternative could be to add the intents to the state and let the observer set + // the onClick listeners. + synchronized (mActionsLock) { + if (mActions != null && index < mActions.size()) { + intent = mActions.get(index); + } + } + if (intent != null) { + try { + intent.send(); + } catch (PendingIntent.CanceledException e) { + Log.d(TAG, "failed to send action intent", e); + } + } + } + + void loadDimens() { + mAlbumArtRadius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius); + mAlbumArtSize = (int) mContext.getResources().getDimension( + R.dimen.qs_media_album_size); + } + } + + /** Observer for state changes of lock screen media controls. */ + private static final class KeyguardMediaObserver implements Observer<KeyguardMedia> { + + private final View mRootView; + private final MediaHeaderView mMediaHeaderView; + private final ImageView mAlbumView; + private final ImageView mAppIconView; + private final TextView mAppNameView; + private final TextView mTitleView; + private final TextView mArtistView; + private final List<ImageButton> mButtonViews = new ArrayList<>(); + + KeyguardMediaObserver(View v) { + mRootView = v; + mMediaHeaderView = v instanceof MediaHeaderView ? (MediaHeaderView) v : null; + mAlbumView = v.findViewById(R.id.album_art); + mAppIconView = v.findViewById(R.id.icon); + mAppNameView = v.findViewById(R.id.app_name); + mTitleView = v.findViewById(R.id.header_title); + mArtistView = v.findViewById(R.id.header_artist); + for (int i = 0; i < ACTION_IDS.length; i++) { + mButtonViews.add(v.findViewById(ACTION_IDS[i])); + } + } + + /** Updates lock screen media player views when state changes. */ + @Override + public void onChanged(KeyguardMedia data) { + if (data == null) { + mRootView.setVisibility(View.GONE); + return; + } + mRootView.setVisibility(View.VISIBLE); + + // Background color + if (mMediaHeaderView != null) { + mMediaHeaderView.setBackgroundColor(data.getBackgroundColor()); + } + + // Album art + if (mAlbumView != null) { + mAlbumView.setImageDrawable(data.getArtwork()); + mAlbumView.setVisibility(data.getArtwork() == null ? View.GONE : View.VISIBLE); + } + + // App icon + if (mAppIconView != null) { + Drawable iconDrawable = data.getAppIcon(); + iconDrawable.setTint(data.getForegroundColor()); + mAppIconView.setImageDrawable(iconDrawable); + } + + // App name + if (mAppNameView != null) { + String appNameString = data.getApp(); + mAppNameView.setText(appNameString); + mAppNameView.setTextColor(data.getForegroundColor()); + } + + // Song name + if (mTitleView != null) { + mTitleView.setText(data.getSong()); + mTitleView.setTextColor(data.getForegroundColor()); + } + + // Artist name + if (mArtistView != null) { + mArtistView.setText(data.getArtist()); + mArtistView.setTextColor(data.getForegroundColor()); + } + + // Control buttons + for (int i = 0; i < ACTION_IDS.length; i++) { + ImageButton button = mButtonViews.get(i); + if (button == null) { + continue; + } + Drawable icon = data.getActionIcons().get(i); + if (icon == null) { + button.setVisibility(View.GONE); + button.setImageDrawable(null); + } else { + button.setVisibility(View.VISIBLE); + button.setImageDrawable(icon); + button.setImageTintList(ColorStateList.valueOf(data.getForegroundColor())); + } + } + } + } +} 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/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index c9ce8a10cca7..01c2faa62403 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -74,6 +74,7 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.bubbles.dagger.BubbleModule; import com.android.systemui.dump.DumpManager; +import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.PinnedStackListenerForwarder; @@ -174,6 +175,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private IStatusBarService mBarService; + private SysUiState mSysUiState; // Used for determining view rect for touch interaction private Rect mTempRect = new Rect(); @@ -290,11 +292,12 @@ public class BubbleController implements ConfigurationController.ConfigurationLi NotifPipeline notifPipeline, FeatureFlags featureFlags, DumpManager dumpManager, - FloatingContentCoordinator floatingContentCoordinator) { + FloatingContentCoordinator floatingContentCoordinator, + SysUiState sysUiState) { this(context, notificationShadeWindowController, statusBarStateController, shadeController, data, null /* synchronizer */, configurationController, interruptionStateProvider, zenModeController, notifUserManager, groupManager, entryManager, - notifPipeline, featureFlags, dumpManager, floatingContentCoordinator); + notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState); } /** @@ -315,7 +318,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi NotifPipeline notifPipeline, FeatureFlags featureFlags, DumpManager dumpManager, - FloatingContentCoordinator floatingContentCoordinator) { + FloatingContentCoordinator floatingContentCoordinator, + SysUiState sysUiState) { dumpManager.registerDumpable(TAG, this); mContext = context; mShadeController = shadeController; @@ -340,6 +344,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi }); configurationController.addCallback(this /* configurationListener */); + mSysUiState = sysUiState; mBubbleData = data; mBubbleData.setListener(mBubbleDataListener); @@ -593,7 +598,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi private void ensureStackViewCreated() { if (mStackView == null) { mStackView = new BubbleStackView( - mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator); + mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator, + mSysUiState); ViewGroup nsv = mNotificationShadeWindowController.getNotificationShadeView(); int bubbleScrimIndex = nsv.indexOfChild(nsv.findViewById(R.id.scrim_for_bubble)); int stackIndex = bubbleScrimIndex + 1; // Show stack above bubble scrim. @@ -957,9 +963,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi String key = orderedKeys[i]; NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key); rankingMap.getRanking(key, mTmpRanking); - if (mBubbleData.hasBubbleWithKey(key) && !mTmpRanking.canBubble()) { + boolean isActiveBubble = mBubbleData.hasBubbleWithKey(key); + if (isActiveBubble && !mTmpRanking.canBubble()) { mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED); - } else if (entry != null && mTmpRanking.isBubble()) { + } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) { entry.setFlagBubble(true); onEntryUpdated(entry); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index fcbd9121fcda..2bd15188b7d3 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -208,7 +208,7 @@ public class BubbleData { b -> { notificationEntryUpdated(bubble, /* suppressFlyout */ false, /* showInShade */ true); - setSelectedBubbleInternal(bubble); + setSelectedBubble(bubble); }, mContext, stack, factory); dispatchPendingChanges(); @@ -233,6 +233,7 @@ public class BubbleData { Bubble b = mOverflowBubbles.get(i); if (b.getKey().equals(entry.getKey())) { moveOverflowBubbleToPending(b); + b.setEntry(entry); return b; } } @@ -240,6 +241,7 @@ public class BubbleData { for (int i = 0; i < mPendingBubbles.size(); i++) { Bubble b = mPendingBubbles.get(i); if (b.getKey().equals(entry.getKey())) { + b.setEntry(entry); return b; } } @@ -403,6 +405,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 +450,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); @@ -757,6 +761,17 @@ public class BubbleData { } @VisibleForTesting(visibility = PRIVATE) + Bubble getOverflowBubbleWithKey(String key) { + for (int i = 0; i < mOverflowBubbles.size(); i++) { + Bubble bubble = mOverflowBubbles.get(i); + if (bubble.getKey().equals(key)) { + return bubble; + } + } + return null; + } + + @VisibleForTesting(visibility = PRIVATE) void setTimeSource(TimeSource timeSource) { mTimeSource = timeSource; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java index 7636c6712e41..b65198595095 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java @@ -21,14 +21,20 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import android.app.Activity; +import android.app.Notification; +import android.app.Person; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Bundle; +import android.os.Parcelable; +import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; +import android.widget.TextView; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -67,13 +73,22 @@ public class BubbleOverflowActivity extends Activity { mEmptyState = findViewById(R.id.bubble_overflow_empty_state); mRecyclerView = findViewById(R.id.bubble_overflow_recycler); + + Resources res = getResources(); + final int columns = res.getInteger(R.integer.bubbles_overflow_columns); mRecyclerView.setLayoutManager( - new GridLayoutManager(getApplicationContext(), - getResources().getInteger(R.integer.bubbles_overflow_columns))); + new GridLayoutManager(getApplicationContext(), columns)); + + DisplayMetrics displayMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + final int viewWidth = displayMetrics.widthPixels / columns; + + final int maxOverflowBubbles = res.getInteger(R.integer.bubbles_max_overflow); + final int rows = (int) Math.ceil((double) maxOverflowBubbles / columns); + final int viewHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height) / rows; - int bubbleMargin = getResources().getDimensionPixelSize(R.dimen.bubble_overflow_margin); mAdapter = new BubbleOverflowAdapter(mOverflowBubbles, - mBubbleController::promoteBubbleFromOverflow, bubbleMargin); + mBubbleController::promoteBubbleFromOverflow, viewWidth, viewHeight); mRecyclerView.setAdapter(mAdapter); onDataChanged(mBubbleController.getOverflowBubbles()); mBubbleController.setOverflowCallback(() -> { @@ -139,39 +154,48 @@ public class BubbleOverflowActivity extends Activity { class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> { private Consumer<Bubble> mPromoteBubbleFromOverflow; private List<Bubble> mBubbles; - private int mBubbleMargin; + private int mWidth; + private int mHeight; - public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble, - int bubbleMargin) { + public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble, int width, + int height) { mBubbles = list; mPromoteBubbleFromOverflow = promoteBubble; - mBubbleMargin = bubbleMargin; + mWidth = width; + mHeight = height; } @Override public BubbleOverflowAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - BadgedImageView view = (BadgedImageView) LayoutInflater.from(parent.getContext()) - .inflate(R.layout.bubble_view, parent, false); + LinearLayout overflowView = (LinearLayout) LayoutInflater.from(parent.getContext()) + .inflate(R.layout.bubble_overflow_view, parent, false); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ); - params.setMargins(mBubbleMargin, mBubbleMargin, mBubbleMargin, mBubbleMargin); - view.setLayoutParams(params); - return new ViewHolder(view); + LinearLayout.LayoutParams.WRAP_CONTENT); + params.width = mWidth; + params.height = mHeight; + overflowView.setLayoutParams(params); + return new ViewHolder(overflowView); } @Override public void onBindViewHolder(ViewHolder vh, int index) { - Bubble bubble = mBubbles.get(index); + Bubble b = mBubbles.get(index); - vh.mBadgedImageView.update(bubble); - vh.mBadgedImageView.setOnClickListener(view -> { - mBubbles.remove(bubble); + vh.iconView.update(b); + vh.iconView.setOnClickListener(view -> { + mBubbles.remove(b); notifyDataSetChanged(); - mPromoteBubbleFromOverflow.accept(bubble); + mPromoteBubbleFromOverflow.accept(b); }); + + Bubble.FlyoutMessage message = b.getFlyoutMessage(); + if (message != null && message.senderName != null) { + vh.textView.setText(message.senderName); + } else { + vh.textView.setText(b.getAppName()); + } } @Override @@ -180,11 +204,13 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V } public static class ViewHolder extends RecyclerView.ViewHolder { - public BadgedImageView mBadgedImageView; + public BadgedImageView iconView; + public TextView textView; - public ViewHolder(BadgedImageView v) { + public ViewHolder(LinearLayout v) { super(v); - mBadgedImageView = v; + iconView = v.findViewById(R.id.bubble_view); + textView = v.findViewById(R.id.bubble_view_name); } } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index cff371f93f3d..7191a203ea8c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -80,6 +80,8 @@ import com.android.systemui.R; import com.android.systemui.bubbles.animation.ExpandedAnimationController; import com.android.systemui.bubbles.animation.PhysicsAnimationLayout; import com.android.systemui.bubbles.animation.StackAnimationController; +import com.android.systemui.model.SysUiState; +import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.util.DismissCircleView; import com.android.systemui.util.FloatingContentCoordinator; @@ -241,6 +243,7 @@ public class BubbleStackView extends FrameLayout { private BubbleTouchHandler mTouchHandler; private BubbleController.BubbleExpandListener mExpandListener; + private SysUiState mSysUiState; private boolean mViewUpdatedRequested = false; private boolean mIsExpansionAnimating = false; @@ -437,7 +440,8 @@ public class BubbleStackView extends FrameLayout { public BubbleStackView(Context context, BubbleData data, @Nullable SurfaceSynchronizer synchronizer, - FloatingContentCoordinator floatingContentCoordinator) { + FloatingContentCoordinator floatingContentCoordinator, + SysUiState sysUiState) { super(context); mBubbleData = data; @@ -445,6 +449,8 @@ public class BubbleStackView extends FrameLayout { mTouchHandler = new BubbleTouchHandler(this, data, context); setOnTouchListener(mTouchHandler); + mSysUiState = sysUiState; + Resources res = getResources(); mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); @@ -940,7 +946,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 +956,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) { @@ -1054,6 +1061,11 @@ public class BubbleStackView extends FrameLayout { if (shouldExpand == mIsExpanded) { return; } + + mSysUiState + .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand) + .commitUpdate(mContext.getDisplayId()); + if (mIsExpanded) { animateCollapse(); logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java index 27c9e9895324..e84e932c9e61 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java @@ -21,6 +21,7 @@ import android.content.Context; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.bubbles.BubbleData; import com.android.systemui.dump.DumpManager; +import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -62,7 +63,8 @@ public interface BubbleModule { NotifPipeline notifPipeline, FeatureFlags featureFlags, DumpManager dumpManager, - FloatingContentCoordinator floatingContentCoordinator) { + FloatingContentCoordinator floatingContentCoordinator, + SysUiState sysUiState) { return new BubbleController( context, notificationShadeWindowController, @@ -79,6 +81,7 @@ public interface BubbleModule { notifPipeline, featureFlags, dumpManager, - floatingContentCoordinator); + floatingContentCoordinator, + sysUiState); } } 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/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt index 098caf61a873..0c41f7e5df5a 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt @@ -84,8 +84,6 @@ class ControlsProviderSelectorActivity @Inject constructor( requireViewById<TextView>(R.id.title).text = resources.getText(R.string.controls_providers_title) - requireViewById<TextView>(R.id.subtitle).text = - resources.getText(R.string.controls_providers_subtitle) requireViewById<Button>(R.id.done).setOnClickListener { this@ControlsProviderSelectorActivity.finishAffinity() @@ -117,4 +115,4 @@ class ControlsProviderSelectorActivity @Inject constructor( currentUserTracker.stopTracking() super.onDestroy() } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 34dcca2859e7..b0db4370f38d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -172,7 +172,7 @@ class ControlsUiControllerImpl @Inject constructor ( val inflater = LayoutInflater.from(context) inflater.inflate(R.layout.controls_no_favorites, parent, true) val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle) - subtitle.setVisibility(View.VISIBLE) + subtitle.setText(context.resources.getString(R.string.controls_seeding_in_progress)) } private fun showInitialSetupView(items: List<SelectionItem>) { @@ -184,6 +184,9 @@ class ControlsUiControllerImpl @Inject constructor ( val viewGroup = parent.requireViewById(R.id.controls_no_favorites_group) as ViewGroup viewGroup.setOnClickListener(launchSelectorActivityListener(context)) + val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle) + subtitle.setText(context.resources.getString(R.string.quick_controls_subtitle)) + val iconRowGroup = parent.requireViewById(R.id.controls_icon_row) as ViewGroup items.forEach { val imageView = inflater.inflate(R.layout.controls_icon, viewGroup, false) as ImageView diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 09d7d26e4dfe..b3299916356c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -490,6 +490,9 @@ public class DozeTriggers implements DozeMachine.Part { public void onPowerSaveChanged(boolean active) { if (mDozeHost.isPowerSaveActive()) { mMachine.requestState(DozeMachine.State.DOZE); + } else if (mMachine.getState() == DozeMachine.State.DOZE + && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) { + mMachine.requestState(DozeMachine.State.DOZE_AOD); } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 73539f9380e6..6514ca44cf98 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -1849,7 +1849,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, .alpha(1) .translationX(0) .translationY(0) - .setDuration(300) + .setDuration(450) .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .setUpdateListener(animation -> { float animatedValue = animation.getAnimatedFraction(); @@ -1878,7 +1878,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, .alpha(0) .translationX(mGlobalActionsLayout.getAnimationOffsetX()) .translationY(mGlobalActionsLayout.getAnimationOffsetY()) - .setDuration(300) + .setDuration(550) .withEndAction(this::completeDismiss) .setInterpolator(new LogAccelerateInterpolator()) .setUpdateListener(animation -> { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 2cc3d9e22a7d..96494cfe640f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -305,7 +305,8 @@ public class KeyguardSliceProvider extends SliceProvider implements oldInstance.onDestroy(); } mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern); - mPendingIntent = PendingIntent.getActivity(getContext(), 0, new Intent(), 0); + mPendingIntent = PendingIntent.getActivity(getContext(), 0, + new Intent(getContext(), KeyguardSliceProvider.class), 0); mMediaManager.addCallback(this); mStatusBarStateController.addCallback(this); mNextAlarmController.addCallback(this); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index c28705575e86..6ce5e7cf506c 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()) { @@ -346,6 +346,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper .crop(tx, mLeash, destinationBounds) + .resetScale(tx, mLeash, destinationBounds) .round(tx, mLeash, mInPip); scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE, null); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java index 014f3b51b4dc..0b076559ae36 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -99,14 +99,14 @@ public class PipResizeGestureHandler { mEnablePipResize = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, PIP_USER_RESIZE, - /* defaultValue = */ false); + /* defaultValue = */ true); deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor, new DeviceConfig.OnPropertiesChangedListener() { @Override public void onPropertiesChanged(DeviceConfig.Properties properties) { if (properties.getKeyset().contains(PIP_USER_RESIZE)) { mEnablePipResize = properties.getBoolean( - PIP_USER_RESIZE, /* defaultValue = */ false); + PIP_USER_RESIZE, /* defaultValue = */ true); } } }); @@ -208,7 +208,8 @@ public class PipResizeGestureHandler { final Rect currentPipBounds = mMotionHelper.getBounds(); mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(), mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, - mMinSize.y, mMaxSize, true, true)); + mMinSize.y, mMaxSize, true, + mLastDownBounds.width() > mLastDownBounds.height())); mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds); mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds, null); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 5ccf8c7e9212..33cc086a8d9f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -133,6 +133,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne new LocalMediaManager.DeviceCallback() { @Override public void onDeviceListUpdate(List<MediaDevice> devices) { + if (mLocalMediaManager == null) { + return; + } MediaDevice currentDevice = mLocalMediaManager.getCurrentConnectedDevice(); // Check because this can be called several times while changing devices if (mDevice == null || !mDevice.equals(currentDevice)) { @@ -293,14 +296,17 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mMediaPlayers.size() > 0) { ((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE); - // Set up listener for device changes - // TODO: integrate with MediaTransferManager? - InfoMediaManager imm = - new InfoMediaManager(mContext, null, null, mLocalBluetoothManager); - mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager, imm, null); - mLocalMediaManager.startScan(); - mDevice = mLocalMediaManager.getCurrentConnectedDevice(); - mLocalMediaManager.registerCallback(mDeviceCallback); + if (mLocalMediaManager == null) { + // Set up listener for device changes + // TODO: integrate with MediaTransferManager? + InfoMediaManager imm = + new InfoMediaManager(mContext, null, null, mLocalBluetoothManager); + mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager, imm, + null); + mLocalMediaManager.startScan(); + mDevice = mLocalMediaManager.getCurrentConnectedDevice(); + mLocalMediaManager.registerCallback(mDeviceCallback); + } } } @@ -323,8 +329,11 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mMediaCarousel.removeView(player.getView()); if (mMediaPlayers.size() == 0) { ((View) mMediaCarousel.getParent()).setVisibility(View.GONE); - mLocalMediaManager.stopScan(); - mLocalMediaManager.unregisterCallback(mDeviceCallback); + if (mLocalMediaManager != null) { + mLocalMediaManager.stopScan(); + mLocalMediaManager.unregisterCallback(mDeviceCallback); + mLocalMediaManager = null; + } } return true; } @@ -397,6 +406,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mLocalMediaManager != null) { mLocalMediaManager.stopScan(); mLocalMediaManager.unregisterCallback(mDeviceCallback); + mLocalMediaManager = null; } super.onDetachedFromWindow(); } diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java index 3cdc01d95f58..dea8c32dc88d 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java +++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java @@ -38,7 +38,7 @@ public class CurrentUserObservable { @Override protected void onInactive() { super.onInactive(); - mTracker.startTracking(); + mTracker.stopTracking(); } }; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 3879c164a84c..1aa78311a989 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -25,6 +25,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.Nullable; +import android.app.ActivityTaskManager; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -129,6 +130,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, private int mDividerInsets; private final Display mDefaultDisplay; + private boolean mSupportSplitScreenMultiWindow; private int mDividerSize; private int mTouchElevation; @@ -282,6 +284,8 @@ public class DividerView extends FrameLayout implements OnTouchListener, final DisplayManager displayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY); + mSupportSplitScreenMultiWindow = + ActivityTaskManager.supportsSplitScreenMultiWindow(mContext); } @Override @@ -354,6 +358,11 @@ public class DividerView extends FrameLayout implements OnTouchListener, @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (!mSupportSplitScreenMultiWindow) { + super.onLayout(changed, left, top, right, bottom); + return; + } + if (mFirstLayout) { // Wait for first layout so that the ViewRootImpl surface has been created. initializeSurfaceState(); @@ -1085,6 +1094,13 @@ public class DividerView extends FrameLayout implements OnTouchListener, crop.offsetTo(-(otherTaskRect.left - otherRect.left), -(otherTaskRect.top - otherRect.top)); t.setWindowCrop(mTiles.mSecondarySurface, crop); + // Reposition home and recents surfaces or they would be positioned relatively to its + // parent (split-screen secondary task) position. + for (int i = mTiles.mHomeAndRecentsSurfaces.size() - 1; i >= 0; --i) { + t.setPosition(mTiles.mHomeAndRecentsSurfaces.get(i), + mTiles.mHomeBounds.left - otherTaskRect.left, + mTiles.mHomeBounds.top - otherTaskRect.top); + } final SurfaceControl dividerCtrl = getWindowSurfaceControl(); if (dividerCtrl != null) { if (isHorizontalDivision()) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java index 8bbb548b0ecf..6cb7f4ff7204 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java @@ -26,12 +26,15 @@ import static android.window.WindowOrganizer.TaskOrganizer; import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; +import android.graphics.Rect; import android.os.RemoteException; import android.util.Log; import android.view.Display; -import android.window.ITaskOrganizer; import android.view.SurfaceControl; import android.view.SurfaceSession; +import android.window.ITaskOrganizer; + +import java.util.ArrayList; class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub { private static final String TAG = "SplitScreenTaskOrganizer"; @@ -43,6 +46,8 @@ class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub { SurfaceControl mSecondarySurface; SurfaceControl mPrimaryDim; SurfaceControl mSecondaryDim; + ArrayList<SurfaceControl> mHomeAndRecentsSurfaces = new ArrayList<>(); + Rect mHomeBounds = new Rect(); final Divider mDivider; SplitScreenTaskOrganizer(Divider divider) { @@ -82,11 +87,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/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java index 8724e490a558..6ed7afe152df 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java @@ -29,9 +29,9 @@ import android.graphics.Rect; import android.os.RemoteException; import android.util.Log; import android.view.Display; +import android.view.WindowManagerGlobal; import android.window.IWindowContainer; import android.window.WindowContainerTransaction; -import android.view.WindowManagerGlobal; import android.window.WindowOrganizer; import com.android.internal.annotations.GuardedBy; @@ -157,6 +157,7 @@ public class WindowManagerProxy { for (int i = homeStacks.size() - 1; i >= 0; --i) { wct.setBounds(homeStacks.get(i), homeBounds); } + layout.mTiles.mHomeBounds.set(homeBounds); return isHomeResizable; } @@ -180,13 +181,17 @@ public class WindowManagerProxy { if (rootTasks.isEmpty()) { return false; } + tiles.mHomeAndRecentsSurfaces.clear(); for (int i = rootTasks.size() - 1; i >= 0; --i) { - if (rootTasks.get(i).configuration.windowConfiguration.getWindowingMode() + final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i); + if (isHomeOrRecentTask(rootTask)) { + tiles.mHomeAndRecentsSurfaces.add(rootTask.token.getLeash()); + } + if (rootTask.configuration.windowConfiguration.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { continue; } - wct.reparent(rootTasks.get(i).token, tiles.mSecondary.token, - true /* onTop */); + wct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */); } boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct); WindowOrganizer.applyTransaction(wct); @@ -213,6 +218,7 @@ public class WindowManagerProxy { // Set launch root first so that any task created after getChildContainers and // before reparent (pretty unlikely) are put into fullscreen. TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null); + tiles.mHomeAndRecentsSurfaces.clear(); // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished // plus specific APIs to clean this up. List<ActivityManager.RunningTaskInfo> primaryChildren = 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/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 8945f360f7b8..a3faa80485d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -67,32 +67,9 @@ class NotificationShadeDepthController @Inject constructor( private var updateScheduled: Boolean = false private var shadeExpansion = 0f @VisibleForTesting - var shadeSpring = SpringAnimation(this, object : - FloatPropertyCompat<NotificationShadeDepthController>("shadeBlurRadius") { - override fun setValue(rect: NotificationShadeDepthController?, value: Float) { - shadeBlurRadius = value.toInt() - } - - override fun getValue(rect: NotificationShadeDepthController?): Float { - return shadeBlurRadius.toFloat() - } - }) - private val zoomInterpolator = Interpolators.ACCELERATE_DECELERATE - - /** - * Radius that we're animating to. - */ - private var pendingShadeBlurRadius = -1 - - /** - * Shade blur radius on the current frame. - */ - private var shadeBlurRadius = 0 - set(value) { - if (field == value) return - field = value - scheduleUpdate() - } + var shadeSpring = DepthAnimation() + @VisibleForTesting + var globalActionsSpring = DepthAnimation() /** * Blur radius of the wake-up animation on this frame. @@ -103,7 +80,6 @@ class NotificationShadeDepthController @Inject constructor( field = value scheduleUpdate() } - private var globalDialogVisibility = 0f /** * Callback that updates the window blur value and is called only once per frame. @@ -111,12 +87,9 @@ class NotificationShadeDepthController @Inject constructor( private val updateBlurCallback = Choreographer.FrameCallback { updateScheduled = false - val blur = max(shadeBlurRadius, - max(wakeAndUnlockBlurRadius, blurUtils.blurRadiusOfRatio(globalDialogVisibility))) + val blur = max(max(shadeSpring.radius, wakeAndUnlockBlurRadius), globalActionsSpring.radius) blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur) - val rawZoom = max(blurUtils.ratioOfBlurRadius(blur), globalDialogVisibility) - wallpaperManager.setWallpaperZoomOut(root.windowToken, - zoomInterpolator.getInterpolation(rawZoom)) + wallpaperManager.setWallpaperZoomOut(root.windowToken, blurUtils.ratioOfBlurRadius(blur)) notificationShadeWindowController.setBackgroundBlurRadius(blur) } @@ -163,8 +136,9 @@ class NotificationShadeDepthController @Inject constructor( } override fun onDozingChanged(isDozing: Boolean) { - if (isDozing && shadeSpring.isRunning) { - shadeSpring.skipToEnd() + if (isDozing) { + shadeSpring.finishIfRunning() + globalActionsSpring.finishIfRunning() } } } @@ -174,10 +148,6 @@ class NotificationShadeDepthController @Inject constructor( if (WAKE_UP_ANIMATION_ENABLED) { keyguardStateController.addCallback(keyguardStateCallback) } - shadeSpring.spring = SpringForce(0.0f) - shadeSpring.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY - shadeSpring.spring.stiffness = SpringForce.STIFFNESS_LOW - shadeSpring.addEndListener { _, _, _, _ -> pendingShadeBlurRadius = -1 } statusBarStateController.addCallback(statusBarStateCallback) } @@ -198,11 +168,7 @@ class NotificationShadeDepthController @Inject constructor( newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion) } - if (pendingShadeBlurRadius == newBlur) { - return - } - pendingShadeBlurRadius = newBlur - shadeSpring.animateToFinalPosition(newBlur.toFloat()) + shadeSpring.animateTo(newBlur) } private fun scheduleUpdate(viewToBlur: View? = null) { @@ -215,19 +181,72 @@ class NotificationShadeDepthController @Inject constructor( } fun updateGlobalDialogVisibility(visibility: Float, dialogView: View) { - if (visibility == globalDialogVisibility) { - return - } - globalDialogVisibility = visibility - scheduleUpdate(dialogView) + globalActionsSpring.animateTo(blurUtils.blurRadiusOfRatio(visibility), dialogView) } override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { IndentingPrintWriter(pw, " ").let { it.println("StatusBarWindowBlurController:") it.increaseIndent() - it.println("shadeBlurRadius: $shadeBlurRadius") + it.println("shadeRadius: ${shadeSpring.radius}") + it.println("globalActionsRadius: ${globalActionsSpring.radius}") it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius") } } + + /** + * Animation helper that smoothly animates the depth using a spring and deals with frame + * invalidation. + */ + inner class DepthAnimation() { + /** + * Blur radius visible on the UI, in pixels. + */ + var radius = 0 + private set + + /** + * Radius that we're animating to. + */ + private var pendingRadius = -1 + + /** + * View on {@link Surface} that wants depth. + */ + private var view: View? = null + + private var springAnimation = SpringAnimation(this, object : + FloatPropertyCompat<DepthAnimation>("blurRadius") { + override fun setValue(rect: DepthAnimation?, value: Float) { + radius = value.toInt() + scheduleUpdate(view) + } + + override fun getValue(rect: DepthAnimation?): Float { + return radius.toFloat() + } + }) + + init { + springAnimation.spring = SpringForce(0.0f) + springAnimation.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY + springAnimation.spring.stiffness = SpringForce.STIFFNESS_MEDIUM + springAnimation.addEndListener { _, _, _, _ -> pendingRadius = -1 } + } + + fun animateTo(newRadius: Int, viewToBlur: View? = null) { + if (pendingRadius == newRadius && view == viewToBlur) { + return + } + view = viewToBlur + pendingRadius = newRadius + springAnimation.animateToFinalPosition(newRadius.toFloat()) + } + + fun finishIfRunning() { + if (springAnimation.isRunning) { + springAnimation.skipToEnd() + } + } + } } 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/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index c6d84ff79bde..d2f781d2e19c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -651,7 +651,7 @@ public class NotificationEntryManager implements */ public void updateNotifications(String reason) { reapplyFilterAndSort(reason); - if (mPresenter != null) { + if (mPresenter != null && !mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { mPresenter.updateNotificationViews(); } } 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/collection/NotifViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt index 0437877d8330..cf670bd5a424 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt @@ -142,9 +142,11 @@ class NotifViewManager @Inject constructor( // To attach rows we can use _this one weird trick_: if the intended view to add does not // have a parent, then simply add it (and its children). entries.forEach { entry -> - val listItem = rowRegistry.requireView(entry) + // TODO: We should eventually map GroupEntry's themselves to views so that we don't + // depend on representativeEntry here which may actually be null in the future + val listItem = rowRegistry.requireView(entry.representativeEntry!!) - if (listItem.view.parent != null) { + if (listItem.view.parent == null) { listContainer.addListItem(listItem) stabilityManager.notifyViewAddition(listItem.view) } @@ -153,7 +155,8 @@ class NotifViewManager @Inject constructor( for ((idx, childEntry) in entry.children.withIndex()) { val childListItem = rowRegistry.requireView(childEntry) // Child hasn't been added yet. add it! - if (!listItem.notificationChildren.contains(childListItem)) { + if (listItem.notificationChildren == null || + !listItem.notificationChildren.contains(childListItem)) { // TODO: old code here just Log.wtf()'d here. This might wreak havoc if (childListItem.view.parent != null) { throw IllegalStateException("trying to add a notification child that " + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 7019b5b42cf4..808e1b38eb43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -108,6 +108,12 @@ public final class NotificationEntry extends ListEntry { /** If this notification was filtered out, then the filter that did the filtering. */ @Nullable NotifFilter mExcludingFilter; + /** + * The NotifFilter, if any, that was active on this notification during the previous run of + * the list builder. + */ + @Nullable NotifFilter mPreviousExcludingFilter; + /** If this was a group child that was promoted to the top level, then who did the promoting. */ @Nullable NotifPromoter mNotifPromoter; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java index f7d6cef92b8f..19f7cefe76a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java @@ -316,6 +316,7 @@ public class ShadeListBuilder implements Dumpable { // Step 7: Lock in our group structure and log anything that's changed since the last run mPipelineState.incrementTo(STATE_FINALIZING); + logFilterChanges(); logParentingChanges(); freeEmptyGroups(); @@ -363,6 +364,9 @@ public class ShadeListBuilder implements Dumpable { entry.setPreviousParent(entry.getParent()); entry.setParent(null); + entry.mPreviousExcludingFilter = entry.mExcludingFilter; + entry.mExcludingFilter = null; + if (entry.mFirstAddedIteration == -1) { entry.mFirstAddedIteration = mIterationCount; } @@ -371,8 +375,10 @@ public class ShadeListBuilder implements Dumpable { mNotifList.clear(); } - private void filterNotifs(Collection<? extends ListEntry> entries, - List<ListEntry> out, List<NotifFilter> filters) { + private void filterNotifs( + Collection<? extends ListEntry> entries, + List<ListEntry> out, + List<NotifFilter> filters) { final long now = mSystemClock.uptimeMillis(); for (ListEntry entry : entries) { if (entry instanceof GroupEntry) { @@ -585,8 +591,9 @@ public class ShadeListBuilder implements Dumpable { * filtered out during any of the filtering steps. */ private void annulAddition(ListEntry entry) { - entry.setSection(-1); - entry.mNotifSection = null; + // TODO: We should null out the entry's section and promoter here. However, if we do that, + // future runs will think that the section changed. We need a mPreviousNotifSection, + // similar to what we do for parents. entry.setParent(null); if (entry.mFirstAddedIteration == mIterationCount) { entry.mFirstAddedIteration = -1; @@ -615,6 +622,17 @@ public class ShadeListBuilder implements Dumpable { mGroups.values().removeIf(ge -> ge.getSummary() == null && ge.getChildren().isEmpty()); } + private void logFilterChanges() { + for (NotificationEntry entry : mAllEntries) { + if (entry.mExcludingFilter != entry.mPreviousExcludingFilter) { + mLogger.logFilterChanged( + entry.getKey(), + entry.mPreviousExcludingFilter, + entry.mExcludingFilter); + } + } + } + private void logParentingChanges() { for (NotificationEntry entry : mAllEntries) { if (entry.getParent() != entry.getPreviousParent()) { @@ -680,21 +698,8 @@ public class ShadeListBuilder implements Dumpable { }; private boolean applyFilters(NotificationEntry entry, long now, List<NotifFilter> filters) { - NotifFilter filter = findRejectingFilter(entry, now, filters); - - if (filter != entry.mExcludingFilter) { - mLogger.logFilterChanged( - entry.getKey(), - entry.mExcludingFilter != null ? entry.mExcludingFilter.getName() : null, - filter != null ? filter.getName() : null); - - // Note that groups and summaries can also be filtered out later if they're part of a - // malformed group. We currently don't have a great way to track that beyond parenting - // change logs. Consider adding something similar to mExcludingFilter for them. - entry.mExcludingFilter = filter; - } - - return filter != null; + entry.mExcludingFilter = findRejectingFilter(entry, now, filters); + return entry.mExcludingFilter != null; } @Nullable private static NotifFilter findRejectingFilter(NotificationEntry entry, long now, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java new file mode 100644 index 000000000000..261ae079a2c8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java @@ -0,0 +1,69 @@ +/* + * 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.collection.coordinator; + +import android.content.pm.UserInfo; +import android.util.SparseArray; + +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; + +import javax.inject.Inject; + +/** + * A coordinator that filters out notifications for other users + * + * The NotifCollection contains the notifs for ALL users, so we need to remove any notifications + * that have been posted specifically to other users. Note that some system notifications are not + * posted to any particular user, and so must be shown to everyone. + * + * TODO: The NotificationLockscreenUserManager currently maintains the list of active user profiles. + * We should spin that off into a standalone section at some point. + */ +public class HideNotifsForOtherUsersCoordinator implements Coordinator { + private final NotificationLockscreenUserManager mLockscreenUserManager; + + @Inject + public HideNotifsForOtherUsersCoordinator( + NotificationLockscreenUserManager lockscreenUserManager) { + mLockscreenUserManager = lockscreenUserManager; + } + + @Override + public void attach(NotifPipeline pipeline) { + pipeline.addPreGroupFilter(mFilter); + mLockscreenUserManager.addUserChangedListener(mUserChangedListener); + } + + private final NotifFilter mFilter = new NotifFilter("NotCurrentUserFilter") { + @Override + public boolean shouldFilterOut(NotificationEntry entry, long now) { + return !mLockscreenUserManager + .isCurrentProfile(entry.getSbn().getUser().getIdentifier()); + } + }; + + private final UserChangedListener mUserChangedListener = new UserChangedListener() { + @Override + public void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) { + mFilter.invalidateList(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java index aaf71f58cb5c..b7738569ad3c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java @@ -95,11 +95,6 @@ public class KeyguardCoordinator implements Coordinator { public boolean shouldFilterOut(NotificationEntry entry, long now) { final StatusBarNotification sbn = entry.getSbn(); - // FILTER OUT the notification when the notification isn't for the current profile - if (!mLockscreenUserManager.isCurrentProfile(sbn.getUserId())) { - return true; - } - // FILTER OUT the notification when the keyguard is showing and... if (mKeyguardStateController.isShowing()) { // ... user settings or the device policy manager doesn't allow lockscreen diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java index 98104f84f30e..03c0ae6fde50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java @@ -49,15 +49,17 @@ public class NotifCoordinators implements Dumpable { public NotifCoordinators( DumpManager dumpManager, FeatureFlags featureFlags, - HeadsUpCoordinator headsUpCoordinator, + HideNotifsForOtherUsersCoordinator hideNotifsForOtherUsersCoordinator, KeyguardCoordinator keyguardCoordinator, RankingCoordinator rankingCoordinator, ForegroundCoordinator foregroundCoordinator, DeviceProvisionedCoordinator deviceProvisionedCoordinator, BubbleCoordinator bubbleCoordinator, + HeadsUpCoordinator headsUpCoordinator, PreparationCoordinator preparationCoordinator) { dumpManager.registerDumpable(TAG, this); mCoordinators.add(new HideLocallyDismissedNotifsCoordinator()); + mCoordinators.add(hideNotifsForOtherUsersCoordinator); mCoordinators.add(keyguardCoordinator); mCoordinators.add(rankingCoordinator); mCoordinators.add(foregroundCoordinator); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java index 7e9e76096873..e9cbf32ee052 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java @@ -45,7 +45,8 @@ public class RankingCoordinator implements Coordinator { public void attach(NotifPipeline pipeline) { mStatusBarStateController.addCallback(mStatusBarStateCallback); - pipeline.addPreGroupFilter(mNotifFilter); + pipeline.addPreGroupFilter(mSuspendedFilter); + pipeline.addPreGroupFilter(mDozingFilter); } /** @@ -53,33 +54,30 @@ public class RankingCoordinator implements Coordinator { * NotifListBuilder invalidates the notification list each time the ranking is updated, * so we don't need to explicitly invalidate this filter on ranking update. */ - private final NotifFilter mNotifFilter = new NotifFilter(TAG) { + private final NotifFilter mSuspendedFilter = new NotifFilter("IsSuspendedFilter") { @Override public boolean shouldFilterOut(NotificationEntry entry, long now) { - // App suspended from Ranking - if (entry.getRanking().isSuspended()) { - return true; - } + return entry.getRanking().isSuspended(); + } + }; + private final NotifFilter mDozingFilter = new NotifFilter("IsDozingFilter") { + @Override + public boolean shouldFilterOut(NotificationEntry entry, long now) { // Dozing + DND Settings from Ranking object if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) { return true; } - if (!mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList()) { - return true; - } - - return false; + return !mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList(); } }; - private final StatusBarStateController.StateListener mStatusBarStateCallback = new StatusBarStateController.StateListener() { @Override public void onDozingChanged(boolean isDozing) { - mNotifFilter.invalidateList(); + mDozingFilter.invalidateList(); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt index 763547ce1638..e946cf16b3f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt @@ -23,6 +23,7 @@ import com.android.systemui.log.LogLevel.WARNING import com.android.systemui.log.dagger.NotificationLog import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import javax.inject.Inject class ShadeListBuilderLogger @Inject constructor( @@ -126,13 +127,13 @@ class ShadeListBuilderLogger @Inject constructor( fun logFilterChanged( key: String, - prevFilter: String?, - newFilter: String? + prevFilter: NotifFilter?, + newFilter: NotifFilter? ) { buffer.log(TAG, INFO, { str1 = key - str2 = prevFilter - str3 = newFilter + str2 = prevFilter?.name + str3 = newFilter?.name }, { "Filter changed for $str1: $str2 -> $str3" }) 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..823b18660bed 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; @@ -6466,7 +6478,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private boolean hasActiveNotifications() { if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { - return mNotifPipeline.getShadeList().isEmpty(); + return !mNotifPipeline.getShadeList().isEmpty(); } else { return mEntryManager.hasActiveNotifications(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index cf9d43eeff73..d70484e9cf41 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; - import android.annotation.IntDef; import android.content.Context; import android.content.res.ColorStateList; @@ -30,241 +28,98 @@ import android.os.Trace; import android.provider.Settings; import android.text.TextUtils; import android.util.AttributeSet; -import android.view.ViewTreeObserver; -import android.view.accessibility.AccessibilityNodeInfo; +import android.util.SparseArray; +import android.view.ViewTreeObserver.OnPreDrawListener; import com.android.internal.graphics.ColorUtils; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.KeyguardAffordanceView; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility; -import com.android.systemui.statusbar.policy.AccessibilityController; -import com.android.systemui.statusbar.policy.KeyguardStateController; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import javax.inject.Inject; -import javax.inject.Named; - /** * Manages the different states and animations of the unlock icon. */ -public class LockIcon extends KeyguardAffordanceView implements - ViewTreeObserver.OnPreDrawListener { - - private static final int STATE_LOCKED = 0; - private static final int STATE_LOCK_OPEN = 1; - private static final int STATE_SCANNING_FACE = 2; - private static final int STATE_BIOMETRICS_ERROR = 3; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final AccessibilityController mAccessibilityController; - private final KeyguardStateController mKeyguardStateController; - private final KeyguardBypassController mBypassController; - private final NotificationWakeUpCoordinator mWakeUpCoordinator; - private final HeadsUpManagerPhone mHeadsUpManager; - - private int mLastState = 0; - private boolean mForceUpdate; - private boolean mTransientBiometricsError; - private boolean mIsFaceUnlockState; - private boolean mSimLocked; - private int mDensity; +public class LockIcon extends KeyguardAffordanceView { + + static final int STATE_LOCKED = 0; + static final int STATE_LOCK_OPEN = 1; + static final int STATE_SCANNING_FACE = 2; + static final int STATE_BIOMETRICS_ERROR = 3; + private float mDozeAmount; + private int mIconColor; + private StateProvider mStateProvider; + private int mOldState; private boolean mPulsing; private boolean mDozing; - private boolean mDocked; - private boolean mBlockUpdates; - private int mIconColor; - private float mDozeAmount; - private boolean mBouncerShowingScrimmed; - private boolean mWakeAndUnlockRunning; - private boolean mKeyguardShowing; - private boolean mShowingLaunchAffordance; private boolean mKeyguardJustShown; - private boolean mUpdatePending; - private boolean mBouncerPreHideAnimation; - private int mStatusBarState = StatusBarState.SHADE; - - private final KeyguardStateController.Callback mKeyguardMonitorCallback = - new KeyguardStateController.Callback() { - @Override - public void onKeyguardShowingChanged() { - boolean force = false; - boolean wasShowing = mKeyguardShowing; - mKeyguardShowing = mKeyguardStateController.isShowing(); - if (!wasShowing && mKeyguardShowing && mBlockUpdates) { - mBlockUpdates = false; - force = true; - } - if (!wasShowing && mKeyguardShowing) { - mKeyguardJustShown = true; - } - update(force); - } - - @Override - public void onKeyguardFadingAwayChanged() { - if (!mKeyguardStateController.isKeyguardFadingAway()) { - mBouncerPreHideAnimation = false; - if (mBlockUpdates) { - mBlockUpdates = false; - update(true /* force */); - } - } - } - - @Override - public void onUnlockedChanged() { - update(); - } - }; - - @Inject - public LockIcon(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - AccessibilityController accessibilityController, - KeyguardBypassController bypassController, - NotificationWakeUpCoordinator wakeUpCoordinator, - KeyguardStateController keyguardStateController, - HeadsUpManagerPhone headsUpManager) { - super(context, attrs); - mContext = context; - mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - mAccessibilityController = accessibilityController; - mBypassController = bypassController; - mWakeUpCoordinator = wakeUpCoordinator; - mKeyguardStateController = keyguardStateController; - mHeadsUpManager = headsUpManager; - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mKeyguardStateController.addCallback(mKeyguardMonitorCallback); - mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); - update(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mKeyguardStateController.removeCallback(mKeyguardMonitorCallback); - } + private final SparseArray<Drawable> mDrawableCache = new SparseArray<>(); - /** - * If we're currently presenting an authentication error message. - */ - public void setTransientBiometricsError(boolean transientBiometricsError) { - mTransientBiometricsError = transientBiometricsError; - update(); - } - - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - final int density = newConfig.densityDpi; - if (density != mDensity) { - mDensity = density; - update(); - } - } - - public void update() { - update(false /* force */); - } - - public void update(boolean force) { - if (force) { - mForceUpdate = true; - } - if (!mUpdatePending) { - mUpdatePending = true; - getViewTreeObserver().addOnPreDrawListener(this); - } - } + private final OnPreDrawListener mOnPreDrawListener = new OnPreDrawListener() { + @Override + public boolean onPreDraw() { + getViewTreeObserver().removeOnPreDrawListener(this); - @Override - public boolean onPreDraw() { - mUpdatePending = false; - getViewTreeObserver().removeOnPreDrawListener(this); - - int state = getState(); - int lastState = mLastState; - boolean keyguardJustShown = mKeyguardJustShown; - mIsFaceUnlockState = state == STATE_SCANNING_FACE; - mLastState = state; - mKeyguardJustShown = false; - - boolean shouldUpdate = lastState != state || mForceUpdate; - if (mBlockUpdates && canBlockUpdates()) { - shouldUpdate = false; - } - if (shouldUpdate) { - mForceUpdate = false; - @LockAnimIndex final int lockAnimIndex = getAnimationIndexForTransition(lastState, - state, mPulsing, mDozing, keyguardJustShown); - boolean isAnim = lockAnimIndex != -1; - int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(state); - - Drawable icon = mContext.getDrawable(iconRes); - final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable - ? (AnimatedVectorDrawable) icon - : null; + int newState = mStateProvider.getState(); + Drawable icon = getIcon(newState); setImageDrawable(icon, false); - if (mIsFaceUnlockState) { - announceForAccessibility(getContext().getString( + + if (newState == STATE_SCANNING_FACE) { + announceForAccessibility(getResources().getString( R.string.accessibility_scanning_face)); } - if (animation != null && isAnim) { + if (icon instanceof AnimatedVectorDrawable) { + final AnimatedVectorDrawable animation = (AnimatedVectorDrawable) icon; animation.forceAnimationOnUI(); animation.clearAnimationCallbacks(); - animation.registerAnimationCallback(new Animatable2.AnimationCallback() { - @Override - public void onAnimationEnd(Drawable drawable) { - if (getDrawable() == animation && state == getState() - && doesAnimationLoop(lockAnimIndex)) { - animation.start(); - } else { - Trace.endAsyncSection("LockIcon#Animation", state); - } - } - }); - Trace.beginAsyncSection("LockIcon#Animation", state); + animation.registerAnimationCallback( + new Animatable2.AnimationCallback() { + @Override + public void onAnimationEnd(Drawable drawable) { + if (getDrawable() == animation + && newState == mStateProvider.getState() + && newState == STATE_SCANNING_FACE) { + animation.start(); + } else { + Trace.endAsyncSection("LockIcon#Animation", newState); + } + } + }); + Trace.beginAsyncSection("LockIcon#Animation", newState); animation.start(); } + + return true; } - updateDarkTint(); + }; - updateIconVisibility(); - updateClickability(); + public LockIcon(Context context, AttributeSet attrs) { + super(context, attrs); + } - return true; + void setStateProvider(StateProvider stateProvider) { + mStateProvider = stateProvider; + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mDrawableCache.clear(); } /** * Update the icon visibility * @return true if the visibility changed */ - boolean updateIconVisibility() { - boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked); - boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning - || mShowingLaunchAffordance; - if (mBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) { - if ((mHeadsUpManager.isHeadsUpGoingAway() || mHeadsUpManager.hasPinnedHeadsUp() - || mStatusBarState == StatusBarState.KEYGUARD) - && !mWakeUpCoordinator.getNotificationsFullyHidden()) { - invisible = true; - } - } - boolean wasInvisible = getVisibility() == INVISIBLE; - if (invisible != wasInvisible) { - setVisibility(invisible ? INVISIBLE : VISIBLE); + boolean updateIconVisibility(boolean visible) { + boolean wasVisible = getVisibility() == VISIBLE; + if (visible != wasVisible) { + setVisibility(visible ? VISIBLE : INVISIBLE); animate().cancel(); - if (!invisible) { + if (visible) { setScaleX(0); setScaleY(0); animate() @@ -280,49 +135,47 @@ public class LockIcon extends KeyguardAffordanceView implements return false; } - private boolean canBlockUpdates() { - return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway(); + void update(int oldState, boolean pulsing, boolean dozing, boolean keyguardJustShown) { + mOldState = oldState; + mPulsing = pulsing; + mDozing = dozing; + mKeyguardJustShown = keyguardJustShown; + + getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener); } - private void updateClickability() { - if (mAccessibilityController == null) { - return; - } - boolean canLock = mKeyguardStateController.isMethodSecure() - && mKeyguardStateController.canDismissLockScreen(); - boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled(); - setClickable(clickToUnlock); - setLongClickable(canLock && !clickToUnlock); - setFocusable(mAccessibilityController.isAccessibilityEnabled()); + void setDozeAmount(float dozeAmount) { + mDozeAmount = dozeAmount; + updateDarkTint(); } - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - boolean fingerprintRunning = mKeyguardUpdateMonitor.isFingerprintDetectionRunning(); - // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong - // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to - // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the - // check of whether non-strong biometric is allowed - boolean unlockingAllowed = mKeyguardUpdateMonitor - .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */); - if (fingerprintRunning && unlockingAllowed) { - AccessibilityNodeInfo.AccessibilityAction unlock - = new AccessibilityNodeInfo.AccessibilityAction( - AccessibilityNodeInfo.ACTION_CLICK, - getContext().getString(R.string.accessibility_unlock_without_fingerprint)); - info.addAction(unlock); - info.setHintText(getContext().getString( - R.string.accessibility_waiting_for_fingerprint)); - } else if (mIsFaceUnlockState) { - //Avoid 'button' to be spoken for scanning face - info.setClassName(LockIcon.class.getName()); - info.setContentDescription(getContext().getString( - R.string.accessibility_scanning_face)); + void onThemeChange(int iconColor) { + mDrawableCache.clear(); + mIconColor = iconColor; + updateDarkTint(); + } + + private void updateDarkTint() { + int color = ColorUtils.blendARGB(mIconColor, Color.WHITE, mDozeAmount); + setImageTintList(ColorStateList.valueOf(color)); + } + + private Drawable getIcon(int newState) { + @LockAnimIndex final int lockAnimIndex = + getAnimationIndexForTransition(mOldState, newState, mPulsing, mDozing, + mKeyguardJustShown); + + boolean isAnim = lockAnimIndex != -1; + int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(newState); + + if (!mDrawableCache.contains(iconRes)) { + mDrawableCache.put(iconRes, getResources().getDrawable(iconRes)); } + + return mDrawableCache.get(iconRes); } - private int getIconForState(int state) { + static int getIconForState(int state) { int iconRes; switch (state) { case STATE_LOCKED: @@ -343,11 +196,7 @@ public class LockIcon extends KeyguardAffordanceView implements return iconRes; } - private boolean doesAnimationLoop(@LockAnimIndex int lockAnimIndex) { - return lockAnimIndex == SCANNING; - } - - private static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing, + static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing, boolean dozing, boolean keyguardJustShown) { // Never animate when screen is off @@ -367,42 +216,10 @@ public class LockIcon extends KeyguardAffordanceView implements return -1; } - public void setBouncerShowingScrimmed(boolean bouncerShowing) { - mBouncerShowingScrimmed = bouncerShowing; - if (mBypassController.getBypassEnabled()) { - update(); - } - } - - /** - * Animate padlock opening when bouncer challenge is solved. - */ - public void onBouncerPreHideAnimation() { - mBouncerPreHideAnimation = true; - update(); - } - - void setIconColor(int iconColor) { - mIconColor = iconColor; - updateDarkTint(); - } - - void setSimLocked(boolean simLocked) { - mSimLocked = simLocked; - } - - /** Set if the device is docked. */ - public void setDocked(boolean docked) { - if (mDocked != docked) { - mDocked = docked; - update(); - } - } - @Retention(RetentionPolicy.SOURCE) @IntDef({ERROR, UNLOCK, LOCK, SCANNING}) @interface LockAnimIndex {} - private static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3; + static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3; private static final int[][] LOCK_ANIM_RES_IDS = new int[][] { { R.anim.lock_to_error, @@ -433,7 +250,7 @@ public class LockIcon extends KeyguardAffordanceView implements private int getThemedAnimationResId(@LockAnimIndex int lockAnimIndex) { final String setting = TextUtils.emptyIfNull( Settings.Secure.getString(getContext().getContentResolver(), - Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)); + Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)); if (setting.contains("com.android.theme.icon_pack.circular.android")) { return LOCK_ANIM_RES_IDS[1][lockAnimIndex]; } else if (setting.contains("com.android.theme.icon_pack.filled.android")) { @@ -444,83 +261,8 @@ public class LockIcon extends KeyguardAffordanceView implements return LOCK_ANIM_RES_IDS[0][lockAnimIndex]; } - private int getState() { - KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class); - if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing - || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) { - return STATE_LOCK_OPEN; - } else if (mTransientBiometricsError) { - return STATE_BIOMETRICS_ERROR; - } else if (updateMonitor.isFaceDetectionRunning() && !mPulsing) { - return STATE_SCANNING_FACE; - } else { - return STATE_LOCKED; - } - } - - /** - * When keyguard is in pulsing (AOD2) state. - * @param pulsing {@code true} when pulsing. - */ - public void setPulsing(boolean pulsing) { - mPulsing = pulsing; - update(); + interface StateProvider { + int getState(); } - private void updateDarkTint() { - int color = ColorUtils.blendARGB(mIconColor, Color.WHITE, mDozeAmount); - setImageTintList(ColorStateList.valueOf(color)); - } - - /** - * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the - * icon on top of the black front scrim. - * @param wakeAndUnlock are we wake and unlocking - * @param isUnlock are we currently unlocking - */ - public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) { - if (wakeAndUnlock) { - mWakeAndUnlockRunning = true; - } - if (isUnlock && mBypassController.getBypassEnabled() && canBlockUpdates()) { - // We don't want the icon to change while we are unlocking - mBlockUpdates = true; - } - update(); - } - - /** - * When we're launching an affordance, like double pressing power to open camera. - */ - public void onShowingLaunchAffordanceChanged(boolean showing) { - mShowingLaunchAffordance = showing; - update(); - } - - /** - * Called whenever the scrims become opaque, transparent or semi-transparent. - */ - public void onScrimVisibilityChanged(@ScrimVisibility int scrimsVisible) { - if (mWakeAndUnlockRunning - && scrimsVisible == ScrimController.TRANSPARENT) { - mWakeAndUnlockRunning = false; - update(); - } - } - - void setDozing(boolean dozing) { - mDozing = dozing; - update(); - } - - void setDozeAmount(float dozeAmount) { - mDozeAmount = dozeAmount; - updateDarkTint(); - } - - /** Set the StatusBarState. */ - public void setStatusBarState(int statusBarState) { - mStatusBarState = statusBarState; - updateIconVisibility(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index 2b1a8a472d9e..f7c861b84a68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -16,11 +16,19 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.statusbar.phone.LockIcon.STATE_BIOMETRICS_ERROR; +import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCKED; +import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCK_OPEN; +import static com.android.systemui.statusbar.phone.LockIcon.STATE_SCANNING_FACE; + +import android.content.res.Configuration; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.hardware.biometrics.BiometricSourceType; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import androidx.annotation.Nullable; @@ -29,15 +37,18 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator.WakeUpListener; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.Optional; @@ -59,6 +70,21 @@ public class LockscreenLockIconController { private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; private final KeyguardBypassController mKeyguardBypassController; private final Optional<DockManager> mDockManager; + private final KeyguardStateController mKeyguardStateController; + private final Resources mResources; + private final HeadsUpManagerPhone mHeadsUpManagerPhone; + private boolean mKeyguardShowing; + private boolean mKeyguardJustShown; + private boolean mBlockUpdates; + private boolean mPulsing; + private boolean mDozing; + private boolean mSimLocked; + private boolean mTransientBiometricsError; + private boolean mDocked; + private boolean mWakeAndUnlockRunning; + private boolean mShowingLaunchAffordance; + private boolean mBouncerShowingScrimmed; + private int mStatusBarState = StatusBarState.SHADE; private LockIcon mLockIcon; private View.OnAttachStateChangeListener mOnAttachStateChangeListener = @@ -69,10 +95,13 @@ public class LockscreenLockIconController { mConfigurationController.addCallback(mConfigurationListener); mNotificationWakeUpCoordinator.addListener(mWakeUpListener); mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); + mKeyguardStateController.addCallback(mKeyguardMonitorCallback); mDockManager.ifPresent(dockManager -> dockManager.addListener(mDockEventListener)); + mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); mConfigurationListener.onThemeChanged(); + update(); } @Override @@ -81,7 +110,7 @@ public class LockscreenLockIconController { mConfigurationController.removeCallback(mConfigurationListener); mNotificationWakeUpCoordinator.removeListener(mWakeUpListener); mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); - + mKeyguardStateController.removeCallback(mKeyguardMonitorCallback); mDockManager.ifPresent(dockManager -> dockManager.removeListener(mDockEventListener)); } @@ -91,32 +120,44 @@ public class LockscreenLockIconController { new StatusBarStateController.StateListener() { @Override public void onDozingChanged(boolean isDozing) { - mLockIcon.setDozing(isDozing); + setDozing(isDozing); } @Override public void onDozeAmountChanged(float linear, float eased) { - mLockIcon.setDozeAmount(eased); + if (mLockIcon != null) { + mLockIcon.setDozeAmount(eased); + } } @Override public void onStateChanged(int newState) { - mLockIcon.setStatusBarState(newState); + setStatusBarState(newState); } }; private final ConfigurationListener mConfigurationListener = new ConfigurationListener() { + private int mDensity; + @Override public void onThemeChanged() { + if (mLockIcon == null) { + return; + } + TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes( null, new int[]{ R.attr.wallpaperTextColor }, 0, 0); int iconColor = typedArray.getColor(0, Color.WHITE); typedArray.recycle(); - mLockIcon.setIconColor(iconColor); + mLockIcon.onThemeChange(iconColor); } @Override public void onDensityOrFontScaleChanged() { + if (mLockIcon == null) { + return; + } + ViewGroup.LayoutParams lp = mLockIcon.getLayoutParams(); if (lp == null) { return; @@ -125,24 +166,41 @@ public class LockscreenLockIconController { lp.height = mLockIcon.getResources().getDimensionPixelSize( R.dimen.keyguard_lock_height); mLockIcon.setLayoutParams(lp); - mLockIcon.update(true /* force */); + update(true /* force */); } @Override public void onLocaleListChanged() { + if (mLockIcon == null) { + return; + } + mLockIcon.setContentDescription( mLockIcon.getResources().getText(R.string.accessibility_unlock_button)); - mLockIcon.update(true /* force */); + update(true /* force */); + } + + @Override + public void onConfigChanged(Configuration newConfig) { + final int density = newConfig.densityDpi; + if (density != mDensity) { + mDensity = density; + update(); + } } }; private final WakeUpListener mWakeUpListener = new WakeUpListener() { @Override + public void onPulseExpansionChanged(boolean expandingChanged) { + } + + @Override public void onFullyHiddenChanged(boolean isFullyHidden) { if (mKeyguardBypassController.getBypassEnabled()) { - boolean changed = mLockIcon.updateIconVisibility(); + boolean changed = updateIconVisibility(); if (changed) { - mLockIcon.update(); + update(); } } } @@ -152,30 +210,103 @@ public class LockscreenLockIconController { new KeyguardUpdateMonitorCallback() { @Override public void onSimStateChanged(int subId, int slotId, int simState) { - mLockIcon.setSimLocked(mKeyguardUpdateMonitor.isSimPinSecure()); - mLockIcon.update(); + mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); + update(); } @Override public void onKeyguardVisibilityChanged(boolean showing) { - mLockIcon.update(); + update(); } @Override public void onBiometricRunningStateChanged(boolean running, BiometricSourceType biometricSourceType) { - mLockIcon.update(); + update(); } @Override public void onStrongAuthStateChanged(int userId) { - mLockIcon.update(); + update(); } }; private final DockManager.DockEventListener mDockEventListener = - event -> mLockIcon.setDocked(event == DockManager.STATE_DOCKED - || event == DockManager.STATE_DOCKED_HIDE); + event -> { + boolean docked = + event == DockManager.STATE_DOCKED || event == DockManager.STATE_DOCKED_HIDE; + if (docked != mDocked) { + mDocked = docked; + update(); + } + }; + + private final KeyguardStateController.Callback mKeyguardMonitorCallback = + new KeyguardStateController.Callback() { + @Override + public void onKeyguardShowingChanged() { + boolean force = false; + boolean wasShowing = mKeyguardShowing; + mKeyguardShowing = mKeyguardStateController.isShowing(); + if (!wasShowing && mKeyguardShowing && mBlockUpdates) { + mBlockUpdates = false; + force = true; + } + if (!wasShowing && mKeyguardShowing) { + mKeyguardJustShown = true; + } + update(force); + } + + @Override + public void onKeyguardFadingAwayChanged() { + if (!mKeyguardStateController.isKeyguardFadingAway()) { + if (mBlockUpdates) { + mBlockUpdates = false; + update(true /* force */); + } + } + } + + @Override + public void onUnlockedChanged() { + update(); + } + }; + + private final View.AccessibilityDelegate mAccessibilityDelegate = + new View.AccessibilityDelegate() { + @Override + public void onInitializeAccessibilityNodeInfo(View host, + AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + boolean fingerprintRunning = + mKeyguardUpdateMonitor.isFingerprintDetectionRunning(); + // Only checking if unlocking with Biometric is allowed (no matter strong or + // non-strong as long as primary auth, i.e. PIN/pattern/password, is not + // required), so it's ok to pass true for isStrongBiometric to + // isUnlockingWithBiometricAllowed() to bypass the check of whether non-strong + // biometric is allowed + boolean unlockingAllowed = mKeyguardUpdateMonitor + .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */); + if (fingerprintRunning && unlockingAllowed) { + AccessibilityNodeInfo.AccessibilityAction unlock = + new AccessibilityNodeInfo.AccessibilityAction( + AccessibilityNodeInfo.ACTION_CLICK, + mResources.getString( + R.string.accessibility_unlock_without_fingerprint)); + info.addAction(unlock); + info.setHintText(mResources.getString( + R.string.accessibility_waiting_for_fingerprint)); + } else if (getState() == STATE_SCANNING_FACE) { + //Avoid 'button' to be spoken for scanning face + info.setClassName(LockIcon.class.getName()); + info.setContentDescription(mResources.getString( + R.string.accessibility_scanning_face)); + } + } + }; + private int mLastState; @Inject public LockscreenLockIconController(LockscreenGestureLogger lockscreenGestureLogger, @@ -188,7 +319,10 @@ public class LockscreenLockIconController { ConfigurationController configurationController, NotificationWakeUpCoordinator notificationWakeUpCoordinator, KeyguardBypassController keyguardBypassController, - @Nullable DockManager dockManager) { + @Nullable DockManager dockManager, + KeyguardStateController keyguardStateController, + @Main Resources resources, + HeadsUpManagerPhone headsUpManagerPhone) { mLockscreenGestureLogger = lockscreenGestureLogger; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; @@ -200,24 +334,31 @@ public class LockscreenLockIconController { mNotificationWakeUpCoordinator = notificationWakeUpCoordinator; mKeyguardBypassController = keyguardBypassController; mDockManager = dockManager == null ? Optional.empty() : Optional.of(dockManager); + mKeyguardStateController = keyguardStateController; + mResources = resources; + mHeadsUpManagerPhone = headsUpManagerPhone; mKeyguardIndicationController.setLockIconController(this); } /** * Associate the controller with a {@link LockIcon} + * + * TODO: change to an init method and inject the view. */ public void attach(LockIcon lockIcon) { mLockIcon = lockIcon; mLockIcon.setOnClickListener(this::handleClick); mLockIcon.setOnLongClickListener(this::handleLongClick); + mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate); + mLockIcon.setStateProvider(this::getState); if (mLockIcon.isAttachedToWindow()) { mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon); } mLockIcon.addOnAttachStateChangeListener(mOnAttachStateChangeListener); - mLockIcon.setStatusBarState(mStatusBarStateController.getState()); + setStatusBarState(mStatusBarStateController.getState()); } public LockIcon getView() { @@ -228,8 +369,10 @@ public class LockscreenLockIconController { * Called whenever the scrims become opaque, transparent or semi-transparent. */ public void onScrimVisibilityChanged(Integer scrimsVisible) { - if (mLockIcon != null) { - mLockIcon.onScrimVisibilityChanged(scrimsVisible); + if (mWakeAndUnlockRunning + && scrimsVisible == ScrimController.TRANSPARENT) { + mWakeAndUnlockRunning = false; + update(); } } @@ -237,55 +380,56 @@ public class LockscreenLockIconController { * Propagate {@link StatusBar} pulsing state. */ public void setPulsing(boolean pulsing) { - if (mLockIcon != null) { - mLockIcon.setPulsing(pulsing); - } + mPulsing = pulsing; + update(); } /** - * Called when the biometric authentication mode changes. - * - * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()} - * @param isUnlock If the type is {@link BiometricUnlockController#isBiometricUnlock()} () + * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the + * icon on top of the black front scrim. + * @param wakeAndUnlock are we wake and unlocking + * @param isUnlock are we currently unlocking */ public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) { - if (mLockIcon != null) { - mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock, isUnlock); + if (wakeAndUnlock) { + mWakeAndUnlockRunning = true; + } + if (isUnlock && mKeyguardBypassController.getBypassEnabled() && canBlockUpdates()) { + // We don't want the icon to change while we are unlocking + mBlockUpdates = true; } + update(); } /** * When we're launching an affordance, like double pressing power to open camera. */ public void onShowingLaunchAffordanceChanged(Boolean showing) { - if (mLockIcon != null) { - mLockIcon.onShowingLaunchAffordanceChanged(showing); - } + mShowingLaunchAffordance = showing; + update(); } /** Sets whether the bouncer is showing. */ public void setBouncerShowingScrimmed(boolean bouncerShowing) { - if (mLockIcon != null) { - mLockIcon.setBouncerShowingScrimmed(bouncerShowing); + mBouncerShowingScrimmed = bouncerShowing; + if (mKeyguardBypassController.getBypassEnabled()) { + update(); } } /** - * When {@link KeyguardBouncer} starts to be dismissed and starts to play its animation. + * Animate padlock opening when bouncer challenge is solved. */ public void onBouncerPreHideAnimation() { - if (mLockIcon != null) { - mLockIcon.onBouncerPreHideAnimation(); - } + update(); } /** * If we're currently presenting an authentication error message. */ public void setTransientBiometricsError(boolean transientBiometricsError) { - if (mLockIcon != null) { - mLockIcon.setTransientBiometricsError(transientBiometricsError); - } + mTransientBiometricsError = transientBiometricsError; + update(); } private boolean handleLongClick(View view) { @@ -306,4 +450,90 @@ public class LockscreenLockIconController { } mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); } + + private void update() { + update(false /* force */); + } + + private void update(boolean force) { + int state = getState(); + boolean shouldUpdate = mLastState != state || force; + if (mBlockUpdates && canBlockUpdates()) { + shouldUpdate = false; + } + if (shouldUpdate && mLockIcon != null) { + mLockIcon.update(mLastState, mPulsing, mDozing, mKeyguardJustShown); + } + mLastState = state; + mKeyguardJustShown = false; + updateIconVisibility(); + updateClickability(); + } + + private int getState() { + if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing + || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) { + return STATE_LOCK_OPEN; + } else if (mTransientBiometricsError) { + return STATE_BIOMETRICS_ERROR; + } else if (mKeyguardUpdateMonitor.isFaceDetectionRunning() && !mPulsing) { + return STATE_SCANNING_FACE; + } else { + return STATE_LOCKED; + } + } + + private boolean canBlockUpdates() { + return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway(); + } + + private void setDozing(boolean isDozing) { + mDozing = isDozing; + update(); + } + + /** Set the StatusBarState. */ + private void setStatusBarState(int statusBarState) { + mStatusBarState = statusBarState; + updateIconVisibility(); + } + + /** + * Update the icon visibility + * @return true if the visibility changed + */ + private boolean updateIconVisibility() { + boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked); + boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning + || mShowingLaunchAffordance; + if (mKeyguardBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) { + if ((mHeadsUpManagerPhone.isHeadsUpGoingAway() + || mHeadsUpManagerPhone.hasPinnedHeadsUp() + || mStatusBarState == StatusBarState.KEYGUARD) + && !mNotificationWakeUpCoordinator.getNotificationsFullyHidden()) { + invisible = true; + } + } + + if (mLockIcon == null) { + return false; + } + + return mLockIcon.updateIconVisibility(!invisible); + } + + private void updateClickability() { + if (mAccessibilityController == null) { + return; + } + boolean canLock = mKeyguardStateController.isMethodSecure() + && mKeyguardStateController.canDismissLockScreen(); + boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled(); + if (mLockIcon != null) { + mLockIcon.setClickable(clickToUnlock); + mLockIcon.setLongClickable(canLock && !clickToUnlock); + mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled()); + } + } + } 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/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 1c1e7c4eaa4a..977a307a3d95 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -66,7 +66,7 @@ public class PhoneStatusBarView extends PanelBar { } }; private DarkReceiver mBattery; - private int mRotationOrientation; + private int mRotationOrientation = -1; @Nullable private View mCenterIconSpace; @Nullable diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index 760a6d6a71c3..c6f79831e022 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -99,11 +99,12 @@ public class StatusBarWindowView extends FrameLayout { } // padding needed for corner cutout. - int leftCornerCutoutPadding = 0; - int rightCornerCutoutPadding = 0; + int leftCornerCutoutPadding = cutout.getSafeInsetLeft(); + int rightCornerCutoutPadding = cutout.getSafeInsetRight(); if (cornerCutoutPadding != null) { - leftCornerCutoutPadding = cornerCutoutPadding.first; - rightCornerCutoutPadding = cornerCutoutPadding.second; + leftCornerCutoutPadding = Math.max(leftCornerCutoutPadding, cornerCutoutPadding.first); + rightCornerCutoutPadding = Math.max(rightCornerCutoutPadding, + cornerCutoutPadding.second); } return new Pair<>( diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index 56aae17f451e..c63712389a80 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -34,7 +34,6 @@ import com.android.systemui.qs.QuickStatusBarHeader; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; -import com.android.systemui.statusbar.phone.LockIcon; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -148,11 +147,6 @@ public class InjectionInflationController { KeyguardMessageArea createKeyguardMessageArea(); /** - * Creates the keyguard LockIcon. - */ - LockIcon createLockIcon(); - - /** * Creates the QSPanel. */ QSPanel createQSPanel(); diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java index 54118390325c..bae5bb41aa5a 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -20,6 +20,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.res.Configuration; +import android.graphics.Point; import android.os.Handler; import android.os.RemoteException; import android.util.Slog; @@ -97,7 +98,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation() != pd.mRotation && isImeShowing(displayId)) { - pd.startAnimation(true); + pd.startAnimation(true, false /* forceRestart */); } } @@ -200,7 +201,15 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged continue; } if (activeControl.getType() == InsetsState.ITYPE_IME) { - mImeSourceControl = activeControl; + mHandler.post(() -> { + final Point lastSurfacePosition = mImeSourceControl != null + ? mImeSourceControl.getSurfacePosition() : null; + mImeSourceControl = activeControl; + if (!activeControl.getSurfacePosition().equals(lastSurfacePosition) + && mAnimation != null) { + startAnimation(mImeShowing, true /* forceRestart */); + } + }); } } } @@ -212,7 +221,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged return; } if (DEBUG) Slog.d(TAG, "Got showInsets for ime"); - startAnimation(true /* show */); + startAnimation(true /* show */, false /* forceRestart */); } @Override @@ -221,7 +230,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged return; } if (DEBUG) Slog.d(TAG, "Got hideInsets for ime"); - startAnimation(false /* show */); + startAnimation(false /* show */, false /* forceRestart */); } /** @@ -239,7 +248,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged return imeSource.getFrame().top + (int) surfaceOffset; } - private void startAnimation(final boolean show) { + private void startAnimation(final boolean show, final boolean forceRestart) { final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME); if (imeSource == null || mImeSourceControl == null) { return; @@ -250,7 +259,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged + (mAnimationDirection == DIRECTION_SHOW ? "SHOW" : (mAnimationDirection == DIRECTION_HIDE ? "HIDE" : "NONE"))); } - if ((mAnimationDirection == DIRECTION_SHOW && show) + if (!forceRestart && (mAnimationDirection == DIRECTION_SHOW && show) || (mAnimationDirection == DIRECTION_HIDE && !show)) { return; } @@ -270,11 +279,6 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged final float shownY = defaultY; final float startY = show ? hiddenY : shownY; final float endY = show ? shownY : hiddenY; - if (mImeShowing && show) { - // IME is already showing, so set seek to end - seekValue = shownY; - seek = true; - } mImeShowing = show; mAnimation = ValueAnimator.ofFloat(startY, endY); mAnimation.setDuration( 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..072bc446fd21 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt @@ -0,0 +1,145 @@ +/* + * 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.arch.core.executor.ArchTaskExecutor +import androidx.arch.core.executor.TaskExecutor +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 songView: TextView + private lateinit var artistView: TextView + @Mock private lateinit var mockIcon: Icon + + private val taskExecutor: TaskExecutor = object : TaskExecutor() { + public override fun executeOnDiskIO(runnable: Runnable) { + runnable.run() + } + public override fun postToMainThread(runnable: Runnable) { + runnable.run() + } + public override fun isMainThread(): Boolean { + return true + } + } + + @Before + public fun setup() { + fakeExecutor = FakeExecutor(FakeSystemClock()) + keyguardMediaPlayer = KeyguardMediaPlayer(context, fakeExecutor) + mockIcon = mock(Icon::class.java) + + mockView = mock(View::class.java) + songView = TextView(context) + artistView = TextView(context) + whenever<TextView>(mockView.findViewById(R.id.header_title)).thenReturn(songView) + whenever<TextView>(mockView.findViewById(R.id.header_artist)).thenReturn(artistView) + + mediaMetadata = MediaMetadata.Builder() + entry = NotificationEntryBuilder() + + ArchTaskExecutor.getInstance().setDelegate(taskExecutor) + + keyguardMediaPlayer.bindView(mockView) + } + + @After + public fun tearDown() { + keyguardMediaPlayer.unbindView() + ArchTaskExecutor.getInstance().setDelegate(null) + } + + @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()) + FakeExecutor.exhaustExecutors(fakeExecutor) + verify(mockView).setVisibility(View.VISIBLE) + } + + @Test + public fun testClearControls() { + keyguardMediaPlayer.clearControls() + FakeExecutor.exhaustExecutors(fakeExecutor) + verify(mockView).setVisibility(View.GONE) + } + + @Test + public fun testSongName() { + val song: String = "Song" + mediaMetadata.putText(MediaMetadata.METADATA_KEY_TITLE, song) + + keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build()) + + assertThat(fakeExecutor.runAllReady()).isEqualTo(1) + assertThat(songView.getText()).isEqualTo(song) + } + + @Test + public fun testArtistName() { + val artist: String = "Artist" + mediaMetadata.putText(MediaMetadata.METADATA_KEY_ARTIST, artist) + + keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build()) + + assertThat(fakeExecutor.runAllReady()).isEqualTo(1) + assertThat(artistView.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/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 6e612d7124ed..6f3fbb9cbd2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -62,7 +62,9 @@ import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dump.DumpManager; +import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationRemoveInterceptor; @@ -90,6 +92,8 @@ import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.FloatingContentCoordinator; import com.android.systemui.util.InjectionInflationController; +import com.google.common.collect.ImmutableList; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -136,6 +140,9 @@ public class BubbleControllerTest extends SysuiTestCase { @Mock private FloatingContentCoordinator mFloatingContentCoordinator; + private SysUiState mSysUiState; + private boolean mSysUiStateBubblesExpanded; + @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor; @Captor @@ -229,6 +236,11 @@ public class BubbleControllerTest extends SysuiTestCase { mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); + mSysUiState = new SysUiState(); + mSysUiState.addCallback(sysUiFlags -> + mSysUiStateBubblesExpanded = + (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0); + TestableNotificationInterruptStateProviderImpl interruptionStateProvider = new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), mock(PowerManager.class), @@ -257,7 +269,8 @@ public class BubbleControllerTest extends SysuiTestCase { mNotifPipeline, mFeatureFlagsOldPipeline, mDumpManager, - mFloatingContentCoordinator); + mFloatingContentCoordinator, + mSysUiState); mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener); mBubbleController.setExpandListener(mBubbleExpandListener); @@ -277,6 +290,7 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleController.hasBubbles()); verify(mBubbleStateChangeListener).onHasBubblesChanged(true); + assertFalse(mSysUiStateBubblesExpanded); } @Test @@ -284,6 +298,7 @@ public class BubbleControllerTest extends SysuiTestCase { assertFalse(mBubbleController.hasBubbles()); mBubbleController.updateBubble(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); + assertFalse(mSysUiStateBubblesExpanded); } @Test @@ -300,6 +315,25 @@ public class BubbleControllerTest extends SysuiTestCase { assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey())); verify(mNotificationEntryManager, times(2)).updateNotifications(anyString()); verify(mBubbleStateChangeListener).onHasBubblesChanged(false); + + assertFalse(mSysUiStateBubblesExpanded); + } + + @Test + public void testPromoteBubble_autoExpand() { + mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.removeBubble( + mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE); + + Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getEntry().getKey()); + assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b)); + + Bubble b2 = mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()); + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(b2); + + mBubbleController.promoteBubbleFromOverflow(b); + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(b); } @Test @@ -323,6 +357,8 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mNotificationEntryManager, times(1)).performRemoveNotification( eq(mRow.getEntry().getSbn()), anyInt()); assertFalse(mBubbleController.hasBubbles()); + + assertFalse(mSysUiStateBubblesExpanded); } @Test @@ -340,6 +376,8 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mNotificationEntryManager, times(3)).updateNotifications(any()); assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey())); assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey())); + + assertFalse(mSysUiStateBubblesExpanded); } @Test @@ -363,6 +401,8 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); assertTrue(mNotificationShadeWindowController.getBubbleExpanded()); + assertTrue(mSysUiStateBubblesExpanded); + // Make sure the notif is suppressed assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( mRow.getEntry())); @@ -372,6 +412,8 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey()); assertFalse(mBubbleController.isStackExpanded()); assertFalse(mNotificationShadeWindowController.getBubbleExpanded()); + + assertFalse(mSysUiStateBubblesExpanded); } @Test @@ -395,6 +437,8 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey()); + assertTrue(mSysUiStateBubblesExpanded); + // Last added is the one that is expanded assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry()); assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( @@ -416,6 +460,8 @@ public class BubbleControllerTest extends SysuiTestCase { // Collapse mBubbleController.collapseStack(); assertFalse(mBubbleController.isStackExpanded()); + + assertFalse(mSysUiStateBubblesExpanded); } @Test @@ -437,6 +483,8 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); + assertTrue(mSysUiStateBubblesExpanded); + // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( mRow.getEntry())); @@ -463,6 +511,8 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); + assertTrue(mSysUiStateBubblesExpanded); + // Notif is suppressed after expansion assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( mRow.getEntry())); @@ -493,6 +543,8 @@ public class BubbleControllerTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleController.expandStack(); + assertTrue(mSysUiStateBubblesExpanded); + assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey()); @@ -522,6 +574,8 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey()); verify(mBubbleStateChangeListener).onHasBubblesChanged(false); assertFalse(mBubbleController.hasBubbles()); + + assertFalse(mSysUiStateBubblesExpanded); } @Test @@ -541,6 +595,8 @@ public class BubbleControllerTest extends SysuiTestCase { // # of bubbles should change verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */); + + assertFalse(mSysUiStateBubblesExpanded); } @Test @@ -559,6 +615,8 @@ public class BubbleControllerTest extends SysuiTestCase { // # of bubbles should change verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */); + + assertTrue(mSysUiStateBubblesExpanded); } @Test @@ -579,6 +637,8 @@ public class BubbleControllerTest extends SysuiTestCase { // # of bubbles should change verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */); + + assertFalse(mSysUiStateBubblesExpanded); } @Test @@ -605,6 +665,8 @@ public class BubbleControllerTest extends SysuiTestCase { // # of bubbles should change verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */); + + assertFalse(mSysUiStateBubblesExpanded); } @Test @@ -619,6 +681,8 @@ public class BubbleControllerTest extends SysuiTestCase { mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL); mBubbleController.expandStackAndSelectBubble(key); + + assertTrue(mSysUiStateBubblesExpanded); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java index 624464401a4a..a31e3f8d7cc9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java @@ -58,6 +58,7 @@ import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dump.DumpManager; +import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -132,6 +133,9 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { private KeyguardBypassController mKeyguardBypassController; @Mock private FloatingContentCoordinator mFloatingContentCoordinator; + + private SysUiState mSysUiState = new SysUiState(); + @Captor private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor; private TestableBubbleController mBubbleController; @@ -242,7 +246,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mNotifPipeline, mFeatureFlagsNewPipeline, mDumpManager, - mFloatingContentCoordinator); + mFloatingContentCoordinator, + mSysUiState); mBubbleController.addNotifCallback(mNotifCallback); mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener); mBubbleController.setExpandListener(mBubbleExpandListener); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java index d3d90c408468..f4861028e81a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java @@ -19,6 +19,7 @@ package com.android.systemui.bubbles; import android.content.Context; import com.android.systemui.dump.DumpManager; +import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -52,12 +53,13 @@ public class TestableBubbleController extends BubbleController { NotifPipeline notifPipeline, FeatureFlags featureFlags, DumpManager dumpManager, - FloatingContentCoordinator floatingContentCoordinator) { + FloatingContentCoordinator floatingContentCoordinator, + SysUiState sysUiState) { super(context, notificationShadeWindowController, statusBarStateController, shadeController, data, Runnable::run, configurationController, interruptionStateProvider, zenModeController, lockscreenUserManager, groupManager, entryManager, - notifPipeline, featureFlags, dumpManager, floatingContentCoordinator); + notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState); setInflateSynchronously(true); } } 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/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt index f061f34072d0..f4583f99f2d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt @@ -22,7 +22,6 @@ import android.testing.TestableLooper.RunWithLooper import android.view.Choreographer import android.view.View import android.view.ViewRootImpl -import androidx.dynamicanimation.animation.SpringAnimation import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -35,10 +34,14 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.Mock -import org.mockito.Mockito.* +import org.mockito.Mockito.`when` +import org.mockito.Mockito.any +import org.mockito.Mockito.anyFloat +import org.mockito.Mockito.anyString +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit @RunWith(AndroidTestingRunner::class) @@ -56,7 +59,8 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var root: View @Mock private lateinit var viewRootImpl: ViewRootImpl - @Mock private lateinit var shadeSpring: SpringAnimation + @Mock private lateinit var shadeSpring: NotificationShadeDepthController.DepthAnimation + @Mock private lateinit var globalActionsSpring: NotificationShadeDepthController.DepthAnimation @JvmField @Rule val mockitoRule = MockitoJUnit.rule() private lateinit var statusBarStateListener: StatusBarStateController.StateListener @@ -76,6 +80,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { keyguardStateController, choreographer, wallpaperManager, notificationShadeWindowController, dumpManager) notificationShadeDepthController.shadeSpring = shadeSpring + notificationShadeDepthController.globalActionsSpring = globalActionsSpring notificationShadeDepthController.root = root val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java) @@ -92,7 +97,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { fun onPanelExpansionChanged_apliesBlur_ifShade() { notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */, false /* tracking */) - verify(shadeSpring).animateToFinalPosition(eq(maxBlur.toFloat())) + verify(shadeSpring).animateTo(eq(maxBlur), any()) } @Test @@ -102,13 +107,13 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { statusBarState = StatusBarState.KEYGUARD statusBarStateListener.onStateChanged(statusBarState) - verify(shadeSpring).animateToFinalPosition(eq(0f)) + verify(shadeSpring).animateTo(eq(0), any()) } @Test - fun updateGlobalDialogVisibility_schedulesUpdate() { + fun updateGlobalDialogVisibility_appliesBlur() { notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root) - verify(choreographer).postFrameCallback(any()) + verify(globalActionsSpring).animateTo(eq(maxBlur / 2), safeEq(root)) } private fun <T : Any> safeEq(value: T): T { 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/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java index 261dc829c7e2..f4fbd7b7d8a8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java @@ -48,7 +48,7 @@ public class NotificationEntryBuilder { /* ListEntry properties */ private GroupEntry mParent; - private int mSection; + private int mSection = -1; public NotificationEntry build() { StatusBarNotification sbn = mSbn != null ? mSbn : mSbnBuilder.build(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java new file mode 100644 index 000000000000..87fc02062ad4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java @@ -0,0 +1,106 @@ +/* + * 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.collection.coordinator; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.util.SparseArray; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable.PluggableListener; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class HideNotifsForOtherUsersCoordinatorTest extends SysuiTestCase { + + @Mock private NotificationLockscreenUserManager mLockscreenUserManager; + @Mock private NotifPipeline mNotifPipeline; + @Mock private PluggableListener<NotifFilter> mInvalidationListener; + + @Captor private ArgumentCaptor<UserChangedListener> mUserChangedListenerCaptor; + @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor; + + private UserChangedListener mCapturedUserChangeListener; + private NotifFilter mCapturedNotifFilter; + + private NotificationEntry mEntry = new NotificationEntryBuilder().build(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + HideNotifsForOtherUsersCoordinator coordinator = + new HideNotifsForOtherUsersCoordinator(mLockscreenUserManager); + coordinator.attach(mNotifPipeline); + + verify(mLockscreenUserManager).addUserChangedListener(mUserChangedListenerCaptor.capture()); + verify(mNotifPipeline).addPreGroupFilter(mNotifFilterCaptor.capture()); + + mCapturedUserChangeListener = mUserChangedListenerCaptor.getValue(); + mCapturedNotifFilter = mNotifFilterCaptor.getValue(); + + mCapturedNotifFilter.setInvalidationListener(mInvalidationListener); + } + + @Test + public void testFilterOutNotifsFromOtherProfiles() { + // GIVEN that all notifs are NOT for the current user + when(mLockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false); + + // THEN they should all be filtered out + assertTrue(mCapturedNotifFilter.shouldFilterOut(mEntry, 0)); + } + + @Test + public void testPreserveNotifsFromThisProfile() { + // GIVEN that all notifs ARE for the current user + when(mLockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(true); + + // THEN none should be filtered out + assertFalse(mCapturedNotifFilter.shouldFilterOut(mEntry, 0)); + } + + @Test + public void testFilterIsInvalidatedWhenProfilesChange() { + // WHEN the current user profiles change + mCapturedUserChangeListener.onCurrentProfilesChanged(new SparseArray<>()); + + // THEN the filter is invalidated + verify(mInvalidationListener).onPluggableInvalidated(mCapturedNotifFilter); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java index c4f3a1611afc..4f481081855f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java @@ -102,16 +102,6 @@ public class KeyguardCoordinatorTest extends SysuiTestCase { } @Test - public void notificationNotForCurrentProfile() { - // GIVEN the notification isn't for the given user - setupUnfilteredState(mEntry); - when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(false); - - // THEN filter out the entry - assertTrue(mKeyguardFilter.shouldFilterOut(mEntry, 0)); - } - - @Test public void keyguardNotShowing() { // GIVEN the lockscreen isn't showing setupUnfilteredState(mEntry); @@ -229,9 +219,6 @@ public class KeyguardCoordinatorTest extends SysuiTestCase { * KeyguardNotificationCoordinator when the keyguard is showing. */ private void setupUnfilteredState(NotificationEntry entry) { - // notification is for current profile - when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(true); - // keyguard is showing when(mKeyguardStateController.isShowing()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java index e84f9cf352ed..85acbe6d440b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java @@ -19,9 +19,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -42,6 +41,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -51,20 +51,23 @@ public class RankingCoordinatorTest extends SysuiTestCase { @Mock private StatusBarStateController mStatusBarStateController; @Mock private NotifPipeline mNotifPipeline; + + @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor; + private NotificationEntry mEntry; - private RankingCoordinator mRankingCoordinator; - private NotifFilter mRankingFilter; + private NotifFilter mCapturedSuspendedFilter; + private NotifFilter mCapturedDozingFilter; @Before public void setup() { MockitoAnnotations.initMocks(this); - mRankingCoordinator = new RankingCoordinator(mStatusBarStateController); + RankingCoordinator rankingCoordinator = new RankingCoordinator(mStatusBarStateController); mEntry = new NotificationEntryBuilder().build(); - ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class); - mRankingCoordinator.attach(mNotifPipeline); - verify(mNotifPipeline, times(1)).addPreGroupFilter(filterCaptor.capture()); - mRankingFilter = filterCaptor.getValue(); + rankingCoordinator.attach(mNotifPipeline); + verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture()); + mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0); + mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1); } @Test @@ -73,7 +76,7 @@ public class RankingCoordinatorTest extends SysuiTestCase { mEntry.setRanking(getRankingForUnfilteredNotif().build()); // THEN don't filter out the notification - assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0)); + assertFalse(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0)); } @Test @@ -84,7 +87,7 @@ public class RankingCoordinatorTest extends SysuiTestCase { .build()); // THEN filter out the notification - assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0)); + assertTrue(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0)); } @Test @@ -98,13 +101,13 @@ public class RankingCoordinatorTest extends SysuiTestCase { when(mStatusBarStateController.isDozing()).thenReturn(true); // THEN filter out the notification - assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0)); + assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0)); // WHEN it's not dozing (showing the notification list) when(mStatusBarStateController.isDozing()).thenReturn(false); // THEN don't filter out the notification - assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0)); + assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0)); } @Test @@ -118,13 +121,13 @@ public class RankingCoordinatorTest extends SysuiTestCase { when(mStatusBarStateController.isDozing()).thenReturn(true); // THEN don't filter out the notification - assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0)); + assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0)); // WHEN it's not dozing (showing the notification list) when(mStatusBarStateController.isDozing()).thenReturn(false); // THEN filter out the notification - assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0)); + assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0)); } private RankingBuilder getRankingForUnfilteredNotif() { 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/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java index 487885ac244e..85b5d70c883c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java @@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.res.Resources; import android.view.View; import androidx.test.filters.SmallTest; @@ -35,6 +36,7 @@ import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Test; @@ -71,6 +73,12 @@ public class LockscreenIconControllerTest extends SysuiTestCase { private KeyguardBypassController mKeyguardBypassController; @Mock private DockManager mDockManager; + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private Resources mResources; + @Mock + private HeadsUpManagerPhone mHeadsUpManagerPhone; @Before @@ -81,7 +89,8 @@ public class LockscreenIconControllerTest extends SysuiTestCase { mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils, mShadeController, mAccessibilityController, mKeyguardIndicationController, mStatusBarStateController, mConfigurationController, mNotificationWakeUpCoordinator, - mKeyguardBypassController, mDockManager); + mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources, + mHeadsUpManagerPhone); mLockIconController.attach(mLockIcon); } diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp index 2fbba68f1e03..6af5fe54f281 100644 --- a/packages/Tethering/common/TetheringLib/Android.bp +++ b/packages/Tethering/common/TetheringLib/Android.bp @@ -60,6 +60,7 @@ java_library { hostdex: true, // for hiddenapi check visibility: ["//frameworks/base/packages/Tethering:__subpackages__"], apex_available: ["com.android.tethering"], + permitted_packages: ["android.net"], } stubs_defaults { @@ -125,17 +126,17 @@ droidstubs { java_library { name: "framework-tethering-stubs-publicapi", srcs: [":framework-tethering-stubs-srcs-publicapi"], - sdk_version: "current", + defaults: ["framework-module-stubs-lib-defaults-publicapi"], } java_library { name: "framework-tethering-stubs-systemapi", srcs: [":framework-tethering-stubs-srcs-systemapi"], - sdk_version: "system_current", + defaults: ["framework-module-stubs-lib-defaults-systemapi"], } java_library { name: "framework-tethering-stubs-module_libs_api", srcs: [":framework-tethering-stubs-srcs-module_libs_api"], - sdk_version: "module_current", + defaults: ["framework-module-stubs-lib-defaults-systemapi"], } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 36113acf97d3..c84892d675f9 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -303,7 +303,8 @@ public class Tethering { final UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); - mTetheringRestriction = new UserRestrictionActionListener(userManager, this); + mTetheringRestriction = new UserRestrictionActionListener( + userManager, this, mNotificationUpdater); mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); @@ -369,9 +370,10 @@ public class Tethering { mActiveDataSubId = subId; updateConfiguration(); + mNotificationUpdater.onActiveDataSubscriptionIdChanged(subId); // To avoid launching unexpected provisioning checks, ignore re-provisioning // when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning() - // ill be triggered again when CarrierConfig is loaded. + // will be triggered again when CarrierConfig is loaded. if (mEntitlementMgr.getCarrierConfig(mConfig) != null) { mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); } else { @@ -431,9 +433,7 @@ public class Tethering { // Called by wifi when the number of soft AP clients changed. @Override public void onConnectedClientsChanged(final List<WifiClient> clients) { - if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, clients)) { - reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients()); - } + updateConnectedClients(clients); } } @@ -635,7 +635,10 @@ public class Tethering { Context.ETHERNET_SERVICE); synchronized (mPublicSync) { if (enable) { - if (mEthernetCallback != null) return TETHER_ERROR_NO_ERROR; + if (mEthernetCallback != null) { + Log.d(TAG, "Ethernet tethering already started"); + return TETHER_ERROR_NO_ERROR; + } mEthernetCallback = new EthernetCallback(); mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback); @@ -996,11 +999,14 @@ public class Tethering { protected static class UserRestrictionActionListener { private final UserManager mUserManager; private final Tethering mWrapper; + private final TetheringNotificationUpdater mNotificationUpdater; public boolean mDisallowTethering; - public UserRestrictionActionListener(UserManager um, Tethering wrapper) { + public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper, + @NonNull TetheringNotificationUpdater updater) { mUserManager = um; mWrapper = wrapper; + mNotificationUpdater = updater; mDisallowTethering = false; } @@ -1019,13 +1025,21 @@ public class Tethering { return; } - // TODO: Add user restrictions notification. - final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0); - - if (newlyDisallowed && isTetheringActiveOnDevice) { - mWrapper.untetherAll(); - // TODO(b/148139325): send tetheringSupported on restriction change + if (!newlyDisallowed) { + // Clear the restricted notification when user is allowed to have tethering + // function. + mNotificationUpdater.tetheringRestrictionLifted(); + return; } + + // Restricted notification is shown when tethering function is disallowed on + // user's device. + mNotificationUpdater.notifyTetheringDisabledByRestriction(); + + // Untether from all downstreams since tethering is disallowed. + mWrapper.untetherAll(); + + // TODO(b/148139325): send tetheringSupported on restriction change } } @@ -1559,6 +1573,7 @@ public class Tethering { mIPv6TetheringCoordinator.removeActiveDownstream(who); mOffload.excludeDownstreamInterface(who.interfaceName()); mForwardedDownstreams.remove(who); + updateConnectedClients(null /* wifiClients */); // If this is a Wi-Fi interface, tell WifiManager of any errors // or the inactive serving state. @@ -2141,6 +2156,12 @@ public class Tethering { return false; } + private void updateConnectedClients(final List<WifiClient> wifiClients) { + if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, wifiClients)) { + reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients()); + } + } + private IpServer.Callback makeControlCallback() { return new IpServer.Callback() { @Override @@ -2155,10 +2176,7 @@ public class Tethering { @Override public void dhcpLeasesChanged() { - if (mConnectedClientsTracker.updateConnectedClients( - mForwardedDownstreams, null /* wifiClients */)) { - reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients()); - } + updateConnectedClients(null /* wifiClients */); } }; } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java index b97f75268a3b..992cdd8de6a7 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java @@ -29,12 +29,14 @@ import android.content.Intent; import android.content.res.Resources; import android.os.UserHandle; import android.provider.Settings; +import android.telephony.SubscriptionManager; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import androidx.annotation.ArrayRes; import androidx.annotation.DrawableRes; +import androidx.annotation.IntDef; import androidx.annotation.IntRange; import androidx.annotation.NonNull; @@ -54,10 +56,15 @@ import com.android.networkstack.tethering.R; public class TetheringNotificationUpdater { private static final String TAG = TetheringNotificationUpdater.class.getSimpleName(); private static final String CHANNEL_ID = "TETHERING_STATUS"; + private static final String WIFI_DOWNSTREAM = "WIFI"; + private static final String USB_DOWNSTREAM = "USB"; + private static final String BLUETOOTH_DOWNSTREAM = "BT"; private static final boolean NOTIFY_DONE = true; private static final boolean NO_NOTIFY = false; // Id to update and cancel tethering notification. Must be unique within the tethering app. - private static final int NOTIFY_ID = 20191115; + private static final int ENABLE_NOTIFICATION_ID = 1000; + // Id to update and cancel restricted notification. Must be unique within the tethering app. + private static final int RESTRICTED_NOTIFICATION_ID = 1001; @VisibleForTesting static final int NO_ICON_ID = 0; @VisibleForTesting @@ -65,14 +72,25 @@ public class TetheringNotificationUpdater { private final Context mContext; private final NotificationManager mNotificationManager; private final NotificationChannel mChannel; - // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2. - // This value has to be made 1 2 and 4, and OR'd with the others. + // WARNING : the constructor is called on a different thread. Thread safety therefore // relies on this value being initialized to 0, and not any other value. If you need // to change this, you will need to change the thread where the constructor is invoked, // or to introduce synchronization. + // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2. + // This value has to be made 1 2 and 4, and OR'd with the others. private int mDownstreamTypesMask = DOWNSTREAM_NONE; + // WARNING : this value is not able to being initialized to 0 and must have volatile because + // telephony service is not guaranteed that is up before tethering service starts. If telephony + // is up later than tethering, TetheringNotificationUpdater will use incorrect and valid + // subscription id(0) to query resources. Therefore, initialized subscription id must be + // INVALID_SUBSCRIPTION_ID. + private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + + @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID}) + @interface NotificationId {} + public TetheringNotificationUpdater(@NonNull final Context context) { mContext = context; mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0) @@ -88,19 +106,46 @@ public class TetheringNotificationUpdater { public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) { if (mDownstreamTypesMask == downstreamTypesMask) return; mDownstreamTypesMask = downstreamTypesMask; - updateNotification(); + updateEnableNotification(); + } + + /** Called when active data subscription id changed */ + public void onActiveDataSubscriptionIdChanged(final int subId) { + if (mActiveDataSubId == subId) return; + mActiveDataSubId = subId; + updateEnableNotification(); } - private void updateNotification() { + @VisibleForTesting + Resources getResourcesForSubId(@NonNull final Context c, final int subId) { + return SubscriptionManager.getResourcesForSubId(c, subId); + } + + private void updateEnableNotification() { final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE; if (tetheringInactive || setupNotification() == NO_NOTIFY) { - clearNotification(); + clearNotification(ENABLE_NOTIFICATION_ID); } } - private void clearNotification() { - mNotificationManager.cancel(null /* tag */, NOTIFY_ID); + @VisibleForTesting + void tetheringRestrictionLifted() { + clearNotification(RESTRICTED_NOTIFICATION_ID); + } + + private void clearNotification(@NotificationId final int id) { + mNotificationManager.cancel(null /* tag */, id); + } + + @VisibleForTesting + void notifyTetheringDisabledByRestriction() { + final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); + final String title = res.getString(R.string.disable_tether_notification_title); + final String message = res.getString(R.string.disable_tether_notification_message); + + showNotification(R.drawable.stat_sys_tether_general, title, message, + RESTRICTED_NOTIFICATION_ID); } /** @@ -110,16 +155,17 @@ public class TetheringNotificationUpdater { * * @return downstream types mask value. */ + @VisibleForTesting @IntRange(from = 0, to = 7) - private int getDownstreamTypesMask(@NonNull final String types) { + int getDownstreamTypesMask(@NonNull final String types) { int downstreamTypesMask = DOWNSTREAM_NONE; final String[] downstreams = types.split("\\|"); for (String downstream : downstreams) { - if ("USB".equals(downstream.trim())) { + if (USB_DOWNSTREAM.equals(downstream.trim())) { downstreamTypesMask |= (1 << TETHERING_USB); - } else if ("WIFI".equals(downstream.trim())) { + } else if (WIFI_DOWNSTREAM.equals(downstream.trim())) { downstreamTypesMask |= (1 << TETHERING_WIFI); - } else if ("BT".equals(downstream.trim())) { + } else if (BLUETOOTH_DOWNSTREAM.equals(downstream.trim())) { downstreamTypesMask |= (1 << TETHERING_BLUETOOTH); } } @@ -134,9 +180,8 @@ public class TetheringNotificationUpdater { * * @return {@link android.util.SparseArray} with downstream types and icon id info. */ - @NonNull - private SparseArray<Integer> getIcons(@ArrayRes int id) { - final Resources res = mContext.getResources(); + @VisibleForTesting + SparseArray<Integer> getIcons(@ArrayRes int id, @NonNull Resources res) { final String[] array = res.getStringArray(id); final SparseArray<Integer> icons = new SparseArray<>(); for (String config : array) { @@ -161,8 +206,9 @@ public class TetheringNotificationUpdater { } private boolean setupNotification() { - final Resources res = mContext.getResources(); - final SparseArray<Integer> downstreamIcons = getIcons(R.array.tethering_notification_icons); + final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); + final SparseArray<Integer> downstreamIcons = + getIcons(R.array.tethering_notification_icons, res); final int iconId = downstreamIcons.get(mDownstreamTypesMask, NO_ICON_ID); if (iconId == NO_ICON_ID) return NO_NOTIFY; @@ -170,12 +216,12 @@ public class TetheringNotificationUpdater { final String title = res.getString(R.string.tethering_notification_title); final String message = res.getString(R.string.tethering_notification_message); - showNotification(iconId, title, message); + showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID); return NOTIFY_DONE; } private void showNotification(@DrawableRes final int iconId, @NonNull final String title, - @NonNull final String message) { + @NonNull final String message, @NotificationId final int id) { final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS); final PendingIntent pi = PendingIntent.getActivity( mContext.createContextAsUser(UserHandle.CURRENT, 0), @@ -193,6 +239,6 @@ public class TetheringNotificationUpdater { .setContentIntent(pi) .build(); - mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification); + mNotificationManager.notify(null /* tag */, id, notification); } } diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt new file mode 100644 index 000000000000..b86949185c69 --- /dev/null +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt @@ -0,0 +1,262 @@ +/* + * 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.connectivity.tethering + +import android.app.Notification +import android.app.NotificationManager +import android.content.Context +import android.content.res.Resources +import android.net.ConnectivityManager.TETHERING_BLUETOOTH +import android.net.ConnectivityManager.TETHERING_USB +import android.net.ConnectivityManager.TETHERING_WIFI +import android.os.UserHandle +import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.internal.util.test.BroadcastInterceptingContext +import com.android.networkstack.tethering.R +import com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.reset +import org.mockito.Mockito.times +import org.mockito.Mockito.verifyZeroInteractions +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +const val TEST_SUBID = 1 +const val WIFI_ICON_ID = 1 +const val USB_ICON_ID = 2 +const val BT_ICON_ID = 3 +const val GENERAL_ICON_ID = 4 +const val WIFI_MASK = 1 shl TETHERING_WIFI +const val USB_MASK = 1 shl TETHERING_USB +const val BT_MASK = 1 shl TETHERING_BLUETOOTH +const val TITTLE = "Tethering active" +const val MESSAGE = "Tap here to set up." +const val TEST_TITTLE = "Hotspot active" +const val TEST_MESSAGE = "Tap to set up hotspot." + +@RunWith(AndroidJUnit4::class) +@SmallTest +class TetheringNotificationUpdaterTest { + // lateinit used here for mocks as they need to be reinitialized between each test and the test + // should crash if they are used before being initialized. + @Mock private lateinit var mockContext: Context + @Mock private lateinit var notificationManager: NotificationManager + @Mock private lateinit var defaultResources: Resources + @Mock private lateinit var testResources: Resources + + // lateinit for this class under test, as it should be reset to a different instance for every + // tests but should always be initialized before use (or the test should crash). + private lateinit var notificationUpdater: TetheringNotificationUpdater + + private val ENABLE_ICON_CONFIGS = arrayOf( + "USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth", + "WIFI|BT;android.test:drawable/general", "WIFI|USB;android.test:drawable/general", + "USB|BT;android.test:drawable/general", "WIFI|USB|BT;android.test:drawable/general") + + private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) { + override fun createContextAsUser(user: UserHandle, flags: Int) = + if (user == UserHandle.ALL) mockContext else this + } + + private inner class WrappedNotificationUpdater(c: Context) : TetheringNotificationUpdater(c) { + override fun getResourcesForSubId(context: Context, subId: Int) = + if (subId == TEST_SUBID) testResources else defaultResources + } + + private fun setupResources() { + doReturn(ENABLE_ICON_CONFIGS).`when`(defaultResources) + .getStringArray(R.array.tethering_notification_icons) + doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources) + .getStringArray(R.array.tethering_notification_icons) + doReturn(TITTLE).`when`(defaultResources).getString(R.string.tethering_notification_title) + doReturn(MESSAGE).`when`(defaultResources) + .getString(R.string.tethering_notification_message) + doReturn(TEST_TITTLE).`when`(testResources).getString(R.string.tethering_notification_title) + doReturn(TEST_MESSAGE).`when`(testResources) + .getString(R.string.tethering_notification_message) + doReturn(USB_ICON_ID).`when`(defaultResources) + .getIdentifier(eq("android.test:drawable/usb"), any(), any()) + doReturn(BT_ICON_ID).`when`(defaultResources) + .getIdentifier(eq("android.test:drawable/bluetooth"), any(), any()) + doReturn(GENERAL_ICON_ID).`when`(defaultResources) + .getIdentifier(eq("android.test:drawable/general"), any(), any()) + doReturn(WIFI_ICON_ID).`when`(testResources) + .getIdentifier(eq("android.test:drawable/wifi"), any(), any()) + } + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + val context = TestContext(InstrumentationRegistry.getInstrumentation().context) + doReturn(notificationManager).`when`(mockContext) + .getSystemService(Context.NOTIFICATION_SERVICE) + notificationUpdater = WrappedNotificationUpdater(context) + setupResources() + } + + private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE) + private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT) + + private fun verifyNotification(iconId: Int = 0, title: String = "", text: String = "") { + verify(notificationManager, never()).cancel(any(), anyInt()) + + val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) + verify(notificationManager, times(1)) + .notify(any(), anyInt(), notificationCaptor.capture()) + + val notification = notificationCaptor.getValue() + assertEquals(iconId, notification.smallIcon.resId) + assertEquals(title, notification.title()) + assertEquals(text, notification.text()) + + reset(notificationManager) + } + + private fun verifyNoNotification() { + verify(notificationManager, times(1)).cancel(any(), anyInt()) + verify(notificationManager, never()).notify(any(), anyInt(), any()) + + reset(notificationManager) + } + + @Test + fun testNotificationWithDownstreamChanged() { + // Wifi downstream. No notification. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyNoNotification() + + // Same downstream changed. Nothing happened. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyZeroInteractions(notificationManager) + + // Wifi and usb downstreams. Show enable notification + notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK) + verifyNotification(GENERAL_ICON_ID, TITTLE, MESSAGE) + + // Usb downstream. Still show enable notification. + notificationUpdater.onDownstreamChanged(USB_MASK) + verifyNotification(USB_ICON_ID, TITTLE, MESSAGE) + + // No downstream. No notification. + notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) + verifyNoNotification() + } + + @Test + fun testNotificationWithActiveDataSubscriptionIdChanged() { + // Usb downstream. Showed enable notification with default resource. + notificationUpdater.onDownstreamChanged(USB_MASK) + verifyNotification(USB_ICON_ID, TITTLE, MESSAGE) + + // Same subId changed. Nothing happened. + notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID) + verifyZeroInteractions(notificationManager) + + // Set test sub id. Clear notification with test resource. + notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) + verifyNoNotification() + + // Wifi downstream. Show enable notification with test resource. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyNotification(WIFI_ICON_ID, TEST_TITTLE, TEST_MESSAGE) + + // No downstream. No notification. + notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) + verifyNoNotification() + } + + private fun assertIconNumbers(number: Int, configs: Array<String?>) { + doReturn(configs).`when`(defaultResources) + .getStringArray(R.array.tethering_notification_icons) + assertEquals(number, notificationUpdater.getIcons( + R.array.tethering_notification_icons, defaultResources).size()) + } + + @Test + fun testGetIcons() { + assertIconNumbers(0, arrayOfNulls<String>(0)) + assertIconNumbers(0, arrayOf(null, "")) + assertIconNumbers(3, arrayOf( + // These configurations are invalid with wrong strings or symbols. + ";", ",", "|", "|,;", "WIFI", "1;2", " U SB; ", "bt;", "WIFI;USB;BT", "WIFI|USB|BT", + "WIFI,BT,USB", " WIFI| | | USB, test:drawable/test", + // This configuration is valid with two downstream types (USB, BT). + "USB|,,,,,|BT;drawable/test ", + // This configuration is valid with one downstream types (WIFI). + " WIFI ; android.test:drawable/xxx ")) + } + + @Test + fun testGetDownstreamTypesMask() { + assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("")) + assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("1")) + assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("WIFI_P2P")) + assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("usb")) + assertEquals(WIFI_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI ")) + assertEquals(USB_MASK, notificationUpdater.getDownstreamTypesMask("USB | B T")) + assertEquals(BT_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI: | BT")) + assertEquals(WIFI_MASK or USB_MASK, + notificationUpdater.getDownstreamTypesMask("1|2|USB|WIFI|BLUETOOTH||")) + } + + @Test + fun testSetupRestrictedNotification() { + val title = InstrumentationRegistry.getInstrumentation().context.resources + .getString(R.string.disable_tether_notification_title) + val message = InstrumentationRegistry.getInstrumentation().context.resources + .getString(R.string.disable_tether_notification_message) + val disallowTitle = "Tether function is disallowed" + val disallowMessage = "Please contact your admin" + doReturn(title).`when`(defaultResources) + .getString(R.string.disable_tether_notification_title) + doReturn(message).`when`(defaultResources) + .getString(R.string.disable_tether_notification_message) + doReturn(disallowTitle).`when`(testResources) + .getString(R.string.disable_tether_notification_title) + doReturn(disallowMessage).`when`(testResources) + .getString(R.string.disable_tether_notification_message) + + // User restrictions on. Show restricted notification. + notificationUpdater.notifyTetheringDisabledByRestriction() + verifyNotification(R.drawable.stat_sys_tether_general, title, message) + + // User restrictions off. Clear notification. + notificationUpdater.tetheringRestrictionLifted() + verifyNoNotification() + + // Set test sub id. No notification. + notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) + verifyNoNotification() + + // User restrictions on again. Show restricted notification with test resource. + notificationUpdater.notifyTetheringDisabledByRestriction() + verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage) + } +}
\ No newline at end of file diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 60d7ad1c5b1e..5ead1106d75a 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -210,7 +210,6 @@ public class TetheringTest { private PhoneStateListener mPhoneStateListener; private InterfaceConfigurationParcel mInterfaceConfiguration; - private class TestContext extends BroadcastInterceptingContext { TestContext(Context base) { super(base); @@ -1073,13 +1072,15 @@ public class TetheringTest { when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions); final Tethering.UserRestrictionActionListener ural = - new Tethering.UserRestrictionActionListener(mUserManager, mockTethering); + new Tethering.UserRestrictionActionListener( + mUserManager, mockTethering, mNotificationUpdater); ural.mDisallowTethering = currentDisallow; ural.onUserRestrictionsChanged(); - verify(mockTethering, times(expectedInteractionsWithShowNotification)) - .untetherAll(); + verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification)) + .notifyTetheringDisabledByRestriction(); + verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll(); } @Test @@ -1087,7 +1088,7 @@ public class TetheringTest { final String[] emptyActiveIfacesList = new String[]{}; final boolean currDisallow = false; final boolean nextDisallow = true; - final int expectedInteractionsWithShowNotification = 0; + final int expectedInteractionsWithShowNotification = 1; runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList, expectedInteractionsWithShowNotification); @@ -1399,6 +1400,7 @@ public class TetheringTest { mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration(); assertEquals(fakeSubId, newConfig.activeDataSubId); + verify(mNotificationUpdater, times(1)).onActiveDataSubscriptionIdChanged(eq(fakeSubId)); } @Test diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 60c3d7859984..1b180e3357d7 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2001,17 +2001,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId, currentTargets, str -> str); scheduleNotifyClientsOfServicesStateChangeLocked(userState); - - // Disable accessibility shortcut key if there's no shortcut installed. - if (currentTargets.isEmpty()) { - final long identity = Binder.clearCallingIdentity(); - try { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 0, userState.mUserId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } } private boolean canRequestAndRequestsTouchExplorationLocked( diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index e49c1ed47c93..c6a54fc3d206 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -50,6 +50,7 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.DeviceConfig; @@ -63,6 +64,7 @@ import android.util.LocalLog; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.TimeUtils; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillManager.SmartSuggestionMode; @@ -151,6 +153,7 @@ public final class AutofillManagerService private final LocalLog mWtfHistory = new LocalLog(50); private final AutofillCompatState mAutofillCompatState = new AutofillCompatState(); + private final DisabledInfoCache mDisabledInfoCache = new DisabledInfoCache(); private final LocalService mLocalService = new LocalService(); private final ActivityManagerInternal mAm; @@ -302,14 +305,15 @@ public final class AutofillManagerService @Override // from AbstractMasterSystemService protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled) { - return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory, - mWtfHistory, resolvedUserId, mUi, mAutofillCompatState, disabled); + return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory, mWtfHistory, + resolvedUserId, mUi, mAutofillCompatState, disabled, mDisabledInfoCache); } @Override // AbstractMasterSystemService protected void onServiceRemoved(@NonNull AutofillManagerServiceImpl service, @UserIdInt int userId) { service.destroyLocked(); + mDisabledInfoCache.remove(userId); mAutofillCompatState.removeCompatibilityModeRequests(userId); } @@ -835,15 +839,10 @@ public final class AutofillManagerService private void injectDisableAppInfo(@NonNull AutofillOptions options, int userId, String packageName) { - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - options.appDisabledExpiration = service.getAppDisabledExpirationLocked( - packageName); - options.disabledActivities = service.getAppDisabledActivitiesLocked( - packageName); - } - } + options.appDisabledExpiration = + mDisabledInfoCache.getAppDisabledExpiration(userId, packageName); + options.disabledActivities = + mDisabledInfoCache.getAppDisabledActivities(userId, packageName); } } @@ -867,6 +866,234 @@ public final class AutofillManagerService } /** + * Stores autofill disable information, i.e. {@link AutofillDisabledInfo}, keyed by user id. + * The information is cleaned up when the service is removed. + */ + static final class DisabledInfoCache { + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private final SparseArray<AutofillDisabledInfo> mCache = new SparseArray<>(); + + void remove(@UserIdInt int userId) { + synchronized (mLock) { + mCache.remove(userId); + } + } + + void addDisabledAppLocked(@UserIdInt int userId, @NonNull String packageName, + long expiration) { + Preconditions.checkNotNull(packageName); + synchronized (mLock) { + AutofillDisabledInfo info = + getOrCreateAutofillDisabledInfoByUserIdLocked(userId); + info.putDisableAppsLocked(packageName, expiration); + } + } + + void addDisabledActivityLocked(@UserIdInt int userId, @NonNull ComponentName componentName, + long expiration) { + Preconditions.checkNotNull(componentName); + synchronized (mLock) { + AutofillDisabledInfo info = + getOrCreateAutofillDisabledInfoByUserIdLocked(userId); + info.putDisableActivityLocked(componentName, expiration); + } + } + + boolean isAutofillDisabledLocked(@UserIdInt int userId, + @NonNull ComponentName componentName) { + Preconditions.checkNotNull(componentName); + final boolean disabled; + synchronized (mLock) { + final AutofillDisabledInfo info = mCache.get(userId); + disabled = info != null ? info.isAutofillDisabledLocked(componentName) : false; + } + return disabled; + } + + long getAppDisabledExpiration(@UserIdInt int userId, @NonNull String packageName) { + Preconditions.checkNotNull(packageName); + final Long expiration; + synchronized (mLock) { + final AutofillDisabledInfo info = mCache.get(userId); + expiration = info != null ? info.getAppDisabledExpirationLocked(packageName) : 0; + } + return expiration; + } + + @Nullable + ArrayMap<String, Long> getAppDisabledActivities(@UserIdInt int userId, + @NonNull String packageName) { + Preconditions.checkNotNull(packageName); + final ArrayMap<String, Long> disabledList; + synchronized (mLock) { + final AutofillDisabledInfo info = mCache.get(userId); + disabledList = + info != null ? info.getAppDisabledActivitiesLocked(packageName) : null; + } + return disabledList; + } + + void dump(@UserIdInt int userId, String prefix, PrintWriter pw) { + synchronized (mLock) { + final AutofillDisabledInfo info = mCache.get(userId); + if (info != null) { + info.dumpLocked(prefix, pw); + } + } + } + + @NonNull + private AutofillDisabledInfo getOrCreateAutofillDisabledInfoByUserIdLocked( + @UserIdInt int userId) { + AutofillDisabledInfo info = mCache.get(userId); + if (info == null) { + info = new AutofillDisabledInfo(); + mCache.put(userId, info); + } + return info; + } + } + + /** + * The autofill disable information. + * <p> + * This contains disable information set by the AutofillService, e.g. disabled application + * expiration, disable activity expiration. + */ + private static final class AutofillDisabledInfo { + /** + * Apps disabled by the service; key is package name, value is when they will be enabled + * again. + */ + private ArrayMap<String, Long> mDisabledApps; + /** + * Activities disabled by the service; key is component name, value is when they will be + * enabled again. + */ + private ArrayMap<ComponentName, Long> mDisabledActivities; + + void putDisableAppsLocked(@NonNull String packageName, long expiration) { + if (mDisabledApps == null) { + mDisabledApps = new ArrayMap<>(1); + } + mDisabledApps.put(packageName, expiration); + } + + void putDisableActivityLocked(@NonNull ComponentName componentName, long expiration) { + if (mDisabledActivities == null) { + mDisabledActivities = new ArrayMap<>(1); + } + mDisabledActivities.put(componentName, expiration); + } + + long getAppDisabledExpirationLocked(@NonNull String packageName) { + if (mDisabledApps == null) { + return 0; + } + final Long expiration = mDisabledApps.get(packageName); + return expiration != null ? expiration : 0; + } + + ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) { + if (mDisabledActivities != null) { + final int size = mDisabledActivities.size(); + ArrayMap<String, Long> disabledList = null; + for (int i = 0; i < size; i++) { + final ComponentName component = mDisabledActivities.keyAt(i); + if (packageName.equals(component.getPackageName())) { + if (disabledList == null) { + disabledList = new ArrayMap<>(); + } + final long expiration = mDisabledActivities.valueAt(i); + disabledList.put(component.flattenToShortString(), expiration); + } + } + return disabledList; + } + return null; + } + + boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) { + // Check activities first. + long elapsedTime = 0; + if (mDisabledActivities != null) { + elapsedTime = SystemClock.elapsedRealtime(); + final Long expiration = mDisabledActivities.get(componentName); + if (expiration != null) { + if (expiration >= elapsedTime) return true; + // Restriction expired - clean it up. + if (sVerbose) { + Slog.v(TAG, "Removing " + componentName.toShortString() + + " from disabled list"); + } + mDisabledActivities.remove(componentName); + } + } + + // Then check apps. + final String packageName = componentName.getPackageName(); + if (mDisabledApps == null) return false; + + final Long expiration = mDisabledApps.get(packageName); + if (expiration == null) return false; + + if (elapsedTime == 0) { + elapsedTime = SystemClock.elapsedRealtime(); + } + + if (expiration >= elapsedTime) return true; + + // Restriction expired - clean it up. + if (sVerbose) Slog.v(TAG, "Removing " + packageName + " from disabled list"); + mDisabledApps.remove(packageName); + return false; + } + + void dumpLocked(String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("Disabled apps: "); + if (mDisabledApps == null) { + pw.println("N/A"); + } else { + final int size = mDisabledApps.size(); + pw.println(size); + final StringBuilder builder = new StringBuilder(); + final long now = SystemClock.elapsedRealtime(); + for (int i = 0; i < size; i++) { + final String packageName = mDisabledApps.keyAt(i); + final long expiration = mDisabledApps.valueAt(i); + builder.append(prefix).append(prefix) + .append(i).append(". ").append(packageName).append(": "); + TimeUtils.formatDuration((expiration - now), builder); + builder.append('\n'); + } + pw.println(builder); + } + + pw.print(prefix); pw.print("Disabled activities: "); + if (mDisabledActivities == null) { + pw.println("N/A"); + } else { + final int size = mDisabledActivities.size(); + pw.println(size); + final StringBuilder builder = new StringBuilder(); + final long now = SystemClock.elapsedRealtime(); + for (int i = 0; i < size; i++) { + final ComponentName component = mDisabledActivities.keyAt(i); + final long expiration = mDisabledActivities.valueAt(i); + builder.append(prefix).append(prefix) + .append(i).append(". ").append(component).append(": "); + TimeUtils.formatDuration((expiration - now), builder); + builder.append('\n'); + } + pw.println(builder); + } + } + } + + /** * Compatibility mode metadata associated with all services. * * <p>This object is defined here instead of on each {@link AutofillManagerServiceImpl} because diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 6fbe1410bbad..d1805d96cad8 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -67,7 +67,6 @@ import android.util.LocalLog; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; -import android.util.TimeUtils; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillManager.SmartSuggestionMode; @@ -80,6 +79,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.server.LocalServices; import com.android.server.autofill.AutofillManagerService.AutofillCompatState; +import com.android.server.autofill.AutofillManagerService.DisabledInfoCache; import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks; import com.android.server.autofill.ui.AutoFillUI; import com.android.server.contentcapture.ContentCaptureManagerInternal; @@ -90,7 +90,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Random; - /** * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the * app's {@link IAutoFillService} implementation. @@ -125,19 +124,6 @@ final class AutofillManagerServiceImpl private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService; /** - * Apps disabled by the service; key is package name, value is when they will be enabled again. - */ - @GuardedBy("mLock") - private ArrayMap<String, Long> mDisabledApps; - - /** - * Activities disabled by the service; key is component name, value is when they will be enabled - * again. - */ - @GuardedBy("mLock") - private ArrayMap<ComponentName, Long> mDisabledActivities; - - /** * Data used for field classification. */ @GuardedBy("mLock") @@ -186,10 +172,12 @@ final class AutofillManagerServiceImpl private final ContentCaptureManagerInternal mContentCaptureManagerInternal; + private final DisabledInfoCache mDisabledInfoCache; + AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui, AutofillCompatState autofillCompatState, - boolean disabled) { + boolean disabled, DisabledInfoCache disableCache) { super(master, lock, userId); mUiLatencyHistory = uiLatencyHistory; @@ -200,7 +188,7 @@ final class AutofillManagerServiceImpl mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class); mContentCaptureManagerInternal = LocalServices.getService( ContentCaptureManagerInternal.class); - + mDisabledInfoCache = disableCache; updateLocked(disabled); } @@ -1045,45 +1033,7 @@ final class AutofillManagerServiceImpl pw.println(isInlineSuggestionsEnabled()); pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune); - pw.print(prefix); pw.print("Disabled apps: "); - - if (mDisabledApps == null) { - pw.println("N/A"); - } else { - final int size = mDisabledApps.size(); - pw.println(size); - final StringBuilder builder = new StringBuilder(); - final long now = SystemClock.elapsedRealtime(); - for (int i = 0; i < size; i++) { - final String packageName = mDisabledApps.keyAt(i); - final long expiration = mDisabledApps.valueAt(i); - builder.append(prefix).append(prefix) - .append(i).append(". ").append(packageName).append(": "); - TimeUtils.formatDuration((expiration - now), builder); - builder.append('\n'); - } - pw.println(builder); - } - - pw.print(prefix); pw.print("Disabled activities: "); - - if (mDisabledActivities == null) { - pw.println("N/A"); - } else { - final int size = mDisabledActivities.size(); - pw.println(size); - final StringBuilder builder = new StringBuilder(); - final long now = SystemClock.elapsedRealtime(); - for (int i = 0; i < size; i++) { - final ComponentName component = mDisabledActivities.keyAt(i); - final long expiration = mDisabledActivities.valueAt(i); - builder.append(prefix).append(prefix) - .append(i).append(". ").append(component).append(": "); - TimeUtils.formatDuration((expiration - now), builder); - builder.append('\n'); - } - pw.println(builder); - } + mDisabledInfoCache.dump(mUserId, prefix, pw); final int size = mSessions.size(); if (size == 0) { @@ -1480,15 +1430,13 @@ final class AutofillManagerServiceImpl void disableAutofillForApp(@NonNull String packageName, long duration, int sessionId, boolean compatMode) { synchronized (mLock) { - if (mDisabledApps == null) { - mDisabledApps = new ArrayMap<>(1); - } long expiration = SystemClock.elapsedRealtime() + duration; // Protect it against overflow if (expiration < 0) { expiration = Long.MAX_VALUE; } - mDisabledApps.put(packageName, expiration); + mDisabledInfoCache.addDisabledAppLocked(mUserId, packageName, expiration); + int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration; mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP, packageName, getServicePackageName(), sessionId, compatMode) @@ -1502,15 +1450,12 @@ final class AutofillManagerServiceImpl void disableAutofillForActivity(@NonNull ComponentName componentName, long duration, int sessionId, boolean compatMode) { synchronized (mLock) { - if (mDisabledActivities == null) { - mDisabledActivities = new ArrayMap<>(1); - } long expiration = SystemClock.elapsedRealtime() + duration; // Protect it against overflow if (expiration < 0) { expiration = Long.MAX_VALUE; } - mDisabledActivities.put(componentName, expiration); + mDisabledInfoCache.addDisabledActivityLocked(mUserId, componentName, expiration); final int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration; @@ -1528,74 +1473,12 @@ final class AutofillManagerServiceImpl } } - // Called by AutofillManagerService - long getAppDisabledExpirationLocked(@NonNull String packageName) { - if (mDisabledApps == null) { - return 0; - } - final Long expiration = mDisabledApps.get(packageName); - return expiration != null ? expiration : 0; - } - - // Called by AutofillManagerService - @Nullable - ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) { - if (mDisabledActivities != null) { - final int size = mDisabledActivities.size(); - ArrayMap<String, Long> disabledList = null; - for (int i = 0; i < size; i++) { - final ComponentName component = mDisabledActivities.keyAt(i); - if (packageName.equals(component.getPackageName())) { - if (disabledList == null) { - disabledList = new ArrayMap<>(); - } - final long expiration = mDisabledActivities.valueAt(i); - disabledList.put(component.flattenToShortString(), expiration); - } - } - return disabledList; - } - return null; - } - /** * Checks if autofill is disabled by service to the given activity. */ @GuardedBy("mLock") private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) { - // Check activities first. - long elapsedTime = 0; - if (mDisabledActivities != null) { - elapsedTime = SystemClock.elapsedRealtime(); - final Long expiration = mDisabledActivities.get(componentName); - if (expiration != null) { - if (expiration >= elapsedTime) return true; - // Restriction expired - clean it up. - if (sVerbose) { - Slog.v(TAG, "Removing " + componentName.toShortString() - + " from disabled list"); - } - mDisabledActivities.remove(componentName); - } - } - - // Then check apps. - final String packageName = componentName.getPackageName(); - if (mDisabledApps == null) return false; - - final Long expiration = mDisabledApps.get(packageName); - if (expiration == null) return false; - - if (elapsedTime == 0) { - elapsedTime = SystemClock.elapsedRealtime(); - } - - if (expiration >= elapsedTime) return true; - - // Restriction expired - clean it up. - if (sVerbose) Slog.v(TAG, "Removing " + packageName + " from disabled list"); - mDisabledApps.remove(packageName); - return false; + return mDisabledInfoCache.isAutofillDisabledLocked(mUserId, componentName); } // Called by AutofillManager, checks UID. diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java index 5de817101177..4ba2c3d0a0d3 100644 --- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java +++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java @@ -199,7 +199,10 @@ final class InlineSuggestionSession { return false; } - if (!mImeInputViewStarted || !autofillId.equalsIgnoreSession(mImeFieldId)) { + // TODO(b/151846600): IME doesn't have access to the virtual id of the webview, so we + // only compare the view id for now. + if (!mImeInputViewStarted || mImeFieldId == null + || autofillId.getViewId() != mImeFieldId.getViewId()) { if (sDebug) { Log.d(TAG, "onInlineSuggestionsResponseLocked not sent because input view is not " diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java index c39e93a84b48..2306329e689c 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java @@ -165,7 +165,8 @@ public final class InlineSuggestionFactory { Slog.w(TAG, "InlinePresentation not found in dataset"); continue; } - if (!includeDataset(dataset, fieldIndex, filterText)) { + if (!inlinePresentation.isPinned() // don't filter pinned suggestions + && !includeDataset(dataset, fieldIndex, filterText)) { continue; } InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset, diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java index b54ec4ea9441..0fdabd09f9d4 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java @@ -21,13 +21,17 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.contentsuggestions.ClassificationsRequest; +import android.app.contentsuggestions.ContentSuggestionsManager; import android.app.contentsuggestions.IClassificationsCallback; import android.app.contentsuggestions.IContentSuggestionsManager; import android.app.contentsuggestions.ISelectionsCallback; import android.app.contentsuggestions.SelectionsRequest; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.ColorSpace; +import android.graphics.GraphicBuffer; import android.os.Binder; import android.os.Bundle; import android.os.RemoteException; @@ -133,7 +137,7 @@ public class ContentSuggestionsManagerService extends if (service != null) { // TODO(b/147324195): Temporarily pass bitmap until we change the service API. imageContextRequestExtras.putParcelable(EXTRA_BITMAP, bitmap); - service.provideContextImageLocked(/* taskId = */ -1, imageContextRequestExtras); + service.provideContextImageFromBitmapLocked(imageContextRequestExtras); } else { if (VERBOSE) { Slog.v(TAG, "provideContextImageLocked: no service for " + userId); @@ -152,10 +156,28 @@ public class ContentSuggestionsManagerService extends } enforceCaller(UserHandle.getCallingUserId(), "provideContextImage"); + GraphicBuffer snapshotBuffer = null; + int colorSpaceId = 0; + + // Skip taking TaskSnapshot when bitmap is provided. + if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) { + // Can block, so call before acquiring the lock. + ActivityManager.TaskSnapshot snapshot = + mActivityTaskManagerInternal.getTaskSnapshotBlocking(taskId, false); + if (snapshot != null) { + snapshotBuffer = snapshot.getSnapshot(); + ColorSpace colorSpace = snapshot.getColorSpace(); + if (colorSpace != null) { + colorSpaceId = colorSpace.getId(); + } + } + } + synchronized (mLock) { final ContentSuggestionsPerUserService service = getServiceForUserLocked(userId); if (service != null) { - service.provideContextImageLocked(taskId, imageContextRequestExtras); + service.provideContextImageLocked(taskId, snapshotBuffer, colorSpaceId, + imageContextRequestExtras); } else { if (VERBOSE) { Slog.v(TAG, "provideContextImageLocked: no service for " + userId); diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java index 7828050223f4..cf53b169515e 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java @@ -19,17 +19,14 @@ package com.android.server.contentsuggestions; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityManager; import android.app.AppGlobals; import android.app.contentsuggestions.ClassificationsRequest; -import android.app.contentsuggestions.ContentSuggestionsManager; import android.app.contentsuggestions.IClassificationsCallback; import android.app.contentsuggestions.ISelectionsCallback; import android.app.contentsuggestions.SelectionsRequest; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; -import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.os.Bundle; import android.os.RemoteException; @@ -95,26 +92,17 @@ public final class ContentSuggestionsPerUserService extends } @GuardedBy("mLock") - void provideContextImageLocked(int taskId, @NonNull Bundle imageContextRequestExtras) { + void provideContextImageFromBitmapLocked(@NonNull Bundle bitmapContainingExtras) { + // No task or snapshot provided, the bitmap is contained in the extras + provideContextImageLocked(-1, null, 0, bitmapContainingExtras); + } + + @GuardedBy("mLock") + void provideContextImageLocked(int taskId, @Nullable GraphicBuffer snapshot, + int colorSpaceIdForSnapshot, @NonNull Bundle imageContextRequestExtras) { RemoteContentSuggestionsService service = ensureRemoteServiceLocked(); if (service != null) { - GraphicBuffer snapshotBuffer = null; - int colorSpaceId = 0; - - // Skip taking TaskSnapshot when bitmap is provided. - if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) { - ActivityManager.TaskSnapshot snapshot = - mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false); - if (snapshot != null) { - snapshotBuffer = snapshot.getSnapshot(); - ColorSpace colorSpace = snapshot.getColorSpace(); - if (colorSpace != null) { - colorSpaceId = colorSpace.getId(); - } - } - } - - service.provideContextImage(taskId, snapshotBuffer, colorSpaceId, + service.provideContextImage(taskId, snapshot, colorSpaceIdForSnapshot, imageContextRequestExtras); } } 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/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 03ca1c610f82..1bf559a17021 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -112,9 +112,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int USER_SWITCHED_TIME_MS = 200; // Delay for the addProxy function in msec private static final int ADD_PROXY_DELAY_MS = 100; + // Delay for retrying enable and disable in msec + private static final int ENABLE_DISABLE_DELAY_MS = 300; private static final int MESSAGE_ENABLE = 1; private static final int MESSAGE_DISABLE = 2; + private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3; + private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4; private static final int MESSAGE_REGISTER_ADAPTER = 20; private static final int MESSAGE_UNREGISTER_ADAPTER = 21; private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30; @@ -136,6 +140,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int RESTORE_SETTING_TO_OFF = 0; private static final int MAX_ERROR_RESTART_RETRIES = 6; + private static final int MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES = 10; // Bluetooth persisted setting is off private static final int BLUETOOTH_OFF = 0; @@ -166,6 +171,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; + private int mWaitForEnableRetry; + private int mWaitForDisableRetry; private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; @@ -1678,8 +1685,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; case MESSAGE_ENABLE: + int quietEnable = msg.arg1; + if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) + || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { + // We are handling enable or disable right now, wait for it. + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE, + quietEnable, 0), ENABLE_DISABLE_DELAY_MS); + break; + } + if (DBG) { - Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth); + Slog.d(TAG, "MESSAGE_ENABLE(" + quietEnable + "): mBluetooth = " + + mBluetooth); } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; @@ -1702,7 +1719,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } - mQuietEnable = (msg.arg1 == 1); + mQuietEnable = (quietEnable == 1); if (mBluetooth == null) { handleEnable(mQuietEnable); } else { @@ -1711,8 +1728,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // the previous Bluetooth process has exited. The // waiting period has three components: // (a) Wait until the local state is STATE_OFF. This - // is accomplished by - // "waitForState(Set.of(BluetoothAdapter.STATE_OFF))". + // is accomplished by sending delay a message + // MESSAGE_HANDLE_ENABLE_DELAYED // (b) Wait until the STATE_OFF state is updated to // all components. // (c) Wait until the Bluetooth process exits, and @@ -1722,33 +1739,108 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // message. The delay time is backed off if Bluetooth // continuously failed to turn on itself. // - waitForState(Set.of(BluetoothAdapter.STATE_OFF)); - Message restartMsg = - mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); + mWaitForEnableRetry = 0; + Message enableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED); + mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS); } break; case MESSAGE_DISABLE: + if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mBinding + || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { + // We are handling enable or disable right now, wait for it. + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_DISABLE), + ENABLE_DISABLE_DELAY_MS); + break; + } + if (DBG) { - Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth); + Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth + + ", mBinding = " + mBinding); } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); + if (mEnable && mBluetooth != null) { - waitForState(Set.of(BluetoothAdapter.STATE_ON)); + mWaitForDisableRetry = 0; + Message disableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + } else { mEnable = false; handleDisable(); - waitForState(Set.of(BluetoothAdapter.STATE_OFF, - BluetoothAdapter.STATE_TURNING_ON, - BluetoothAdapter.STATE_TURNING_OFF, - BluetoothAdapter.STATE_BLE_TURNING_ON, - BluetoothAdapter.STATE_BLE_ON, - BluetoothAdapter.STATE_BLE_TURNING_OFF)); - } else { + } + break; + + case MESSAGE_HANDLE_ENABLE_DELAYED: { + // The Bluetooth is turning off, wait for STATE_OFF + if (mState != BluetoothAdapter.STATE_OFF) { + if (mWaitForEnableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForEnableRetry++; + Message enableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED); + mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for STATE_OFF timeout"); + } + } + // Either state is changed to STATE_OFF or reaches the maximum retry, we + // should move forward to the next step. + mWaitForEnableRetry = 0; + Message restartMsg = + mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); + mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); + Slog.d(TAG, "Handle enable is finished"); + break; + } + + case MESSAGE_HANDLE_DISABLE_DELAYED: { + boolean disabling = (msg.arg1 == 1); + Slog.d(TAG, "MESSAGE_HANDLE_DISABLE_DELAYED: disabling:" + disabling); + if (!disabling) { + // The Bluetooth is turning on, wait for STATE_ON + if (mState != BluetoothAdapter.STATE_ON) { + if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForDisableRetry++; + Message disableDelayedMsg = mHandler.obtainMessage( + MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, + ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for STATE_ON timeout"); + } + } + // Either state is changed to STATE_ON or reaches the maximum retry, we + // should move forward to the next step. + mWaitForDisableRetry = 0; mEnable = false; handleDisable(); + // Wait for state exiting STATE_ON + Message disableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + } else { + // The Bluetooth is turning off, wait for exiting STATE_ON + if (mState == BluetoothAdapter.STATE_ON) { + if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForDisableRetry++; + Message disableDelayedMsg = mHandler.obtainMessage( + MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, + ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for exiting STATE_ON timeout"); + } + } + // Either state is exited from STATE_ON or reaches the maximum retry, we + // should move forward to the next step. + Slog.d(TAG, "Handle disable is finished"); } break; + } case MESSAGE_RESTORE_USER_SETTING: if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) { @@ -2124,6 +2216,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.writeLock().lock(); if ((mBluetooth == null) && (!mBinding)) { + Slog.d(TAG, "binding Bluetooth service"); //Start bind timeout and bind Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS); @@ -2493,6 +2586,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { writer.println(" " + app.getPackageName()); } + writer.println("\nBluetoothManagerService:"); + writer.println(" mEnable:" + mEnable); + writer.println(" mQuietEnable:" + mQuietEnable); + writer.println(" mEnableExternal:" + mEnableExternal); + writer.println(" mQuietEnableExternal:" + mQuietEnableExternal); + writer.println(""); writer.flush(); if (args.length == 0) { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index ec84ae73d655..76a8e1474a95 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -40,6 +40,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.uidRulesToString; @@ -50,6 +51,7 @@ import static android.system.OsConstants.IPPROTO_UDP; import static java.util.Map.Entry; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; @@ -2702,10 +2704,18 @@ public class ConnectivityService extends IConnectivityManager.Stub switch (msg.what) { case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: { - final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj; + NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj; if (networkCapabilities.hasConnectivityManagedCapability()) { Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability."); } + if (networkCapabilities.hasTransport(TRANSPORT_TEST)) { + // Make sure the original object is not mutated. NetworkAgent normally + // makes a copy of the capabilities when sending the message through + // the Messenger, but if this ever changes, not making a defensive copy + // here will give attack vectors to clients using this code path. + networkCapabilities = new NetworkCapabilities(networkCapabilities); + networkCapabilities.restrictCapabilitesForTestNetwork(); + } updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities); break; } @@ -5778,7 +5788,16 @@ public class ConnectivityService extends IConnectivityManager.Stub public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) { - enforceNetworkFactoryPermission(); + if (networkCapabilities.hasTransport(TRANSPORT_TEST)) { + enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS); + // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in + // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never + // sees capabilities that may be malicious, which might prevent mistakes in the future. + networkCapabilities = new NetworkCapabilities(networkCapabilities); + networkCapabilities.restrictCapabilitesForTestNetwork(); + } else { + enforceNetworkFactoryPermission(); + } LinkProperties lp = new LinkProperties(linkProperties); lp.ensureDirectlyConnectedRoutes(); 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/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 0bf81e0678dc..9018caa8d7b6 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -43,6 +43,7 @@ import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIE import static android.os.storage.OnObbStateChangeListener.MOUNTED; import static android.os.storage.OnObbStateChangeListener.UNMOUNTED; import static android.os.storage.StorageManager.PROP_FUSE; +import static android.os.storage.StorageManager.PROP_LEGACY_OP_STICKY; import static android.os.storage.StorageManager.PROP_SETTINGS_FUSE; import static com.android.internal.util.XmlUtils.readIntAttribute; @@ -155,6 +156,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils; import com.android.server.pm.Installer; +import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.storage.AppFuseBridge; import com.android.server.storage.StorageSessionController; import com.android.server.storage.StorageSessionController.ExternalStorageServiceException; @@ -902,6 +904,7 @@ class StorageManagerService extends IStorageManager.Stub refreshIsolatedStorageSettings(); } }); + updateLegacyStorageOpSticky(); // For now, simply clone property when it changes DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, mContext.getMainExecutor(), (properties) -> { @@ -1778,6 +1781,13 @@ class StorageManagerService extends IStorageManager.Stub } } + private void updateLegacyStorageOpSticky() { + final boolean propertyValue = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, + "legacy_storage_op_sticky", true); + SystemProperties.set(PROP_LEGACY_OP_STICKY, propertyValue ? "true" : "false"); + } + private void start() { connectStoraged(); connectVold(); @@ -3268,6 +3278,25 @@ class StorageManagerService extends IStorageManager.Stub } } + @Override + public void fixupAppDir(String path) { + final Matcher matcher = KNOWN_APP_DIR_PATHS.matcher(path); + if (matcher.matches()) { + AndroidPackage pkg = mPmInternal.getPackage(matcher.group(3)); + if (pkg != null) { + try { + mVold.fixupAppDir(path + "/", pkg.getUid()); + } catch (RemoteException | ServiceSpecificException e) { + Log.e(TAG, "Failed to fixup app dir for " + pkg.getPackageName(), e); + } + } else { + Log.e(TAG, "Can't find package belonging to " + path); + } + } else { + Log.e(TAG, "Path " + path + " is not a valid application-specific directory"); + } + } + /** Not thread safe */ class AppFuseMountScope extends AppFuseBridge.MountScope { private boolean mMounted = false; diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java index 8f914fe6f59f..94d6b135e554 100644 --- a/services/core/java/com/android/server/SystemServerInitThreadPool.java +++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java @@ -25,6 +25,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.Preconditions; import com.android.server.am.ActivityManagerService; +import com.android.server.utils.TimingsTraceAndSlog; import java.util.ArrayList; import java.util.List; @@ -93,6 +94,8 @@ public class SystemServerInitThreadPool { mPendingTasks.add(description); } return mService.submit(() -> { + TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog(); + traceLog.traceBegin("InitThreadPoolExec:" + description); if (IS_DEBUGGABLE) { Slog.d(TAG, "Started executing " + description); } @@ -100,6 +103,7 @@ public class SystemServerInitThreadPool { runnable.run(); } catch (RuntimeException e) { Slog.e(TAG, "Failure in " + description + ": " + e, e); + traceLog.traceEnd(); throw e; } synchronized (mPendingTasks) { @@ -108,6 +112,7 @@ public class SystemServerInitThreadPool { if (IS_DEBUGGABLE) { Slog.d(TAG, "Finished executing " + description); } + traceLog.traceEnd(); }); } @@ -132,7 +137,10 @@ public class SystemServerInitThreadPool { */ static void shutdown() { synchronized (LOCK) { + TimingsTraceAndSlog t = new TimingsTraceAndSlog(); + t.traceBegin("WaitInitThreadPoolShutdown"); if (sInstance == null) { + t.traceEnd(); Slog.wtf(TAG, "Already shutdown", new Exception()); return; } @@ -147,6 +155,7 @@ public class SystemServerInitThreadPool { } catch (InterruptedException e) { Thread.currentThread().interrupt(); dumpStackTraces(); + t.traceEnd(); throw new IllegalStateException(TAG + " init interrupted"); } if (!terminated) { @@ -160,11 +169,13 @@ public class SystemServerInitThreadPool { synchronized (sInstance.mPendingTasks) { copy.addAll(sInstance.mPendingTasks); } + t.traceEnd(); throw new IllegalStateException("Cannot shutdown. Unstarted tasks " + unstartedRunnables + " Unfinished tasks " + copy); } sInstance = null; // Make eligible for GC Slog.d(TAG, "Shutdown successful"); + t.traceEnd(); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 4cfcd2b218d1..8b2976d0e878 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8780,6 +8780,27 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public boolean isUidActiveOrForeground(int uid, String callingPackage) { + if (!hasUsageStatsPermission(callingPackage)) { + enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, + "isUidActiveOrForeground"); + } + synchronized (this) { + final boolean isActive = isUidActiveLocked(uid); + if (isActive) { + return true; + } + } + final boolean isForeground = mAtmInternal.isUidForeground(uid); + if (isForeground) { + Slog.wtf(TAG, "isUidActiveOrForeground: isUidActive false but " + + " isUidForeground true, uid:" + uid + + " callingPackage:" + callingPackage); + } + return isForeground; + } + + @Override public void setPersistentVrThread(int tid) { mActivityTaskManager.setPersistentVrThread(tid); } diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java index 03f4a54086a8..ba89fce0b3f8 100644 --- a/services/core/java/com/android/server/am/BugReportHandlerUtil.java +++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java @@ -16,20 +16,15 @@ package com.android.server.am; -import static android.app.AppOpsManager.OP_NONE; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import android.app.Activity; import android.app.BroadcastOptions; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Binder; -import android.os.BugreportManager; -import android.os.BugreportParams; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; @@ -115,17 +110,9 @@ public final class BugReportHandlerUtil { options.setBackgroundActivityStartsAllowed(true); final long identity = Binder.clearCallingIdentity(); try { - // Handler app's BroadcastReceiver should call setResultCode(Activity.RESULT_OK) to - // let ResultBroadcastReceiver know the handler app is available. - context.sendOrderedBroadcastAsUser(intent, - UserHandle.of(handlerUser), + context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser), android.Manifest.permission.DUMP, - OP_NONE, options.toBundle(), - new ResultBroadcastReceiver(), - /* scheduler= */ null, - Activity.RESULT_CANCELED, - /* initialData= */ null, - /* initialExtras= */ null); + options.toBundle()); } catch (RuntimeException e) { Slog.e(TAG, "Error while trying to launch bugreport handler app.", e); return false; @@ -189,19 +176,4 @@ public final class BugReportHandlerUtil { Binder.restoreCallingIdentity(identity); } } - - private static class ResultBroadcastReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (getResultCode() == Activity.RESULT_OK) { - return; - } - - Slog.w(TAG, "Request bug report because handler app seems to be not available."); - BugreportManager bugreportManager = context.getSystemService(BugreportManager.class); - bugreportManager.requestBugreport( - new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE), - /* shareTitle= */null, /* shareDescription= */ null); - } - } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 1f0146ad815d..071058c113b2 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -776,6 +776,8 @@ public class AudioService extends IAudioService.Stub mDeviceBroker = new AudioDeviceBroker(mContext, this); + mRecordMonitor = new RecordingActivityMonitor(mContext); + // must be called before readPersistedSettings() which needs a valid mStreamVolumeAlias[] // array initialized by updateStreamVolumeAlias() updateStreamVolumeAlias(false /*updateVolumes*/, TAG); @@ -797,8 +799,6 @@ public class AudioService extends IAudioService.Stub mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor); - mRecordMonitor = new RecordingActivityMonitor(mContext); - readAndSetLowRamDevice(); mIsCallScreeningModeSupported = AudioSystem.isCallScreeningModeSupported(); @@ -1981,8 +1981,7 @@ public class AudioService extends IAudioService.Stub } flags &= ~AudioManager.FLAG_FIXED_VOLUME; - if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) && - mFixedVolumeDevices.contains(device)) { + if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) { flags |= AudioManager.FLAG_FIXED_VOLUME; // Always toggle between max safe volume and 0 for fixed volume devices where safe @@ -2059,7 +2058,7 @@ public class AudioService extends IAudioService.Stub !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) { Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex); mVolumeController.postDisplaySafeVolumeWarning(flags); - } else if (!mFullVolumeDevices.contains(device) + } else if (!isFullVolumeDevice(device) && (streamState.adjustIndex(direction * step, device, caller) || streamState.mIsMuted)) { // Post message to set system volume (it in turn will post a @@ -2121,7 +2120,7 @@ public class AudioService extends IAudioService.Stub if (mHdmiCecSink && streamTypeAlias == AudioSystem.STREAM_MUSIC // vol change on a full volume device - && mFullVolumeDevices.contains(device)) { + && isFullVolumeDevice(device)) { int keyCode = KeyEvent.KEYCODE_UNKNOWN; switch (direction) { case AudioManager.ADJUST_RAISE: @@ -2590,8 +2589,7 @@ public class AudioService extends IAudioService.Stub } flags &= ~AudioManager.FLAG_FIXED_VOLUME; - if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) && - mFixedVolumeDevices.contains(device)) { + if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) { flags |= AudioManager.FLAG_FIXED_VOLUME; // volume is either 0 or max allowed for fixed volume devices @@ -2780,7 +2778,7 @@ public class AudioService extends IAudioService.Stub if (streamType == AudioSystem.STREAM_MUSIC) { flags = updateFlagsForTvPlatform(flags); - if (mFullVolumeDevices.contains(device)) { + if (isFullVolumeDevice(device)) { flags &= ~AudioManager.FLAG_SHOW_UI; } } @@ -2826,7 +2824,7 @@ public class AudioService extends IAudioService.Stub int device, boolean force, String caller) { - if (mFullVolumeDevices.contains(device)) { + if (isFullVolumeDevice(device)) { return; } VolumeStreamState streamState = mStreamStates[streamType]; @@ -3036,7 +3034,7 @@ public class AudioService extends IAudioService.Stub index = 0; } if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) && - mFixedVolumeDevices.contains(device)) { + isFixedVolumeDevice(device)) { index = mStreamStates[streamType].getMaxIndex(); } return (index + 5) / 10; @@ -4570,6 +4568,7 @@ public class AudioService extends IAudioService.Stub public void setWiredDeviceConnectionState(int type, @ConnectionState int state, String address, String name, String caller) { + enforceModifyAudioRoutingPermission(); if (state != CONNECTION_STATE_CONNECTED && state != CONNECTION_STATE_DISCONNECTED) { throw new IllegalArgumentException("Invalid state " + state); @@ -5165,7 +5164,7 @@ public class AudioService extends IAudioService.Stub } else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device) && isAvrcpAbsVolSupported) { index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10); - } else if (mFullVolumeDevices.contains(device)) { + } else if (isFullVolumeDevice(device)) { index = (mIndexMax + 5)/10; } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) { index = (mIndexMax + 5)/10; @@ -5188,7 +5187,7 @@ public class AudioService extends IAudioService.Stub } else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device) && isAvrcpAbsVolSupported) { index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10); - } else if (mFullVolumeDevices.contains(device)) { + } else if (isFullVolumeDevice(device)) { index = (mIndexMax + 5)/10; } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) { index = (mIndexMax + 5)/10; @@ -5389,8 +5388,8 @@ public class AudioService extends IAudioService.Stub for (int i = 0; i < mIndexMap.size(); i++) { int device = mIndexMap.keyAt(i); int index = mIndexMap.valueAt(i); - if (mFullVolumeDevices.contains(device) - || (mFixedVolumeDevices.contains(device) && index != 0)) { + if (isFullVolumeDevice(device) + || (isFixedVolumeDevice(device) && index != 0)) { mIndexMap.put(device, mIndexMax); } applyDeviceVolume_syncVSS(device, isAvrcpAbsVolSupported); @@ -8235,4 +8234,23 @@ public class AudioService extends IAudioService.Stub new HashMap<IBinder, AudioPolicyProxy>(); @GuardedBy("mAudioPolicies") private int mAudioPolicyCounter = 0; + + //====================== + // Helper functions for full and fixed volume device + //====================== + private boolean isFixedVolumeDevice(int deviceType) { + if (deviceType == AudioSystem.DEVICE_OUT_REMOTE_SUBMIX + && mRecordMonitor.isLegacyRemoteSubmixActive()) { + return false; + } + return mFixedVolumeDevices.contains(deviceType); + } + + private boolean isFullVolumeDevice(int deviceType) { + if (deviceType == AudioSystem.DEVICE_OUT_REMOTE_SUBMIX + && mRecordMonitor.isLegacyRemoteSubmixActive()) { + return false; + } + return mFullVolumeDevices.contains(deviceType); + } } diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java index 5c5096251e79..65f221899818 100644 --- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java +++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java @@ -18,6 +18,7 @@ package com.android.server.audio; import android.content.Context; import android.content.pm.PackageManager; +import android.media.AudioDeviceInfo; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecordingConfiguration; @@ -35,6 +36,8 @@ import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; /** * Class to receive and dispatch updates from AudioSystem about recording configurations. @@ -49,6 +52,16 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin // playback configurations that do not contain uid/package name information. private boolean mHasPublicClients = false; + + // When legacy remote submix device is active, remote submix device should not be fixed and + // full volume device. When legacy remote submix device is active, there will be a recording + // activity using device with type as {@link AudioSystem.DEVICE_OUT_REMOTE_SUBMIX} and address + // as {@link AudioSystem.LEGACY_REMOTE_SUBMIX_ADDRESS}. Cache riid of legacy remote submix + // since remote submix state is not cached in mRecordStates. + private AtomicInteger mLegacyRemoteSubmixRiid = + new AtomicInteger(AudioManager.RECORD_RIID_INVALID); + private AtomicBoolean mLegacyRemoteSubmixActive = new AtomicBoolean(false); + static final class RecordingState { private final int mRiid; private final RecorderDeathHandler mDeathHandler; @@ -137,6 +150,16 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin final AudioRecordingConfiguration config = createRecordingConfiguration( uid, session, source, recordingInfo, portId, silenced, activeSource, clientEffects, effects); + if (source == MediaRecorder.AudioSource.REMOTE_SUBMIX) { + final AudioDeviceInfo device = config.getAudioDevice(); + if (AudioSystem.LEGACY_REMOTE_SUBMIX_ADDRESS.equals(device.getAddress())) { + mLegacyRemoteSubmixRiid.set(riid); + if (event == AudioManager.RECORD_CONFIG_EVENT_START + || event == AudioManager.RECORD_CONFIG_EVENT_UPDATE) { + mLegacyRemoteSubmixActive.set(true); + } + } + } if (MediaRecorder.isSystemOnlyAudioSource(source)) { // still want to log event, it just won't appear in recording configurations; sEventLogger.log(new RecordingEvent(event, riid, config).printLog(TAG)); @@ -170,6 +193,9 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin * Receive an event from the client about a tracked recorder */ public void recorderEvent(int riid, int event) { + if (mLegacyRemoteSubmixRiid.get() == riid) { + mLegacyRemoteSubmixActive.set(event == AudioManager.RECORDER_STATE_STARTED); + } int configEvent = event == AudioManager.RECORDER_STATE_STARTED ? AudioManager.RECORD_CONFIG_EVENT_START : event == AudioManager.RECORDER_STATE_STOPPED @@ -323,6 +349,13 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin } /** + * Return true if legacy remote submix device is active. Otherwise, return false. + */ + boolean isLegacyRemoteSubmixActive() { + return mLegacyRemoteSubmixActive.get(); + } + + /** * Create a recording configuration from the provided parameters * @param uid * @param session diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index d45ffd9918e4..ff8e3a973641 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -202,8 +202,7 @@ public class AuthService extends SystemService { // Only allow internal clients to call canAuthenticate with a different userId. final int callingUserId = UserHandle.getCallingUserId(); - Slog.d(TAG, "canAuthenticate, userId: " + userId + ", callingUserId: " + callingUserId - + ", authenticators: " + authenticators); + if (userId != callingUserId) { checkInternalPermission(); } else { @@ -212,8 +211,14 @@ public class AuthService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { - return mBiometricService.canAuthenticate( + final int result = mBiometricService.canAuthenticate( opPackageName, userId, callingUserId, authenticators); + Slog.d(TAG, "canAuthenticate" + + ", userId: " + userId + + ", callingUserId: " + callingUserId + + ", authenticators: " + authenticators + + ", result: " + result); + return result; } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java index 766e5c4d638f..5d334c22f2db 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java @@ -66,6 +66,8 @@ public abstract class AuthenticationClient extends ClientMonitor { public abstract boolean wasUserDetected(); + public abstract boolean isStrongBiometric(); + public AuthenticationClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId, @@ -167,9 +169,15 @@ public abstract class AuthenticationClient extends ClientMonitor { } if (isBiometricPrompt() && listener != null) { // BiometricService will add the token to keystore - listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken); + listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken, + isStrongBiometric()); } else if (!isBiometricPrompt() && listener != null) { - KeyStore.getInstance().addAuthToken(byteToken); + if (isStrongBiometric()) { + KeyStore.getInstance().addAuthToken(byteToken); + } else { + Slog.d(getLogTag(), "Skipping addAuthToken"); + } + try { // Explicitly have if/else here to make it super obvious in case the code is // touched in the future. diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index e7c09baf3aeb..233416d663d9 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -266,7 +266,8 @@ public class BiometricService extends SystemService { SomeArgs args = (SomeArgs) msg.obj; handleAuthenticationSucceeded( (boolean) args.arg1 /* requireConfirmation */, - (byte[]) args.arg2 /* token */); + (byte[]) args.arg2 /* token */, + (boolean) args.arg3 /* isStrongBiometric */); args.recycle(); break; } @@ -568,10 +569,12 @@ public class BiometricService extends SystemService { final IBiometricServiceReceiverInternal mInternalReceiver = new IBiometricServiceReceiverInternal.Stub() { @Override - public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token) { + public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token, + boolean isStrongBiometric) { SomeArgs args = SomeArgs.obtain(); args.arg1 = requireConfirmation; args.arg2 = token; + args.arg3 = isStrongBiometric; mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget(); } @@ -761,8 +764,13 @@ public class BiometricService extends SystemService { + " config_biometric_sensors?"); } + // Note that we allow BIOMETRIC_CONVENIENCE to register because BiometricService + // also does / will do other things such as keep track of lock screen timeout, etc. + // Just because a biometric is registered does not mean it can participate in + // the android.hardware.biometrics APIs. if (strength != Authenticators.BIOMETRIC_STRONG - && strength != Authenticators.BIOMETRIC_WEAK) { + && strength != Authenticators.BIOMETRIC_WEAK + && strength != Authenticators.BIOMETRIC_CONVENIENCE) { throw new IllegalStateException("Unsupported strength"); } @@ -1189,8 +1197,10 @@ public class BiometricService extends SystemService { BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL); } } else { + // This should not be possible via the public API surface and is here mainly for + // "correctness". An exception should have been thrown before getting here. Slog.e(TAG, "No authenticators requested"); - return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE); + return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT); } } @@ -1286,7 +1296,8 @@ public class BiometricService extends SystemService { return modality; } - private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) { + private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token, + boolean isStrongBiometric) { try { // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded // after user dismissed/canceled dialog). @@ -1295,9 +1306,16 @@ public class BiometricService extends SystemService { return; } - // Store the auth token and submit it to keystore after the dialog is confirmed / - // animating away. - mCurrentAuthSession.mTokenEscrow = token; + if (isStrongBiometric) { + // Store the auth token and submit it to keystore after the dialog is confirmed / + // animating away. + mCurrentAuthSession.mTokenEscrow = token; + } else { + if (token != null) { + Slog.w(TAG, "Dropping authToken for non-strong biometric"); + } + } + if (!requireConfirmation) { mCurrentAuthSession.mState = STATE_AUTHENTICATED_PENDING_SYSUI; } else { diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index ebd407d0e981..45b93834c1e2 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -413,8 +413,8 @@ public abstract class BiometricServiceBase extends SystemService throw new UnsupportedOperationException("Stub!"); } - default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token) - throws RemoteException { + default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token, + boolean isStrongBiometric) throws RemoteException { throw new UnsupportedOperationException("Stub!"); } @@ -451,10 +451,11 @@ public abstract class BiometricServiceBase extends SystemService } @Override - public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token) - throws RemoteException { + public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token, + boolean isStrongBiometric) throws RemoteException { if (getWrapperReceiver() != null) { - getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token); + getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token, + isStrongBiometric); } } diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index fd54129a3263..3ecf87c6860f 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -236,6 +236,11 @@ public class FaceService extends BiometricServiceBase { } @Override + public boolean isStrongBiometric() { + return FaceService.this.isStrongBiometric(); + } + + @Override public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token) { final boolean result = super.onAuthenticated(identifier, authenticated, token); diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index acb1a2ffb754..8520f5aa0632 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -161,6 +161,11 @@ public class FingerprintService extends BiometricServiceBase { } @Override + public boolean isStrongBiometric() { + return FingerprintService.this.isStrongBiometric(); + } + + @Override public int handleFailedAttempt() { final int currentUser = ActivityManager.getCurrentUser(); mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1); diff --git a/services/core/java/com/android/server/incident/IncidentCompanionService.java b/services/core/java/com/android/server/incident/IncidentCompanionService.java index 87fe785ca614..ad08663a7d76 100644 --- a/services/core/java/com/android/server/incident/IncidentCompanionService.java +++ b/services/core/java/com/android/server/incident/IncidentCompanionService.java @@ -50,6 +50,9 @@ import java.util.List; */ public class IncidentCompanionService extends SystemService { static final String TAG = "IncidentCompanionService"; + // TODO(b/152289743): Expose below intent. + private static final String INTENT_CHECK_USER_CONSENT = + "com.android.internal.intent.action.CHECK_USER_CONSENT"; /** * Dump argument for proxying restricted image dumps to the services @@ -89,6 +92,12 @@ public class IncidentCompanionService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { + Intent intent = new Intent(INTENT_CHECK_USER_CONSENT); + intent.setPackage(callingPackage); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + getContext().sendBroadcast(intent, android.Manifest.permission.DUMP); + mPendingReports.authorizeReport(callingUid, callingPackage, receiverClass, reportId, flags, listener); } finally { diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 0b22586bb373..e6129b9b1f32 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -76,7 +76,6 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputMonitor; -import android.view.InputWindowHandle; import android.view.KeyEvent; import android.view.PointerIcon; import android.view.Surface; @@ -221,8 +220,7 @@ public class InputManagerService extends IInputManager.Stub int policyFlags); private static native VerifiedInputEvent nativeVerifyInputEvent(long ptr, InputEvent event); private static native void nativeToggleCapsLock(long ptr, int deviceId); - private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles, - int displayId); + private static native void nativeDisplayRemoved(long ptr, int displayId); private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen); private static native void nativeSetSystemUiVisibility(long ptr, int visibility); private static native void nativeSetFocusedApplication(long ptr, @@ -1536,7 +1534,7 @@ public class InputManagerService extends IInputManager.Stub /** Clean up input window handles of the given display. */ public void onDisplayRemoved(int displayId) { - nativeSetInputWindows(mPtr, null /* windowHandles */, displayId); + nativeDisplayRemoved(mPtr, displayId); } @Override diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 58e332ab6d1d..c1fbcfba864a 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -653,9 +653,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0); mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0); - mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context, - GnssLocationProvider.this::onNetworkAvailable, mLooper); - // App ops service to keep track of who is accessing the GPS mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -677,6 +674,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mNIHandler = new GpsNetInitiatedHandler(context, mNetInitiatedListener, mSuplEsEnabled); + mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context, + GnssLocationProvider.this::onNetworkAvailable, mLooper, mNIHandler); + sendMessage(INITIALIZE_HANDLER, 0, null); mGnssStatusListenerHelper = new GnssStatusListenerHelper(mContext, mHandler) { diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java index 93227bd78a81..5d6474bdbccc 100644 --- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java +++ b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java @@ -29,12 +29,22 @@ import android.os.PowerManager; import android.provider.Telephony.Carriers; import android.telephony.ServiceState; import android.telephony.TelephonyManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.PreciseCallState; +import android.telephony.PhoneStateListener; import android.util.Log; +import com.android.internal.location.GpsNetInitiatedHandler; + import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; +import java.util.Map; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Iterator; /** * Handles network connection requests and network state change updates for AGPS data download. @@ -86,6 +96,9 @@ class GnssNetworkConnectivityHandler { private HashMap<Network, NetworkAttributes> mAvailableNetworkAttributes = new HashMap<>(HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS); + // Phone State Listeners to track all the active sub IDs + private HashMap<Integer, SubIdPhoneStateListener> mPhoneStateListeners; + private final ConnectivityManager mConnMgr; private final Handler mHandler; @@ -94,6 +107,9 @@ class GnssNetworkConnectivityHandler { private int mAGpsDataConnectionState; private InetAddress mAGpsDataConnectionIpAddr; private int mAGpsType; + private int mActiveSubId = -1; + private final GpsNetInitiatedHandler mNiHandler; + private final Context mContext; @@ -166,18 +182,109 @@ class GnssNetworkConnectivityHandler { GnssNetworkConnectivityHandler(Context context, GnssNetworkListener gnssNetworkListener, - Looper looper) { + Looper looper, + GpsNetInitiatedHandler niHandler) { mContext = context; mGnssNetworkListener = gnssNetworkListener; + SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class); + if (subManager != null) { + subManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); + } + PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); mHandler = new Handler(looper); + mNiHandler = niHandler; mConnMgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); mSuplConnectivityCallback = createSuplConnectivityCallback(); } + /** + * SubId Phone State Listener is used cache the last active Sub ID when a call is made, + * which will be used during an emergency call to set the Network Specifier to the particular + * sub when an emergency supl connection is requested + */ + private final class SubIdPhoneStateListener extends PhoneStateListener { + private Integer mSubId; + SubIdPhoneStateListener(Integer subId) { + mSubId = subId; + } + @Override + public void onPreciseCallStateChanged(PreciseCallState state) { + if (state.PRECISE_CALL_STATE_ACTIVE == state.getForegroundCallState()) { + mActiveSubId = mSubId; + if (DEBUG) Log.d(TAG, "mActiveSubId: " + mActiveSubId); + } + } + }; + + /** + * Subscription Changed Listener is used to get all active subscriptions and create a + * Phone State Listener for each Sub ID that we find in the active subscription list + */ + private final SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangeListener + = new SubscriptionManager.OnSubscriptionsChangedListener() { + @Override + public void onSubscriptionsChanged() { + if (mPhoneStateListeners == null) { + // Capacity=2 Load-Factor=1.0, as typically no more than 2 SIMs + mPhoneStateListeners = new HashMap<Integer, SubIdPhoneStateListener>(2,1); + } + SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class); + TelephonyManager telManager = mContext.getSystemService(TelephonyManager.class); + if (subManager != null && telManager != null) { + List<SubscriptionInfo> subscriptionInfoList = + subManager.getActiveSubscriptionInfoList(); + HashSet<Integer> activeSubIds = new HashSet<Integer>(); + if (subscriptionInfoList != null) { + if (DEBUG) Log.d(TAG, "Active Sub List size: " + subscriptionInfoList.size()); + // populate phone state listeners with all new active subs + for (SubscriptionInfo subInfo : subscriptionInfoList) { + activeSubIds.add(subInfo.getSubscriptionId()); + if (!mPhoneStateListeners.containsKey(subInfo.getSubscriptionId())) { + TelephonyManager subIdTelManager = + telManager.createForSubscriptionId(subInfo.getSubscriptionId()); + if (subIdTelManager != null) { + if (DEBUG) Log.d(TAG, "Listener sub" + subInfo.getSubscriptionId()); + SubIdPhoneStateListener subIdPhoneStateListener = + new SubIdPhoneStateListener(subInfo.getSubscriptionId()); + mPhoneStateListeners.put(subInfo.getSubscriptionId(), + subIdPhoneStateListener); + subIdTelManager.listen(subIdPhoneStateListener, + PhoneStateListener.LISTEN_PRECISE_CALL_STATE); + } + } + } + } + // clean up phone state listeners than no longer have active subs + Iterator<Map.Entry<Integer, SubIdPhoneStateListener> > iterator = + mPhoneStateListeners.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry<Integer, SubIdPhoneStateListener> element = iterator.next(); + if (!activeSubIds.contains(element.getKey())) { + TelephonyManager subIdTelManager = + telManager.createForSubscriptionId(element.getKey()); + if (subIdTelManager != null) { + if (DEBUG) Log.d(TAG, "unregister listener sub " + element.getKey()); + subIdTelManager.listen(element.getValue(), + PhoneStateListener.LISTEN_NONE); + // removes the element from mPhoneStateListeners + iterator.remove(); + } else { + Log.e(TAG, "Telephony Manager for Sub " + element.getKey() + " null"); + } + } + } + // clean up cached active phone call sub if it is no longer an active sub + if (!activeSubIds.contains(mActiveSubId)) { + mActiveSubId = -1; + } + } + } + }; + void registerNetworkCallbacks() { // register for connectivity change events. NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder(); @@ -467,6 +574,12 @@ class GnssNetworkConnectivityHandler { NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder(); networkRequestBuilder.addCapability(getNetworkCapability(mAGpsType)); networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + // During an emergency call, and when we have cached the Active Sub Id, we set the + // Network Specifier so that the network request goes to the correct Sub Id + if (mNiHandler.getInEmergency() && mActiveSubId >= 0) { + if (DEBUG) Log.d(TAG, "Adding Network Specifier: " + Integer.toString(mActiveSubId)); + networkRequestBuilder.setNetworkSpecifier(Integer.toString(mActiveSubId)); + } NetworkRequest networkRequest = networkRequestBuilder.build(); mConnMgr.requestNetwork( networkRequest, @@ -598,6 +711,15 @@ class GnssNetworkConnectivityHandler { } TelephonyManager phone = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + // During an emergency call with an active sub id, get the Telephony Manager specific + // to the active sub to get the correct value from getServiceState and getNetworkType + if (mNiHandler.getInEmergency() && mActiveSubId >= 0) { + TelephonyManager subIdTelManager = + phone.createForSubscriptionId(mActiveSubId); + if (subIdTelManager != null) { + phone = subIdTelManager; + } + } ServiceState serviceState = phone.getServiceState(); String projection = null; String selection = null; diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java index e5cb5540b781..f2eb176aa676 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,46 +147,18 @@ 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"); - 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"); + if (!isMessageStyle) { + logBubbleError(r.getKey(), "must be Notification.MessageStyle"); 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; } /** - * @return whether the user has enabled the provided notification to bubble, does not - * account for policy. + * @return whether the user has enabled the provided notification to bubble, and if the + * developer has provided valid information for the notification to bubble. */ @VisibleForTesting boolean canBubble(NotificationRecord r, String pkg, int userId) { @@ -217,8 +184,17 @@ public class BubbleExtractor implements NotificationSignalExtractor { } String shortcutId = metadata.getShortcutId(); - boolean shortcutValid = shortcutId != null - && mShortcutHelper.hasValidShortcutInfo(shortcutId, pkg, r.getUser()); + String notificationShortcutId = r.getShortcutInfo() != null + ? r.getShortcutInfo().getId() + : null; + boolean shortcutValid = false; + if (notificationShortcutId != null && shortcutId != null) { + // NoMan already checks validity of shortcut, just check if they match. + shortcutValid = shortcutId.equals(notificationShortcutId); + } else if (shortcutId != null) { + shortcutValid = + mShortcutHelper.getValidShortcutInfo(shortcutId, pkg, r.getUser()) != null; + } if (metadata.getIntent() == null && !shortcutValid) { // Should have a shortcut if intent is null logBubbleError(r.getKey(), diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f65f187eddc5..7f805bef520d 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -139,6 +139,7 @@ import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.companion.ICompanionDeviceManager; import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProvider; @@ -243,7 +244,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; @@ -388,10 +388,9 @@ public class NotificationManagerService extends SystemService { * still post toasts created with * {@link android.widget.Toast#makeText(Context, CharSequence, int)} and its variants while * in the background. - * - * TODO(b/144152069): Add @EnabledAfter(Q) to target R+ after assessing impact on dogfood */ @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) private static final long CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK = 128611929L; private IActivityManager mAm; @@ -2752,24 +2751,18 @@ public class NotificationManagerService extends SystemService { @Override public void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @Nullable ITransientNotificationCallback callback) { - enqueueToast(pkg, token, text, null, duration, displayId, callback, false); + enqueueToast(pkg, token, text, null, duration, displayId, callback); } @Override public void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId) { - enqueueToast(pkg, token, null, callback, duration, displayId, null, true); - } - - @Override - public void enqueueTextOrCustomToast(String pkg, IBinder token, - ITransientNotification callback, int duration, int displayId, boolean isCustom) { - enqueueToast(pkg, token, null, callback, duration, displayId, null, isCustom); + enqueueToast(pkg, token, null, callback, duration, displayId, null); } private void enqueueToast(String pkg, IBinder token, @Nullable CharSequence text, @Nullable ITransientNotification callback, int duration, int displayId, - @Nullable ITransientNotificationCallback textCallback, boolean isCustom) { + @Nullable ITransientNotificationCallback textCallback) { if (DBG) { Slog.i(TAG, "enqueueToast pkg=" + pkg + " token=" + token + " duration=" + duration + " displayId=" + displayId); @@ -2808,11 +2801,15 @@ public class NotificationManagerService extends SystemService { } boolean isAppRenderedToast = (callback != null); - if (isAppRenderedToast && isCustom && !isSystemToast - && !isPackageInForegroundForToast(pkg, callingUid)) { + if (isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(pkg, + callingUid)) { boolean block; long id = Binder.clearCallingIdentity(); try { + // CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is gated on targetSdk, so block will be + // false for apps with targetSdk < R. For apps with targetSdk R+, text toasts + // are not app-rendered, so isAppRenderedToast == true means it's a custom + // toast. block = mPlatformCompat.isChangeEnabledByPackageName( CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, pkg, callingUser.getIdentifier()); @@ -2825,11 +2822,6 @@ public class NotificationManagerService extends SystemService { Binder.restoreCallingIdentity(id); } if (block) { - // TODO(b/144152069): Remove informative toast - mUiHandler.post(() -> Toast.makeText(getContext(), - "Background custom toast blocked for package " + pkg + ".\n" - + "See g.co/dev/toast.", - Toast.LENGTH_SHORT).show()); Slog.w(TAG, "Blocking custom toast from package " + pkg + " due to package not in the foreground"); return; @@ -3454,7 +3446,7 @@ public class NotificationManagerService extends SystemService { ArrayList<ConversationChannelWrapper> conversations = mPreferencesHelper.getConversations(onlyImportant); for (ConversationChannelWrapper conversation : conversations) { - conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo( + conversation.setShortcutInfo(mShortcutHelper.getValidShortcutInfo( conversation.getNotificationChannel().getConversationId(), conversation.getPkg(), UserHandle.of(UserHandle.getUserId(conversation.getUid())))); @@ -3477,7 +3469,7 @@ public class NotificationManagerService extends SystemService { ArrayList<ConversationChannelWrapper> conversations = mPreferencesHelper.getConversations(pkg, uid); for (ConversationChannelWrapper conversation : conversations) { - conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo( + conversation.setShortcutInfo(mShortcutHelper.getValidShortcutInfo( conversation.getNotificationChannel().getConversationId(), pkg, UserHandle.of(UserHandle.getUserId(uid)))); @@ -3639,13 +3631,23 @@ public class NotificationManagerService extends SystemService { } /** + * @deprecated Use {@link #getActiveNotificationsWithAttribution(String, String)} instead. + */ + @Deprecated + @Override + public StatusBarNotification[] getActiveNotifications(String callingPkg) { + return getActiveNotificationsWithAttribution(callingPkg, null); + } + + /** * System-only API for getting a list of current (i.e. not cleared) notifications. * * Requires ACCESS_NOTIFICATIONS which is signature|system. * @returns A list of all the notifications, in natural order. */ @Override - public StatusBarNotification[] getActiveNotifications(String callingPkg) { + public StatusBarNotification[] getActiveNotificationsWithAttribution(String callingPkg, + String callingAttributionTag) { // enforce() will ensure the calling uid has the correct permission getContext().enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NOTIFICATIONS, @@ -3655,7 +3657,8 @@ public class NotificationManagerService extends SystemService { int uid = Binder.getCallingUid(); // noteOp will check to make sure the callingPkg matches the uid - if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) + if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg, + callingAttributionTag, null) == AppOpsManager.MODE_ALLOWED) { synchronized (mNotificationLock) { tmp = new StatusBarNotification[mNotificationList.size()]; @@ -3737,12 +3740,24 @@ public class NotificationManagerService extends SystemService { } /** - * System-only API for getting a list of recent (cleared, no longer shown) notifications. + * @deprecated Use {@link #getHistoricalNotificationsWithAttribution} instead. */ + @Deprecated @Override @RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS) public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count, boolean includeSnoozed) { + return getHistoricalNotificationsWithAttribution(callingPkg, null, count, + includeSnoozed); + } + + /** + * System-only API for getting a list of recent (cleared, no longer shown) notifications. + */ + @Override + @RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS) + public StatusBarNotification[] getHistoricalNotificationsWithAttribution(String callingPkg, + String callingAttributionTag, int count, boolean includeSnoozed) { // enforce() will ensure the calling uid has the correct permission getContext().enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NOTIFICATIONS, @@ -3752,7 +3767,8 @@ public class NotificationManagerService extends SystemService { int uid = Binder.getCallingUid(); // noteOp will check to make sure the callingPkg matches the uid - if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) + if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg, + callingAttributionTag, null) == AppOpsManager.MODE_ALLOWED) { synchronized (mArchive) { tmp = mArchive.getArray(count, includeSnoozed); @@ -3768,7 +3784,8 @@ public class NotificationManagerService extends SystemService { @Override @WorkerThread @RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS) - public NotificationHistory getNotificationHistory(String callingPkg) { + public NotificationHistory getNotificationHistory(String callingPkg, + String callingAttributionTag) { // enforce() will ensure the calling uid has the correct permission getContext().enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NOTIFICATIONS, @@ -3776,7 +3793,8 @@ public class NotificationManagerService extends SystemService { int uid = Binder.getCallingUid(); // noteOp will check to make sure the callingPkg matches the uid - if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) + if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg, + callingAttributionTag, null) == AppOpsManager.MODE_ALLOWED) { IntArray currentUserIds = mUserProfiles.getCurrentProfileIds(); Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryReadHistory"); @@ -5648,7 +5666,8 @@ public class NotificationManagerService extends SystemService { } } - r.setShortcutInfo(mShortcutHelper.getShortcutInfo(notification.getShortcutId(), pkg, user)); + r.setShortcutInfo(mShortcutHelper.getValidShortcutInfo( + notification.getShortcutId(), pkg, user)); if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r, r.getSbn().getOverrideGroupKey() != null)) { diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java index 7bbb3b117517..f1ce3a7d9f48 100644 --- a/services/core/java/com/android/server/notification/ShortcutHelper.java +++ b/services/core/java/com/android/server/notification/ShortcutHelper.java @@ -121,7 +121,10 @@ class ShortcutHelper { mLauncherAppsService = launcherApps; } - ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) { + /** + * Only returns shortcut info if it's found and if it's {@link ShortcutInfo#isLongLived()}. + */ + ShortcutInfo getValidShortcutInfo(String shortcutId, String packageName, UserHandle user) { if (mLauncherAppsService == null) { return null; } @@ -135,20 +138,15 @@ class ShortcutHelper { query.setShortcutIds(Arrays.asList(shortcutId)); query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED); List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user); - return shortcuts != null && shortcuts.size() > 0 + ShortcutInfo info = shortcuts != null && shortcuts.size() > 0 ? shortcuts.get(0) : null; + return info != null && info.isLongLived() ? info : null; } finally { Binder.restoreCallingIdentity(token); } } - boolean hasValidShortcutInfo(String shortcutId, String packageName, - UserHandle user) { - ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user); - return shortcutInfo != null && shortcutInfo.isLongLived(); - } - /** * Shortcut based bubbles require some extra work to listen for shortcut changes. * diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b79f75a2d335..58a9d9c5f6bf 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -161,6 +161,7 @@ import android.content.pm.DataLoaderType; import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureInfo; import android.content.pm.IDexModuleRegisterCallback; +import android.content.pm.IPackageChangeObserver; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageDeleteObserver2; @@ -178,6 +179,7 @@ import android.content.pm.InstrumentationInfo; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.KeySet; import android.content.pm.ModuleInfo; +import android.content.pm.PackageChangeEvent; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; @@ -812,6 +814,10 @@ public class PackageManagerService extends IPackageManager.Stub private final OverlayConfig mOverlayConfig; + @GuardedBy("itself") + final private ArrayList<IPackageChangeObserver> mPackageChangeObservers = + new ArrayList<>(); + /** * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors. * @@ -5064,15 +5070,16 @@ public class PackageManagerService extends IPackageManager.Stub * action and a {@code android.intent.category.BROWSABLE} category</li> * </ul> */ - int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps) { + int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps, + boolean matchSystemOnly) { return updateFlagsForResolve(flags, userId, callingUid, - wantInstantApps, false /*onlyExposedExplicitly*/); + wantInstantApps, matchSystemOnly, false /*onlyExposedExplicitly*/); } int updateFlagsForResolve(int flags, int userId, int callingUid, - boolean wantInstantApps, boolean onlyExposedExplicitly) { + boolean wantInstantApps, boolean onlyExposedExplicitly, boolean matchSystemOnly) { // Safe mode means we shouldn't match any third-party components - if (mSafeMode) { + if (mSafeMode || matchSystemOnly) { flags |= PackageManager.MATCH_SYSTEM_ONLY; } if (getInstantAppPackageName(callingUid) != null) { @@ -6164,7 +6171,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); - flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart); + flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart, + intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); @@ -6196,7 +6204,8 @@ public class PackageManagerService extends IPackageManager.Stub intent = updateIntentForResolve(intent); final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); final int flags = updateFlagsForResolve( - 0, userId, callingUid, false /*includeInstantApps*/); + 0, userId, callingUid, false /*includeInstantApps*/, + intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); synchronized (mLock) { @@ -6517,7 +6526,8 @@ public class PackageManagerService extends IPackageManager.Stub android.provider.Settings.Global.getInt(mContext.getContentResolver(), android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1; flags = updateFlagsForResolve( - flags, userId, callingUid, false /*includeInstantApps*/); + flags, userId, callingUid, false /*includeInstantApps*/, + intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); intent = updateIntentForResolve(intent); // writer synchronized (mLock) { @@ -6729,7 +6739,8 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized (mLock) { int flags = updateFlagsForResolve(0, parent.id, callingUid, - false /*includeInstantApps*/); + false /*includeInstantApps*/, + intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr( intent, resolvedType, flags, sourceUserId, parent.id); return xpDomainInfo != null; @@ -6815,7 +6826,8 @@ public class PackageManagerService extends IPackageManager.Stub } flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart, - comp != null || pkgName != null /*onlyExposedExplicitly*/); + comp != null || pkgName != null /*onlyExposedExplicitly*/, + intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); if (comp != null) { final List<ResolveInfo> list = new ArrayList<>(1); final ActivityInfo ai = getActivityInfo(comp, flags, userId); @@ -7600,7 +7612,8 @@ public class PackageManagerService extends IPackageManager.Stub String resolvedType, int flags, int userId) { if (!mUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); - flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/); + flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, + intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); @@ -7786,7 +7799,8 @@ public class PackageManagerService extends IPackageManager.Stub false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); - flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/); + flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, + intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -7876,7 +7890,8 @@ public class PackageManagerService extends IPackageManager.Stub private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid) { if (!mUserManager.exists(userId)) return null; - flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/); + flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, + false /* matchSystemOnly */); List<ResolveInfo> query = queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); if (query != null) { @@ -7907,7 +7922,8 @@ public class PackageManagerService extends IPackageManager.Stub false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); - flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps); + flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps, + false /* matchSystemOnly */); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -8044,7 +8060,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); final String instantAppPkgName = getInstantAppPackageName(callingUid); - flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/); + flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, + false /* matchSystemOnly */); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -16458,9 +16475,56 @@ public class PackageManagerService extends IPackageManager.Stub // BackgroundDexOptService will remove it from its blacklist. // TODO: Layering violation BackgroundDexOptService.notifyPackageChanged(packageName); + + notifyPackageChangeObserversOnUpdate(reconciledPkg); } } + private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) { + final PackageSetting pkgSetting = reconciledPkg.pkgSetting; + final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.installResult; + final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.removedInfo; + + PackageChangeEvent pkgChangeEvent = new PackageChangeEvent(); + pkgChangeEvent.packageName = pkgSetting.pkg.getPackageName(); + pkgChangeEvent.version = pkgSetting.versionCode; + pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.lastUpdateTime; + pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.isUpdate); + pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.dataRemoved); + pkgChangeEvent.isDeleted = false; + + notifyPackageChangeObservers(pkgChangeEvent); + } + + private void notifyPackageChangeObserversOnDelete(String packageName, long version) { + PackageChangeEvent pkgChangeEvent = new PackageChangeEvent(); + pkgChangeEvent.packageName = packageName; + pkgChangeEvent.version = version; + pkgChangeEvent.lastUpdateTimeMillis = 0L; + pkgChangeEvent.newInstalled = false; + pkgChangeEvent.dataRemoved = false; + pkgChangeEvent.isDeleted = true; + + notifyPackageChangeObservers(pkgChangeEvent); + } + + private void notifyPackageChangeObservers(PackageChangeEvent event) { + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "notifyPackageChangeObservers"); + synchronized (mPackageChangeObservers) { + for(IPackageChangeObserver observer : mPackageChangeObservers) { + try { + observer.onPackageChanged(event); + } catch(RemoteException e) { + Log.wtf(TAG, e); + } + } + } + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + /** * The set of data needed to successfully install the prepared package. This includes data that * will be used to scan and reconcile the package. @@ -17521,6 +17585,7 @@ public class PackageManagerService extends IPackageManager.Stub } catch (RemoteException e) { Log.i(TAG, "Observer no longer exists."); } //end catch + notifyPackageChangeObserversOnDelete(packageName, versionCode); }); } @@ -22980,8 +23045,49 @@ public class PackageManagerService extends IPackageManager.Stub } } + private final class PackageChangeObserverDeathRecipient implements IBinder.DeathRecipient { + private final IPackageChangeObserver mObserver; + + PackageChangeObserverDeathRecipient(IPackageChangeObserver observer) { + mObserver = observer; + } + + @Override + public void binderDied() { + synchronized (mPackageChangeObservers) { + mPackageChangeObservers.remove(mObserver); + Log.d(TAG, "Size of mPackageChangeObservers after removing dead observer is " + + mPackageChangeObservers.size()); + } + } + } + private class PackageManagerNative extends IPackageManagerNative.Stub { @Override + public void registerPackageChangeObserver(@NonNull IPackageChangeObserver observer) { + synchronized (mPackageChangeObservers) { + try { + observer.asBinder().linkToDeath( + new PackageChangeObserverDeathRecipient(observer), 0); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage()); + } + mPackageChangeObservers.add(observer); + Log.d(TAG, "Size of mPackageChangeObservers after registry is " + + mPackageChangeObservers.size()); + } + } + + @Override + public void unregisterPackageChangeObserver(@NonNull IPackageChangeObserver observer) { + synchronized (mPackageChangeObservers) { + mPackageChangeObservers.remove(observer); + Log.d(TAG, "Size of mPackageChangeObservers after unregistry is " + + mPackageChangeObservers.size()); + } + } + + @Override public String[] getAllPackages() { return PackageManagerService.this.getAllPackages().toArray(new String[0]); } @@ -24381,7 +24487,8 @@ public class PackageManagerService extends IPackageManager.Stub } else { synchronized (mLock) { boolean manifestWhitelisted = - mPackages.get(packageName).isAllowDontAutoRevokePermmissions(); + mPackages.get(packageName).getAutoRevokePermissions() + == ApplicationInfo.AUTO_REVOKE_DISALLOWED; return manifestWhitelisted; } } diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index 3cc10d194dec..5a1e8e2661b8 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -230,6 +230,10 @@ public class PackageInfoUtils { info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos; } + info.seInfo = AndroidPackageUtils.getSeInfo(pkg, pkgSetting); + info.primaryCpuAbi = AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting); + info.secondaryCpuAbi = AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting); + info.flags |= appInfoFlags(pkg, pkgSetting); info.privateFlags |= appInfoPrivateFlags(pkg, pkgSetting); return info; diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 79d0c2db4448..04c965e69588 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -21,6 +21,8 @@ import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_IGNORED; +import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISALLOWED; +import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED; import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; @@ -3238,31 +3240,25 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override public List<String> getAutoRevokeExemptionRequestedPackages(int userId) { - mContext.enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, - "Must hold " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY); - - List<String> result = new ArrayList<>(); - mPackageManagerInt.forEachInstalledPackage(pkg -> { - if (pkg.isDontAutoRevokePermmissions()) { - result.add(pkg.getPackageName()); - } - }, userId); - - return result; + return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISCOURAGED, userId); } @Override public List<String> getAutoRevokeExemptionGrantedPackages(int userId) { + return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISALLOWED, userId); + } + + @NonNull + private List<String> getPackagesWithAutoRevokePolicy(int autoRevokePolicy, int userId) { mContext.enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "Must hold " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY); List<String> result = new ArrayList<>(); mPackageManagerInt.forEachInstalledPackage(pkg -> { - if (pkg.isAllowDontAutoRevokePermmissions()) { + if (pkg.getAutoRevokePermissions() == autoRevokePolicy) { result.add(pkg.getPackageName()); } }, userId); - return result; } diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 161f30449a52..27288d852fb2 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -30,6 +30,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.AppOpsManagerInternal; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -47,6 +48,7 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManagerInternal; import android.permission.PermissionControllerManager; +import android.provider.Settings; import android.provider.Telephony; import android.telecom.TelecomManager; import android.util.ArrayMap; @@ -70,7 +72,9 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; /** @@ -180,8 +184,6 @@ public final class PermissionPolicyService extends SystemService { intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); intentFilter.addDataScheme("package"); - - /* TODO ntmyren: enable receiver when test flakes are fixed getContext().registerReceiverAsUser(new BroadcastReceiver() { final List<Integer> mUserSetupUids = new ArrayList<>(200); final Map<UserHandle, PermissionControllerManager> mPermControllerManagers = @@ -232,7 +234,6 @@ public final class PermissionPolicyService extends SystemService { manager.updateUserSensitiveForApp(uid); } }, UserHandle.ALL, intentFilter, null, null); - */ } /** diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java index 39aeafc345ea..d6c48a00d33d 100644 --- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java +++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java @@ -26,6 +26,7 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INST import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.storage.StorageManager.PROP_LEGACY_OP_STICKY; import static java.lang.Integer.min; @@ -36,6 +37,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Build; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.StorageManagerInternal; @@ -63,6 +65,9 @@ public abstract class SoftRestrictedPermissionPolicy { } }; + private static final boolean isLegacyStorageAppOpStickyGlobal = SystemProperties.getBoolean( + PROP_LEGACY_OP_STICKY, /*defaultValue*/true); + /** * TargetSDK is per package. To make sure two apps int the same shared UID do not fight over * what to set, always compute the combined targetSDK. @@ -136,9 +141,12 @@ public abstract class SoftRestrictedPermissionPolicy { shouldPreserveLegacyExternalStorage = pkg.hasPreserveLegacyExternalStorage() && smInternal.hasLegacyExternalStorage(appInfo.uid); targetSDK = getMinimumTargetSDK(context, appInfo, user); + // LEGACY_STORAGE op is normally sticky for apps targetig <= Q. + // However, this device can be configured to make it non-sticky. + boolean isLegacyAppOpSticky = isLegacyStorageAppOpStickyGlobal + && targetSDK <= Build.VERSION_CODES.Q; shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0 - || (targetSDK > Build.VERSION_CODES.Q - && !shouldPreserveLegacyExternalStorage); + || (!isLegacyAppOpSticky && !shouldPreserveLegacyExternalStorage); } else { isWhiteListed = false; shouldApplyRestriction = false; diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING index acf3f79e3626..74958b692eb2 100644 --- a/services/core/java/com/android/server/power/TEST_MAPPING +++ b/services/core/java/com/android/server/power/TEST_MAPPING @@ -3,6 +3,7 @@ { "name": "CtsBatterySavingTestCases", "options": [ + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.LargeTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"} ] @@ -11,6 +12,7 @@ "name": "FrameworksMockingServicesTests", "options": [ {"include-filter": "com.android.server.power"}, + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"} ] }, @@ -18,6 +20,7 @@ "name": "FrameworksServicesTests", "options": [ {"include-filter": "com.android.server.power"}, + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"}, { "exclude-filter": "com.android.server.power.PowerManagerServiceTest#testWakefulnessAwake_ShouldWakeUpWhenPluggedIn" diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 74c3a9ec375b..2783d0b83254 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -767,7 +767,7 @@ public class ThermalManagerService extends SystemService { protected boolean connectToHal() { synchronized (mHalLock) { try { - mThermalHal10 = android.hardware.thermal.V1_0.IThermal.getService(); + mThermalHal10 = android.hardware.thermal.V1_0.IThermal.getService(true); mThermalHal10.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE); Slog.i(TAG, @@ -902,7 +902,7 @@ public class ThermalManagerService extends SystemService { protected boolean connectToHal() { synchronized (mHalLock) { try { - mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService(); + mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService(true); mThermalHal11.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE); mThermalHal11.registerThermalCallback(mThermalCallback11); @@ -1046,7 +1046,7 @@ public class ThermalManagerService extends SystemService { protected boolean connectToHal() { synchronized (mHalLock) { try { - mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService(); + mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService(true); mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE); mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false, 0 /* not used */); diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java index 63048f6b95b3..a9e8d3fc7a3a 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java @@ -34,6 +34,7 @@ import android.media.soundtrigger_middleware.SoundModel; import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; import android.media.soundtrigger_middleware.Status; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.util.Log; @@ -139,6 +140,14 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware throw new ServiceSpecificException(((RecoverableException) e).errorCode, e.getMessage()); } + + /* Throwing an exception is not enough in this case. When the HAL behaves unexpectedly, the + system service and the HAL must be reset and the client must be notified. Without a full + reset in this catastrophic case, the state of the HAL and the system service cannot be + guaranteed to the client. + */ + Log.wtf(TAG, "Crashing system server due to unrecoverable exception", e); + Process.killProcess(Process.myPid()); throw new InternalServerError(e); } @@ -377,7 +386,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware mCallback = callback; mHandle = handle; try { - mCallback.asBinder().linkToDeath(null, 0); + mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -680,7 +689,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware try { mDelegate.detach(); mDelegate = null; - mCallback.asBinder().unlinkToDeath(null, 0); + mCallback.asBinder().unlinkToDeath(this, 0); mModules.get(mHandle).remove(this); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 6d53786ddf41..3477d82f4d71 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4618,6 +4618,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } catch (Exception e) { Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e); } + // The activity may be waiting for stop, but that is no longer appropriate if we are + // starting the activity again + mStackSupervisor.mStoppingActivities.remove(this); } return false; } @@ -4667,7 +4670,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * and {@link #shouldPauseActivity(ActivityRecord)}. */ private boolean shouldStartActivity() { - return mVisibleRequested && isState(STOPPED); + return mVisibleRequested && (isState(STOPPED) || isState(STOPPING)); } /** diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index e8bfe8ef63ac..fa0ad505f1a9 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -36,7 +36,6 @@ import static android.app.WindowConfiguration.windowingModeToString; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; @@ -303,9 +302,6 @@ class ActivityStack extends Task { private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1; - // TODO(task-hierarchy): remove when tiles can be actual parents - TaskTile mTile = null; - private final Handler mHandler; private class ActivityStackHandler extends Handler { @@ -551,10 +547,10 @@ class ActivityStack extends Task { } ActivityStack(ActivityTaskManagerService atmService, int id, int activityType, - ActivityInfo info, Intent intent) { + ActivityInfo info, Intent intent, boolean createdByOrganizer) { this(atmService, id, info, intent, null /*voiceSession*/, null /*voiceInteractor*/, null /*taskDescription*/, null /*stack*/); - + mCreatedByOrganizer = createdByOrganizer; setActivityType(activityType); } @@ -601,20 +597,11 @@ class ActivityStack extends Task { } @Override - public void resolveTileOverrideConfiguration(Configuration newParentConfig) { - super.resolveTileOverrideConfiguration(newParentConfig); - if (mTile != null) { - // If this is a virtual child of a tile, simulate the parent-child relationship - mTile.updateResolvedConfig(getResolvedOverrideConfiguration()); - } - } - - @Override public void onConfigurationChanged(Configuration newParentConfig) { // Calling Task#onConfigurationChanged() for leaf task since the ops in this method are // particularly for ActivityStack, like preventing bounds changes when inheriting certain // windowing mode. - if (!isRootTask() || this instanceof TaskTile) { + if (!isRootTask()) { super.onConfigurationChanged(newParentConfig); return; } @@ -689,6 +676,9 @@ class ActivityStack extends Task { @Override public void setWindowingMode(int windowingMode) { + // Reset the cached result of toString() + stringName = null; + // Calling Task#setWindowingMode() for leaf task since this is the a specialization of // {@link #setWindowingMode(int)} for ActivityStack. if (!isRootTask()) { @@ -742,7 +732,6 @@ class ActivityStack extends Task { final int currentOverrideMode = getRequestedOverrideWindowingMode(); final DisplayContent display = getDisplay(); final Task topTask = getTopMostTask(); - final ActivityStack splitScreenStack = display.getRootSplitScreenPrimaryTask(); int windowingMode = preferredWindowingMode; if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED && isTransientWindowingMode(currentMode)) { @@ -756,14 +745,14 @@ class ActivityStack extends Task { windowingMode = display.validateWindowingMode(windowingMode, null /* ActivityRecord */, topTask, getActivityType()); } - if (splitScreenStack == this + if (display.getRootSplitScreenPrimaryTask() == this && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { // Resolution to split-screen secondary for the primary split-screen stack means // we want to leave split-screen mode. windowingMode = mRestoreOverrideWindowingMode; } - final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryTask(); + final boolean alreadyInSplitScreenMode = display.isSplitScreenModeActivated(); // Don't send non-resizeable notifications if the windowing mode changed was a side effect // of us entering split-screen mode. @@ -831,7 +820,7 @@ class ActivityStack extends Task { return; } - if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && splitScreenStack != null) { + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && alreadyInSplitScreenMode) { // We already have a split-screen stack in this display, so just move the tasks over. // TODO: Figure-out how to do all the stuff in // AMS.setTaskWindowingModeSplitScreenPrimary @@ -1063,7 +1052,7 @@ class ActivityStack extends Task { final DisplayContent display = getDisplay(); if (inSplitScreenSecondaryWindowingMode()) { - // If the stack is in split-screen seconardy mode, we need to make sure we move the + // If the stack is in split-screen secondary mode, we need to make sure we move the // primary split-screen stack forward in the case it is currently behind a fullscreen // stack so both halves of the split-screen appear on-top and the fullscreen stack isn't // cutting between them. @@ -1085,12 +1074,13 @@ class ActivityStack extends Task { display.moveHomeStackToFront(reason + " returnToHome"); } - final boolean movingTask = task != null; - display.positionStackAtTop(this, !movingTask /* includingParents */, reason); - if (movingTask) { - // This also moves the entire hierarchy branch to top, including parents - positionChildAtTop(task); + if (isRootTask()) { + display.positionStackAtTop(this, false /* includingParents */, reason); + } + if (task == null) { + task = this; } + task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */); } /** @@ -1116,12 +1106,6 @@ class ActivityStack extends Task { } } - @Override - boolean isFocusable() { - // Special check for tile which isn't really in the hierarchy - return mTile != null ? mTile.isFocusable() : super.isFocusable(); - } - boolean isTopActivityFocusable() { final ActivityRecord r = topRunningActivity(); return r != null ? r.isFocusable() @@ -2588,6 +2572,13 @@ class ActivityStack extends Task { if (r == null || r.app != app) { return null; } + if (r.isActivityTypeHome() && mAtmService.mHomeProcess == app) { + // Home activities should not be force-finished as we have nothing else to go + // back to. AppErrors will get to it after two crashes in MIN_CRASH_INTERVAL. + Slog.w(TAG, " Not force finishing home activity " + + r.intent.getComponent().flattenToShortString()); + return null; + } Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); Task finishedTask = r.getTask(); @@ -3542,6 +3533,10 @@ class ActivityStack extends Task { @Override void onChildPositionChanged(WindowContainer child) { + if (isOrganized()) { + mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */); + } + if (!mChildren.contains(child)) { return; } @@ -3572,11 +3567,6 @@ class ActivityStack extends Task { if (oldDisplay != null && oldDisplay.isRemoving()) { postReparent(); } - if (mTile != null && getSurfaceControl() != null) { - // by now, the TaskStack should already have been reparented, so we can reparent its - // surface here - reparentSurfaceControl(getPendingTransaction(), mTile.getSurfaceControl()); - } } void reparent(DisplayContent newParent, boolean onTop) { @@ -3614,16 +3604,7 @@ class ActivityStack extends Task { @Override void getRelativeDisplayedPosition(Point outPos) { - // check for tile which is "virtually" a parent. - if (mTile != null) { - final Rect dispBounds = getDisplayedBounds(); - outPos.set(dispBounds.left, dispBounds.top); - final Rect parentBounds = mTile.getBounds(); - outPos.offset(-parentBounds.left, -parentBounds.top); - } else { - super.getRelativeDisplayedPosition(outPos); - } - + super.getRelativeDisplayedPosition(outPos); final int outset = getStackOutset(); outPos.x -= outset; outPos.y -= outset; @@ -3633,16 +3614,6 @@ class ActivityStack extends Task { if (mSurfaceControl == null) { return; } - if (mTile != null) { - // Tile controls crop, so the app needs to be able to draw its background outside of - // the stack bounds for when the tile crop gets bigger than the stack. - if (mLastSurfaceSize.equals(0, 0)) { - return; - } - transaction.setWindowCrop(mSurfaceControl, null); - mLastSurfaceSize.set(0, 0); - return; - } final Rect stackBounds = getDisplayedBounds(); int width = stackBounds.width(); @@ -3666,9 +3637,6 @@ class ActivityStack extends Task { @Override void onDisplayChanged(DisplayContent dc) { - if (mTile != null && dc != mTile.getDisplay()) { - mTile.removeChild(this); - } super.onDisplayChanged(dc); if (isRootTask()) { updateSurfaceBounds(); @@ -3781,20 +3749,6 @@ class ActivityStack extends Task { return super.checkCompleteDeferredRemoval(); } - @Override - int getOrientation() { - return (canSpecifyOrientation()) ? super.getOrientation() : SCREEN_ORIENTATION_UNSET; - } - - private boolean canSpecifyOrientation() { - final int windowingMode = getWindowingMode(); - final int activityType = getActivityType(); - return windowingMode == WINDOWING_MODE_FULLSCREEN - || activityType == ACTIVITY_TYPE_HOME - || activityType == ACTIVITY_TYPE_RECENTS - || activityType == ACTIVITY_TYPE_ASSISTANT; - } - public DisplayInfo getDisplayInfo() { return mDisplayContent.getDisplayInfo(); } @@ -3825,49 +3779,6 @@ class ActivityStack extends Task { return shouldSleepActivities() || mAtmService.mShuttingDown; } - TaskTile getTile() { - return mTile; - } - - /** - * Don't call this directly. instead use {@link TaskTile#addChild} or - * {@link TaskTile#removeChild}. - */ - void setTile(TaskTile tile) { - TaskTile origTile = mTile; - mTile = tile; - final ConfigurationContainer parent = getParent(); - if (parent != null) { - onConfigurationChanged(parent.getConfiguration()); - } - - // Reparent to tile surface or back to original parent - if (getSurfaceControl() == null) { - return; - } - if (mTile != null) { - // don't use reparentSurfaceControl because we need to bypass taskorg check - mSurfaceAnimator.reparent(getPendingTransaction(), mTile.getSurfaceControl()); - } else if (mTile == null && origTile != null) { - mSurfaceAnimator.reparent(getPendingTransaction(), getParentSurfaceControl()); - } - } - - @Override - public SurfaceControl getParentSurfaceControl() { - // Tile is a "virtual" parent, so we need to intercept the parent surface here - return mTile != null ? mTile.getSurfaceControl() : super.getParentSurfaceControl(); - } - - @Override - void removeImmediately() { - // TODO(task-hierarchy): remove this override when tiles are in hierarchy - if (mTile != null) { - mTile.removeChild(this); - } - super.removeImmediately(); - } - @Override public void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) { @@ -3912,7 +3823,7 @@ class ActivityStack extends Task { proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight()); } - proto.write(CREATED_BY_ORGANIZER, this instanceof TaskTile); + proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer); proto.end(token); } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 57f357d384b5..4652f49f116c 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -423,6 +423,10 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final ActivityStack toStack = mToDisplay.getOrCreateStack( null, mTmpOptions, task, task.getActivityType(), mOnTop); + if (task == toStack) { + // The task was reused as the root task. + return; + } if (mOnTop) { final boolean isTopTask = task == mTopTask; @@ -1704,7 +1708,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mRootWindowContainer.getLaunchStack(null, aOptions, task, onTop); final WindowContainer parent = task.getParent(); - if (parent == stack) { + if (parent == stack || task == stack) { // Nothing else to do since it is already restored in the right stack. return true; } @@ -2237,7 +2241,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final boolean isSecondaryDisplayPreferred = (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY); final boolean inSplitScreenMode = actualStack != null - && actualStack.getDisplay().hasSplitScreenPrimaryTask(); + && actualStack.getDisplay().isSplitScreenModeActivated(); if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) { return; @@ -2284,16 +2288,14 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) { // Dismiss docked stack. If task appeared to be in docked stack but is not resizable - // we need to move it to top of fullscreen stack, otherwise it will be covered. - - final ActivityStack dockedStack = - task.getStack().getDisplay().getRootSplitScreenPrimaryTask(); - if (dockedStack != null) { + final DisplayContent display = task.getStack().getDisplay(); + if (display.isSplitScreenModeActivated()) { // Display a warning toast that we tried to put an app that doesn't support // split-screen in split-screen. mService.getTaskChangeNotificationController() .notifyActivityDismissingDockedStack(); - dockedStack.getDisplay().onSplitScreenModeDismissed(); - dockedStack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS, + display.onSplitScreenModeDismissed(); + display.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS, true /* notifyClients */); } return; @@ -2602,7 +2604,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { "startActivityFromRecents: Task " + taskId + " not found."); } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && task.getWindowingMode() != windowingMode) { - mService.moveTaskToSplitScreenPrimaryTile(task, true /* toTop */); + mService.moveTaskToSplitScreenPrimaryTask(task, true /* toTop */); } if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 14ca7cbae4b3..da1c045dba16 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -30,7 +30,6 @@ import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WaitResult.LAUNCH_STATE_COLD; import static android.app.WaitResult.LAUNCH_STATE_HOT; -import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -78,6 +77,7 @@ import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY; import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1566,7 +1566,7 @@ class ActivityStarter { } if (!mAvoidMoveToFront && mDoResume) { - mTargetStack.moveToFront("reuseOrNewTask"); + mTargetStack.getStack().moveToFront("reuseOrNewTask", targetTask); if (mOptions != null) { if (mPreferredWindowingMode != WINDOWING_MODE_UNDEFINED) { mTargetStack.setWindowingMode(mPreferredWindowingMode); @@ -2364,6 +2364,7 @@ class ActivityStarter { private void setTargetStackIfNeeded(ActivityRecord intentActivity) { mTargetStack = intentActivity.getRootTask(); mTargetStack.mLastPausedActivity = null; + Task intentTask = intentActivity.getTask(); // If the target task is not in the front, then we need to bring it to the front... // except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have // the same behavior as if a new instance was being started, which means not bringing it @@ -2374,7 +2375,7 @@ class ActivityStarter { final ActivityRecord curTop = (focusStack == null) ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); final Task topTask = curTop != null ? curTop.getTask() : null; - differentTopTask = topTask != intentActivity.getTask() + differentTopTask = topTask != intentTask || (focusStack != null && topTask != focusStack.getTopMostTask()); } else { // The existing task should always be different from those in other displays. @@ -2391,7 +2392,6 @@ class ActivityStarter { intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask()); } - final Task intentTask = intentActivity.getTask(); final ActivityStack launchStack = getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions); if (launchStack == null || launchStack == mTargetStack) { @@ -2400,6 +2400,14 @@ class ActivityStarter { // new intent has delivered. final boolean isSplitScreenTopStack = mTargetStack.isTopSplitScreenStack(); + // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels + // tasks hierarchies. + if (mTargetStack != intentTask + && mTargetStack != intentTask.getParent().asTask()) { + intentTask.getParent().positionChildAt(POSITION_TOP, intentTask, + false /* includingParents */); + intentTask = intentTask.getParent().asTask(); + } // We only want to move to the front, if we aren't going to launch on a // different stack. If we launch on a different stack, we will put the // task on top there. @@ -2420,8 +2428,8 @@ class ActivityStarter { // Need to update mTargetStack because if task was moved out of it, the original stack may // be destroyed. mTargetStack = intentActivity.getRootTask(); - mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(), - WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack); + mSupervisor.handleNonResizableTaskIfNeeded(intentTask, WINDOWING_MODE_UNDEFINED, + DEFAULT_DISPLAY, mTargetStack); } private void resumeTargetStackIfNeeded() { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index edc87e5a4d88..2263795f9442 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -548,8 +548,11 @@ public abstract class ActivityTaskManagerInternal { /** * Gets bitmap snapshot of the provided task id. + * + * <p>Warning! this may restore the snapshot from disk so can block, don't call in a latency + * sensitive environment. */ - public abstract ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId, + public abstract ActivityManager.TaskSnapshot getTaskSnapshotBlocking(int taskId, boolean isLowResolution); /** Returns true if uid is considered foreground for activity start purposes. */ diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index f7278e70cede..a5b0026398b6 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -31,12 +31,12 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -122,6 +122,7 @@ import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_ import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import android.Manifest; import android.annotation.IntDef; @@ -144,7 +145,6 @@ import android.app.IApplicationThread; import android.app.IAssistDataReceiver; import android.app.INotificationManager; import android.app.IRequestFinishCallback; -import android.window.ITaskOrganizerController; import android.app.ITaskStackListener; import android.app.Notification; import android.app.NotificationManager; @@ -230,9 +230,9 @@ import android.util.proto.ProtoOutputStream; import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; +import android.view.WindowManager; import android.window.IWindowOrganizerController; import android.window.WindowContainerTransaction; -import android.view.WindowManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -1274,9 +1274,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT; a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchActivityType(ACTIVITY_TYPE_DREAM); + try { getActivityStartController().obtainStarter(intent, "dream") .setActivityInfo(a) + .setActivityOptions(options.toBundle()) .setIsDream(true) .execute(); return true; @@ -2349,16 +2353,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final ActivityStack stack = task.getStack(); - // Convert some windowing-mode changes into root-task reparents for split-screen. - if (stack.getTile() != null) { - stack.getDisplay().onSplitScreenModeDismissed(); - } if (toTop) { stack.moveToFront("setTaskWindowingMode", task); } - stack.setWindowingMode(windowingMode); - stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS, - true /* notifyClients */); + // Convert some windowing-mode changes into root-task reparents for split-screen. + if (stack.inSplitScreenWindowingMode()) { + stack.getDisplay().onSplitScreenModeDismissed(); + + } else { + stack.setWindowingMode(windowingMode); + stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS, + true /* notifyClients */); + } return true; } finally { Binder.restoreCallingIdentity(ident); @@ -2755,24 +2761,22 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final int prevMode = task.getWindowingMode(); - moveTaskToSplitScreenPrimaryTile(task, toTop); + moveTaskToSplitScreenPrimaryTask(task, toTop); return prevMode != task.getWindowingMode(); } - void moveTaskToSplitScreenPrimaryTile(Task task, boolean toTop) { - ActivityStack stack = task.getStack(); - TaskTile tile = null; - for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) { - tile = stack.getDisplay().getStackAt(i).asTile(); - if (tile != null && tile.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - break; - } + void moveTaskToSplitScreenPrimaryTask(Task task, boolean toTop) { + final DisplayContent display = task.getDisplayContent(); + final ActivityStack primarySplitTask = display.getRootSplitScreenPrimaryTask(); + if (primarySplitTask == null) { + throw new IllegalStateException("Can't enter split without associated organized task"); } - if (tile == null) { - throw new IllegalStateException("Can't enter split without associated tile"); + + if (toTop) { + display.positionStackAt(POSITION_TOP, primarySplitTask, false /* includingParents */); } WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop); + wct.reparent(task.getStack().mRemoteToken, primarySplitTask.mRemoteToken, toTop); mWindowOrganizerController.applyTransaction(wct); } @@ -3239,7 +3243,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final ActivityStack stack = r.getRootTask(); final Task task = stack.getDisplay().createStack(stack.getWindowingMode(), - stack.getActivityType(), !ON_TOP, ainfo, intent); + stack.getActivityType(), !ON_TOP, ainfo, intent, + false /* createdByOrganizer */); if (!mRecentTasks.addToBottom(task)) { // The app has too many tasks already and we can't add any more @@ -4278,19 +4283,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { synchronized (mGlobalLock) { final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); - TaskTile primary = null; - TaskTile secondary = null; - for (int i = dc.getStackCount() - 1; i >= 0; --i) { - final TaskTile t = dc.getStackAt(i).asTile(); - if (t == null) { - continue; - } - if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - primary = t; - } else if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { - secondary = t; - } - } + final Task primary = dc.getRootSplitScreenPrimaryTask(); + final Task secondary = dc.getTask(t -> t.mCreatedByOrganizer && t.isRootTask() + && t.inSplitScreenSecondaryWindowingMode()); if (primary == null || secondary == null) { return; } @@ -7477,10 +7472,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId, - boolean isLowResolution) { + public ActivityManager.TaskSnapshot getTaskSnapshotBlocking( + int taskId, boolean isLowResolution) { return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution, - false /* restoreFromDisk */); + true /* restoreFromDisk */); } @Override diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 33dd9cf4ee05..1036af67c0db 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; @@ -467,6 +468,10 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { return getActivityType() == ACTIVITY_TYPE_ASSISTANT; } + public boolean isActivityTypeDream() { + return getActivityType() == ACTIVITY_TYPE_DREAM; + } + public boolean isActivityTypeStandard() { return getActivityType() == ACTIVITY_TYPE_STANDARD; } 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..a90016afcc49 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; @@ -199,7 +200,6 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.view.IDisplayWindowInsetsController; import android.view.ISystemGestureExclusionListener; -import android.window.ITaskOrganizer; import android.view.IWindow; import android.view.InputChannel; import android.view.InputDevice; @@ -216,6 +216,7 @@ import android.view.ViewRootImpl; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManagerPolicyConstants.PointerEventListener; +import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; @@ -455,6 +456,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final Configuration mTmpConfiguration = new Configuration(); + private ArrayList<Task> mTmpTasks = new ArrayList<>(); + /** Remove this display when animation on it has completed. */ private boolean mDeferredRemoval; @@ -653,8 +656,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final RootWindowContainer.FindTaskResult mTmpFindTaskResult = new RootWindowContainer.FindTaskResult(); - // When non-null, new stacks get put into this tile. - TaskTile mLaunchTile = null; + // When non-null, new tasks get put into this root task. + Task mLaunchRootTask = null; + + // Used in performing layout + private boolean mTmpWindowsBehindIme; private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> { WindowStateAnimator winAnimator = w.mWinAnimator; @@ -747,6 +753,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo + " parentHidden=" + w.isParentWindowHidden()); } + // Sets mBehindIme for each window. Windows behind IME can get IME insets. + w.mBehindIme = mTmpWindowsBehindIme; + if (w == mInputMethodWindow) { + mTmpWindowsBehindIme = true; + } + // If this view is GONE, then skip it -- keep the current frame, and let the caller know // so they can ignore it if they want. (We do the normal layout for INVISIBLE windows, // since that means "perform layout as normal, just don't display"). @@ -1305,8 +1317,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mDisplayRotation.isWaitingForRemoteRotation()) { return; } - // Clear the record because the display will sync to current rotation. - mFixedRotationLaunchingApp = null; final boolean configUpdated = updateDisplayOverrideConfigurationLocked(); if (configUpdated) { @@ -1497,7 +1507,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo startFixedRotationTransform(r, rotation); mAppTransition.registerListenerLocked(new WindowManagerInternal.AppTransitionListener() { void done() { - r.clearFixedRotationTransform(); + r.finishFixedRotationTransform(); mAppTransition.unregisterListener(this); } @@ -1526,7 +1536,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (token != mFixedRotationLaunchingApp) { return false; } - if (updateOrientation()) { + // Update directly because the app which will change the orientation of display is ready. + if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) { sendNewConfiguration(); return true; } @@ -1572,7 +1583,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * @param oldRotation the rotation we are coming from. * @param rotation the rotation to apply. */ - void applyRotationLocked(final int oldRotation, final int rotation) { + private void applyRotation(final int oldRotation, final int rotation) { mDisplayRotation.applyCurrentRotation(rotation); final boolean rotateSeamlessly = mDisplayRotation.isRotatingSeamlessly(); final Transaction transaction = getPendingTransaction(); @@ -2127,12 +2138,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } /** @return The primary split-screen task, and {@code null} otherwise. */ - ActivityStack getRootSplitScreenPrimaryTask() { + @Nullable ActivityStack getRootSplitScreenPrimaryTask() { return mTaskContainers.getRootSplitScreenPrimaryTask(); } - boolean hasSplitScreenPrimaryTask() { - return getRootSplitScreenPrimaryTask() != null; + boolean isSplitScreenModeActivated() { + Task task = getRootSplitScreenPrimaryTask(); + return task != null && task.hasChild(); } ActivityStack getRootPinnedTask() { @@ -2599,7 +2611,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } amendWindowTapExcludeRegion(mTouchExcludeRegion); // TODO(multi-display): Support docked stacks on secondary displays. - if (mDisplayId == DEFAULT_DISPLAY && getRootSplitScreenPrimaryTask() != null) { + if (mDisplayId == DEFAULT_DISPLAY && isSplitScreenModeActivated()) { mDividerControllerLocked.getTouchRegion(mTmpRect); mTmpRegion.set(mTmpRect); mTouchExcludeRegion.op(mTmpRegion, Op.UNION); @@ -2622,8 +2634,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // If the task is home stack and it is resizable and visible (top of its root task), we want // to exclude the docked stack from touch so we need the entire screen area and not just a // small portion which the home stack currently is resized to. - if (task.isActivityTypeHome() && task.isVisible() && task.getStack().getTile() != null - && task.isResizeable()) { + if (task.isActivityTypeHome() && task.isVisible() && task.isResizeable()) { mDisplayContent.getBounds(mTmpRect); } else { task.getDimBounds(mTmpRect); @@ -4013,6 +4024,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mTmpWindow = null; mTmpInitial = initial; + // Used to indicate that we have processed the IME window. + mTmpWindowsBehindIme = false; + // First perform layout of any root windows (not attached to another window). forAllWindows(mPerformLayout, true /* traverseTopToBottom */); @@ -4294,7 +4308,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); } /** @@ -4327,15 +4341,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @VisibleForTesting ActivityStack getTopStack() { - // TODO(task-hierarchy): Just grab index -1 once tiles are in hierarchy. - for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) { - final ActivityStack child = mTaskContainers.getChildAt(i); - if (child instanceof TaskTile) { - continue; - } - return child; - } - return null; + final int count = mTaskContainers.getChildCount(); + return count > 0 ? mTaskContainers.getChildAt(count - 1) : null; } int getIndexOf(ActivityStack stack) { @@ -4374,10 +4381,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } private void addStackReferenceIfNeeded(ActivityStack stack) { - // TODO(task-hierarchy): Remove when tiles are in hierarchy. - if (stack instanceof TaskTile) { - return; - } if (stack.isActivityTypeHome()) { if (mRootHomeTask != null) { if (!stack.isDescendantOf(mRootHomeTask)) { @@ -4389,27 +4392,26 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mRootHomeTask = stack; } } + + if (!stack.isRootTask()) { + return; + } final int windowingMode = stack.getWindowingMode(); if (windowingMode == WINDOWING_MODE_PINNED) { if (mRootPinnedTask != null) { - if (!stack.isDescendantOf(mRootPinnedTask)) { - throw new IllegalArgumentException( - "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask - + " already exist on display=" + this + " stack=" + stack); - } - } else { - mRootPinnedTask = stack; + throw new IllegalArgumentException( + "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask + + " already exist on display=" + this + " stack=" + stack); } + mRootPinnedTask = stack; } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { if (mRootSplitScreenPrimaryTask != null) { - if (!stack.isDescendantOf(mRootSplitScreenPrimaryTask)) { - throw new IllegalArgumentException("addStackReferenceIfNeeded:" - + " split-screen-primary" + " stack=" + mRootSplitScreenPrimaryTask - + " already exist on display=" + this + " stack=" + stack); - } - } else { - mRootSplitScreenPrimaryTask = stack; + throw new IllegalArgumentException( + "addStackReferenceIfNeeded: split screen primary stack=" + + mRootSplitScreenPrimaryTask + + " already exist on display=" + this + " stack=" + stack); } + mRootSplitScreenPrimaryTask = stack; } } @@ -4429,6 +4431,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo position = findPositionForStack(position, stack, true /* adding */); super.addChild(stack, position); + mAtmService.updateSleepIfNeededLocked(); // The reparenting case is handled in WindowContainer. if (!stack.mReparenting) { @@ -4440,6 +4443,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo protected void removeChild(ActivityStack stack) { super.removeChild(stack); mDisplayContent.onStackRemoved(stack); + mAtmService.updateSleepIfNeededLocked(); removeStackReferenceIfNeeded(stack); } @@ -4494,6 +4498,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ private int findPositionForStack(int requestedPosition, ActivityStack stack, boolean adding) { + if (stack.isActivityTypeDream()) { + return POSITION_TOP; + } + if (stack.inPinnedWindowingMode()) { return POSITION_TOP; } @@ -4644,10 +4652,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Apps and their containers are not allowed to specify an orientation while using // root tasks...except for the home stack if it is not resizable and currently // visible (top of) its root task. - if (mRootHomeTask != null && mRootHomeTask.isVisible() - && mRootHomeTask.getTile() != null) { + if (mRootHomeTask != null && mRootHomeTask.isVisible()) { final Task topMost = mRootHomeTask.getTopMostTask(); - final boolean resizable = topMost == null && topMost.isResizeable(); + final boolean resizable = topMost != null && topMost.isResizeable(); if (!(resizable && mRootHomeTask.matchParentBounds())) { final int orientation = mRootHomeTask.getOrientation(); if (orientation != SCREEN_ORIENTATION_UNSET) { @@ -4791,17 +4798,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mSplitScreenDividerAnchor = null; } } - - @Override - void onChildPositionChanged(WindowContainer child) { - // TODO(task-hierarchy): Move functionality to TaskTile when it's a proper parent. - TaskTile tile = ((ActivityStack) child).getTile(); - if (tile == null) { - return; - } - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged( - tile, false /* force */); - } } private class WindowContainers extends DisplayChildWindowContainer<WindowContainer> { @@ -4982,7 +4978,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private boolean skipImeWindowsDuringTraversal(DisplayContent dc) { // We skip IME windows so they're processed just above their target, except // in split-screen mode where we process the IME containers above the docked divider. - return dc.mInputMethodTarget != null && !dc.hasSplitScreenPrimaryTask(); + return dc.mInputMethodTarget != null && !dc.isSplitScreenModeActivated(); } /** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */ @@ -5653,7 +5649,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo void addStack(ActivityStack stack, int position) { setStackOnDisplay(stack, position); positionStackAt(stack, position); - mAtmService.updateSleepIfNeededLocked(); + } + + void addStackReferenceIfNeeded(ActivityStack stack) { + mTaskContainers.addStackReferenceIfNeeded(stack); + } + + void removeStackReferenceIfNeeded(ActivityStack stack) { + mTaskContainers.removeStackReferenceIfNeeded(stack); } void onStackRemoved(ActivityStack stack) { @@ -5664,7 +5667,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mPreferredTopFocusableStack = null; } releaseSelfIfNeeded(); - mAtmService.updateSleepIfNeededLocked(); onStackOrderChanged(stack); } @@ -5702,6 +5704,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo "positionStackAt: Can only have one task on display=" + this); } + final boolean movingToTop = wasContained && position >= getStackCount() - 1; + // Reset mPreferredTopFocusableStack before positioning to top or {@link + // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top + // resumed activity. + if (movingToTop && stack.isFocusable()) { + mPreferredTopFocusableStack = null; + } + // Since positionChildAt() is called during the creation process of pinned stacks, // ActivityStack#getStack() can be null. positionStackAt(position, stack, includingParents); @@ -5711,7 +5721,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // we are looking for top focusable stack. The condition {@code wasContained} restricts the // preferred stack is set only when moving an existing stack to top instead of adding a new // stack that may be too early (e.g. in the middle of launching or reparenting). - if (wasContained && position >= getStackCount() - 1 && stack.isFocusableAndVisible()) { + if (movingToTop && stack.isFocusableAndVisible()) { mPreferredTopFocusableStack = stack; } else if (mPreferredTopFocusableStack == stack) { mPreferredTopFocusableStack = null; @@ -5754,18 +5764,54 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** * Returns an existing stack compatible with the windowing mode and activity type or creates one * if a compatible stack doesn't exist. + * @see #getOrCreateStack(int, int, boolean, Intent, Task, boolean) + */ + ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop) { + return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */, + null /* candidateTask */, false /* createdByOrganizer */); + } + + /** + * When two level tasks are required for given windowing mode and activity type, returns an + * existing compatible root task or creates a new one. + * For one level task, the candidate task would be reused to also be the root task or create + * a new root task if no candidate task. * @see #getStack(int, int) * @see #createStack(int, int, boolean) */ - ActivityStack getOrCreateStack(int windowingMode, int activityType, - boolean onTop) { + ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop, + Intent intent, Task candidateTask, boolean createdByOrganizer) { if (!alwaysCreateStack(windowingMode, activityType)) { ActivityStack stack = getStack(windowingMode, activityType); if (stack != null) { return stack; } + } else if (candidateTask != null) { + final ActivityStack stack = (ActivityStack) candidateTask; + final int position = onTop ? POSITION_TOP : POSITION_BOTTOM; + if (isSplitScreenModeActivated()) { + final Task splitRootSecondary = getTask(t -> t.mCreatedByOrganizer && t.isRootTask() + && t.inSplitScreenSecondaryWindowingMode()); + if (stack.getParent() == null) { + splitRootSecondary.addChild(stack, position); + } else if (stack.getParent() != splitRootSecondary) { + stack.reparent(splitRootSecondary, position); + } + } else if (stack.getDisplay() != this || !stack.isRootTask()) { + if (stack.getParent() == null) { + addStack(stack, position); + } else { + stack.reparent(this, onTop); + } + } + // Update windowing mode if necessary, e.g. moving a pinned task to fullscreen. + if (candidateTask.getWindowingMode() != windowingMode) { + candidateTask.setWindowingMode(windowingMode); + } + return stack; } - return createStack(windowingMode, activityType, onTop); + return createStack(windowingMode, activityType, onTop, null /*info*/, intent, + createdByOrganizer); } /** @@ -5783,7 +5829,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // UNDEFINED windowing mode is a valid result and means that the new stack will inherit // it's display's windowing mode. windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType); - return getOrCreateStack(windowingMode, activityType, onTop); + return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */, + candidateTask, false /* createdByOrganizer */); } @VisibleForTesting @@ -5792,7 +5839,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } ActivityStack createStack(int windowingMode, int activityType, boolean onTop) { - return createStack(windowingMode, activityType, onTop, null /*info*/, null /*intent*/); + return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */, + false /* createdByOrganizer */); } /** @@ -5804,25 +5852,29 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will * be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}. * @param onTop If true the stack will be created at the top of the display, else at the bottom. + * @param info The started activity info. + * @param intent The intent that started this task. + * @param createdByOrganizer @{code true} if this is created by task organizer, @{code false} + * otherwise. * @return The newly created stack. */ ActivityStack createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info, - Intent intent) { + Intent intent, boolean createdByOrganizer) { if (mSingleTaskInstance && getStackCount() > 0) { // Create stack on default display instead since this display can only contain 1 stack. // TODO: Kinda a hack, but better that having the decision at each call point. Hoping // this goes away once ActivityView is no longer using virtual displays. return mRootWindowContainer.getDefaultDisplay().createStack( - windowingMode, activityType, onTop, info, intent); + windowingMode, activityType, onTop, info, intent, createdByOrganizer); } - if (activityType == ACTIVITY_TYPE_UNDEFINED) { + if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) { // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants - // anything else should be passing it in anyways... + // anything else should be passing it in anyways...except for the task organizer. activityType = ACTIVITY_TYPE_STANDARD; } - if (activityType != ACTIVITY_TYPE_STANDARD) { + if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) { // For now there can be only one stack of a particular non-standard activity type on a // display. So, get that ignoring whatever windowing mode it is currently in. ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); @@ -5841,39 +5893,39 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } final int stackId = getNextStackId(); - return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent); + return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent, + createdByOrganizer); } - /** @return the tile to create the next stack in. */ - private TaskTile updateLaunchTile(int windowingMode) { + /** @return the root task to create the next task in. */ + private Task updateLaunchRootTask(int windowingMode) { if (!isSplitScreenWindowingMode(windowingMode)) { - // Only split-screen windowing modes interact with tiles. + // Only split-screen windowing modes can do this currently... return null; } for (int i = getStackCount() - 1; i >= 0; --i) { - final TaskTile t = getStackAt(i).asTile(); - if (t == null || t.getRequestedOverrideWindowingMode() != windowingMode) { + final Task t = getStackAt(i); + if (!t.mCreatedByOrganizer || t.getRequestedOverrideWindowingMode() != windowingMode) { continue; } - // If not already set, pick a launch tile which is not the one we are launching - // into. - if (mLaunchTile == null) { + // If not already set, pick a launch root which is not the one we are launching into. + if (mLaunchRootTask == null) { for (int j = 0, n = getStackCount(); j < n; ++j) { - TaskTile tt = getStackAt(j).asTile(); - if (tt != t) { - mLaunchTile = tt; + final Task tt = getStackAt(j); + if (tt.mCreatedByOrganizer && tt != t) { + mLaunchRootTask = tt; break; } } } return t; } - return mLaunchTile; + return mLaunchRootTask; } @VisibleForTesting - ActivityStack createStackUnchecked(int windowingMode, int activityType, - int stackId, boolean onTop, ActivityInfo info, Intent intent) { + ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId, + boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) { if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) { throw new IllegalArgumentException("Stack with windowing mode cannot with non standard " + "activity type."); @@ -5883,19 +5935,25 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo info.applicationInfo = new ApplicationInfo(); } - TaskTile tile = updateLaunchTile(windowingMode); - if (tile != null) { - // Since this stack will be put into a tile, its windowingMode will be inherited. + // Task created by organizer are added as root. + Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode); + if (launchRootTask != null) { + // Since this stack will be put into a root task, its windowingMode will be inherited. windowingMode = WINDOWING_MODE_UNDEFINED; } + final ActivityStack stack = (ActivityStack) Task.create(mAtmService, stackId, activityType, - info, intent); - addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM); - stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */, - false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */, - true /* creating */); - if (tile != null) { - tile.addChild(stack, 0 /* index */); + info, intent, createdByOrganizer); + if (launchRootTask != null) { + launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM); + if (onTop) { + positionStackAtTop((ActivityStack) launchRootTask, false /* includingParents */); + } + } else { + addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM); + stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */, + false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */, + true /* creating */); } return stack; } @@ -6030,7 +6088,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mTmpFindTaskResult.clear(); for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = getStackAt(stackNdx); - if (!r.hasCompatibleActivityType(stack)) { + if (!r.hasCompatibleActivityType(stack) && stack.isLeafTask()) { if (DEBUG_TASKS) { Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack); } @@ -6102,7 +6160,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final int activityType = activityTypes[j]; for (int i = getStackCount() - 1; i >= 0; --i) { final ActivityStack stack = getStackAt(i); - if (stack.getActivityType() == activityType) { + // Collect the root tasks that are currently being organized. + if (stack.isOrganized()) { + for (int k = stack.getChildCount() - 1; k >= 0; --k) { + final ActivityStack childStack = (ActivityStack) stack.getChildAt(k); + if (childStack.getActivityType() == activityType) { + stacks.add(childStack); + } + } + } else if (stack.getActivityType() == activityType) { stacks.add(stack); } } @@ -6116,13 +6182,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo void onSplitScreenModeDismissed() { mAtmService.deferWindowLayout(); try { - mLaunchTile = null; - for (int i = getStackCount() - 1; i >= 0; --i) { - final TaskTile t = getStackAt(i).asTile(); - if (t != null) { - t.removeAllChildren(); - } - } + mLaunchRootTask = null; + moveSplitScreenTasksToFullScreen(); } finally { final ActivityStack topFullscreenStack = getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN); @@ -6140,6 +6201,24 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } + private void moveSplitScreenTasksToFullScreen() { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + mTmpTasks.clear(); + forAllTasks(task -> { + if (task.mCreatedByOrganizer && task.inSplitScreenWindowingMode() && task.hasChild()) { + mTmpTasks.add(task); + } + }); + + for (int i = mTmpTasks.size() - 1; i >= 0; i--) { + final Task root = mTmpTasks.get(i); + for (int j = 0; j < root.getChildCount(); j++) { + wct.reparent(root.getChildAt(j).mRemoteToken, null, true /* toTop */); + } + } + mAtmService.mWindowOrganizerController.applyTransaction(wct); + } + /** * Returns true if the {@param windowingMode} is supported based on other parameters passed in. * @param windowingMode The windowing mode we are checking support for. @@ -6252,7 +6331,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - final boolean inSplitScreenMode = hasSplitScreenPrimaryTask(); + final boolean inSplitScreenMode = isSplitScreenModeActivated(); if (!inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) { // Switch to the display's windowing mode if we are not in split-screen mode and we are @@ -6277,10 +6356,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } boolean isTopNotPinnedStack(ActivityStack stack) { - // TODO(task-hierarchy): Remove when tiles are in hierarchy. - if (stack instanceof TaskTile) { - return false; - } for (int i = getStackCount() - 1; i >= 0; --i) { final ActivityStack current = getStackAt(i); if (!current.inPinnedWindowingMode()) { @@ -6417,15 +6492,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { - final int currRotation = - getRequestedOverrideConfiguration().windowConfiguration.getRotation(); - if (currRotation != ROTATION_UNDEFINED - && currRotation != overrideConfiguration.windowConfiguration.getRotation()) { - applyRotationLocked(currRotation, - overrideConfiguration.windowConfiguration.getRotation()); - } - mCurrentOverrideConfigurationChanges = - getRequestedOverrideConfiguration().diff(overrideConfiguration); + final Configuration currOverrideConfig = getRequestedOverrideConfiguration(); + final int currRotation = currOverrideConfig.windowConfiguration.getRotation(); + final int overrideRotation = overrideConfiguration.windowConfiguration.getRotation(); + if (currRotation != ROTATION_UNDEFINED && currRotation != overrideRotation) { + if (mFixedRotationLaunchingApp != null) { + mFixedRotationLaunchingApp.clearFixedRotationTransform( + () -> applyRotation(currRotation, overrideRotation)); + // Clear the record because the display will sync to current rotation. + mFixedRotationLaunchingApp = null; + } else { + applyRotation(currRotation, overrideRotation); + } + } + mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration); super.onRequestedOverrideConfigurationChanged(overrideConfiguration); mCurrentOverrideConfigurationChanges = 0; mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration, this); @@ -6502,7 +6582,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // If default display is in split-window mode, set windowing mode of the stack // to split-screen secondary. Otherwise, set the windowing mode to undefined by // default to let stack inherited the windowing mode from the new display. - final int windowingMode = toDisplay.hasSplitScreenPrimaryTask() + final int windowingMode = toDisplay.isSplitScreenModeActivated() ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_UNDEFINED; stack.reparent(toDisplay, true /* onTop */); @@ -6606,27 +6686,35 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * already top-most. */ ActivityStack getStackAbove(ActivityStack stack) { - final int stackIndex = getIndexOf(stack) + 1; - return (stackIndex < getStackCount()) ? getStackAt(stackIndex) : null; + final WindowContainer wc = stack.getParent(); + final int index = wc.mChildren.indexOf(stack) + 1; + return (index < wc.mChildren.size()) ? (ActivityStack) wc.mChildren.get(index) : null; } /** * Adjusts the {@param stack} behind the last visible stack in the display if necessary. * Generally used in conjunction with {@link #moveStackBehindStack}. */ + // TODO(b/151575894): Remove special stack movement methods. void moveStackBehindBottomMostVisibleStack(ActivityStack stack) { if (stack.shouldBeVisible(null)) { // Skip if the stack is already visible return; } - // Move the stack to the bottom to not affect the following visibility checks - positionStackAtBottom(stack); + final boolean isRootTask = stack.isRootTask(); + if (isRootTask) { + // Move the stack to the bottom to not affect the following visibility checks + positionStackAtBottom(stack); + } else { + stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */); + } // Find the next position where the stack should be placed - final int numStacks = getStackCount(); + final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount(); for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) { - final ActivityStack s = getStackAt(stackNdx); + final ActivityStack s = isRootTask ? getStackAt(stackNdx) + : (ActivityStack) stack.getParent().getChildAt(stackNdx); if (s == stack) { continue; } @@ -6635,7 +6723,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo || winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; if (s.shouldBeVisible(null) && isValidWindowingMode) { // Move the provided stack to behind this stack - positionStackAt(stack, Math.max(0, stackNdx - 1)); + final int position = Math.max(0, stackNdx - 1); + if (isRootTask) { + positionStackAt(stack, position); + } else { + stack.getParent().positionChildAt(position, stack, false /*includingParents */); + } break; } } @@ -6651,15 +6744,25 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return; } + final WindowContainer parent = stack.getParent(); + if (parent == null || parent != behindStack.getParent()) { + return; + } + // Note that positionChildAt will first remove the given stack before inserting into the // list, so we need to adjust the insertion index to account for the removed index // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the // position internally - final int stackIndex = getIndexOf(stack); - final int behindStackIndex = getIndexOf(behindStack); + final int stackIndex = parent.mChildren.indexOf(stack); + final int behindStackIndex = parent.mChildren.indexOf(behindStack); final int insertIndex = stackIndex <= behindStackIndex ? behindStackIndex - 1 : behindStackIndex; - positionStackAt(stack, Math.max(0, insertIndex)); + final int position = Math.max(0, insertIndex); + if (stack.isRootTask()) { + positionStackAt(stack, position); + } else { + parent.positionChildAt(position, stack, false /* includingParents */); + } } void ensureActivitiesVisible(ActivityRecord starting, int configChanges, @@ -6696,19 +6799,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return getHomeActivityForUser(mRootWindowContainer.mCurrentUser); } - // TODO(task-hierarchy): Remove when tiles are in hierarchy. - void addTile(TaskTile tile) { - mTaskContainers.addChild(tile, POSITION_BOTTOM); - ITaskOrganizer organizer = mAtmService.mTaskOrganizerController.getTaskOrganizer( - tile.getWindowingMode()); - tile.setTaskOrganizer(organizer); - } - - // TODO(task-hierarchy): Remove when tiles are in hierarchy. - void removeTile(TaskTile tile) { - mTaskContainers.removeChild(tile); - } - @Nullable ActivityRecord getHomeActivityForUser(int userId) { final ActivityStack homeStack = getRootHomeTask(); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 34acabe76296..f593393eecfb 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -29,7 +29,6 @@ import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; import static android.view.InsetsState.ITYPE_CAPTION_BAR; -import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_LEFT_GESTURES; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -115,7 +114,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 +176,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 +330,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 +388,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 +407,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 +426,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) @@ -1503,14 +1492,8 @@ public class DisplayPolicy { */ public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { displayFrames.onBeginLayout(); - final InsetsState insetsState = - mDisplayContent.getInsetsStateController().getRawInsetsState(); - - // Reset the frame of IME so that the layout of windows above IME won't get influenced. - // Once we layout the IME, frames will be set again on the source. - insetsState.getSource(ITYPE_IME).setFrame(0, 0, 0, 0); - - updateInsetsStateForDisplayCutout(displayFrames, insetsState); + updateInsetsStateForDisplayCutout(displayFrames, + mDisplayContent.getInsetsStateController().getRawInsetsState()); mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); @@ -1537,25 +1520,7 @@ public class DisplayPolicy { && (mNotificationShade.getAttrs().privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; - // When the navigation bar isn't visible, we put up a fake input window to catch all - // touch events. This way we can detect when the user presses anywhere to bring back the - // nav bar and ensure the application doesn't see the event. - if (navVisible || navAllowedHidden) { - if (mInputConsumer != null) { - mInputConsumer.dismiss(); - mHandler.sendMessage( - mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer)); - mInputConsumer = null; - } - } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) { - mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer( - mHandler.getLooper(), - INPUT_CONSUMER_NAVIGATION, - HideNavInputEventReceiver::new); - // As long as mInputConsumer is active, hover events are not dispatched to the app - // and the pointer icon is likely to become stale. Hide it to avoid confusion. - InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL); - } + updateHideNavInputEventReceiver(navVisible, navAllowedHidden); // For purposes of positioning and showing the nav bar, if we have decided that it can't // be hidden (because of the screen aspect ratio), then take that into account. @@ -1577,6 +1542,28 @@ public class DisplayPolicy { mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation; } + void updateHideNavInputEventReceiver(boolean navVisible, boolean navAllowedHidden) { + // When the navigation bar isn't visible, we put up a fake input window to catch all + // touch events. This way we can detect when the user presses anywhere to bring back the + // nav bar and ensure the application doesn't see the event. + if (navVisible || navAllowedHidden) { + if (mInputConsumer != null) { + mInputConsumer.dismiss(); + mHandler.sendMessage( + mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer)); + mInputConsumer = null; + } + } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) { + mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer( + mHandler.getLooper(), + INPUT_CONSUMER_NAVIGATION, + HideNavInputEventReceiver::new); + // As long as mInputConsumer is active, hover events are not dispatched to the app + // and the pointer icon is likely to become stale. Hide it to avoid confusion. + InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL); + } + } + private static void updateInsetsStateForDisplayCutout(DisplayFrames displayFrames, InsetsState state) { if (displayFrames.mDisplayCutout.getDisplayCutout().isEmpty()) { @@ -2638,15 +2625,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 +3138,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 +3817,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/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 60b817c13236..af89a05bfa62 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -484,11 +484,8 @@ public class DisplayRotation { prepareNormalRotationAnimation(); } - // TODO(b/147469351): Remove the restriction. - if (mDisplayContent.mFixedRotationLaunchingApp == null) { - // Give a remote handler (system ui) some time to reposition things. - startRemoteRotation(oldRotation, mRotation); - } + // Give a remote handler (system ui) some time to reposition things. + startRemoteRotation(oldRotation, mRotation); return true; } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index e2c7d52fa673..18332b9484c0 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -62,7 +62,7 @@ final class InputMonitor { // When true, need to call updateInputWindowsLw(). private boolean mUpdateInputWindowsNeeded = true; private boolean mUpdateInputWindowsPending; - private boolean mApplyImmediately; + private boolean mUpdateInputWindowsImmediately; // Currently focused input window handle. private InputWindowHandle mFocusedInputWindowHandle; @@ -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 */); } @@ -347,14 +347,20 @@ final class InputMonitor { } } - void updateInputWindowsImmediately() { + /** + * Immediately update the input transaction and merge into the passing Transaction that could be + * collected and applied later. + */ + void updateInputWindowsImmediately(SurfaceControl.Transaction t) { mHandler.removeCallbacks(mUpdateInputWindows); - mApplyImmediately = true; + mUpdateInputWindowsImmediately = true; mUpdateInputWindows.run(); - mApplyImmediately = false; + mUpdateInputWindowsImmediately = false; + t.merge(mInputTransaction); } - /* Called when the current input focus changes. + /** + * Called when the current input focus changes. * Layer assignment is assumed to be complete by the time this is called. */ public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { @@ -465,10 +471,7 @@ final class InputMonitor { if (mAddWallpaperInputConsumerHandle) { mWallpaperInputConsumer.show(mInputTransaction, 0); } - - if (mApplyImmediately) { - mInputTransaction.apply(); - } else { + if (!mUpdateInputWindowsImmediately) { mDisplayContent.getPendingTransaction().merge(mInputTransaction); mDisplayContent.scheduleAnimation(); } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index bb0278964f85..fda70d14db2b 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -25,6 +25,7 @@ import static android.view.InsetsController.ANIMATION_TYPE_SHOW; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.SyncRtSurfaceTransactionApplier.applyParams; +import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; @@ -91,6 +92,13 @@ class InsetsPolicy { || focusedWin != getNavControlTarget(focusedWin) || focusedWin.getRequestedInsetsState().getSource(ITYPE_NAVIGATION_BAR) .isVisible()); + updateHideNavInputEventReceiver(); + } + + private void updateHideNavInputEventReceiver() { + mPolicy.updateHideNavInputEventReceiver(!isHidden(ITYPE_NAVIGATION_BAR), + mFocusedWin != null + && mFocusedWin.mAttrs.insetsFlags.behavior != BEHAVIOR_SHOW_BARS_BY_TOUCH); } boolean isHidden(@InternalInsetsType int type) { @@ -169,6 +177,7 @@ class InsetsPolicy { if (windowState == getNavControlTarget(mFocusedWin)) { mNavBar.setVisible(state.getSource(ITYPE_NAVIGATION_BAR).isVisible()); } + updateHideNavInputEventReceiver(); } /** diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index a4bdfb351c1f..04454a5b33ec 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -88,8 +88,8 @@ class InsetsStateController { final InsetsSourceProvider provider = target.getControllableInsetProvider(); final @InternalInsetsType int type = provider != null ? provider.getSource().getType() : ITYPE_INVALID; - return getInsetsForTypeAndWindowingMode(type, target.getWindowingMode(), - target.isAlwaysOnTop()); + return getInsetsForDispatchInner(type, target.getWindowingMode(), target.isAlwaysOnTop(), + isAboveIme(target)); } InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { @@ -97,13 +97,24 @@ class InsetsStateController { final WindowToken token = mDisplayContent.getWindowToken(attrs.token); final @WindowingMode int windowingMode = token != null ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED; - final boolean alwaysOnTop = token != null - ? token.isAlwaysOnTop() : false; - return getInsetsForTypeAndWindowingMode(type, windowingMode, alwaysOnTop); + final boolean alwaysOnTop = token != null && token.isAlwaysOnTop(); + return getInsetsForDispatchInner(type, windowingMode, alwaysOnTop, isAboveIme(token)); + } + + private boolean isAboveIme(WindowContainer target) { + final WindowState imeWindow = mDisplayContent.mInputMethodWindow; + if (target == null || imeWindow == null) { + return false; + } + if (target instanceof WindowState) { + final WindowState win = (WindowState) target; + return win.needsRelativeLayeringToIme() || !win.mBehindIme; + } + return false; } private static @InternalInsetsType int getInsetsTypeForWindowType(int type) { - switch(type) { + switch (type) { case TYPE_STATUS_BAR: return ITYPE_STATUS_BAR; case TYPE_NAVIGATION_BAR: @@ -116,8 +127,8 @@ class InsetsStateController { } /** @see #getInsetsForDispatch */ - private InsetsState getInsetsForTypeAndWindowingMode(@InternalInsetsType int type, - @WindowingMode int windowingMode, boolean isAlwaysOnTop) { + private InsetsState getInsetsForDispatchInner(@InternalInsetsType int type, + @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) { InsetsState state = mState; if (type != ITYPE_INVALID) { @@ -158,6 +169,11 @@ class InsetsStateController { state.removeSource(ITYPE_NAVIGATION_BAR); } + if (aboveIme) { + state = new InsetsState(state); + state.removeSource(ITYPE_IME); + } + return state; } diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 44034edaa4bf..6b39fd2a70e3 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -406,12 +406,11 @@ class KeyguardController { // show on top of the lock screen. In this can we want to dismiss the docked // stack since it will be complicated/risky to try to put the activity on top // of the lock screen in the right fullscreen configuration. - final ActivityStack stack = - mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask(); - if (stack == null) { + final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); + if (!display.isSplitScreenModeActivated()) { return; } - mRootWindowContainer.getDefaultDisplay().onSplitScreenModeDismissed(); + display.onSplitScreenModeDismissed(); } } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index bd5666dd9a27..244ba82ce32c 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -21,6 +21,7 @@ import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; import static android.app.ActivityManager.RECENT_WITH_EXCLUDED; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; @@ -1298,6 +1299,7 @@ class RecentTasks { switch (task.getActivityType()) { case ACTIVITY_TYPE_HOME: case ACTIVITY_TYPE_RECENTS: + case ACTIVITY_TYPE_DREAM: // Ignore certain activity types completely return false; case ACTIVITY_TYPE_ASSISTANT: diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index adafdec40d37..9089240fe9d1 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -388,11 +388,12 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // surfaces needs to be done immediately. mWindowManager.executeAppTransition(); - if (targetStack.getTile() != null) { + final Task rootTask = targetStack.getRootTask(); + if (rootTask.isOrganized()) { // Client state may have changed during the recents animation, so force // send task info so the client can synchronize its state. mService.mTaskOrganizerController.dispatchTaskInfoChanged( - targetStack.mTile, true /* force */); + rootTask, true /* force */); } } catch (Exception e) { Slog.e(TAG, "Failed to clean up recents activity", e); diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 3e5cb50d6101..a30b70de267d 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -667,7 +667,7 @@ public class RecentsAnimationController implements DeathRecipient { mTargetActivityRecord.token); } if (mTargetActivityRecord.hasFixedRotationTransform()) { - mTargetActivityRecord.clearFixedRotationTransform(); + mTargetActivityRecord.finishFixedRotationTransform(); } } diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java index 45f8a15979f4..420997a5b82d 100644 --- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java +++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java @@ -20,7 +20,6 @@ import static com.android.server.wm.ActivityStack.TAG_ADD_REMOVE; import static com.android.server.wm.ActivityStack.TAG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; -import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE; import android.app.ActivityOptions; import android.content.Intent; @@ -233,48 +232,42 @@ class ResetTargetTaskHelper { } final ActivityTaskManagerService atmService = mTargetStack.mAtmService; - final ArrayList<Task> createdTasks = new ArrayList<>(); + DisplayContent display = mTargetStack.getDisplay(); + final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance(); + if (singleTaskInstanceDisplay) { + display = atmService.mRootWindowContainer.getDefaultDisplay(); + } + + final int windowingMode = mTargetStack.getWindowingMode(); + final int activityType = mTargetStack.getActivityType(); + while (!mPendingReparentActivities.isEmpty()) { final ActivityRecord r = mPendingReparentActivities.remove(0); - final ActivityRecord bottom = mTargetStack.getBottomMostActivity(); - final Task targetTask; - if (bottom != null && r.taskAffinity.equals(bottom.getTask().affinity)) { + final boolean alwaysCreateTask = DisplayContent.alwaysCreateStack(windowingMode, + activityType); + final Task task = alwaysCreateTask + ? display.getBottomMostTask() : mTargetStack.getBottomMostTask(); + Task targetTask = null; + if (task != null && r.taskAffinity.equals(task.affinity)) { // If the activity currently at the bottom has the same task affinity as // the one we are moving, then merge it into the same task. - targetTask = bottom.getTask(); + targetTask = task; if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + r + " out to bottom task " + targetTask); - } else { - targetTask = mTargetStack.reuseOrCreateTask( - r.info, null /*intent*/, false /*toTop*/); + } + if (targetTask == null) { + if (alwaysCreateTask) { + targetTask = display.getOrCreateStack(windowingMode, activityType, + false /* onTop */); + } else { + targetTask = mTargetStack.reuseOrCreateTask(r.info, null /*intent*/, + false /*toTop*/); + } targetTask.affinityIntent = r.intent; - createdTasks.add(targetTask); - if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " - + r + " out to new task " + targetTask); } r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded"); atmService.mStackSupervisor.mRecentTasks.add(targetTask); } - - DisplayContent display = mTargetStack.getDisplay(); - final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance(); - if (singleTaskInstanceDisplay) { - display = atmService.mRootWindowContainer.getDefaultDisplay(); - } - - final int windowingMode = mTargetStack.getWindowingMode(); - final int activityType = mTargetStack.getActivityType(); - if (!singleTaskInstanceDisplay && !display.alwaysCreateStack(windowingMode, activityType)) { - return; - } - - while (!createdTasks.isEmpty()) { - final Task targetTask = createdTasks.remove(createdTasks.size() - 1); - final ActivityStack targetStack = display.getOrCreateStack( - windowingMode, activityType, false /* onTop */); - targetTask.reparent(targetStack, false /* toTop */, REPARENT_LEAVE_STACK_IN_PLACE, - false /* animate */, true /* deferResume */, "resetTargetTask"); - } } private boolean takeOption(ActivityRecord p, boolean noOptions) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ebf1bc988b91..6e56bf4dafd7 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; @@ -1970,8 +1971,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final int focusStackId = topFocusedStack != null ? topFocusedStack.getRootTaskId() : INVALID_TASK_ID; // We dismiss the docked stack whenever we switch users. - final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask(); - if (dockedStack != null) { + if (getDefaultDisplay().isSplitScreenModeActivated()) { getDefaultDisplay().onSplitScreenModeDismissed(); } // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will @@ -2110,20 +2110,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final ActivityStack stack; if (singleActivity) { stack = r.getRootTask(); + stack.setWindowingMode(WINDOWING_MODE_PINNED); } else { - // In the case of multiple activities, we will create a new stack for it and then - // move the PIP activity into the stack. - // We will then perform a windowing mode change for both scenarios. - stack = display.createStack( - r.getRootTask().getRequestedOverrideWindowingMode(), - r.getActivityType(), ON_TOP, r.info, r.intent); + // In the case of multiple activities, we will create a new task for it and then + // move the PIP activity into the task. + stack = display.createStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP, + r.info, r.intent, false /* createdByOrganizer */); + // There are multiple activities in the task and moving the top activity should // reveal/leave the other activities in their original task. r.reparent(stack, MAX_VALUE, "moveActivityToStack"); } - stack.setWindowingMode(WINDOWING_MODE_PINNED); - // Reset the state that indicates it can enter PiP while pausing after we've moved it // to the pinned stack r.supportsEnterPipOnTaskSwitch = false; @@ -2799,16 +2797,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (stack == null && r != null) { stack = r.getRootTask(); } + int windowingMode = launchParams != null ? launchParams.mWindowingMode + : WindowConfiguration.WINDOWING_MODE_UNDEFINED; if (stack != null) { display = stack.getDisplay(); if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) { - int windowingMode = launchParams != null ? launchParams.mWindowingMode - : WindowConfiguration.WINDOWING_MODE_UNDEFINED; if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { windowingMode = display.resolveWindowingMode(r, options, candidateTask, activityType); } - if (stack.isCompatible(windowingMode, activityType)) { + // Always allow organized tasks that created by organizer since the activity type + // of an organized task is decided by the activity type of its top child, which + // could be incompatible with the given windowing mode and activity type. + if (stack.isCompatible(windowingMode, activityType) || stack.mCreatedByOrganizer) { return stack; } if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY @@ -2826,6 +2827,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) { display = getDefaultDisplay(); + if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { + windowingMode = display.resolveWindowingMode(r, options, candidateTask, + activityType); + } } return display.getOrCreateStack(r, options, candidateTask, activityType, onTop); @@ -2847,7 +2852,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * @param candidateTask The possible task the activity might be put in. * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null. */ - private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r, + @VisibleForTesting + ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r, @Nullable Task candidateTask, @Nullable ActivityOptions options, @Nullable LaunchParamsController.LaunchParams launchParams) { final DisplayContent displayContent = getDisplayContentOrCreate(displayId); @@ -2868,6 +2874,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (attachedDisplayId == INVALID_DISPLAY || attachedDisplayId == displayId) { return candidateTask.getStack(); } + // Or the candidate task is already a root task that can be reused by reparenting + // it to the target display. + if (candidateTask.isRootTask()) { + final ActivityStack stack = candidateTask.getStack(); + displayContent.moveStackToDisplay(stack, true /* onTop */); + return stack; + } } int windowingMode; @@ -2915,11 +2928,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome(); case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents(); case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant(); + case ACTIVITY_TYPE_DREAM: return r.isActivityTypeDream(); } - // TODO(task-hierarchy): Find another way to differentiate tile from normal stack once it is - // part of the hierarchy - if (stack instanceof TaskTile) { - // Don't launch directly into tiles. + if (stack.mCreatedByOrganizer) { + // Don't launch directly into task created by organizer...but why can't we? return false; } // There is a 1-to-1 relationship between stack and task when not in diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index eca7d2e19288..7a41ea57610d 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -20,6 +20,9 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED; import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM; import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP; @@ -114,7 +117,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.res.Configuration; -import android.graphics.Point; import android.graphics.Rect; import android.os.Debug; import android.os.IBinder; @@ -129,11 +131,11 @@ import android.util.DisplayMetrics; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; -import android.window.ITaskOrganizer; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.Surface; import android.view.SurfaceControl; +import android.window.ITaskOrganizer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; @@ -489,6 +491,17 @@ class Task extends WindowContainer<WindowContainer> { PictureInPictureParams mPictureInPictureParams = new PictureInPictureParams.Builder().build(); /** + * This task was created by the task organizer which has the following implementations. + * <ul> + * <lis>The task won't be removed when it is empty. Removal has to be an explicit request + * from the task organizer.</li> + * <li>Unlike other non-root tasks, it's direct children are visible to the task + * organizer for ordering purposes.</li> + * </ul> + */ + boolean mCreatedByOrganizer; + + /** * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int, * ActivityInfo, Intent, TaskDescription)} instead. */ @@ -509,22 +522,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 +547,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; @@ -594,6 +591,14 @@ class Task extends WindowContainer<WindowContainer> { voiceInteractor = _voiceInteractor; setIntent(activity, intent, info); setMinDimensions(info); + // Before we began to reuse a root task (old ActivityStack) as the leaf task, we used to + // create a leaf task in this case. Therefore now we won't send out the task created + // notification when we decide to reuse it here, so we send out the notification below. + // The reason why the created notification sent out when root task is created doesn't work + // is that realActivity isn't set until setIntent() method above is called for the first + // time. Eventually this notification will be removed when we can populate those information + // when root task is created. + mAtmService.getTaskChangeNotificationController().notifyTaskCreated(mTaskId, realActivity); return this; } @@ -602,11 +607,7 @@ class Task extends WindowContainer<WindowContainer> { return; } - // TODO(xutan): Removed type check after stack and task is merged. - // Before the real merge of stack and task, we need to avoid saving state of stacks. Once - // the merge is finished we can just pass DisplayContent because both windowing mode and - // bounds are set in the merged task. - if (oldParent instanceof ActivityStack) { + if (isLeafTask()) { // This task is going away, so save the last state if necessary. saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent()); } @@ -1365,7 +1366,7 @@ class Task extends WindowContainer<WindowContainer> { if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) { return applicationType; } - return getChildAt(0).getActivityType(); + return getTopChild().getActivityType(); } @Override @@ -1378,6 +1379,12 @@ class Task extends WindowContainer<WindowContainer> { ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addChild: %s at top.", this); + // A rootable task that is now being added to be the child of an organized task. Making + // sure the stack references is keep updated. + if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) { + mDisplayContent.addStackReferenceIfNeeded((ActivityStack) child); + } + // Make sure the list of display UID whitelists is updated // now that this record is in a new task. mRootWindowContainer.updateUIDsPresentOnDisplay(); @@ -1418,6 +1425,11 @@ class Task extends WindowContainer<WindowContainer> { @Override void removeChild(WindowContainer child) { + // A rootable child task that is now being removed from an organized task. Making sure + // the stack references is keep updated. + if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) { + mDisplayContent.removeStackReferenceIfNeeded((ActivityStack) child); + } removeChild(child, "removeChild"); } @@ -1465,8 +1477,9 @@ class Task extends WindowContainer<WindowContainer> { mStackSupervisor.removeTask(this, false /* killProcess */, !REMOVE_FROM_RECENTS, reason); } - } else if (!mReuseTask) { + } else if (!mReuseTask && !mCreatedByOrganizer) { // Remove entire task if it doesn't have any activity left and it isn't marked for reuse + // or created by task organizer. if (!isRootTask) { getStack().removeChild(this, reason); } @@ -1878,7 +1891,12 @@ class Task extends WindowContainer<WindowContainer> { final Task parentTask = getParent().asTask(); if (parentTask != null) { parentTask.onActivityStateChanged(record, state, reason); - return; + // We still want to update the resumed activity if the parent task is created by + // organizer in order to keep the information synced once got reparented out from the + // organized task. + if (!parentTask.mCreatedByOrganizer) { + return; + } } if (record == mResumedActivity && state != RESUMED) { @@ -2312,18 +2330,30 @@ class Task extends WindowContainer<WindowContainer> { return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize); } - void resolveTileOverrideConfiguration(Configuration newParentConfig) { + private void resolveOrganizedOverrideConfiguration(Configuration newParentConfig) { super.resolveOverrideConfiguration(newParentConfig); + if (!isOrganized()) { + return; + } + + final Task root = getRootTask(); + if (root == this) { + return; + } + + // Ensure to have the same windowing mode for the child tasks that controlled by task org. + getResolvedOverrideConfiguration().windowConfiguration + .setWindowingMode(root.getWindowingMode()); } @Override void resolveOverrideConfiguration(Configuration newParentConfig) { - if (!isLeafTask()) { - resolveTileOverrideConfiguration(newParentConfig); + if (!isLeafTask() || mCreatedByOrganizer) { + resolveOrganizedOverrideConfiguration(newParentConfig); return; } mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds()); - resolveTileOverrideConfiguration(newParentConfig); + resolveOrganizedOverrideConfiguration(newParentConfig); int windowingMode = getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode(); if (windowingMode == WINDOWING_MODE_UNDEFINED) { @@ -2412,7 +2442,9 @@ class Task extends WindowContainer<WindowContainer> { } Rect updateOverrideConfigurationFromLaunchBounds() { - final Rect bounds = getLaunchBounds(); + // If the task is controlled by another organized task, do not set override + // configurations and let its parent (organized task) to control it; + final Rect bounds = isOrganized() && !isRootTask() ? null : getLaunchBounds(); setBounds(bounds); if (bounds != null && !bounds.isEmpty()) { // TODO: Review if we actually want to do this - we are setting the launch bounds @@ -2596,7 +2628,7 @@ class Task extends WindowContainer<WindowContainer> { // preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid. if (suggestedPosition == POSITION_BOTTOM && minPosition == 0) { return POSITION_BOTTOM; - } else if (suggestedPosition == POSITION_TOP && maxPosition == (size - 1)) { + } else if (suggestedPosition == POSITION_TOP && maxPosition >= (size - 1)) { return POSITION_TOP; } // Reset position based on minimum/maximum possible positions. @@ -3110,7 +3142,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 @@ -3215,6 +3247,20 @@ class Task extends WindowContainer<WindowContainer> { } @Override + int getOrientation(int candidate) { + return canSpecifyOrientation() ? super.getOrientation(candidate) : SCREEN_ORIENTATION_UNSET; + } + + private boolean canSpecifyOrientation() { + final int windowingMode = getWindowingMode(); + final int activityType = getActivityType(); + return windowingMode == WINDOWING_MODE_FULLSCREEN + || activityType == ACTIVITY_TYPE_HOME + || activityType == ACTIVITY_TYPE_RECENTS + || activityType == ACTIVITY_TYPE_ASSISTANT; + } + + @Override boolean fillsParent() { return matchParentBounds(); } @@ -3380,14 +3426,12 @@ class Task extends WindowContainer<WindowContainer> { info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode(); info.configuration.setTo(getConfiguration()); info.token = mRemoteToken; - // Get's the first non-undefined activity type among this and children. Can't use - // configuration.windowConfiguration because that would only be this level. - info.topActivityType = getActivityType(); //TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child // order changes. final Task top = getTopMostTask(); info.resizeMode = top != null ? top.mResizeMode : mResizeMode; + info.topActivityType = top.getActivityType(); if (mPictureInPictureParams.empty()) { info.pictureInPictureParams = null; @@ -3418,10 +3462,6 @@ class Task extends WindowContainer<WindowContainer> { return this; } - TaskTile asTile() { - return null; - } - // TODO(task-merge): Figure-out how this should work with hierarchy tasks. boolean shouldBeVisible(ActivityRecord starting) { return true; @@ -3717,8 +3757,9 @@ class Task extends WindowContainer<WindowContainer> { } static Task create(ActivityTaskManagerService service, int taskId, int activityType, - ActivityInfo info, Intent intent) { - return getTaskFactory().create(service, taskId, activityType, info, intent); + ActivityInfo info, Intent intent, boolean createdByOrganizer) { + return getTaskFactory().create(service, taskId, activityType, info, intent, + createdByOrganizer); } static Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info, @@ -3740,8 +3781,9 @@ class Task extends WindowContainer<WindowContainer> { */ static class TaskFactory { Task create(ActivityTaskManagerService service, int taskId, int activityType, - ActivityInfo info, Intent intent) { - return new ActivityStack(service, taskId, activityType, info, intent); + ActivityInfo info, Intent intent, boolean createdByOrganizer) { + return new ActivityStack(service, taskId, activityType, info, intent, + createdByOrganizer); } Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info, @@ -4014,23 +4056,32 @@ 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. - if (((ActivityStack) rootTask).getTile() != null) { + if (rootTask.mTaskOrganizer == null) { + // You are obviously not organized... return false; } - return rootTask == this && rootTask.mTaskOrganizer != null; + if (rootTask == this) { + // Root tasks can be organized. + return true; + } + if (rootTask.mCreatedByOrganizer && getParent() == rootTask) { + // Direct children of tasks added by the organizer can the organized. + return true; + } + + return false; } @Override protected void reparentSurfaceControl(SurfaceControl.Transaction t, SurfaceControl newParent) { /** - * Avoid yanking back control from the TaskOrganizer, which has presumably reparented the - * Surface in to its own hierarchy. + * Avoid reparenting SurfaceControl of the organized tasks that are always on top, since + * the surfaces should be controlled by the organizer itself, like bubbles. */ - if (isControlledByTaskOrganizer()) { + if (isOrganized() && isAlwaysOnTop()) { return; } super.reparentSurfaceControl(t, newParent); @@ -4060,11 +4111,13 @@ class Task extends WindowContainer<WindowContainer> { return true; } - // Called on Binder death. - void taskOrganizerDied() { + void taskOrganizerUnregistered() { mTaskOrganizer = null; mLastTaskOrganizerWindowingMode = -1; onTaskOrganizerChanged(); + if (mCreatedByOrganizer) { + removeImmediately(); + } } /** @@ -4117,27 +4170,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..4382e9d578ad 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; @@ -27,19 +28,22 @@ import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDO import android.annotation.Nullable; import android.app.ActivityManager.RunningTaskInfo; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; -import android.window.ITaskOrganizerController; +import android.util.SparseArray; import android.window.ITaskOrganizer; +import android.window.ITaskOrganizerController; import android.window.IWindowContainer; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.WeakHashMap; @@ -49,6 +53,7 @@ import java.util.WeakHashMap; */ class TaskOrganizerController extends ITaskOrganizerController.Stub { private static final String TAG = "TaskOrganizerController"; + private static final LinkedList<TaskOrganizerState> EMPTY_LIST = new LinkedList<>(); /** * Masks specifying which configurations are important to report back to an organizer when @@ -71,32 +76,20 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { @Override public void binderDied() { synchronized (mGlobalLock) { - final TaskOrganizerState state = - mTaskOrganizerStates.get(mTaskOrganizer.asBinder()); - state.releaseTasks(); - mTaskOrganizerStates.remove(mTaskOrganizer.asBinder()); - if (mTaskOrganizersForWindowingMode.get(mWindowingMode) == mTaskOrganizer) { - mTaskOrganizersForWindowingMode.remove(mWindowingMode); - } + final TaskOrganizerState state = mTaskOrganizerStates.remove( + mTaskOrganizer.asBinder()); + state.dispose(); } } }; - class TaskOrganizerState { - ITaskOrganizer mOrganizer; - DeathRecipient mDeathRecipient; - int mWindowingMode; - - ArrayList<Task> mOrganizedTasks = new ArrayList<>(); - - // Save the TaskOrganizer which we replaced registration for - // so it can be re-registered if we unregister. - TaskOrganizerState mReplacementFor; - boolean mDisposed = false; + private class TaskOrganizerState { + private final ITaskOrganizer mOrganizer; + private final DeathRecipient mDeathRecipient; + private final int mWindowingMode; + private final ArrayList<Task> mOrganizedTasks = new ArrayList<>(); - - TaskOrganizerState(ITaskOrganizer organizer, int windowingMode, - @Nullable TaskOrganizerState replacing) { + TaskOrganizerState(ITaskOrganizer organizer, int windowingMode) { mOrganizer = organizer; mDeathRecipient = new DeathRecipient(organizer, windowingMode); try { @@ -105,13 +98,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { Slog.e(TAG, "TaskOrganizer failed to register death recipient"); } mWindowingMode = windowingMode; - mReplacementFor = replacing; } 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 +111,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); } @@ -127,35 +119,26 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void dispose() { - mDisposed = true; releaseTasks(); - handleReplacement(); + mTaskOrganizersForWindowingMode.get(mWindowingMode).remove(this); } - void releaseTasks() { + private void releaseTasks() { for (int i = mOrganizedTasks.size() - 1; i >= 0; i--) { final Task t = mOrganizedTasks.get(i); - t.taskOrganizerDied(); removeTask(t); - } - } - - void handleReplacement() { - if (mReplacementFor != null && !mReplacementFor.mDisposed) { - mTaskOrganizersForWindowingMode.put(mWindowingMode, mReplacementFor); + t.taskOrganizerUnregistered(); } } void unlinkDeath() { - mDisposed = true; mOrganizer.asBinder().unlinkToDeath(mDeathRecipient, 0); } - }; - - - final HashMap<Integer, TaskOrganizerState> mTaskOrganizersForWindowingMode = new HashMap(); - final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap(); + } + private final SparseArray<LinkedList<TaskOrganizerState>> mTaskOrganizersForWindowingMode = + new SparseArray<>(); + private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>(); private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); @@ -194,11 +177,17 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { Slog.w(TAG, "Task organizer already exists for windowing mode: " + windowingMode); } - final TaskOrganizerState previousState = - mTaskOrganizersForWindowingMode.get(windowingMode); - final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode, - previousState); - mTaskOrganizersForWindowingMode.put(windowingMode, state); + + LinkedList<TaskOrganizerState> states; + if (mTaskOrganizersForWindowingMode.contains(windowingMode)) { + states = mTaskOrganizersForWindowingMode.get(windowingMode); + } else { + states = new LinkedList<>(); + mTaskOrganizersForWindowingMode.put(windowingMode, states); + } + final TaskOrganizerState previousState = states.peekLast(); + final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode); + states.add(state); mTaskOrganizerStates.put(organizer.asBinder(), state); if (previousState == null) { @@ -219,16 +208,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { @Override public void unregisterTaskOrganizer(ITaskOrganizer organizer) { - final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); + final TaskOrganizerState state = mTaskOrganizerStates.remove(organizer.asBinder()); state.unlinkDeath(); - if (mTaskOrganizersForWindowingMode.get(state.mWindowingMode) == state) { - mTaskOrganizersForWindowingMode.remove(state.mWindowingMode); - } state.dispose(); } ITaskOrganizer getTaskOrganizer(int windowingMode) { - final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode); + final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode, + EMPTY_LIST).peekLast(); if (state == null) { return null; } @@ -255,11 +242,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (display == null) { return null; } - final int nextId = display.getNextStackId(); - TaskTile tile = new TaskTile(mService, nextId, windowingMode); - display.addTile(tile); - RunningTaskInfo out = tile.getTaskInfo(); - mLastSentTaskInfos.put(tile, out); + + final Task task = display.getOrCreateStack(windowingMode, ACTIVITY_TYPE_UNDEFINED, + false /* onTop */, new Intent(), null /* candidateTask */, + true /* createdByOrganizer */); + RunningTaskInfo out = task.getTaskInfo(); + mLastSentTaskInfos.put(task, out); return out; } } finally { @@ -273,11 +261,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - TaskTile tile = TaskTile.forToken(token.asBinder()); - if (tile == null) { - return false; + final Task task = WindowContainer.fromBinder(token.asBinder()).asTask(); + if (task == null) return false; + if (!task.mCreatedByOrganizer) { + throw new IllegalArgumentException( + "Attempt to delete task not created by organizer task=" + task); } - tile.removeImmediately(); + task.removeImmediately(); return true; } } finally { @@ -358,12 +348,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (task == null) { return null; } - ActivityStack rootTask = (ActivityStack) task.getRootTask(); - final TaskTile tile = rootTask.getTile(); - if (tile != null) { - rootTask = tile; - } - return rootTask.mRemoteToken; + return task.getRootTask().mRemoteToken; } } finally { Binder.restoreCallingIdentity(origId); @@ -371,7 +356,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } @Override - public void setLaunchRoot(int displayId, @Nullable IWindowContainer tile) { + public void setLaunchRoot(int displayId, @Nullable IWindowContainer token) { enforceStackPermission("setLaunchRoot()"); final long origId = Binder.clearCallingIdentity(); try { @@ -380,16 +365,21 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (display == null) { return; } - TaskTile taskTile = tile == null ? null : TaskTile.forToken(tile.asBinder()); - if (taskTile == null) { - display.mLaunchTile = null; + Task task = token == null + ? null : WindowContainer.fromBinder(token.asBinder()).asTask(); + if (task == null) { + display.mLaunchRootTask = null; return; } - if (taskTile.getDisplay() != display) { + if (!task.mCreatedByOrganizer) { + throw new IllegalArgumentException("Attempt to set task not created by " + + "organizer as launch root task=" + task); + } + if (task.getDisplayContent() != display) { throw new RuntimeException("Can't set launch root for display " + displayId - + " to task on display " + taskTile.getDisplay().getDisplayId()); + + " to task on display " + task.getDisplayContent().getDisplayId()); } - display.mLaunchTile = taskTile; + display.mLaunchRootTask = task; } } finally { Binder.restoreCallingIdentity(origId); @@ -411,25 +401,25 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { Slog.e(TAG, "Can't get children of " + parent + " because it is not valid."); return null; } - // For now, only support returning children of persistent root tasks (of which the - // only current implementation is TaskTile). - if (!(container instanceof TaskTile)) { + final Task task = container.asTask(); + if (task == null) { + Slog.e(TAG, container + " is not a task..."); + return null; + } + // For now, only support returning children of tasks created by the organizer. + if (!task.mCreatedByOrganizer) { Slog.w(TAG, "Can only get children of root tasks created via createRootTask"); return null; } ArrayList<RunningTaskInfo> out = new ArrayList<>(); - // Tiles aren't real parents, so we need to go through stacks on the display to - // ensure correct ordering. - final DisplayContent dc = container.getDisplayContent(); - for (int i = dc.getStackCount() - 1; i >= 0; --i) { - final ActivityStack as = dc.getStackAt(i); - if (as.getTile() == container) { - if (activityTypes != null - && !ArrayUtils.contains(activityTypes, as.getActivityType())) { - continue; - } - out.add(as.getTaskInfo()); + for (int i = task.getChildCount() - 1; i >= 0; --i) { + final Task child = task.getChildAt(i).asTask(); + if (child == null) continue; + if (activityTypes != null + && !ArrayUtils.contains(activityTypes, child.getActivityType())) { + continue; } + out.add(child.getTaskInfo()); } return out; } @@ -451,12 +441,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } ArrayList<RunningTaskInfo> out = new ArrayList<>(); for (int i = dc.getStackCount() - 1; i >= 0; --i) { - final ActivityStack task = dc.getStackAt(i); - if (task.getTile() != null) { - // a tile is supposed to look like a parent, so don't include their - // "children" here. They can be accessed via getChildTasks() - continue; - } + final Task task = dc.getStackAt(i); if (activityTypes != null && !ArrayUtils.contains(activityTypes, task.getActivityType())) { continue; diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index f046e8adc478..be0d6f8a0b9f 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -268,8 +268,9 @@ class TaskPositioner implements IBinder.DeathRecipient { mDisplayContent.getDisplayRotation().pause(); // Notify InputMonitor to take mDragWindowHandle. - mDisplayContent.getInputMonitor().updateInputWindowsImmediately(); - new SurfaceControl.Transaction().syncInputWindows().apply(true); + final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); + mDisplayContent.getInputMonitor().updateInputWindowsImmediately(t); + t.syncInputWindows().apply(); final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics(); mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics); diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java deleted file mode 100644 index 822f840005b7..000000000000 --- a/services/core/java/com/android/server/wm/TaskTile.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * 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.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; -import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; - -import android.app.ActivityManager; -import android.app.TaskInfo; -import android.app.WindowConfiguration; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.os.IBinder; -import android.util.Slog; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.function.Consumer; - -/** - * A Tile. Right now this acts as a proxy for manipulating non-child stacks. Eventually, this - * can become an actual parent. - */ -// TODO(task-hierarchy): Remove when tasks can nest >2 or when single tasks can handle their -// own lifecycles. -public class TaskTile extends ActivityStack { - private static final String TAG = "TaskTile"; - final ArrayList<WindowContainer> mChildren = new ArrayList<>(); - - private static ActivityInfo createEmptyActivityInfo() { - ActivityInfo info = new ActivityInfo(); - info.applicationInfo = new ApplicationInfo(); - return info; - } - - TaskTile(ActivityTaskManagerService atmService, int id, int windowingMode) { - super(atmService, id, new Intent() /*intent*/, null /*affinityIntent*/, null /*affinity*/, - null /*rootAffinity*/, null /*realActivity*/, null /*origActivity*/, - false /*rootWasReset*/, false /*autoRemoveRecents*/, false /*askedCompatMode*/, - 0 /*userId*/, 0 /*effectiveUid*/, null /*lastDescription*/, - System.currentTimeMillis(), true /*neverRelinquishIdentity*/, - new ActivityManager.TaskDescription(), id, INVALID_TASK_ID, INVALID_TASK_ID, - 0 /*taskAffiliationColor*/, 0 /*callingUid*/, "" /*callingPackage*/, - null /*callingFeatureId*/, RESIZE_MODE_RESIZEABLE, - false /*supportsPictureInPicture*/, false /*_realActivitySuspended*/, - false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE, - createEmptyActivityInfo(), null /*voiceSession*/, null /*voiceInteractor*/, - null /*stack*/); - getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode); - } - - @Override - void onDisplayChanged(DisplayContent dc) { - mDisplayContent = null; - if (dc != null) { - dc.getPendingTransaction().merge(getPendingTransaction()); - } - mDisplayContent = dc; - // Virtual parent, so don't notify children. - } - - @Override - TaskTile asTile() { - return this; - } - - @Override - protected void addChild(WindowContainer child, Comparator<WindowContainer> comparator) { - throw new RuntimeException("Improper use of addChild() on Tile"); - } - - @Override - void addChild(WindowContainer child, int index) { - mChildren.add(child); - if (child instanceof ActivityStack) { - ((ActivityStack) child).setTile(this); - } - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged( - this, false /* force */); - } - - @Override - void removeChild(WindowContainer child) { - if (child instanceof ActivityStack) { - ((ActivityStack) child).setTile(null); - } - mChildren.remove(child); - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged( - this, false /* force */); - } - - void removeAllChildren() { - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowContainer child = mChildren.get(i); - if (child instanceof ActivityStack) { - ((ActivityStack) child).setTile(null); - } - } - mChildren.clear(); - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged( - this, false /* force */); - } - - @Override - protected int getChildCount() { - // Currently 0 as this isn't a proper hierarchy member yet. - return 0; - } - - @Override - public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) { - Configuration c = new Configuration(getRequestedOverrideConfiguration()); - c.windowConfiguration.setWindowingMode(windowingMode); - onRequestedOverrideConfigurationChanged(c); - } - - @Override - public void onConfigurationChanged(Configuration newParentConfig) { - super.onConfigurationChanged(newParentConfig); - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowContainer child = mChildren.get(i); - child.onConfigurationChanged(child.getParent().getConfiguration()); - } - } - - void forAllTileActivities(Consumer<ActivityRecord> callback) { - for (int i = mChildren.size() - 1; i >= 0; --i) { - mChildren.get(i).forAllActivities(callback, true /* traverseTopToBottom */); - } - } - - /** - * Until this can be part of the hierarchy, the Stack level can use this utility during - * resolveOverrideConfig to simulate inheritance. - */ - void updateResolvedConfig(Configuration inOutResolvedConfig) { - Rect resolveBounds = inOutResolvedConfig.windowConfiguration.getBounds(); - if (resolveBounds.isEmpty()) { - resolveBounds.set(getRequestedOverrideBounds()); - } - int stackMode = inOutResolvedConfig.windowConfiguration.getWindowingMode(); - if (stackMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED - || stackMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN) { - // Also replace FULLSCREEN because we interpret FULLSCREEN as "fill parent" - inOutResolvedConfig.windowConfiguration.setWindowingMode( - getRequestedOverrideWindowingMode()); - } - if (inOutResolvedConfig.smallestScreenWidthDp - == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { - inOutResolvedConfig.smallestScreenWidthDp = - getRequestedOverrideConfiguration().smallestScreenWidthDp; - } - if (inOutResolvedConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) { - inOutResolvedConfig.screenWidthDp = getRequestedOverrideConfiguration().screenWidthDp; - } - if (inOutResolvedConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - inOutResolvedConfig.screenHeightDp = getRequestedOverrideConfiguration().screenHeightDp; - } - Rect resolveAppBounds = inOutResolvedConfig.windowConfiguration.getAppBounds(); - if (resolveAppBounds == null || resolveAppBounds.isEmpty()) { - inOutResolvedConfig.windowConfiguration.setAppBounds( - getRequestedOverrideConfiguration().windowConfiguration.getAppBounds()); - } - } - - @Override - void fillTaskInfo(TaskInfo info) { - super.fillTaskInfo(info); - WindowContainer top = null; - // Check mChildren.isEmpty directly because hasChild() -> getChildCount() always returns 0 - if (!mChildren.isEmpty()) { - // Find the top-most root task which is a virtual child of this Tile. Because this is a - // virtual parent, the mChildren order here isn't changed during hierarchy operations. - WindowContainer parent = mChildren.get(0).getParent(); - for (int i = parent.getChildCount() - 1; i >= 0; --i) { - if (mChildren.contains(parent.getChildAt(i))) { - top = parent.getChildAt(i); - break; - } - } - } - final Task topTask = top == null ? null : top.getTopMostTask(); - boolean isResizable = topTask == null || topTask.isResizeable(); - info.resizeMode = isResizable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE; - info.topActivityType = top == null ? ACTIVITY_TYPE_UNDEFINED : top.getActivityType(); - } - - @Override - void removeImmediately() { - removeAllChildren(); - super.removeImmediately(); - } - - @Override - void taskOrganizerDied() { - super.taskOrganizerDied(); - removeImmediately(); - } - - static TaskTile forToken(IBinder token) { - try { - return (TaskTile) ((TaskToken) 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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 23ba528b6aee..075772566d56 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7746,19 +7746,23 @@ public class WindowManagerService extends IWindowManager.Stub public void syncInputTransactions() { waitForAnimationsToComplete(); + // Collect all input transactions from all displays to make sure we could sync all input + // windows at same time. + final SurfaceControl.Transaction t = mTransactionFactory.get(); synchronized (mGlobalLock) { mWindowPlacerLocked.performSurfacePlacementIfScheduled(); mRoot.forAllDisplays(displayContent -> - displayContent.getInputMonitor().updateInputWindowsImmediately()); + displayContent.getInputMonitor().updateInputWindowsImmediately(t)); } - mTransactionFactory.get().syncInputWindows().apply(true); + t.syncInputWindows().apply(); } private void waitForAnimationsToComplete() { synchronized (mGlobalLock) { long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS; - while (mRoot.isAnimating(TRANSITION | CHILDREN) && timeoutRemaining > 0) { + while ((mAnimator.isAnimationScheduled() + || mRoot.isAnimating(TRANSITION | CHILDREN)) && timeoutRemaining > 0) { long startTime = System.currentTimeMillis(); try { mGlobalLock.wait(timeoutRemaining); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index ebfe1096540e..5f21e1799958 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 @@ -156,17 +159,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub false /* preserveWindow */); try { for (int i = haveConfigChanges.size() - 1; i >= 0; --i) { - final WindowContainer wc = haveConfigChanges.valueAt(i); - final Task task = wc.asTask(); - final TaskTile tile = task != null ? task.asTile() : null; - if (tile != null) { - // Special case for tile. Can't override normal forAllActivities - // because it generates duplicate calls and messes up existing - // code-paths. - tile.forAllTileActivities(f); - } else { - wc.forAllActivities(f); - } + haveConfigChanges.valueAt(i).forAllActivities(f); } } finally { f.recycle(); @@ -220,51 +213,65 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub private int sanitizeAndApplyHierarchyOp(WindowContainer container, WindowContainerTransaction.HierarchyOp hop) { - if (!(container instanceof Task)) { + final Task task = container.asTask(); + if (task == null) { throw new IllegalArgumentException("Invalid container in hierarchy op"); } - if (container.getDisplayContent() == null) { - Slog.w(TAG, "Container is no longer attached: " + container); + final DisplayContent dc = task.getDisplayContent(); + if (dc == null) { + Slog.w(TAG, "Container is no longer attached: " + task); return 0; } + final ActivityStack as = (ActivityStack) task; + if (hop.isReparent()) { - // special case for tiles since they are "virtual" parents - if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) { - ActivityStack as = (ActivityStack) container; - TaskTile newParent = hop.getNewParent() == null ? null - : (TaskTile) WindowContainer.fromBinder(hop.getNewParent()); - if (as.getTile() != newParent) { - if (as.getTile() != null) { - as.getTile().removeChild(as); - } - if (newParent != null) { - if (!as.affectedBySplitScreenResize()) { - return 0; - } - newParent.addChild(as, POSITION_TOP); + final boolean isNonOrganizedRootableTask = + (task.isRootTask() && !task.mCreatedByOrganizer) + || task.getParent().asTask().mCreatedByOrganizer; + if (isNonOrganizedRootableTask) { + Task newParent = hop.getNewParent() == null ? null + : WindowContainer.fromBinder(hop.getNewParent()).asTask(); + if (task.getParent() != newParent) { + if (newParent == null) { + // Re-parent task to display as a root task. + dc.moveStackToDisplay(as, hop.getToTop()); + } else if (newParent.inMultiWindowMode() && !task.isResizeable() + && task.isLeafTask()) { + Slog.w(TAG, "Can't support task that doesn't support multi-window mode in" + + " multi-window mode... newParent=" + newParent + " task=" + task); + return 0; + } else { + // Clear the window crop on root task since it may not be updated after + // reparent (no longer be a root task) + task.getSurfaceControl().setWindowCrop(null); + task.reparent((ActivityStack) newParent, + hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, + false /*moveParents*/, "sanitizeAndApplyHierarchyOp"); } - } - if (hop.getToTop()) { - as.getDisplay().positionStackAtTop(as, false /* includingParents */); } else { - as.getDisplay().positionStackAtBottom(as); + final ActivityStack rootTask = + (ActivityStack) (newParent != null ? newParent : task.getRootTask()); + if (hop.getToTop()) { + as.getDisplay().positionStackAtTop(rootTask, false /* includingParents */); + } else { + as.getDisplay().positionStackAtBottom(rootTask); + } } - } else if (container instanceof Task) { + } else { throw new RuntimeException("Reparenting leaf Tasks is not supported now."); } } else { // Ugh, of course ActivityStack has its own special reorder logic... - if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) { - ActivityStack as = (ActivityStack) container; + if (task.isRootTask()) { if (hop.getToTop()) { - as.getDisplay().positionStackAtTop(as, false /* includingParents */); + dc.positionStackAtTop(as, false /* includingParents */); } else { - as.getDisplay().positionStackAtBottom(as); + dc.positionStackAtBottom(as); } } else { - container.getParent().positionChildAt( + task.getParent().positionChildAt( hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, - container, false /* includingParents */); + task, false /* includingParents */); } } return TRANSACT_EFFECTS_LIFECYCLE; @@ -318,6 +325,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/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 161152ba0d74..c4d700c9aca3 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -131,6 +131,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT; import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION; import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET; @@ -138,7 +139,6 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT; -import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT; import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; @@ -608,6 +608,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean mSeamlesslyRotated = false; /** + * Indicates if this window is behind IME. Only windows behind IME can get insets from IME. + */ + boolean mBehindIme = false; + + /** * Surface insets from the previous call to relayout(), used to track * if we are changing the Surface insets. */ @@ -2270,9 +2275,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return false; } - if (PixelFormat.formatHasAlpha(mAttrs.format) && mAttrs.alpha == 0) { - // Support legacy use cases where completely transparent windows can still be ime target - // with FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set. + if (PixelFormat.formatHasAlpha(mAttrs.format)) { + // Support legacy use cases where transparent windows can still be ime target with + // FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set. // Certain apps listen for IME insets using transparent windows and ADJUST_NOTHING to // manually synchronize app content to IME animation b/144619551. // TODO(b/145812508): remove this once new focus management is complete b/141738570 @@ -3338,7 +3343,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } final ActivityStack stack = task.getStack(); - if (stack == null) { + if (stack == null || stack.mCreatedByOrganizer) { return; } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 850c36238513..3c2b6ec9711d 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -480,26 +480,42 @@ class WindowToken extends WindowContainer<WindowState> { } /** - * Clears the transformation and continue updating the orientation change of display. Only the - * state owner can clear the transform state. + * Finishes the transform and continue updating the orientation change of display. Only the + * state owner can finish the transform state. */ - void clearFixedRotationTransform() { + void finishFixedRotationTransform() { + if (mFixedRotationTransformState == null || mFixedRotationTransformState.mOwner != this) { + return; + } + final boolean changed = + mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(this); + // If it is not the launching app or the display is not rotated, make sure the transform is + // cleared and the configuration is restored from parent. + if (!changed) { + clearFixedRotationTransform(null /* applyDisplayRotation */); + onConfigurationChanged(getParent().getConfiguration()); + } + } + + /** + * Clears the transform and apply display rotation if the action is given. The caller needs to + * refresh the configuration of this container after this method call. + */ + void clearFixedRotationTransform(Runnable applyDisplayRotation) { final FixedRotationTransformState state = mFixedRotationTransformState; - if (state == null || state.mOwner != this) { + if (state == null) { return; } + state.resetTransform(); // Clear the flag so if the display will be updated to the same orientation, the transform - // won't take effect. The state is cleared at the end, because it is used to indicate that - // other windows can use seamless rotation when applying rotation to display. + // won't take effect. state.mIsTransforming = false; - final boolean changed = - mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(this); - // If it is not the launching app or the display is not rotated, make sure the merged - // override configuration is restored from parent. - if (!changed) { - onMergedOverrideConfigurationChanged(); + if (applyDisplayRotation != null) { + applyDisplayRotation.run(); } + // The state is cleared at the end, because it is used to indicate that other windows can + // use seamless rotation when applying rotation to display. for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { state.mAssociatedTokens.get(i).mFixedRotationTransformState = null; } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index e3f9ae8969b3..9bc5d34c11af 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -206,7 +206,7 @@ public: status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel); status_t pilferPointers(const sp<IBinder>& token); - void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId); + void displayRemoved(JNIEnv* env, int32_t displayId); void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj); void setFocusedDisplay(JNIEnv* env, int32_t displayId); void setInputDispatchMode(bool enabled, bool frozen); @@ -771,55 +771,10 @@ void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration } } -void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, - int32_t displayId) { - std::vector<sp<InputWindowHandle> > windowHandles; - - if (windowHandleObjArray) { - jsize length = env->GetArrayLength(windowHandleObjArray); - for (jsize i = 0; i < length; i++) { - jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i); - if (! windowHandleObj) { - break; // found null element indicating end of used portion of the array - } - - sp<InputWindowHandle> windowHandle = - android_view_InputWindowHandle_getHandle(env, windowHandleObj); - if (windowHandle != nullptr) { - windowHandles.push_back(windowHandle); - } - env->DeleteLocalRef(windowHandleObj); - } - } - - mInputManager->getDispatcher()->setInputWindows(windowHandles, displayId); - - // Do this after the dispatcher has updated the window handle state. - bool newPointerGesturesEnabled = true; - size_t numWindows = windowHandles.size(); - for (size_t i = 0; i < numWindows; i++) { - const sp<InputWindowHandle>& windowHandle = windowHandles[i]; - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - if (windowInfo && windowInfo->hasFocus && (windowInfo->inputFeatures - & InputWindowInfo::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) { - newPointerGesturesEnabled = false; - } - } - - bool pointerGesturesEnabledChanged = false; - { // acquire lock - AutoMutex _l(mLock); - - if (mLocked.pointerGesturesEnabled != newPointerGesturesEnabled) { - mLocked.pointerGesturesEnabled = newPointerGesturesEnabled; - pointerGesturesEnabledChanged = true; - } - } // release lock - - if (pointerGesturesEnabledChanged) { - mInputManager->getReader()->requestRefreshConfiguration( - InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT); - } +void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) { + // Set an empty list to remove all handles from the specific display. + std::vector<sp<InputWindowHandle>> windowHandles; + mInputManager->getDispatcher()->setInputWindows({{displayId, windowHandles}}); } void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId, @@ -1567,11 +1522,10 @@ static void nativeToggleCapsLock(JNIEnv* env, jclass /* clazz */, im->getInputManager()->getReader()->toggleCapsLockState(deviceId); } -static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */, - jlong ptr, jobjectArray windowHandleObjArray, jint displayId) { +static void nativeDisplayRemoved(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); - im->setInputWindows(env, windowHandleObjArray, displayId); + im->displayRemoved(env, displayId); } static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */, @@ -1815,8 +1769,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;", (void*)nativeVerifyInputEvent}, {"nativeToggleCapsLock", "(JI)V", (void*)nativeToggleCapsLock}, - {"nativeSetInputWindows", "(J[Landroid/view/InputWindowHandle;I)V", - (void*)nativeSetInputWindows}, + {"nativeDisplayRemoved", "(JI)V", (void*)nativeDisplayRemoved}, {"nativeSetFocusedApplication", "(JILandroid/view/InputApplicationHandle;)V", (void*)nativeSetFocusedApplication}, {"nativeSetFocusedDisplay", "(JI)V", (void*)nativeSetFocusedDisplay}, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 740c5cb3e75a..09fab3e29fe0 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); } @@ -4405,6 +4434,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { clearDeviceOwnerLocked(getDeviceOwnerAdminLocked(), userHandle); } if (isProfileOwner(adminReceiver, userHandle)) { + if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) { + mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, + false, + UserHandle.of(getProfileParentId(userHandle))); + } final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle, /* parent */ false); clearProfileOwnerLocked(admin, userHandle); @@ -13041,26 +13075,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final boolean addingProfileRestricted = mUserManager.hasUserRestriction( UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle); - UserInfo parentUser = mUserManager.getProfileParent(callingUserId); - final boolean addingProfileRestrictedOnParent = (parentUser != null) - && mUserManager.hasUserRestriction( - UserManager.DISALLOW_ADD_MANAGED_PROFILE, - UserHandle.of(parentUser.id)); - - Slog.i(LOG_TAG, String.format( - "When checking for managed profile provisioning: Has device owner? %b, adding" - + " profile restricted? %b, adding profile restricted on parent? %b", - hasDeviceOwner, addingProfileRestricted, addingProfileRestrictedOnParent)); + if (mUserManager.getUserInfo(callingUserId).isProfile()) { + Slog.i(LOG_TAG, + String.format("Calling user %d is a profile, cannot add another.", + callingUserId)); + // The check is called from inside a managed profile. A managed profile cannot + // be provisioned from within another managed profile. + return CODE_CANNOT_ADD_MANAGED_PROFILE; + } - // If there's a device owner, the restriction on adding a managed profile must be set - // somewhere. - if (hasDeviceOwner && !addingProfileRestricted && !addingProfileRestrictedOnParent) { + // If there's a device owner, the restriction on adding a managed profile must be set. + if (hasDeviceOwner && !addingProfileRestricted) { Slog.wtf(LOG_TAG, "Has a device owner but no restriction on adding a profile."); } - // Do not allow adding a managed profile if there's a restriction, either on the current - // user or its parent user. - if (addingProfileRestricted || addingProfileRestrictedOnParent) { + // Do not allow adding a managed profile if there's a restriction. + if (addingProfileRestricted) { + Slog.i(LOG_TAG, String.format( + "Adding a profile is restricted: User %s Has device owner? %b", + callingUserHandle, hasDeviceOwner)); return CODE_CANNOT_ADD_MANAGED_PROFILE; } // If there's a restriction on removing the managed profile then we have to take it @@ -13069,6 +13102,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { !mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, callingUserHandle); if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) { + Slog.i(LOG_TAG, String.format( + "Cannot add more profiles: Can remove current? %b", canRemoveProfile)); return CODE_CANNOT_ADD_MANAGED_PROFILE; } } finally { @@ -15650,7 +15685,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 +15704,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 +15828,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 +15848,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/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/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 1212f2082404..109c1191523b 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -15,218 +15,227 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.frameworks.servicestests"> - - <uses-permission android:name="android.permission.READ_LOGS" /> - <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" /> - <uses-permission android:name="android.permission.ACCOUNT_MANAGER" /> - <uses-permission android:name="android.permission.WRITE_SETTINGS" /> - <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> - <uses-permission android:name="android.permission.READ_PHONE_STATE" /> - <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> - <uses-permission android:name="android.permission.BROADCAST_STICKY" /> - <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> - <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" /> - <uses-permission android:name="android.permission.WAKE_LOCK" /> - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> - <uses-permission android:name="android.permission.REAL_GET_TASKS" /> - <uses-permission android:name="android.permission.GET_DETAILED_TASKS" /> - <uses-permission android:name="android.permission.REORDER_TASKS" /> - <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" /> - <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" /> - <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" /> - <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> - <uses-permission android:name="android.permission.MANAGE_USERS" /> - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> - <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" /> - <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> - <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> - <uses-permission android:name="android.permission.INTERNET" /> - <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> - <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" /> - <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" /> - <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" /> - <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> - <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> - <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" /> - <uses-permission android:name="android.permission.DELETE_PACKAGES" /> - <uses-permission android:name="android.permission.GET_APP_OPS_STATS" /> - <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> + package="com.android.frameworks.servicestests"> + + <uses-permission android:name="android.permission.READ_LOGS"/> + <uses-permission android:name="android.permission.ACCESS_VR_MANAGER"/> + <uses-permission android:name="android.permission.ACCOUNT_MANAGER"/> + <uses-permission android:name="android.permission.WRITE_SETTINGS"/> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/> + <uses-permission android:name="android.permission.READ_PHONE_STATE"/> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> + <uses-permission android:name="android.permission.BROADCAST_STICKY"/> + <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"/> + <uses-permission android:name="android.permission.MANAGE_APP_TOKENS"/> + <uses-permission android:name="android.permission.WAKE_LOCK"/> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/> + <uses-permission android:name="android.permission.REAL_GET_TASKS"/> + <uses-permission android:name="android.permission.GET_DETAILED_TASKS"/> + <uses-permission android:name="android.permission.REORDER_TASKS"/> + <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY"/> + <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"/> + <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY"/> + <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> + <uses-permission android:name="android.permission.MANAGE_USERS"/> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> + <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS"/> + <uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/> + <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> + <uses-permission android:name="android.permission.INTERNET"/> + <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/> + <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/> + <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"/> + <uses-permission android:name="android.permission.INSTALL_PACKAGES"/> + <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/> + <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> + <uses-permission android:name="android.permission.DELETE_PACKAGES"/> + <uses-permission android:name="android.permission.GET_APP_OPS_STATS"/> + <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS"/> <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES"/> - <uses-permission android:name="android.permission.DEVICE_POWER" /> - <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" /> - <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" /> - <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> - <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> - <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> - <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - <uses-permission android:name="android.permission.STORAGE_INTERNAL" /> - <uses-permission android:name="android.permission.WATCH_APPOPS" /> + <uses-permission android:name="android.permission.DEVICE_POWER"/> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> + <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> + <uses-permission android:name="android.permission.STATUS_BAR_SERVICE"/> + <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER"/> + <uses-permission android:name="android.permission.READ_FRAME_BUFFER"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> + <uses-permission android:name="android.permission.STORAGE_INTERNAL"/> + <uses-permission android:name="android.permission.WATCH_APPOPS"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.SUSPEND_APPS"/> - <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" /> - <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/> <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/> - <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> - <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> - <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> + <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/> + <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/> <uses-permission android:name="android.permission.HARDWARE_TEST"/> <uses-permission android:name="android.permission.BLUETOOTH"/> - <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" /> - <uses-permission android:name="android.permission.DUMP" /> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> + <uses-permission android:name="android.permission.DUMP"/> <uses-permission android:name="android.permission.READ_DREAM_STATE"/> <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/> <!-- Uses API introduced in O (26) --> <uses-sdk android:minSdkVersion="1" - android:targetSdkVersion="26"/> + android:targetSdkVersion="26"/> <application android:testOnly="true"> - <uses-library android:name="android.test.runner" /> + <uses-library android:name="android.test.runner"/> <service android:name="com.android.server.accounts.TestAccountType1AuthenticatorService" - android:exported="false"> + android:exported="false"> <intent-filter> - <action android:name="android.accounts.AccountAuthenticator" /> + <action android:name="android.accounts.AccountAuthenticator"/> </intent-filter> <meta-data android:name="android.accounts.AccountAuthenticator" - android:resource="@xml/test_account_type1_authenticator" /> + android:resource="@xml/test_account_type1_authenticator"/> </service> <service android:name="com.android.server.accounts.TestAccountType2AuthenticatorService" - android:exported="false"> + android:exported="false"> <intent-filter> - <action android:name="android.accounts.AccountAuthenticator" /> + <action android:name="android.accounts.AccountAuthenticator"/> </intent-filter> <meta-data android:name="android.accounts.AccountAuthenticator" - android:resource="@xml/test_account_type2_authenticator" /> + android:resource="@xml/test_account_type2_authenticator"/> </service> <receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver" - android:permission="android.permission.BIND_DEVICE_ADMIN"> + android:permission="android.permission.BIND_DEVICE_ADMIN"> <meta-data android:name="android.app.device_admin" - android:resource="@xml/device_admin_sample" /> + android:resource="@xml/device_admin_sample"/> <intent-filter> - <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> </intent-filter> </receiver> <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin1" - android:permission="android.permission.BIND_DEVICE_ADMIN"> + android:permission="android.permission.BIND_DEVICE_ADMIN"> <meta-data android:name="android.app.device_admin" - android:resource="@xml/device_admin_sample" /> + android:resource="@xml/device_admin_sample"/> <intent-filter> - <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> </intent-filter> </receiver> <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin2" - android:permission="android.permission.BIND_DEVICE_ADMIN"> + android:permission="android.permission.BIND_DEVICE_ADMIN"> <meta-data android:name="android.app.device_admin" - android:resource="@xml/device_admin_sample" /> + android:resource="@xml/device_admin_sample"/> <intent-filter> - <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> </intent-filter> </receiver> <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin3" - android:permission="android.permission.BIND_DEVICE_ADMIN"> + android:permission="android.permission.BIND_DEVICE_ADMIN"> <meta-data android:name="android.app.device_admin" - android:resource="@xml/device_admin_sample" /> + android:resource="@xml/device_admin_sample"/> <intent-filter> - <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> </intent-filter> </receiver> <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$AdminNoPerm"> <meta-data android:name="android.app.device_admin" - android:resource="@xml/device_admin_sample" /> + android:resource="@xml/device_admin_sample"/> <intent-filter> - <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> </intent-filter> </receiver> <service android:name="com.android.server.job.MockPriorityJobService" - android:permission="android.permission.BIND_JOB_SERVICE" /> + android:permission="android.permission.BIND_JOB_SERVICE"/> - <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity" /> - <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" /> - <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" /> + <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity"/> + <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2"/> + <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3"/> <activity android:name="com.android.server.pm.ShortcutTestActivity" - android:enabled="true" android:exported="true" /> + android:enabled="true" + android:exported="true"/> <activity android:name="com.android.server.pm.SuspendedDetailsActivity" - android:enabled="true" - android:permission="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS"> + android:enabled="true" + android:permission="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS"> <intent-filter> - <action android:name="android.intent.action.SHOW_SUSPENDED_APP_DETAILS" /> - <category android:name="android.intent.category.DEFAULT" /> + <action android:name="android.intent.action.SHOW_SUSPENDED_APP_DETAILS"/> + <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> - <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity" /> - <activity android:name="com.android.server.adb.AdbDebuggingManagerTestActivity" /> + <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity"/> + <activity android:name="com.android.server.adb.AdbDebuggingManagerTestActivity"/> <activity-alias android:name="a.ShortcutEnabled" - android:targetActivity="com.android.server.pm.ShortcutTestActivity" - android:enabled="true" android:exported="true"> + android:targetActivity="com.android.server.pm.ShortcutTestActivity" + android:enabled="true" + android:exported="true"> </activity-alias> <activity-alias android:name="a.ShortcutDisabled" - android:targetActivity="com.android.server.pm.ShortcutTestActivity" - android:enabled="false" android:exported="true"> - <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/> + android:targetActivity="com.android.server.pm.ShortcutTestActivity" + android:enabled="false" + android:exported="true"> + <meta-data android:name="android.app.shortcuts" + android:resource="@xml/shortcut_5"/> </activity-alias> <activity-alias android:name="a.ShortcutUnexported" - android:targetActivity="com.android.server.pm.ShortcutTestActivity" - android:enabled="true" android:exported="false"> - <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/> + android:targetActivity="com.android.server.pm.ShortcutTestActivity" + android:enabled="true" + android:exported="false"> + <meta-data android:name="android.app.shortcuts" + android:resource="@xml/shortcut_5"/> </activity-alias> <activity-alias android:name="a.Shortcut1" - android:targetActivity="com.android.server.pm.ShortcutTestActivity" - android:enabled="true" android:exported="true"> - <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_1"/> + android:targetActivity="com.android.server.pm.ShortcutTestActivity" + android:enabled="true" + android:exported="true"> + <meta-data android:name="android.app.shortcuts" + android:resource="@xml/shortcut_1"/> </activity-alias> <activity-alias android:name="a.ShortcutConfigActivity" - android:targetActivity="com.android.server.pm.ShortcutTestActivity"> + android:targetActivity="com.android.server.pm.ShortcutTestActivity"> <intent-filter> - <action android:name="android.intent.action.CREATE_SHORTCUT" /> + <action android:name="android.intent.action.CREATE_SHORTCUT"/> </intent-filter> </activity-alias> <activity-alias android:name="a.DisabledMain" - android:targetActivity="com.android.server.pm.ShortcutTestActivity" - android:enabled="false" android:exported="true"> + android:targetActivity="com.android.server.pm.ShortcutTestActivity" + android:enabled="false" + android:exported="true"> <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.LAUNCHER" /> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.DEFAULT"/> + <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity-alias> <activity-alias android:name="a.UnexportedMain" - android:targetActivity="com.android.server.pm.ShortcutTestActivity" - android:enabled="true" android:exported="false"> + android:targetActivity="com.android.server.pm.ShortcutTestActivity" + android:enabled="true" + android:exported="false"> <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.LAUNCHER" /> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.DEFAULT"/> + <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity-alias> <receiver android:name="com.android.server.appwidget.DummyAppWidget"> <intent-filter> - <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> + <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> </intent-filter> <meta-data android:name="android.appwidget.provider" - android:resource="@xml/dummy_appwidget_info" /> + android:resource="@xml/dummy_appwidget_info"/> </receiver> </application> - <instrumentation - android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.frameworks.servicestests" - android:label="Frameworks Services Tests" /> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.servicestests" + android:label="Frameworks Services Tests"/> </manifest> diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index 3ad905476190..d2925263125d 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -64,10 +64,13 @@ import com.android.internal.statusbar.IStatusBarService; import org.junit.Before; import org.junit.Test; +import org.mockito.AdditionalMatchers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Random; + @SmallTest public class BiometricServiceTest { @@ -347,9 +350,19 @@ public class BiometricServiceTest { } @Test - public void testAuthenticate_happyPathWithoutConfirmation() throws Exception { + public void testAuthenticate_happyPathWithoutConfirmation_strongBiometric() throws Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG); + testAuthenticate_happyPathWithoutConfirmation(true /* isStrongBiometric */); + } + + @Test + public void testAuthenticate_happyPathWithoutConfirmation_weakBiometric() throws Exception { + setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK); + testAuthenticate_happyPathWithoutConfirmation(false /* isStrongBiometric */); + } + private void testAuthenticate_happyPathWithoutConfirmation(boolean isStrongBiometric) + throws Exception { // Start testing the happy path invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, null /* authenticators */); @@ -397,9 +410,11 @@ public class BiometricServiceTest { anyLong() /* sessionId */); // Hardware authenticated + final byte[] HAT = generateRandomHAT(); mBiometricService.mInternalReceiver.onAuthenticationSucceeded( false /* requireConfirmation */, - new byte[69] /* HAT */); + HAT, + isStrongBiometric /* isStrongBiometric */); waitForIdle(); // Waiting for SystemUI to send dismissed callback assertEquals(mBiometricService.mCurrentAuthSession.mState, @@ -413,7 +428,11 @@ public class BiometricServiceTest { null /* credentialAttestation */); waitForIdle(); // HAT sent to keystore - verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class)); + if (isStrongBiometric) { + verify(mBiometricService.mKeyStore).addAuthToken(AdditionalMatchers.aryEq(HAT)); + } else { + verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class)); + } // Send onAuthenticated to client verify(mReceiver1).onAuthenticationSucceeded( BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC); @@ -447,16 +466,29 @@ public class BiometricServiceTest { } @Test - public void testAuthenticate_happyPathWithConfirmation() throws Exception { + public void testAuthenticate_happyPathWithConfirmation_strongBiometric() throws Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + testAuthenticate_happyPathWithConfirmation(true /* isStrongBiometric */); + } + + @Test + public void testAuthenticate_happyPathWithConfirmation_weakBiometric() throws Exception { + setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_WEAK); + testAuthenticate_happyPathWithConfirmation(false /* isStrongBiometric */); + } + + private void testAuthenticate_happyPathWithConfirmation(boolean isStrongBiometric) + throws Exception { invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, true /* requireConfirmation */, null /* authenticators */); // Test authentication succeeded goes to PENDING_CONFIRMATION and that the HAT is not // sent to KeyStore yet + final byte[] HAT = generateRandomHAT(); mBiometricService.mInternalReceiver.onAuthenticationSucceeded( true /* requireConfirmation */, - new byte[69] /* HAT */); + HAT, + isStrongBiometric /* isStrongBiometric */); waitForIdle(); // Waiting for SystemUI to send confirmation callback assertEquals(mBiometricService.mCurrentAuthSession.mState, @@ -468,7 +500,11 @@ public class BiometricServiceTest { BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED, null /* credentialAttestation */); waitForIdle(); - verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class)); + if (isStrongBiometric) { + verify(mBiometricService.mKeyStore).addAuthToken(AdditionalMatchers.aryEq(HAT)); + } else { + verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class)); + } verify(mReceiver1).onAuthenticationSucceeded( BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC); } @@ -909,7 +945,8 @@ public class BiometricServiceTest { mBiometricService.mInternalReceiver.onAuthenticationSucceeded( true /* requireConfirmation */, - new byte[69] /* HAT */); + new byte[69] /* HAT */, + true /* isStrongBiometric */); mBiometricService.mInternalReceiver.onDialogDismissed( BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */); waitForIdle(); @@ -927,6 +964,7 @@ public class BiometricServiceTest { eq(BiometricAuthenticator.TYPE_FACE), eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED), eq(0 /* vendorCode */)); + verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class)); assertNull(mBiometricService.mCurrentAuthSession); } @@ -1238,20 +1276,6 @@ public class BiometricServiceTest { mFingerprintAuthenticator); } - @Test(expected = IllegalStateException.class) - public void testRegistrationWithUnsupportedStrength_throwsIllegalStateException() - throws Exception { - mBiometricService = new BiometricService(mContext, mInjector); - mBiometricService.onStart(); - - // Only STRONG and WEAK are supported. Let's enforce that CONVENIENCE cannot be - // registered. If there is a compelling reason, we can remove this constraint. - mBiometricService.mImpl.registerAuthenticator( - 0 /* id */, 2 /* modality */, - Authenticators.BIOMETRIC_CONVENIENCE /* strength */, - mFingerprintAuthenticator); - } - @Test(expected = IllegalArgumentException.class) public void testRegistrationWithNullAuthenticator_throwsIllegalArgumentException() throws Exception { @@ -1508,4 +1532,13 @@ public class BiometricServiceTest { private static void waitForIdle() { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } + + private byte[] generateRandomHAT() { + byte[] HAT = new byte[69]; + Random random = new Random(); + random.nextBytes(HAT); + return HAT; + } + + } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 7c6ac1717651..baf551e756e8 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -3205,6 +3205,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) .thenReturn(true); setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM); + when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null); mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; } @@ -3246,6 +3247,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) .thenReturn(true); setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM); + when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null); mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; } @@ -3617,14 +3619,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) .thenReturn(true); - when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true); + when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false); when(getServices().userManager.getProfileParent(DpmMockContext.CALLER_USER_HANDLE)) .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); when(getServices().userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE, true)).thenReturn(true); setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE); - mContext.binder.callingUid = DpmMockContext.CALLER_UID; + mContext.binder.callingUid = DpmMockContext.ANOTHER_UID; } public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_primaryUser() diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/Android.bp b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/Android.bp new file mode 100644 index 000000000000..eb1a2921136d --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/Android.bp @@ -0,0 +1,37 @@ +android_test { + name: "TunerResourceManagerTests", + + // Include all test java files. + srcs: [ + "*.java", + ], + + static_libs: [ + "frameworks-base-testutils", + "services.core", + "services.devicepolicy", + "guava", + "androidx.test.core", + "androidx.test.ext.truth", + "androidx.test.runner", + "androidx.test.rules", + "mockito-target-minus-junit4", + "platform-test-annotations", + "truth-prebuilt", + "testables", + "testng", + "servicestests-utils", + "service-permission", + + ], + + libs: [ + "android.test.mock", + "android.test.base", + "android.test.runner", + ], + + platform_apis: true, + test_suites: ["general-tests", "device-tests"], + compile_multilib: "both", +}
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/AndroidManifest.xml b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/AndroidManifest.xml new file mode 100644 index 000000000000..9fa100d5a041 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.tv.tunerresourcemanager"> + <application> + <uses-library android:name="android.test.runner" /> + </application> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.tv.tunerresourcemanager" + android:label="Tuner Resource Manager Test Cases"> + </instrumentation> +</manifest> + + + diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/AndroidTest.xml b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/AndroidTest.xml new file mode 100644 index 000000000000..e3ea6a06a115 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/AndroidTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 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. +--> +<configuration description="Runs Tests for Tuner Resource Manager"> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="TunerResourceManagerTests.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="framework-base-presubmit" /> + <option name="test-tag" value="TunerResourceManagerTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.server.tv.tunerresourcemanager" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration>
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java index 155c6ddd4507..fcbd5072ae35 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -34,6 +34,7 @@ import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerResourceManager; import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -54,6 +55,7 @@ import java.util.Map; * Tests for {@link TunerResourceManagerService} class. */ @SmallTest +@Presubmit @RunWith(JUnit4.class) public class TunerResourceManagerServiceTest { private static final String TAG = "TunerResourceManagerServiceTest"; diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java index ab5665ba99fc..2ff178eba3e1 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java @@ -18,6 +18,7 @@ package com.android.server.tv.tunerresourcemanager; import static com.google.common.truth.Truth.assertThat; import android.media.tv.TvInputService; +import android.platform.test.annotations.Presubmit; import android.util.Slog; import androidx.test.filters.SmallTest; @@ -36,6 +37,7 @@ import java.nio.charset.StandardCharsets; * Tests for {@link UseCasePriorityHints} class. */ @SmallTest +@Presubmit @RunWith(JUnit4.class) public class UseCasePriorityHintsTest { private static final String TAG = "UseCasePriorityHintsTest"; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java index d7fc97c8722d..2578ca892520 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.ActivityManager; @@ -33,6 +34,7 @@ import android.app.NotificationChannel; import android.app.PendingIntent; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ShortcutInfo; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; @@ -110,8 +112,10 @@ public class BubbleCheckerTest extends UiServiceTestCase { void setUpShortcutBubble(boolean isValid) { when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID); - when(mShortcutHelper.hasValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle)) - .thenReturn(isValid); + ShortcutInfo info = mock(ShortcutInfo.class); + when(info.getId()).thenReturn(SHORTCUT_ID); + when(mShortcutHelper.getValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle)) + .thenReturn(isValid ? info : null); when(mBubbleMetadata.getIntent()).thenReturn(null); } 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..d2417f9d10c5 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; @@ -6579,13 +6372,41 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { ShortcutInfo si = mock(ShortcutInfo.class); when(si.getShortLabel()).thenReturn("Hello"); + when(si.isLongLived()).thenReturn(true); when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si)); List<ConversationChannelWrapper> conversations = mBinderService.getConversationsForPackage(PKG_P, mUid).getList(); assertEquals(si, conversations.get(0).getShortcutInfo()); assertEquals(si, conversations.get(1).getShortcutInfo()); + } + @Test + public void testGetConversationsForPackage_shortcut_notLongLived() throws Exception { + mService.setPreferencesHelper(mPreferencesHelper); + ArrayList<ConversationChannelWrapper> convos = new ArrayList<>(); + ConversationChannelWrapper convo1 = new ConversationChannelWrapper(); + NotificationChannel channel1 = new NotificationChannel("a", "a", 1); + channel1.setConversationId("parent1", "convo 1"); + convo1.setNotificationChannel(channel1); + convos.add(convo1); + + ConversationChannelWrapper convo2 = new ConversationChannelWrapper(); + NotificationChannel channel2 = new NotificationChannel("b", "b", 1); + channel2.setConversationId("parent1", "convo 2"); + convo2.setNotificationChannel(channel2); + convos.add(convo2); + when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos); + + ShortcutInfo si = mock(ShortcutInfo.class); + when(si.getShortLabel()).thenReturn("Hello"); + when(si.isLongLived()).thenReturn(false); + when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si)); + + List<ConversationChannelWrapper> conversations = + mBinderService.getConversationsForPackage(PKG_P, mUid).getList(); + assertNull(conversations.get(0).getShortcutInfo()); + assertNull(conversations.get(1).getShortcutInfo()); } @Test 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..ed400ea8e992 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -82,9 +82,8 @@ import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.service.voice.IVoiceInteractionSession; import android.view.Gravity; -import android.window.IWindowContainer; -import android.view.SurfaceControl; import android.window.ITaskOrganizer; +import android.window.IWindowContainer; import androidx.test.filters.SmallTest; @@ -975,7 +974,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // Move activity to split-screen-primary stack and make sure it has the focus. TestSplitOrganizer splitOrg = new TestSplitOrganizer(mService, top.getDisplayId()); - splitOrg.mPrimary.addChild(top.getRootTask(), 0 /* index */); + top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM); top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent"); // Activity must landed on split-screen-secondary when launch adjacent. @@ -1001,8 +1000,8 @@ public class ActivityStarterTests extends ActivityTestsBase { static class TestSplitOrganizer extends ITaskOrganizer.Stub { final ActivityTaskManagerService mService; - TaskTile mPrimary; - TaskTile mSecondary; + Task mPrimary; + Task mSecondary; boolean mInSplit = false; int mDisplayId; TestSplitOrganizer(ActivityTaskManagerService service, int displayId) { @@ -1014,16 +1013,16 @@ public class ActivityStarterTests extends ActivityTestsBase { WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); IWindowContainer primary = mService.mTaskOrganizerController.createRootTask( displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token; - mPrimary = TaskTile.forToken(primary.asBinder()); + mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask(); IWindowContainer secondary = mService.mTaskOrganizerController.createRootTask( displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token; - mSecondary = TaskTile.forToken(secondary.asBinder()); + mSecondary = WindowContainer.fromBinder(secondary.asBinder()).asTask(); } @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) { @@ -1042,7 +1041,7 @@ public class ActivityStarterTests extends ActivityTestsBase { for (int i = dc.getStackCount() - 1; i >= 0; --i) { if (!WindowConfiguration.isSplitScreenWindowingMode( dc.getStackAt(i).getWindowingMode())) { - mSecondary.addChild(dc.getStackAt(i), 0); + dc.getStackAt(i).reparent(mSecondary, POSITION_BOTTOM); } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 4634e2d71573..716369d49036 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -464,7 +464,7 @@ class ActivityTestsBase extends SystemServiceTestsBase { ActivityStack build() { final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId(); final ActivityStack stack = mDisplay.createStackUnchecked(mWindowingMode, - mActivityType, stackId, mOnTop, mInfo, mIntent); + mActivityType, stackId, mOnTop, mInfo, mIntent, false /* createdByOrganizer */); final ActivityStackSupervisor supervisor = mRootWindowContainer.mStackSupervisor; if (mCreateActivity) { 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/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index bfb126f7052c..db7bce4c8753 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -19,10 +19,12 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -147,6 +149,61 @@ public class InsetsStateControllerTest extends WindowTestsBase { } @Test + public void testStripForDispatch_belowIme() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); + + getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null); + + assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME)); + } + + @Test + public void testStripForDispatch_aboveIme() { + final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + + getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null); + + assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME)); + } + + @Test + public void testStripForDispatch_childWindow_altFocusable() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + + final WindowState child = createWindow(app, TYPE_APPLICATION, "child"); + child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; + + final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); + + // IME cannot be the IME target. + ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE; + + getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null); + + assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME)); + } + + @Test + public void testStripForDispatch_childWindow_splitScreen() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + + final WindowState child = createWindow(app, TYPE_APPLICATION, "child"); + child.mAttrs.flags |= FLAG_NOT_FOCUSABLE; + child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + + final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); + + // IME cannot be the IME target. + ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE; + + getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null); + + assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME)); + } + + @Test public void testImeForDispatch() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 406affc27b0a..f242989ab885 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -403,11 +403,11 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { wallpapers.get(0).getConfiguration().orientation); // Wallpaper's transform state is controlled by home, so the invocation should be no-op. - wallpaperWindowToken.clearFixedRotationTransform(); + wallpaperWindowToken.finishFixedRotationTransform(); assertTrue(wallpaperWindowToken.hasFixedRotationTransform()); // Wallpaper's transform state should be cleared with home. - homeActivity.clearFixedRotationTransform(); + homeActivity.finishFixedRotationTransform(); assertFalse(wallpaperWindowToken.hasFixedRotationTransform()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 0ef25824df2a..e841e434ea82 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -788,6 +788,22 @@ public class RootActivityContainerTests extends ActivityTestsBase { } @Test + public void testGetValidLaunchStackOnDisplayWithCandidateRootTask() { + // Create a root task with an activity on secondary display. + final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mService, 300, + 600).build(); + final Task task = new ActivityTestsBase.StackBuilder(mRootWindowContainer).setDisplay( + secondaryDisplay).build(); + final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder(mService) + .setTask(task).build(); + + // Make sure the root task is valid and can be reused on default display. + final ActivityStack stack = mRootWindowContainer.getValidLaunchStackOnDisplay( + DEFAULT_DISPLAY, activity, task, null, null); + assertEquals(task, stack); + } + + @Test public void testSwitchUser_missingHomeRootTask() { doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedStack(); 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..ed635ce3f69e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -37,6 +37,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -60,7 +61,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 +103,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 +117,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 +131,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 +144,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 +159,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 +179,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 +206,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 +219,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 @@ -348,45 +348,62 @@ public class TaskOrganizerTests extends WindowTestsBase { assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType); DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY); - List<TaskTile> infos = getTaskTiles(dc); + List<Task> infos = getTasksCreatedByOrganizer(dc); assertEquals(2, infos.size()); assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token)); - infos = getTaskTiles(dc); + infos = getTasksCreatedByOrganizer(dc); assertEquals(1, infos.size()); assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, infos.get(0).getWindowingMode()); } @Test public void testTileAddRemoveChild() { + ITaskOrganizer listener = new ITaskOrganizer.Stub() { + @Override + public void onTaskAppeared(RunningTaskInfo taskInfo) { } + + @Override + public void onTaskVanished(RunningTaskInfo container) { } + + @Override + public void onTaskInfoChanged(RunningTaskInfo info) throws RemoteException { + } + }; + mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); final ActivityStack stack = createTaskStackOnDisplay( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent); assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode()); - TaskTile tile1 = TaskTile.forToken(info1.token.asBinder()); - tile1.addChild(stack, 0 /* index */); + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertEquals(info1.configuration.windowConfiguration.getWindowingMode(), stack.getWindowingMode()); // Info should reflect new membership - List<TaskTile> tiles = getTaskTiles(mDisplayContent); - info1 = tiles.get(0).getTaskInfo(); + List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent); + info1 = infos.get(0).getTaskInfo(); assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType); // Children inherit configuration Rect newSize = new Rect(10, 10, 300, 300); - Configuration c = new Configuration(tile1.getRequestedOverrideConfiguration()); + Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask(); + Configuration c = new Configuration(task1.getRequestedOverrideConfiguration()); c.windowConfiguration.setBounds(newSize); doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any()); - tile1.onRequestedOverrideConfigurationChanged(c); + task1.onRequestedOverrideConfigurationChanged(c); assertEquals(newSize, stack.getBounds()); - tile1.removeChild(stack); + wct = new WindowContainerTransaction(); + wct.reparent(stack.mRemoteToken, null, true /* onTop */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode()); - tiles = getTaskTiles(mDisplayContent); - info1 = tiles.get(0).getTaskInfo(); + infos = getTasksCreatedByOrganizer(mDisplayContent); + info1 = infos.get(0).getTaskInfo(); assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType); } @@ -396,10 +413,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 { @@ -416,8 +433,10 @@ public class TaskOrganizerTests extends WindowTestsBase { final ActivityStack stack = createTaskStackOnDisplay( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent); - TaskTile tile1 = TaskTile.forToken(info1.token.asBinder()); - tile1.addChild(stack, 0 /* index */); + Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask(); + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertTrue(called[0]); assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType); @@ -425,19 +444,24 @@ public class TaskOrganizerTests extends WindowTestsBase { called[0] = false; final ActivityStack stack2 = createTaskStackOnDisplay( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent); - tile1.addChild(stack2, 0 /* index */); + wct = new WindowContainerTransaction(); + wct.reparent(stack2.mRemoteToken, info1.token, true /* onTop */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertTrue(called[0]); assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType); lastReportedTiles.clear(); called[0] = false; - mDisplayContent.positionStackAtTop(stack, false /* includingParents */); + task1.positionChildAt(POSITION_TOP, stack, false /* includingParents */); assertTrue(called[0]); assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType); lastReportedTiles.clear(); called[0] = false; - tile1.removeAllChildren(); + wct = new WindowContainerTransaction(); + wct.reparent(stack.mRemoteToken, null, true /* onTop */); + wct.reparent(stack2.mRemoteToken, null, true /* onTop */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertTrue(called[0]); assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType); } @@ -447,10 +471,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) { @@ -458,9 +482,11 @@ public class TaskOrganizerTests extends WindowTestsBase { } }; mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer( + listener, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer( listener, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( - mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); @@ -523,13 +549,11 @@ public class TaskOrganizerTests extends WindowTestsBase { lastReportedTiles.get(info1.token.asBinder()).topActivityType); } - private List<TaskTile> getTaskTiles(DisplayContent dc) { - ArrayList<TaskTile> out = new ArrayList<>(); + private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) { + ArrayList<Task> out = new ArrayList<>(); for (int i = dc.getStackCount() - 1; i >= 0; --i) { - final TaskTile t = dc.getStackAt(i).asTile(); - if (t != null) { - out.add(t); - } + final Task t = dc.getStackAt(i); + if (t.mCreatedByOrganizer) out.add(t); } return out; } @@ -667,11 +691,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/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index a25acae3c036..56c19a47b68a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -27,6 +27,7 @@ import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.util.DisplayMetrics.DENSITY_DEFAULT; import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED; @@ -151,7 +152,7 @@ public class TaskRecordTests extends ActivityTestsBase { assertFalse(factory.mCreated); Task.create(mService, 0 /*taskId*/, 0 /*activityType*/, - new ActivityInfo(), new Intent()); + new ActivityInfo(), new Intent(), false /* createdByOrganizer */); assertTrue(factory.mCreated); } finally { @@ -939,6 +940,20 @@ public class TaskRecordTests extends ActivityTestsBase { verify(persister, never()).saveTask(same(task), any()); } + @Test + public void testNotSpecifyOrientationByFloatingTask() { + final Task task = getTestTask(); + final ActivityRecord activity = task.getTopMostActivity(); + final WindowContainer<?> taskContainer = task.getParent(); + activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskContainer.getOrientation()); + + task.setWindowingMode(WINDOWING_MODE_PINNED); + + assertEquals(SCREEN_ORIENTATION_UNSET, taskContainer.getOrientation()); + } + private Task getTestTask() { final ActivityStack stack = new StackBuilder(mRootWindowContainer).build(); return stack.getBottomMostTask(); @@ -1000,7 +1015,7 @@ public class TaskRecordTests extends ActivityTestsBase { @Override Task create(ActivityTaskManagerService service, int taskId, int activityType, - ActivityInfo info, Intent intent) { + ActivityInfo info, Intent intent, boolean createdByOrganizer) { mCreated = true; return null; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 85e4a1668a35..e95ccab38960 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -251,11 +251,9 @@ public class WindowStateTests extends WindowTestsBase { // b/145812508: special legacy use-case for transparent/translucent windows. appWindow.mAttrs.format = PixelFormat.TRANSPARENT; - appWindow.mAttrs.alpha = 0; assertTrue(appWindow.canBeImeTarget()); appWindow.mAttrs.format = PixelFormat.OPAQUE; - appWindow.mAttrs.alpha = 1; appWindow.mAttrs.flags &= ~FLAG_ALT_FOCUSABLE_IM; assertFalse(appWindow.canBeImeTarget()); appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE; diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index 3196758996be..00cb6dc7400d 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -207,7 +207,13 @@ public class SoundTriggerService extends SystemService { @Override public void onBootPhase(int phase) { - if (PHASE_SYSTEM_SERVICES_READY == phase) { + Slog.d(TAG, "onBootPhase: " + phase + " : " + isSafeMode()); + if (PHASE_DEVICE_SPECIFIC_SERVICES_READY == phase) { + if (isSafeMode()) { + Slog.w(TAG, "not enabling SoundTriggerService in safe mode"); + return; + } + initSoundTriggerHelper(); mLocalSoundTriggerService.setSoundTriggerHelper(mSoundTriggerHelper); } else if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) { diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java index 488ee78f6230..47bf14892ccb 100644 --- a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java +++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java @@ -23,6 +23,9 @@ import android.util.Log; import com.android.server.wm.ActivityMetricsLaunchObserver; +import java.io.StringWriter; +import java.io.PrintWriter; + /** * A validator to check the correctness of event sequence during app startup. * @@ -100,7 +103,8 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { @Override public void onIntentStarted(@NonNull Intent intent, long timestampNs) { if (state == State.UNKNOWN) { - Log.wtf(TAG, "IntentStarted during UNKNOWN." + intent); + logWarningWithStackTrace( + String.format("IntentStarted during UNKNOWN. " + intent)); incAccIntentStartedEvents(); return; } @@ -110,7 +114,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { state != State.ACTIVITY_CANCELLED && state != State.ACTIVITY_FINISHED && state != State.REPORT_FULLY_DRAWN) { - Log.wtf(TAG, + logWarningWithStackTrace( String.format("Cannot transition from %s to %s", state, State.INTENT_STARTED)); incAccIntentStartedEvents(); incAccIntentStartedEvents(); @@ -124,12 +128,12 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { @Override public void onIntentFailed() { if (state == State.UNKNOWN) { - Log.wtf(TAG, "IntentFailed during UNKNOWN."); + logWarningWithStackTrace(String.format("onIntentFailed during UNKNOWN.")); decAccIntentStartedEvents(); return; } if (state != State.INTENT_STARTED) { - Log.wtf(TAG, + logWarningWithStackTrace( String.format("Cannot transition from %s to %s", state, State.INTENT_FAILED)); incAccIntentStartedEvents(); return; @@ -143,11 +147,12 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity, @Temperature int temperature) { if (state == State.UNKNOWN) { - Log.wtf(TAG, "onActivityLaunched during UNKNOWN."); + logWarningWithStackTrace( + String.format("onActivityLaunched during UNKNOWN.")); return; } if (state != State.INTENT_STARTED) { - Log.wtf(TAG, + logWarningWithStackTrace( String.format("Cannot transition from %s to %s", state, State.ACTIVITY_LAUNCHED)); incAccIntentStartedEvents(); return; @@ -160,12 +165,13 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { @Override public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) { if (state == State.UNKNOWN) { - Log.wtf(TAG, "onActivityLaunchCancelled during UNKNOWN."); + logWarningWithStackTrace( + String.format("onActivityLaunchCancelled during UNKNOWN.")); decAccIntentStartedEvents(); return; } if (state != State.ACTIVITY_LAUNCHED) { - Log.wtf(TAG, + logWarningWithStackTrace( String.format("Cannot transition from %s to %s", state, State.ACTIVITY_CANCELLED)); incAccIntentStartedEvents(); return; @@ -179,13 +185,14 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity, long timestampNs) { if (state == State.UNKNOWN) { - Log.wtf(TAG, "onActivityLaunchFinished during UNKNOWN."); + logWarningWithStackTrace( + String.format("onActivityLaunchFinished during UNKNOWN.")); decAccIntentStartedEvents(); return; } if (state != State.ACTIVITY_LAUNCHED) { - Log.wtf(TAG, + logWarningWithStackTrace( String.format("Cannot transition from %s to %s", state, State.ACTIVITY_FINISHED)); incAccIntentStartedEvents(); return; @@ -199,7 +206,8 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity, long timestampNs) { if (state == State.UNKNOWN) { - Log.wtf(TAG, "onReportFullyDrawn during UNKNOWN."); + logWarningWithStackTrace( + String.format("onReportFullyDrawn during UNKNOWN.")); return; } if (state == State.INIT) { @@ -207,7 +215,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { } if (state != State.ACTIVITY_FINISHED) { - Log.wtf(TAG, + logWarningWithStackTrace( String.format("Cannot transition from %s to %s", state, State.REPORT_FULLY_DRAWN)); return; } @@ -252,4 +260,11 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { Log.i(TAG, String.format("dec AccIntentStartedEvents to %d", accIntentStartedEvents)); } + + private void logWarningWithStackTrace(String log) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + new Throwable("EventSequenceValidator#getStackTrace").printStackTrace(pw); + Log.w(TAG, String.format("%s\n%s", log, sw)); + } } diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 0dca006f37c0..ffd25c08a8ba 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -2633,6 +2633,7 @@ public abstract class ConnectionService extends Service { * @param request Details about the incoming call. * @return The {@code Connection} object to satisfy this call, or {@code null} to * not handle the call. + * @hide */ public @Nullable Conference onCreateIncomingConference( @Nullable PhoneAccountHandle connectionManagerPhoneAccount, @@ -2717,6 +2718,7 @@ public abstract class ConnectionService extends Service { * @param connectionManagerPhoneAccount See description at * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. * @param request The incoming connection request. + * @hide */ public void onCreateIncomingConferenceFailed( @Nullable PhoneAccountHandle connectionManagerPhoneAccount, @@ -2737,6 +2739,7 @@ public abstract class ConnectionService extends Service { * @param connectionManagerPhoneAccount See description at * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. * @param request The outgoing connection request. + * @hide */ public void onCreateOutgoingConferenceFailed( @Nullable PhoneAccountHandle connectionManagerPhoneAccount, @@ -2805,6 +2808,7 @@ public abstract class ConnectionService extends Service { * @param request Details about the outgoing call. * @return The {@code Conference} object to satisfy this call, or the result of an invocation * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call. + * @hide */ public @Nullable Conference onCreateOutgoingConference( @Nullable PhoneAccountHandle connectionManagerPhoneAccount, diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 0b331744d922..7e02966779a2 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -442,16 +442,40 @@ public final class TelephonyPermissions { // NOTE(b/73308711): If an app has one of the following AppOps bits explicitly revoked, they // will be denied access, even if they have another permission and AppOps bit if needed. - // First, check if we can read the phone state and the SDK version is below R. + // First, check if the SDK version is below R + boolean preR = false; try { ApplicationInfo info = context.getPackageManager().getApplicationInfoAsUser( callingPackage, 0, UserHandle.getUserHandleForUid(Binder.getCallingUid())); - if (info.targetSdkVersion <= Build.VERSION_CODES.Q) { + preR = info.targetSdkVersion <= Build.VERSION_CODES.Q; + } catch (PackageManager.NameNotFoundException nameNotFoundException) { + } + if (preR) { + // SDK < R allows READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or carrier privilege + try { return checkReadPhoneState( context, subId, pid, uid, callingPackage, callingFeatureId, message); + } catch (SecurityException readPhoneStateException) { + } + } else { + // SDK >= R allows READ_PRIVILEGED_PHONE_STATE or carrier privilege + try { + context.enforcePermission( + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message); + // Skip checking for runtime permission since caller has privileged permission + return true; + } catch (SecurityException readPrivilegedPhoneStateException) { + if (SubscriptionManager.isValidSubscriptionId(subId)) { + try { + enforceCarrierPrivilege(context, subId, uid, message); + // Skip checking for runtime permission since caller has carrier privilege + return true; + } catch (SecurityException carrierPrivilegeException) { + } + } } - } catch (SecurityException | PackageManager.NameNotFoundException e) { } + // Can be read with READ_SMS too. try { context.enforcePermission(android.Manifest.permission.READ_SMS, pid, uid, message); diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java index 1c92705b9422..d00049c1ebe5 100644 --- a/telephony/java/android/telephony/CellSignalStrengthCdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java @@ -314,6 +314,8 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements /** * Get the signal strength as dBm + * + * @return min(CDMA RSSI, EVDO RSSI) of the measured cell. */ @Override public int getDbm() { diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index 76d2df918423..9d55f109f751 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -145,6 +145,8 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P /** * Get the signal strength as dBm. + * + * @return the RSSI of the measured cell. */ @Override public int getDbm() { 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/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 2f9a1c8ade0d..f444b77b738e 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -509,16 +509,68 @@ public class AppLaunch extends InstrumentationTestCase { for (int i = 0; i < IORAP_COMPILE_CMD_TIMEOUT; ++i) { IorapCompilationStatus status = waitForIorapCompiled(appPkgName); if (status == IorapCompilationStatus.COMPLETE) { + Log.v(TAG, "compileAppForIorap: success"); + logDumpsysIorapd(appPkgName); return true; } else if (status == IorapCompilationStatus.INSUFFICIENT_TRACES) { + Log.e(TAG, "compileAppForIorap: failed due to insufficient traces"); + logDumpsysIorapd(appPkgName); return false; } // else INCOMPLETE. keep asking iorapd if it's done yet. sleep(1000); } + Log.e(TAG, "compileAppForIorap: failed due to timeout"); + logDumpsysIorapd(appPkgName); return false; } + /** Save the contents of $(adb shell dumpsys iorapd) to the launch_logs directory. */ + private void logDumpsysIorapd(String packageName) throws IOException { + InstrumentationTestRunner instrumentation = + (InstrumentationTestRunner)getInstrumentation(); + Bundle args = instrumentation.getArguments(); + + String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY); + + // Root directory for applaunch file to log the app launch output + // Will be useful in case of simpleperf command is used + File launchRootDir = null; + if (null != launchDirectory && !launchDirectory.isEmpty()) { + launchRootDir = new File(launchDirectory); + if (!launchRootDir.exists() && !launchRootDir.mkdirs()) { + throw new IOException("Unable to create the destination directory " + + launchRootDir + ". Try disabling selinux."); + } + } else { + Log.w(TAG, "logDumpsysIorapd: Missing launch-directory arg"); + return; + } + + File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY); + + if (!launchSubDir.exists() && !launchSubDir.mkdirs()) { + throw new IOException("Unable to create the lauch file sub directory " + + launchSubDir + ". Try disabling selinux."); + } + String path = "iorapd_dumpsys_" + packageName + "_" + System.nanoTime() + ".txt"; + File file = new File(launchSubDir, path); + try (FileOutputStream outputStream = new FileOutputStream(file); + BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(outputStream)); + ParcelFileDescriptor result = getInstrumentation().getUiAutomation(). + executeShellCommand(IORAP_DUMPSYS_CMD); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( + new FileInputStream(result.getFileDescriptor())))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + writer.write(line + "\n"); + } + } + + Log.v(TAG, "logDumpsysIorapd: Saved to file: " + path); + } + enum IorapCompilationStatus { INCOMPLETE, COMPLETE, @@ -715,7 +767,7 @@ public class AppLaunch extends InstrumentationTestCase { .executeShellCommand(String.format("setprop iorapd.readahead.enable %b", enable)); getInstrumentation().getUiAutomation() .executeShellCommand("start iorapd"); - sleep(2000); // give enough time for iorapd to start back up. + sleep(3000); // give enough time for iorapd to start back up. if (enable) { mIorapStatus = IorapStatus.ENABLED; diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java index 42908bef6178..35a6c26ec73f 100644 --- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java +++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java @@ -167,7 +167,7 @@ public class DummyBlobData { final byte[] actualBytes = new byte[lengthBytes]; try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream( - session.openWrite(0L, 0L))) { + session.openRead())) { read(in, actualBytes, offsetBytes, lengthBytes); } @@ -190,7 +190,7 @@ public class DummyBlobData { long offsetBytes, long lengthBytes) throws Exception { final byte[] actualDigest; try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream( - session.openWrite(0L, 0L))) { + session.openRead())) { actualDigest = createSha256Digest(in, offsetBytes, lengthBytes); } 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/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml index 269cec1ccca9..7b85cc84f1f5 100644 --- a/tests/RollbackTest/RollbackTest.xml +++ b/tests/RollbackTest/RollbackTest.xml @@ -23,6 +23,10 @@ <option name="run-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es package "com.google.android.gms.platformconfigurator" --es user '\\*' --esa flags "ModuleConfig__versioned_immediate_commit_packages" --esa types "bytes" --esa values "Cm5vdGFwYWNrYWdlOgA=" com.google.android.gms" /> <option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package "com.google.android.gms.platformconfigurator" --es user '\*' --esa flag "ModuleConfig__immediate_commit_packages" com.google.android.gms" /> <option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package "com.google.android.gms.platformconfigurator" --es user '\*' --esa flag "ModuleConfig__versioned_immediate_commit_packages" com.google.android.gms" /> + <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" /> + <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" /> + <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" /> + <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" /> </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.tests.rollback" /> diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index 5a92d6849434..cab8b4258bc8 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -75,6 +75,12 @@ public class RollbackTest { private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS = "enable_rollback_timeout"; + private static boolean hasRollbackInclude(List<RollbackInfo> rollbacks, String packageName) { + return rollbacks.stream().anyMatch( + ri -> ri.getPackages().stream().anyMatch( + pri -> packageName.equals(pri.getPackageName()))); + } + /** * Test basic rollbacks. */ @@ -113,18 +119,14 @@ public class RollbackTest { // Uninstall TestApp.A Uninstall.packages(TestApp.A); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); - // TODO: There is currently a race condition between when the app is - // uninstalled and when rollback manager deletes the rollback. Fix it - // so that's not the case! for (int i = 0; i < 5; ++i) { - RollbackInfo rollback = getUniqueRollbackInfoForPackage( - rm.getRecentlyCommittedRollbacks(), TestApp.A); - if (rollback != null) { + if (hasRollbackInclude(rm.getRecentlyCommittedRollbacks(), TestApp.A)) { Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect."); Thread.sleep(1000); } } + assertThat(hasRollbackInclude(rm.getRecentlyCommittedRollbacks(), TestApp.A)).isFalse(); // The app should not be available for rollback. waitForUnavailableRollback(TestApp.A); 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/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java index 548af0c54b03..498cb7c1c710 100644 --- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java +++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java @@ -18,7 +18,6 @@ package com.google.android.test.windowinsetstests; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; - import static java.lang.Math.max; import static java.lang.Math.min; @@ -31,6 +30,7 @@ import android.content.Context; import android.graphics.Insets; import android.os.Bundle; import android.util.AttributeSet; +import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; @@ -44,11 +44,11 @@ import android.view.WindowInsetsController.OnControllableInsetsChangedListener; import android.view.animation.LinearInterpolator; import android.widget.LinearLayout; -import androidx.appcompat.app.AppCompatActivity; - import java.util.ArrayList; import java.util.List; +import androidx.appcompat.app.AppCompatActivity; + public class WindowInsetsActivity extends AppCompatActivity { private View mRoot; @@ -191,6 +191,40 @@ public class WindowInsetsActivity extends AppCompatActivity { mTransitions.forEach(it -> it.onFinish(animation)); } }); + + findViewById(R.id.floating_action_button).setOnClickListener( + v -> v.getWindowInsetsController().controlWindowInsetsAnimation(ime(), -1, + new LinearInterpolator(), null /* cancellationSignal */, + new WindowInsetsAnimationControlListener() { + @Override + public void onReady( + WindowInsetsAnimationController controller, + int types) { + ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); + anim.setDuration(1500); + anim.addUpdateListener(animation + -> controller.setInsetsAndAlpha( + controller.getShownStateInsets(), + (float) animation.getAnimatedValue(), + anim.getAnimatedFraction())); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + controller.finish(true); + } + }); + anim.start(); + } + + @Override + public void onCancelled(WindowInsetsAnimationController controller) { + } + + @Override + public void onFinished(WindowInsetsAnimationController controller) { + } + })); } @Override @@ -200,57 +234,6 @@ public class WindowInsetsActivity extends AppCompatActivity { getWindow().getDecorView().post(() -> getWindow().setDecorFitsSystemWindows(false)); } - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - getWindow().getInsetsController().addOnControllableInsetsChangedListener( - new OnControllableInsetsChangedListener() { - - boolean hasControl = false; - @Override - public void onControllableInsetsChanged(WindowInsetsController controller, - int types) { - if ((types & ime()) != 0 && !hasControl) { - hasControl = true; - controller.controlWindowInsetsAnimation(ime(), -1, - new LinearInterpolator(), null /* cancellationSignal */, - new WindowInsetsAnimationControlListener() { - @Override - public void onReady( - WindowInsetsAnimationController controller, - int types) { - ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); - anim.setDuration(1500); - anim.addUpdateListener(animation - -> controller.setInsetsAndAlpha( - controller.getShownStateInsets(), - (float) animation.getAnimatedValue(), - anim.getAnimatedFraction())); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - controller.finish(true); - } - }); - anim.start(); - } - - @Override - public void onFinished( - WindowInsetsAnimationController controller) { - } - - @Override - public void onCancelled( - WindowInsetsAnimationController controller) { - } - }); - } - } - }); - } - static class Transition { private int mEndBottom; private int mStartBottom; diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index b2e8c378d96d..916c33981171 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -463,7 +463,9 @@ public class NetworkCapabilitiesTest { nc1.setSSID(TEST_SSID); nc2.combineCapabilities(nc1); - assertTrue(TEST_SSID.equals(nc2.getSsid())); + if (isAtLeastR()) { + assertTrue(TEST_SSID.equals(nc2.getSsid())); + } // Because they now have the same SSID, the following call should not throw nc2.combineCapabilities(nc1); @@ -601,12 +603,16 @@ public class NetworkCapabilitiesTest { // from nc2. assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(TEST_SSID.equals(nc2.getSsid())); + if (isAtLeastR()) { + assertTrue(TEST_SSID.equals(nc2.getSsid())); + } nc1.setSSID(DIFFERENT_TEST_SSID); nc2.set(nc1); assertEquals(nc1, nc2); - assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid())); + if (isAtLeastR()) { + assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid())); + } nc1.setUids(uidRange(10, 13)); nc2.set(nc1); // Overwrites, as opposed to combineCapabilities diff --git a/tools/stats_log_api_gen/.clang-format b/tools/stats_log_api_gen/.clang-format new file mode 100644 index 000000000000..cead3a079435 --- /dev/null +++ b/tools/stats_log_api_gen/.clang-format @@ -0,0 +1,17 @@ +BasedOnStyle: Google +AllowShortIfStatementsOnASingleLine: true +AllowShortFunctionsOnASingleLine: false +AllowShortLoopsOnASingleLine: true +BinPackArguments: true +BinPackParameters: true +ColumnLimit: 100 +CommentPragmas: NOLINT:.* +ContinuationIndentWidth: 8 +DerivePointerAlignment: false +IndentWidth: 4 +PointerAlignment: Left +TabWidth: 4 +AccessModifierOffset: -4 +IncludeCategories: + - Regex: '^"Log\.h"' + Priority: -1 diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index 47eb63e823e7..bf390935455a 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -15,11 +15,13 @@ */ #include "Collation.h" -#include "frameworks/base/cmds/statsd/src/atoms.pb.h" #include <stdio.h> + #include <map> +#include "frameworks/base/cmds/statsd/src/atoms.pb.h" + namespace android { namespace stats_log_api_gen { @@ -32,55 +34,47 @@ using std::map; const bool dbg = false; - // // AtomDecl class // -AtomDecl::AtomDecl() - :code(0), - name() -{ +AtomDecl::AtomDecl() : code(0), name() { } -AtomDecl::AtomDecl(const AtomDecl &that) - : code(that.code), - name(that.name), - message(that.message), - fields(that.fields), - fieldNumberToAnnotations(that.fieldNumberToAnnotations), - primaryFields(that.primaryFields), - exclusiveField(that.exclusiveField), - defaultState(that.defaultState), - resetState(that.resetState), - nested(that.nested), - uidField(that.uidField), - whitelisted(that.whitelisted) {} - -AtomDecl::AtomDecl(int c, const string& n, const string& m) - :code(c), - name(n), - message(m) -{ +AtomDecl::AtomDecl(const AtomDecl& that) + : code(that.code), + name(that.name), + message(that.message), + fields(that.fields), + fieldNumberToAnnotations(that.fieldNumberToAnnotations), + primaryFields(that.primaryFields), + exclusiveField(that.exclusiveField), + defaultState(that.defaultState), + resetState(that.resetState), + nested(that.nested), + uidField(that.uidField), + whitelisted(that.whitelisted), + truncateTimestamp(that.truncateTimestamp) { } -AtomDecl::~AtomDecl() -{ +AtomDecl::AtomDecl(int c, const string& n, const string& m) : code(c), name(n), message(m) { } +AtomDecl::~AtomDecl() { +} /** - * Print an error message for a FieldDescriptor, including the file name and line number. + * Print an error message for a FieldDescriptor, including the file name and + * line number. */ -static void -print_error(const FieldDescriptor* field, const char* format, ...) -{ +static void print_error(const FieldDescriptor* field, const char* format, ...) { const Descriptor* message = field->containing_type(); const FileDescriptor* file = message->file(); SourceLocation loc; if (field->GetSourceLocation(&loc)) { - // TODO: this will work if we can figure out how to pass --include_source_info to protoc + // TODO: this will work if we can figure out how to pass + // --include_source_info to protoc fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line); } else { fprintf(stderr, "%s: ", file->name().c_str()); @@ -88,15 +82,13 @@ print_error(const FieldDescriptor* field, const char* format, ...) va_list args; va_start(args, format); vfprintf(stderr, format, args); - va_end (args); + va_end(args); } /** * Convert a protobuf type into a java type. */ -static java_type_t -java_type(const FieldDescriptor* field) -{ +static java_type_t java_type(const FieldDescriptor* field) { int protoType = field->type(); switch (protoType) { case FieldDescriptor::TYPE_DOUBLE: @@ -121,12 +113,10 @@ java_type(const FieldDescriptor* field) return JAVA_TYPE_UNKNOWN; case FieldDescriptor::TYPE_MESSAGE: // TODO: not the final package name - if (field->message_type()->full_name() == - "android.os.statsd.AttributionNode") { - return JAVA_TYPE_ATTRIBUTION_CHAIN; - } else if (field->message_type()->full_name() == - "android.os.statsd.KeyValuePair") { - return JAVA_TYPE_KEY_VALUE_PAIR; + if (field->message_type()->full_name() == "android.os.statsd.AttributionNode") { + return JAVA_TYPE_ATTRIBUTION_CHAIN; + } else if (field->message_type()->full_name() == "android.os.statsd.KeyValuePair") { + return JAVA_TYPE_KEY_VALUE_PAIR; } else if (field->options().GetExtension(os::statsd::log_mode) == os::statsd::LogMode::MODE_BYTES) { return JAVA_TYPE_BYTE_ARRAY; @@ -155,307 +145,298 @@ java_type(const FieldDescriptor* field) /** * Gather the enums info. */ -void collate_enums(const EnumDescriptor &enumDescriptor, AtomField *atomField) { +void collate_enums(const EnumDescriptor& enumDescriptor, AtomField* atomField) { for (int i = 0; i < enumDescriptor.value_count(); i++) { atomField->enumValues[enumDescriptor.value(i)->number()] = - enumDescriptor.value(i)->name().c_str(); + enumDescriptor.value(i)->name().c_str(); } } static void addAnnotationToAtomDecl(AtomDecl* atomDecl, const int fieldNumber, - const int annotationId, const AnnotationType annotationType, - const AnnotationValue annotationValue) { + const int annotationId, const AnnotationType annotationType, + const AnnotationValue annotationValue) { if (dbg) { - printf(" Adding annotation to %s: [%d] = {id: %d, type: %d}\n", - atomDecl->name.c_str(), fieldNumber, annotationId, annotationType); + printf(" Adding annotation to %s: [%d] = {id: %d, type: %d}\n", atomDecl->name.c_str(), + fieldNumber, annotationId, annotationType); } - atomDecl->fieldNumberToAnnotations[fieldNumber].insert(make_shared<Annotation>( - annotationId, atomDecl->code, annotationType, annotationValue)); + atomDecl->fieldNumberToAnnotations[fieldNumber].insert( + make_shared<Annotation>(annotationId, atomDecl->code, annotationType, annotationValue)); } -/** - * Gather the info about an atom proto. - */ -int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, - vector<java_type_t> *signature) { - - int errorCount = 0; - - // Build a sorted list of the fields. Descriptor has them in source file - // order. - map<int, const FieldDescriptor *> fields; - for (int j = 0; j < atom->field_count(); j++) { - const FieldDescriptor *field = atom->field(j); - fields[field->number()] = field; - } - - // Check that the parameters start at 1 and go up sequentially. - int expectedNumber = 1; - for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); - it != fields.end(); it++) { - const int number = it->first; - const FieldDescriptor *field = it->second; - if (number != expectedNumber) { - print_error(field, - "Fields must be numbered consecutively starting at 1:" - " '%s' is %d but should be %d\n", - field->name().c_str(), number, expectedNumber); - errorCount++; - expectedNumber = number; - continue; - } - expectedNumber++; - } - - // Check that only allowed types are present. Remove any invalid ones. - for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); - it != fields.end(); it++) { - const FieldDescriptor *field = it->second; - bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) == - os::statsd::LogMode::MODE_BYTES; - - java_type_t javaType = java_type(field); - - if (javaType == JAVA_TYPE_UNKNOWN) { - print_error(field, "Unkown type for field: %s\n", field->name().c_str()); - errorCount++; - continue; - } else if (javaType == JAVA_TYPE_OBJECT && - atomDecl->code < PULL_ATOM_START_ID) { - // Allow attribution chain, but only at position 1. - print_error(field, - "Message type not allowed for field in pushed atoms: %s\n", - field->name().c_str()); - errorCount++; - continue; - } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) { - print_error(field, "Raw bytes type not allowed for field: %s\n", - field->name().c_str()); - errorCount++; - continue; - } - - if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) { - print_error(field, "Cannot mark field %s as bytes.\n", - field->name().c_str()); - errorCount++; - continue; - } - - // Doubles are not supported yet. - if (javaType == JAVA_TYPE_DOUBLE) { - print_error(field, "Doubles are not supported in atoms. Please change field %s to float\n", - field->name().c_str()); - errorCount++; - continue; - } - - if (field->is_repeated() && - !(javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || javaType == JAVA_TYPE_KEY_VALUE_PAIR)) { - print_error(field, - "Repeated fields are not supported in atoms. Please make field %s not " - "repeated.\n", - field->name().c_str()); - errorCount++; - continue; - } - } - - // Check that if there's an attribution chain, it's at position 1. - for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); - it != fields.end(); it++) { - int number = it->first; - if (number != 1) { - const FieldDescriptor *field = it->second; - java_type_t javaType = java_type(field); - if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { - print_error( - field, - "AttributionChain fields must have field id 1, in message: '%s'\n", - atom->name().c_str()); - errorCount++; - } - } - } - - // Build the type signature and the atom data. - for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); - it != fields.end(); it++) { - const FieldDescriptor *field = it->second; - java_type_t javaType = java_type(field); - bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) == - os::statsd::LogMode::MODE_BYTES; - - AtomField atField(field->name(), javaType); - // Generate signature for pushed atoms - if (atomDecl->code < PULL_ATOM_START_ID) { - if (javaType == JAVA_TYPE_ENUM) { - // All enums are treated as ints when it comes to function signatures. - signature->push_back(JAVA_TYPE_INT); - } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) { - signature->push_back(JAVA_TYPE_BYTE_ARRAY); - } else { - signature->push_back(javaType); - } - } - if (javaType == JAVA_TYPE_ENUM) { - // All enums are treated as ints when it comes to function signatures. - collate_enums(*field->enum_type(), &atField); - } - atomDecl->fields.push_back(atField); +static int collate_field_annotations(AtomDecl* atomDecl, const FieldDescriptor* field, + const int fieldNumber, const java_type_t& javaType) { + int errorCount = 0; if (field->options().HasExtension(os::statsd::state_field_option)) { const int option = field->options().GetExtension(os::statsd::state_field_option).option(); if (option != STATE_OPTION_UNSET) { - addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_STATE_OPTION, - ANNOTATION_TYPE_INT, AnnotationValue(option)); + addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_STATE_OPTION, + ANNOTATION_TYPE_INT, AnnotationValue(option)); } if (option == STATE_OPTION_PRIMARY) { - if (javaType == JAVA_TYPE_UNKNOWN || - javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || - javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) { - print_error( - field, - "Invalid primary state field: '%s'\n", - atom->name().c_str()); + if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || + javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) { + print_error(field, "Invalid primary state field: '%s'\n", + atomDecl->message.c_str()); errorCount++; - continue; } - atomDecl->primaryFields.push_back(it->first); - + atomDecl->primaryFields.push_back(fieldNumber); } if (option == STATE_OPTION_PRIMARY_FIELD_FIRST_UID) { if (javaType != JAVA_TYPE_ATTRIBUTION_CHAIN) { - print_error( - field, - "PRIMARY_FIELD_FIRST_UID annotation is only for AttributionChains: '%s'\n", - atom->name().c_str()); + print_error(field, + "PRIMARY_FIELD_FIRST_UID annotation is only for AttributionChains: " + "'%s'\n", + atomDecl->message.c_str()); errorCount++; - continue; } else { atomDecl->primaryFields.push_back(FIRST_UID_IN_CHAIN_ID); } } if (option == STATE_OPTION_EXCLUSIVE) { - if (javaType == JAVA_TYPE_UNKNOWN || - javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || - javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) { - print_error( - field, - "Invalid exclusive state field: '%s'\n", - atom->name().c_str()); + if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || + javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) { + print_error(field, "Invalid exclusive state field: '%s'\n", + atomDecl->message.c_str()); errorCount++; - continue; } if (atomDecl->exclusiveField == 0) { - atomDecl->exclusiveField = it->first; + atomDecl->exclusiveField = fieldNumber; } else { - print_error( - field, - "Cannot have more than one exclusive state field in an atom: '%s'\n", - atom->name().c_str()); + print_error(field, + "Cannot have more than one exclusive state field in an " + "atom: '%s'\n", + atomDecl->message.c_str()); errorCount++; - continue; } if (field->options() .GetExtension(os::statsd::state_field_option) .has_default_state_value()) { - const int defaultState = - field->options().GetExtension(os::statsd::state_field_option) - .default_state_value(); + const int defaultState = field->options() + .GetExtension(os::statsd::state_field_option) + .default_state_value(); atomDecl->defaultState = defaultState; - addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_DEFAULT_STATE, - ANNOTATION_TYPE_INT, AnnotationValue(defaultState)); + addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_DEFAULT_STATE, + ANNOTATION_TYPE_INT, AnnotationValue(defaultState)); } - if (field->options().GetExtension(os::statsd::state_field_option) - .has_reset_state_value()) { - const int resetState = field->options() + if (field->options() .GetExtension(os::statsd::state_field_option) - .reset_state_value(); + .has_reset_state_value()) { + const int resetState = field->options() + .GetExtension(os::statsd::state_field_option) + .reset_state_value(); atomDecl->resetState = resetState; - addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_RESET_STATE, - ANNOTATION_TYPE_INT, AnnotationValue(resetState)); + addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_RESET_STATE, + ANNOTATION_TYPE_INT, AnnotationValue(resetState)); } - if (field->options().GetExtension(os::statsd::state_field_option) - .has_nested()) { + if (field->options().GetExtension(os::statsd::state_field_option).has_nested()) { const bool nested = field->options().GetExtension(os::statsd::state_field_option).nested(); atomDecl->nested = nested; - addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_STATE_NESTED, - ANNOTATION_TYPE_BOOL, AnnotationValue(nested)); + addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_STATE_NESTED, + ANNOTATION_TYPE_BOOL, AnnotationValue(nested)); } } - } + if (field->options().GetExtension(os::statsd::is_uid) == true) { if (javaType != JAVA_TYPE_INT) { - print_error( - field, - "is_uid annotation can only be applied to int32 fields: '%s'\n", - atom->name().c_str()); + print_error(field, "is_uid annotation can only be applied to int32 fields: '%s'\n", + atomDecl->message.c_str()); errorCount++; - continue; } if (atomDecl->uidField == 0) { - atomDecl->uidField = it->first; + atomDecl->uidField = fieldNumber; - addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_IS_UID, - ANNOTATION_TYPE_BOOL, AnnotationValue(true)); + addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_IS_UID, + ANNOTATION_TYPE_BOOL, AnnotationValue(true)); } else { - print_error( - field, - "Cannot have more than one field in an atom with is_uid annotation: '%s'\n", - atom->name().c_str()); + print_error(field, + "Cannot have more than one field in an atom with is_uid " + "annotation: '%s'\n", + atomDecl->message.c_str()); errorCount++; + } + } + + return errorCount; +} + +/** + * Gather the info about an atom proto. + */ +int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>* signature) { + int errorCount = 0; + + // Build a sorted list of the fields. Descriptor has them in source file + // order. + map<int, const FieldDescriptor*> fields; + for (int j = 0; j < atom->field_count(); j++) { + const FieldDescriptor* field = atom->field(j); + fields[field->number()] = field; + } + + // Check that the parameters start at 1 and go up sequentially. + int expectedNumber = 1; + for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end(); + it++) { + const int number = it->first; + const FieldDescriptor* field = it->second; + if (number != expectedNumber) { + print_error(field, + "Fields must be numbered consecutively starting at 1:" + " '%s' is %d but should be %d\n", + field->name().c_str(), number, expectedNumber); + errorCount++; + expectedNumber = number; continue; } + expectedNumber++; } - } - return errorCount; + // Check that only allowed types are present. Remove any invalid ones. + for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end(); + it++) { + const FieldDescriptor* field = it->second; + bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) == + os::statsd::LogMode::MODE_BYTES; + + java_type_t javaType = java_type(field); + + if (javaType == JAVA_TYPE_UNKNOWN) { + print_error(field, "Unknown type for field: %s\n", field->name().c_str()); + errorCount++; + continue; + } else if (javaType == JAVA_TYPE_OBJECT && atomDecl->code < PULL_ATOM_START_ID) { + // Allow attribution chain, but only at position 1. + print_error(field, "Message type not allowed for field in pushed atoms: %s\n", + field->name().c_str()); + errorCount++; + continue; + } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) { + print_error(field, "Raw bytes type not allowed for field: %s\n", field->name().c_str()); + errorCount++; + continue; + } + + if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) { + print_error(field, "Cannot mark field %s as bytes.\n", field->name().c_str()); + errorCount++; + continue; + } + + // Doubles are not supported yet. + if (javaType == JAVA_TYPE_DOUBLE) { + print_error(field, + "Doubles are not supported in atoms. Please change field %s " + "to float\n", + field->name().c_str()); + errorCount++; + continue; + } + + if (field->is_repeated() && + !(javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || javaType == JAVA_TYPE_KEY_VALUE_PAIR)) { + print_error(field, + "Repeated fields are not supported in atoms. Please make " + "field %s not " + "repeated.\n", + field->name().c_str()); + errorCount++; + continue; + } + } + + // Check that if there's an attribution chain, it's at position 1. + for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end(); + it++) { + int number = it->first; + if (number != 1) { + const FieldDescriptor* field = it->second; + java_type_t javaType = java_type(field); + if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { + print_error(field, + "AttributionChain fields must have field id 1, in message: '%s'\n", + atom->name().c_str()); + errorCount++; + } + } + } + + // Build the type signature and the atom data. + for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end(); + it++) { + const FieldDescriptor* field = it->second; + java_type_t javaType = java_type(field); + bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) == + os::statsd::LogMode::MODE_BYTES; + + AtomField atField(field->name(), javaType); + + if (javaType == JAVA_TYPE_ENUM) { + // All enums are treated as ints when it comes to function signatures. + collate_enums(*field->enum_type(), &atField); + } + + // Generate signature for pushed atoms + if (atomDecl->code < PULL_ATOM_START_ID) { + if (javaType == JAVA_TYPE_ENUM) { + // All enums are treated as ints when it comes to function signatures. + signature->push_back(JAVA_TYPE_INT); + } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) { + signature->push_back(JAVA_TYPE_BYTE_ARRAY); + } else { + signature->push_back(javaType); + } + } + + atomDecl->fields.push_back(atField); + + errorCount += collate_field_annotations(atomDecl, field, it->first, javaType); + } + + return errorCount; } -// This function flattens the fields of the AttributionNode proto in an Atom proto and generates -// the corresponding atom decl and signature. -bool get_non_chained_node(const Descriptor *atom, AtomDecl *atomDecl, - vector<java_type_t> *signature) { +// This function flattens the fields of the AttributionNode proto in an Atom +// proto and generates the corresponding atom decl and signature. +bool get_non_chained_node(const Descriptor* atom, AtomDecl* atomDecl, + vector<java_type_t>* signature) { // Build a sorted list of the fields. Descriptor has them in source file // order. - map<int, const FieldDescriptor *> fields; + map<int, const FieldDescriptor*> fields; for (int j = 0; j < atom->field_count(); j++) { - const FieldDescriptor *field = atom->field(j); + const FieldDescriptor* field = atom->field(j); fields[field->number()] = field; } AtomDecl attributionDecl; vector<java_type_t> attributionSignature; - collate_atom(android::os::statsd::AttributionNode::descriptor(), - &attributionDecl, &attributionSignature); + collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl, + &attributionSignature); // Build the type signature and the atom data. bool has_attribution_node = false; - for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); - it != fields.end(); it++) { - const FieldDescriptor *field = it->second; + for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end(); + it++) { + const FieldDescriptor* field = it->second; java_type_t javaType = java_type(field); if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { - atomDecl->fields.insert( - atomDecl->fields.end(), - attributionDecl.fields.begin(), attributionDecl.fields.end()); - signature->insert( - signature->end(), - attributionSignature.begin(), attributionSignature.end()); + atomDecl->fields.insert(atomDecl->fields.end(), attributionDecl.fields.begin(), + attributionDecl.fields.end()); + signature->insert(signature->end(), attributionSignature.begin(), + attributionSignature.end()); has_attribution_node = true; } else { @@ -473,118 +454,129 @@ bool get_non_chained_node(const Descriptor *atom, AtomDecl *atomDecl, return has_attribution_node; } -static void populateFieldNumberToAnnotations( - const AtomDecl& atomDecl, - FieldNumberToAnnotations* fieldNumberToAnnotations) { +static void populateFieldNumberToAnnotations(const AtomDecl& atomDecl, + FieldNumberToAnnotations* fieldNumberToAnnotations) { for (FieldNumberToAnnotations::const_iterator it = atomDecl.fieldNumberToAnnotations.begin(); - it != atomDecl.fieldNumberToAnnotations.end(); it++) { + it != atomDecl.fieldNumberToAnnotations.end(); it++) { const int fieldNumber = it->first; const set<shared_ptr<Annotation>>& insertAnnotationsSource = it->second; set<shared_ptr<Annotation>>& insertAnnotationsTarget = (*fieldNumberToAnnotations)[fieldNumber]; - insertAnnotationsTarget.insert( - insertAnnotationsSource.begin(), - insertAnnotationsSource.end()); + insertAnnotationsTarget.insert(insertAnnotationsSource.begin(), + insertAnnotationsSource.end()); } } /** * Gather the info about the atoms. */ -int collate_atoms(const Descriptor *descriptor, const string& moduleName, Atoms *atoms) { - int errorCount = 0; - - int maxPushedAtomId = 2; - for (int i = 0; i < descriptor->field_count(); i++) { - const FieldDescriptor *atomField = descriptor->field(i); - - if (moduleName != DEFAULT_MODULE_NAME) { - const int moduleCount = atomField->options().ExtensionSize(os::statsd::module); - int j; - for (j = 0; j < moduleCount; ++j) { - const string atomModuleName = atomField->options().GetExtension(os::statsd::module, j); - if (atomModuleName == moduleName) { - break; +int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* atoms) { + int errorCount = 0; + + int maxPushedAtomId = 2; + for (int i = 0; i < descriptor->field_count(); i++) { + const FieldDescriptor* atomField = descriptor->field(i); + + if (moduleName != DEFAULT_MODULE_NAME) { + const int moduleCount = atomField->options().ExtensionSize(os::statsd::module); + int j; + for (j = 0; j < moduleCount; ++j) { + const string atomModuleName = + atomField->options().GetExtension(os::statsd::module, j); + if (atomModuleName == moduleName) { + break; + } } - } - // This atom is not in the module we're interested in; skip it. - if (moduleCount == j) { - if (dbg) { - printf(" Skipping %s (%d)\n", atomField->name().c_str(), atomField->number()); + // This atom is not in the module we're interested in; skip it. + if (moduleCount == j) { + if (dbg) { + printf(" Skipping %s (%d)\n", atomField->name().c_str(), atomField->number()); + } + continue; } - continue; } - } - if (dbg) { - printf(" %s (%d)\n", atomField->name().c_str(), atomField->number()); - } + if (dbg) { + printf(" %s (%d)\n", atomField->name().c_str(), atomField->number()); + } - // StatsEvent only has one oneof, which contains only messages. Don't allow - // other types. - if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) { - print_error(atomField, - "Bad type for atom. StatsEvent can only have message type " - "fields: %s\n", - atomField->name().c_str()); - errorCount++; - continue; - } + // StatsEvent only has one oneof, which contains only messages. Don't allow + // other types. + if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) { + print_error(atomField, + "Bad type for atom. StatsEvent can only have message type " + "fields: %s\n", + atomField->name().c_str()); + errorCount++; + continue; + } - const Descriptor *atom = atomField->message_type(); - AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name()); + const Descriptor* atom = atomField->message_type(); + AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name()); - if (atomField->options().GetExtension(os::statsd::allow_from_any_uid) == true) { - atomDecl.whitelisted = true; - } + if (atomField->options().GetExtension(os::statsd::allow_from_any_uid) == true) { + atomDecl.whitelisted = true; + if (dbg) { + printf("%s is whitelisted\n", atomField->name().c_str()); + } + } - vector<java_type_t> signature; - errorCount += collate_atom(atom, &atomDecl, &signature); - if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) { - print_error(atomField, - "Cannot have a primary field without an exclusive field: %s\n", - atomField->name().c_str()); - errorCount++; - continue; - } + if (atomDecl.code < PULL_ATOM_START_ID && + atomField->options().GetExtension(os::statsd::truncate_timestamp)) { + addAnnotationToAtomDecl(&atomDecl, ATOM_ID_FIELD_NUMBER, + ANNOTATION_ID_TRUNCATE_TIMESTAMP, ANNOTATION_TYPE_BOOL, + AnnotationValue(true)); + if (dbg) { + printf("%s can have timestamp truncated\n", atomField->name().c_str()); + } + } - atoms->decls.insert(atomDecl); - FieldNumberToAnnotations& fieldNumberToAnnotations = atoms->signatureInfoMap[signature]; - populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations); + vector<java_type_t> signature; + errorCount += collate_atom(atom, &atomDecl, &signature); + if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) { + print_error(atomField, "Cannot have a primary field without an exclusive field: %s\n", + atomField->name().c_str()); + errorCount++; + continue; + } - AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name()); - vector<java_type_t> nonChainedSignature; - if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) { - atoms->non_chained_decls.insert(nonChainedAtomDecl); - FieldNumberToAnnotations& fieldNumberToAnnotations = - atoms->nonChainedSignatureInfoMap[nonChainedSignature]; + atoms->decls.insert(atomDecl); + FieldNumberToAnnotations& fieldNumberToAnnotations = atoms->signatureInfoMap[signature]; populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations); - } - if (atomDecl.code < PULL_ATOM_START_ID && atomDecl.code > maxPushedAtomId) { - maxPushedAtomId = atomDecl.code; + AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name()); + vector<java_type_t> nonChainedSignature; + if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) { + atoms->non_chained_decls.insert(nonChainedAtomDecl); + FieldNumberToAnnotations& fieldNumberToAnnotations = + atoms->nonChainedSignatureInfoMap[nonChainedSignature]; + populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations); + } + + if (atomDecl.code < PULL_ATOM_START_ID && atomDecl.code > maxPushedAtomId) { + maxPushedAtomId = atomDecl.code; + } } - } - - atoms->maxPushedAtomId = maxPushedAtomId; - - if (dbg) { - printf("signatures = [\n"); - for (map<vector<java_type_t>, FieldNumberToAnnotations>::const_iterator it = - atoms->signatureInfoMap.begin(); - it != atoms->signatureInfoMap.end(); it++) { - printf(" "); - for (vector<java_type_t>::const_iterator jt = it->first.begin(); - jt != it->first.end(); jt++){ - printf(" %d", (int)*jt); - } - printf("\n"); + + atoms->maxPushedAtomId = maxPushedAtomId; + + if (dbg) { + printf("signatures = [\n"); + for (map<vector<java_type_t>, FieldNumberToAnnotations>::const_iterator it = + atoms->signatureInfoMap.begin(); + it != atoms->signatureInfoMap.end(); it++) { + printf(" "); + for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end(); + jt++) { + printf(" %d", (int)*jt); + } + printf("\n"); + } + printf("]\n"); } - printf("]\n"); - } - return errorCount; + return errorCount; } } // namespace stats_log_api_gen diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h index c6dad1d07d89..2aedb216b9c1 100644 --- a/tools/stats_log_api_gen/Collation.h +++ b/tools/stats_log_api_gen/Collation.h @@ -17,24 +17,24 @@ #ifndef ANDROID_STATS_LOG_API_GEN_COLLATION_H #define ANDROID_STATS_LOG_API_GEN_COLLATION_H - #include <google/protobuf/descriptor.h> -#include "frameworks/base/cmds/statsd/src/atom_field_options.pb.h" +#include <map> #include <set> #include <vector> -#include <map> + +#include "frameworks/base/cmds/statsd/src/atom_field_options.pb.h" namespace android { namespace stats_log_api_gen { +using google::protobuf::Descriptor; +using google::protobuf::FieldDescriptor; using std::map; using std::set; using std::shared_ptr; using std::string; using std::vector; -using google::protobuf::Descriptor; -using google::protobuf::FieldDescriptor; const int PULL_ATOM_START_ID = 10000; @@ -52,26 +52,28 @@ const int STATE_OPTION_EXCLUSIVE = os::statsd::StateField::EXCLUSIVE_STATE; const int STATE_OPTION_PRIMARY_FIELD_FIRST_UID = os::statsd::StateField::PRIMARY_FIELD_FIRST_UID; const int STATE_OPTION_PRIMARY = os::statsd::StateField::PRIMARY_FIELD; +const int ATOM_ID_FIELD_NUMBER = -1; + const string DEFAULT_MODULE_NAME = "DEFAULT"; /** * The types for atom parameters. */ typedef enum { - JAVA_TYPE_UNKNOWN = 0, - - JAVA_TYPE_ATTRIBUTION_CHAIN = 1, - JAVA_TYPE_BOOLEAN = 2, - JAVA_TYPE_INT = 3, - JAVA_TYPE_LONG = 4, - JAVA_TYPE_FLOAT = 5, - JAVA_TYPE_DOUBLE = 6, - JAVA_TYPE_STRING = 7, - JAVA_TYPE_ENUM = 8, - JAVA_TYPE_KEY_VALUE_PAIR = 9, - - JAVA_TYPE_OBJECT = -1, - JAVA_TYPE_BYTE_ARRAY = -2, + JAVA_TYPE_UNKNOWN = 0, + + JAVA_TYPE_ATTRIBUTION_CHAIN = 1, + JAVA_TYPE_BOOLEAN = 2, + JAVA_TYPE_INT = 3, + JAVA_TYPE_LONG = 4, + JAVA_TYPE_FLOAT = 5, + JAVA_TYPE_DOUBLE = 6, + JAVA_TYPE_STRING = 7, + JAVA_TYPE_ENUM = 8, + JAVA_TYPE_KEY_VALUE_PAIR = 9, + + JAVA_TYPE_OBJECT = -1, + JAVA_TYPE_BYTE_ARRAY = -2, } java_type_t; enum AnnotationType { @@ -84,8 +86,10 @@ union AnnotationValue { int intValue; bool boolValue; - AnnotationValue(const int value): intValue(value) {} - AnnotationValue(const bool value): boolValue(value) {} + AnnotationValue(const int value) : intValue(value) { + } + AnnotationValue(const bool value) : boolValue(value) { + } }; struct Annotation { @@ -95,16 +99,18 @@ struct Annotation { AnnotationValue value; inline Annotation(unsigned char annotationId, int atomId, AnnotationType type, - AnnotationValue value): - annotationId(annotationId), atomId(atomId), type(type), value(value) {} - inline ~Annotation() {} + AnnotationValue value) + : annotationId(annotationId), atomId(atomId), type(type), value(value) { + } + inline ~Annotation() { + } inline bool operator<(const Annotation& that) const { return atomId == that.atomId ? annotationId < that.annotationId : atomId < that.atomId; } }; -using FieldNumberToAnnotations = map<int, set<shared_ptr<Annotation>>>; +using FieldNumberToAnnotations = map<int, set<shared_ptr<Annotation>>>; /** * The name and type for an atom field. @@ -113,16 +119,20 @@ struct AtomField { string name; java_type_t javaType; - // If the field is of type enum, the following map contains the list of enum values. + // If the field is of type enum, the following map contains the list of enum + // values. map<int /* numeric value */, string /* value name */> enumValues; - inline AtomField() :name(), javaType(JAVA_TYPE_UNKNOWN) {} - inline AtomField(const AtomField& that) :name(that.name), - javaType(that.javaType), - enumValues(that.enumValues) {} + inline AtomField() : name(), javaType(JAVA_TYPE_UNKNOWN) { + } + inline AtomField(const AtomField& that) + : name(that.name), javaType(that.javaType), enumValues(that.enumValues) { + } - inline AtomField(string n, java_type_t jt) :name(n), javaType(jt) {} - inline ~AtomField() {} + inline AtomField(string n, java_type_t jt) : name(n), javaType(jt) { + } + inline ~AtomField() { + } }; /** @@ -147,6 +157,8 @@ struct AtomDecl { bool whitelisted = false; + bool truncateTimestamp = false; + AtomDecl(); AtomDecl(const AtomDecl& that); AtomDecl(int code, const string& name, const string& message); @@ -169,10 +181,9 @@ struct Atoms { * Gather the information about the atoms. Returns the number of errors. */ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* atoms); -int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, vector<java_type_t> *signature); +int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>* signature); } // namespace stats_log_api_gen } // namespace android - -#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H +#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp index 4f66f68e6d8c..69447527efc8 100644 --- a/tools/stats_log_api_gen/atoms_info_writer.cpp +++ b/tools/stats_log_api_gen/atoms_info_writer.cpp @@ -15,12 +15,13 @@ */ #include "atoms_info_writer.h" -#include "utils.h" #include <map> #include <set> #include <vector> +#include "utils.h" + namespace android { namespace stats_log_api_gen { @@ -42,32 +43,27 @@ static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) { " const static std::set<int> " "kTruncatingTimestampAtomBlackList;\n"); fprintf(out, " const static std::map<int, int> kAtomsWithUidField;\n"); - fprintf(out, - " const static std::set<int> kAtomsWithAttributionChain;\n"); + fprintf(out, " const static std::set<int> kAtomsWithAttributionChain;\n"); fprintf(out, " const static std::map<int, StateAtomFieldOptions> " "kStateAtomsFieldOptions;\n"); - fprintf(out, - " const static std::set<int> kWhitelistedAtoms;\n"); + fprintf(out, " const static std::set<int> kWhitelistedAtoms;\n"); fprintf(out, "};\n"); fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", atoms.maxPushedAtomId); - } static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { - std::set<string> kTruncatingAtomNames = { - "mobile_radio_power_state_changed", - "audio_state_changed", - "call_state_changed", - "phone_signal_strength_changed", - "mobile_bytes_transfer_by_fg_bg", - "mobile_bytes_transfer" - }; + std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed", + "audio_state_changed", + "call_state_changed", + "phone_signal_strength_changed", + "mobile_bytes_transfer_by_fg_bg", + "mobile_bytes_transfer"}; fprintf(out, "const std::set<int> " "AtomsInfo::kTruncatingTimestampAtomBlackList = {\n"); - for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); - atom != atoms.decls.end(); atom++) { + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); + atom++) { if (kTruncatingAtomNames.find(atom->name) != kTruncatingAtomNames.end()) { const string constant = make_constant_name(atom->name); fprintf(out, " %d, // %s\n", atom->code, constant.c_str()); @@ -77,10 +73,9 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { fprintf(out, "};\n"); fprintf(out, "\n"); - fprintf(out, - "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n"); - for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); - atom != atoms.decls.end(); atom++) { + fprintf(out, "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n"); + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); + atom++) { for (vector<AtomField>::const_iterator field = atom->fields.begin(); field != atom->fields.end(); field++) { if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { @@ -94,10 +89,9 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { fprintf(out, "};\n"); fprintf(out, "\n"); - fprintf(out, - "const std::set<int> AtomsInfo::kWhitelistedAtoms = {\n"); - for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); - atom != atoms.decls.end(); atom++) { + fprintf(out, "const std::set<int> AtomsInfo::kWhitelistedAtoms = {\n"); + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); + atom++) { if (atom->whitelisted) { const string constant = make_constant_name(atom->name); fprintf(out, " %d, // %s\n", atom->code, constant.c_str()); @@ -109,8 +103,8 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { fprintf(out, "static std::map<int, int> getAtomUidField() {\n"); fprintf(out, " std::map<int, int> uidField;\n"); - for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); - atom != atoms.decls.end(); atom++) { + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); + atom++) { if (atom->uidField == 0) { continue; } @@ -118,8 +112,8 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { "\n // Adding uid field for atom " "(%d)%s\n", atom->code, atom->name.c_str()); - fprintf(out, " uidField[%d /* %s */] = %d;\n", - atom->code, make_constant_name(atom->name).c_str(), atom->uidField); + fprintf(out, " uidField[%d /* %s */] = %d;\n", atom->code, + make_constant_name(atom->name).c_str(), atom->uidField); } fprintf(out, " return uidField;\n"); @@ -134,8 +128,8 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { "getStateAtomFieldOptions() {\n"); fprintf(out, " std::map<int, StateAtomFieldOptions> options;\n"); fprintf(out, " StateAtomFieldOptions* opt;\n"); - for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); - atom != atoms.decls.end(); atom++) { + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); + atom++) { if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) { continue; } @@ -143,8 +137,8 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { "\n // Adding primary and exclusive fields for atom " "(%d)%s\n", atom->code, atom->name.c_str()); - fprintf(out, " opt = &(options[%d /* %s */]);\n", - atom->code, make_constant_name(atom->name).c_str()); + fprintf(out, " opt = &(options[%d /* %s */]);\n", atom->code, + make_constant_name(atom->name).c_str()); fprintf(out, " opt->primaryFields.reserve(%lu);\n", atom->primaryFields.size()); for (const auto& field : atom->primaryFields) { fprintf(out, " opt->primaryFields.push_back(%d);\n", field); @@ -174,7 +168,7 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { "getStateAtomFieldOptions();\n"); } -int write_atoms_info_header(FILE* out, const Atoms &atoms, const string& namespaceStr) { +int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr) { // Print prelude fprintf(out, "// This file is autogenerated\n"); fprintf(out, "\n"); @@ -195,8 +189,8 @@ int write_atoms_info_header(FILE* out, const Atoms &atoms, const string& namespa return 0; } -int write_atoms_info_cpp(FILE *out, const Atoms &atoms, const string& namespaceStr, - const string& importHeader) { +int write_atoms_info_cpp(FILE* out, const Atoms& atoms, const string& namespaceStr, + const string& importHeader) { // Print prelude fprintf(out, "// This file is autogenerated\n"); fprintf(out, "\n"); diff --git a/tools/stats_log_api_gen/atoms_info_writer.h b/tools/stats_log_api_gen/atoms_info_writer.h index d04e65a1b060..ffe9e439d7ff 100644 --- a/tools/stats_log_api_gen/atoms_info_writer.h +++ b/tools/stats_log_api_gen/atoms_info_writer.h @@ -16,18 +16,18 @@ #pragma once -#include "Collation.h" - #include <stdio.h> #include <string.h> +#include "Collation.h" + namespace android { namespace stats_log_api_gen { using namespace std; int write_atoms_info_cpp(FILE* out, const Atoms& atoms, const string& namespaceStr, - const string& importHeader); + const string& importHeader); int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr); diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp index 18508d2a6d4d..5a22b5c4cd6b 100644 --- a/tools/stats_log_api_gen/java_writer.cpp +++ b/tools/stats_log_api_gen/java_writer.cpp @@ -15,6 +15,7 @@ */ #include "java_writer.h" + #include "java_writer_q.h" #include "utils.h" @@ -22,9 +23,8 @@ namespace android { namespace stats_log_api_gen { static int write_java_q_logger_class( - FILE* out, - const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap, - const AtomDecl &attributionDecl) { + FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap, + const AtomDecl& attributionDecl) { fprintf(out, "\n"); fprintf(out, " // Write logging helper methods for statsd in Q and earlier.\n"); fprintf(out, " private static class QLogger {\n"); @@ -34,29 +34,27 @@ static int write_java_q_logger_class( // Print Q write methods. fprintf(out, "\n"); fprintf(out, " // Write methods.\n"); - write_java_methods_q_schema( - out, signatureInfoMap, attributionDecl, " "); + write_java_methods_q_schema(out, signatureInfoMap, attributionDecl, " "); fprintf(out, " }\n"); return 0; } -static void write_annotations( - FILE* out, int argIndex, - const FieldNumberToAnnotations& fieldNumberToAnnotations) { +static void write_annotations(FILE* out, int argIndex, + const FieldNumberToAnnotations& fieldNumberToAnnotations) { auto it = fieldNumberToAnnotations.find(argIndex); if (it == fieldNumberToAnnotations.end()) { return; } const set<shared_ptr<Annotation>>& annotations = it->second; - for (auto& annotation: annotations) { + for (auto& annotation : annotations) { // TODO(b/151744250): Group annotations for same atoms. // TODO(b/151786433): Write atom constant name instead of atom id literal. fprintf(out, " if (code == %d) {\n", annotation->atomId); - switch(annotation->type) { + switch (annotation->type) { case ANNOTATION_TYPE_INT: - // TODO(b/151776731): Check for reset state annotation and only include reset state - // when field value == default state annotation value. + // TODO(b/151776731): Check for reset state annotation and only include + // reset state when field value == default state annotation value. // TODO(b/151786433): Write annotation constant name instead of // annotation id literal. fprintf(out, " builder.addIntAnnotation((byte) %d, %d);\n", @@ -66,8 +64,7 @@ static void write_annotations( // TODO(b/151786433): Write annotation constant name instead of // annotation id literal. fprintf(out, " builder.addBooleanAnnotation((byte) %d, %s);\n", - annotation->annotationId, - annotation->value.boolValue ? "true" : "false"); + annotation->annotationId, annotation->value.boolValue ? "true" : "false"); break; default: break; @@ -77,24 +74,21 @@ static void write_annotations( } static int write_java_methods( - FILE* out, - const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap, - const AtomDecl &attributionDecl, - const bool supportQ - ) { + FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap, + const AtomDecl& attributionDecl, const bool supportQ) { for (auto signatureInfoMapIt = signatureInfoMap.begin(); - signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { + signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { // Print method signature. fprintf(out, " public static void write(int code"); const vector<java_type_t>& signature = signatureInfoMapIt->first; const FieldNumberToAnnotations& fieldNumberToAnnotations = signatureInfoMapIt->second; int argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { for (auto chainField : attributionDecl.fields) { - fprintf(out, ", %s[] %s", - java_type_name(chainField.javaType), chainField.name.c_str()); + fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), + chainField.name.c_str()); } } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { fprintf(out, ", android.util.SparseArray<Object> valueMap"); @@ -108,134 +102,134 @@ static int write_java_methods( // Print method body. string indent(""); if (supportQ) { - // TODO(b/146235828): Use just SDK_INT check once it is incremented from Q. + // TODO(b/146235828): Use just SDK_INT check once it is incremented from + // Q. fprintf(out, " if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q\n"); - fprintf(out, " || (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q\n"); + fprintf(out, + " || (Build.VERSION.SDK_INT == " + "Build.VERSION_CODES.Q\n"); fprintf(out, " && Build.VERSION.PREVIEW_SDK_INT > 0)) {\n"); indent = " "; } // Start StatsEvent.Builder. - fprintf(out, "%s final StatsEvent.Builder builder = StatsEvent.newBuilder();\n", + fprintf(out, + "%s final StatsEvent.Builder builder = " + "StatsEvent.newBuilder();\n", indent.c_str()); // Write atom code. fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str()); + write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAnnotations); // Write the args. argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { switch (*arg) { - case JAVA_TYPE_BOOLEAN: - fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(), argIndex); - break; - case JAVA_TYPE_INT: - case JAVA_TYPE_ENUM: - fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex); - break; - case JAVA_TYPE_FLOAT: - fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(), argIndex); - break; - case JAVA_TYPE_LONG: - fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex); - break; - case JAVA_TYPE_STRING: - fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(), argIndex); - break; - case JAVA_TYPE_BYTE_ARRAY: - fprintf(out, "%s builder.writeByteArray(null == arg%d ? new byte[0] : arg%d);\n", - indent.c_str(), argIndex, argIndex); - break; - case JAVA_TYPE_ATTRIBUTION_CHAIN: - { - const char* uidName = attributionDecl.fields.front().name.c_str(); - const char* tagName = attributionDecl.fields.back().name.c_str(); + case JAVA_TYPE_BOOLEAN: + fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(), + argIndex); + break; + case JAVA_TYPE_INT: + case JAVA_TYPE_ENUM: + fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex); + break; + case JAVA_TYPE_FLOAT: + fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(), + argIndex); + break; + case JAVA_TYPE_LONG: + fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex); + break; + case JAVA_TYPE_STRING: + fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(), + argIndex); + break; + case JAVA_TYPE_BYTE_ARRAY: + fprintf(out, + "%s builder.writeByteArray(null == arg%d ? new byte[0] : " + "arg%d);\n", + indent.c_str(), argIndex, argIndex); + break; + case JAVA_TYPE_ATTRIBUTION_CHAIN: { + const char* uidName = attributionDecl.fields.front().name.c_str(); + const char* tagName = attributionDecl.fields.back().name.c_str(); - fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str()); - fprintf(out, "%s null == %s ? new int[0] : %s,\n", - indent.c_str(), uidName, uidName); - fprintf(out, "%s null == %s ? new String[0] : %s);\n", - indent.c_str(), tagName, tagName); - break; - } - case JAVA_TYPE_KEY_VALUE_PAIR: - fprintf(out, "\n"); - fprintf(out, - "%s // Write KeyValuePairs.\n", indent.c_str()); - fprintf(out, - "%s final int count = valueMap.size();\n", indent.c_str()); - fprintf(out, - "%s android.util.SparseIntArray intMap = null;\n", - indent.c_str()); - fprintf(out, - "%s android.util.SparseLongArray longMap = null;\n", - indent.c_str()); - fprintf(out, - "%s android.util.SparseArray<String> stringMap = null;\n", - indent.c_str()); - fprintf(out, - "%s android.util.SparseArray<Float> floatMap = null;\n", - indent.c_str()); - fprintf(out, - "%s for (int i = 0; i < count; i++) {\n", indent.c_str()); - fprintf(out, - "%s final int key = valueMap.keyAt(i);\n", indent.c_str()); - fprintf(out, - "%s final Object value = valueMap.valueAt(i);\n", - indent.c_str()); - fprintf(out, - "%s if (value instanceof Integer) {\n", indent.c_str()); - fprintf(out, - "%s if (null == intMap) {\n", indent.c_str()); - fprintf(out, - "%s intMap = new android.util.SparseIntArray();\n", indent.c_str()); - fprintf(out, - "%s }\n", indent.c_str()); - fprintf(out, - "%s intMap.put(key, (Integer) value);\n", indent.c_str()); - fprintf(out, - "%s } else if (value instanceof Long) {\n", indent.c_str()); - fprintf(out, - "%s if (null == longMap) {\n", indent.c_str()); - fprintf(out, - "%s longMap = new android.util.SparseLongArray();\n", indent.c_str()); - fprintf(out, - "%s }\n", indent.c_str()); - fprintf(out, - "%s longMap.put(key, (Long) value);\n", indent.c_str()); - fprintf(out, - "%s } else if (value instanceof String) {\n", indent.c_str()); - fprintf(out, - "%s if (null == stringMap) {\n", indent.c_str()); - fprintf(out, - "%s stringMap = new android.util.SparseArray<>();\n", indent.c_str()); - fprintf(out, - "%s }\n", indent.c_str()); - fprintf(out, - "%s stringMap.put(key, (String) value);\n", indent.c_str()); - fprintf(out, - "%s } else if (value instanceof Float) {\n", indent.c_str()); - fprintf(out, - "%s if (null == floatMap) {\n", indent.c_str()); - fprintf(out, - "%s floatMap = new android.util.SparseArray<>();\n", indent.c_str()); - fprintf(out, - "%s }\n", indent.c_str()); - fprintf(out, - "%s floatMap.put(key, (Float) value);\n", indent.c_str()); - fprintf(out, - "%s }\n", indent.c_str()); - fprintf(out, - "%s }\n", indent.c_str()); - fprintf(out, - "%s builder.writeKeyValuePairs(" - "intMap, longMap, stringMap, floatMap);\n", indent.c_str()); - break; - default: - // Unsupported types: OBJECT, DOUBLE. - fprintf(stderr, "Encountered unsupported type."); - return 1; + fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str()); + fprintf(out, "%s null == %s ? new int[0] : %s,\n", + indent.c_str(), uidName, uidName); + fprintf(out, "%s null == %s ? new String[0] : %s);\n", + indent.c_str(), tagName, tagName); + break; + } + case JAVA_TYPE_KEY_VALUE_PAIR: + fprintf(out, "\n"); + fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str()); + fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str()); + fprintf(out, "%s android.util.SparseIntArray intMap = null;\n", + indent.c_str()); + fprintf(out, "%s android.util.SparseLongArray longMap = null;\n", + indent.c_str()); + fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n", + indent.c_str()); + fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n", + indent.c_str()); + fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str()); + fprintf(out, "%s final int key = valueMap.keyAt(i);\n", + indent.c_str()); + fprintf(out, "%s final Object value = valueMap.valueAt(i);\n", + indent.c_str()); + fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str()); + fprintf(out, "%s if (null == intMap) {\n", indent.c_str()); + fprintf(out, + "%s intMap = new " + "android.util.SparseIntArray();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s intMap.put(key, (Integer) value);\n", + indent.c_str()); + fprintf(out, "%s } else if (value instanceof Long) {\n", + indent.c_str()); + fprintf(out, "%s if (null == longMap) {\n", indent.c_str()); + fprintf(out, + "%s longMap = new " + "android.util.SparseLongArray();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s longMap.put(key, (Long) value);\n", + indent.c_str()); + fprintf(out, "%s } else if (value instanceof String) {\n", + indent.c_str()); + fprintf(out, "%s if (null == stringMap) {\n", indent.c_str()); + fprintf(out, + "%s stringMap = new " + "android.util.SparseArray<>();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s stringMap.put(key, (String) value);\n", + indent.c_str()); + fprintf(out, "%s } else if (value instanceof Float) {\n", + indent.c_str()); + fprintf(out, "%s if (null == floatMap) {\n", indent.c_str()); + fprintf(out, + "%s floatMap = new " + "android.util.SparseArray<>();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s floatMap.put(key, (Float) value);\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, + "%s builder.writeKeyValuePairs(" + "intMap, longMap, stringMap, floatMap);\n", + indent.c_str()); + break; + default: + // Unsupported types: OBJECT, DOUBLE. + fprintf(stderr, "Encountered unsupported type."); + return 1; } write_annotations(out, argIndex, fieldNumberToAnnotations); argIndex++; @@ -251,7 +245,7 @@ static int write_java_methods( fprintf(out, " QLogger.write(code"); argIndex = 1; for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + arg != signature.end(); arg++) { if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { const char* uidName = attributionDecl.fields.front().name.c_str(); const char* tagName = attributionDecl.fields.back().name.c_str(); @@ -266,20 +260,18 @@ static int write_java_methods( argIndex++; } fprintf(out, ");\n"); - fprintf(out, " }\n"); // if + fprintf(out, " }\n"); // if } - fprintf(out, " }\n"); // method + fprintf(out, " }\n"); // method fprintf(out, "\n"); } return 0; - } -int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl, - const string& javaClass, - const string& javaPackage, const bool supportQ, - const bool supportWorkSource) { +int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl, + const string& javaClass, const string& javaPackage, const bool supportQ, + const bool supportWorkSource) { // Print prelude fprintf(out, "// This file is autogenerated\n"); fprintf(out, "\n"); @@ -308,17 +300,14 @@ int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attribut // Print write methods. fprintf(out, " // Write methods\n"); - errors += write_java_methods( - out, atoms.signatureInfoMap, attributionDecl, supportQ); - errors += write_java_non_chained_methods( - out, atoms.nonChainedSignatureInfoMap); + errors += write_java_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ); + errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap); if (supportWorkSource) { errors += write_java_work_source_methods(out, atoms.signatureInfoMap); } if (supportQ) { - errors += write_java_q_logger_class( - out, atoms.signatureInfoMap, attributionDecl); + errors += write_java_q_logger_class(out, atoms.signatureInfoMap, attributionDecl); } fprintf(out, "}\n"); diff --git a/tools/stats_log_api_gen/java_writer.h b/tools/stats_log_api_gen/java_writer.h index 4e1365e8dcce..8b3b50588efc 100644 --- a/tools/stats_log_api_gen/java_writer.h +++ b/tools/stats_log_api_gen/java_writer.h @@ -16,25 +16,23 @@ #pragma once -#include "Collation.h" +#include <stdio.h> +#include <string.h> #include <map> #include <set> #include <vector> -#include <stdio.h> -#include <string.h> +#include "Collation.h" namespace android { namespace stats_log_api_gen { using namespace std; -int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl, - const string& javaClass, - const string& javaPackage, const bool supportQ, +int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl, + const string& javaClass, const string& javaPackage, const bool supportQ, const bool supportWorkSource); } // namespace stats_log_api_gen } // namespace android - diff --git a/tools/stats_log_api_gen/java_writer_q.cpp b/tools/stats_log_api_gen/java_writer_q.cpp index 329c25def441..7d225834aba2 100644 --- a/tools/stats_log_api_gen/java_writer_q.cpp +++ b/tools/stats_log_api_gen/java_writer_q.cpp @@ -15,6 +15,7 @@ */ #include "java_writer_q.h" + #include "utils.h" namespace android { @@ -24,7 +25,8 @@ void write_java_q_logging_constants(FILE* out, const string& indent) { fprintf(out, "%s// Payload limits.\n", indent.c_str()); fprintf(out, "%sprivate static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068;\n", indent.c_str()); fprintf(out, - "%sprivate static final int MAX_EVENT_PAYLOAD = LOGGER_ENTRY_MAX_PAYLOAD - 4;\n", + "%sprivate static final int MAX_EVENT_PAYLOAD = " + "LOGGER_ENTRY_MAX_PAYLOAD - 4;\n", indent.c_str()); // Value types. Must match with EventLog.java and log.h. @@ -37,36 +39,36 @@ void write_java_q_logging_constants(FILE* out, const string& indent) { fprintf(out, "%sprivate static final byte FLOAT_TYPE = 4;\n", indent.c_str()); // Size of each value type. - // Booleans, ints, floats, and enums take 5 bytes, 1 for the type and 4 for the value. + // Booleans, ints, floats, and enums take 5 bytes, 1 for the type and 4 for + // the value. fprintf(out, "\n"); fprintf(out, "%s// Size of each value type.\n", indent.c_str()); fprintf(out, "%sprivate static final int INT_TYPE_SIZE = 5;\n", indent.c_str()); fprintf(out, "%sprivate static final int FLOAT_TYPE_SIZE = 5;\n", indent.c_str()); // Longs take 9 bytes, 1 for the type and 8 for the value. fprintf(out, "%sprivate static final int LONG_TYPE_SIZE = 9;\n", indent.c_str()); - // Strings take 5 metadata bytes: 1 byte is for the type, 4 are for the length. + // Strings take 5 metadata bytes: 1 byte is for the type, 4 are for the + // length. fprintf(out, "%sprivate static final int STRING_TYPE_OVERHEAD = 5;\n", indent.c_str()); fprintf(out, "%sprivate static final int LIST_TYPE_OVERHEAD = 2;\n", indent.c_str()); } int write_java_methods_q_schema( - FILE* out, - const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap, - const AtomDecl &attributionDecl, - const string& indent) { + FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap, + const AtomDecl& attributionDecl, const string& indent) { int requiredHelpers = 0; for (auto signatureInfoMapIt = signatureInfoMap.begin(); - signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { + signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { // Print method signature. vector<java_type_t> signature = signatureInfoMapIt->first; fprintf(out, "%spublic static void write(int code", indent.c_str()); int argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { for (auto chainField : attributionDecl.fields) { - fprintf(out, ", %s[] %s", - java_type_name(chainField.javaType), chainField.name.c_str()); + fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), + chainField.name.c_str()); } } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { fprintf(out, ", android.util.SparseArray<Object> valueMap"); @@ -81,190 +83,174 @@ int write_java_methods_q_schema( fprintf(out, "%s // Initial overhead of the list, timestamp, and atom tag.\n", indent.c_str()); fprintf(out, - "%s int needed = LIST_TYPE_OVERHEAD + LONG_TYPE_SIZE + INT_TYPE_SIZE;\n", + "%s int needed = LIST_TYPE_OVERHEAD + LONG_TYPE_SIZE + " + "INT_TYPE_SIZE;\n", indent.c_str()); argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { switch (*arg) { - case JAVA_TYPE_BOOLEAN: - case JAVA_TYPE_INT: - case JAVA_TYPE_FLOAT: - case JAVA_TYPE_ENUM: - fprintf(out, "%s needed += INT_TYPE_SIZE;\n", indent.c_str()); - break; - case JAVA_TYPE_LONG: - // Longs take 9 bytes, 1 for the type and 8 for the value. - fprintf(out, "%s needed += LONG_TYPE_SIZE;\n", indent.c_str()); - break; - case JAVA_TYPE_STRING: - // Strings take 5 metadata bytes + length of byte encoded string. - fprintf(out, "%s if (arg%d == null) {\n", indent.c_str(), argIndex); - fprintf(out, "%s arg%d = \"\";\n", indent.c_str(), argIndex); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, - "%s byte[] arg%dBytes = " - "arg%d.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n", - indent.c_str(), argIndex, argIndex); - fprintf(out, "%s needed += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n", - indent.c_str(), argIndex); - break; - case JAVA_TYPE_BYTE_ARRAY: - // Byte arrays take 5 metadata bytes + length of byte array. - fprintf(out, "%s if (arg%d == null) {\n", indent.c_str(), argIndex); - fprintf(out, "%s arg%d = new byte[0];\n", indent.c_str(), argIndex); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s needed += STRING_TYPE_OVERHEAD + arg%d.length;\n", - indent.c_str(), argIndex); - break; - case JAVA_TYPE_ATTRIBUTION_CHAIN: - { - const char* uidName = attributionDecl.fields.front().name.c_str(); - const char* tagName = attributionDecl.fields.back().name.c_str(); - // Null checks on the params. - fprintf(out, "%s if (%s == null) {\n", indent.c_str(), uidName); - fprintf(out, "%s %s = new %s[0];\n", indent.c_str(), uidName, - java_type_name(attributionDecl.fields.front().javaType)); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s if (%s == null) {\n", indent.c_str(), tagName); - fprintf(out, "%s %s = new %s[0];\n", indent.c_str(), tagName, - java_type_name(attributionDecl.fields.back().javaType)); - fprintf(out, "%s }\n", indent.c_str()); - - // First check that the lengths of the uid and tag arrays are the same. - fprintf(out, "%s if (%s.length != %s.length) {\n", - indent.c_str(), uidName, tagName); - fprintf(out, "%s return;\n", indent.c_str()); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s int attrSize = LIST_TYPE_OVERHEAD;\n", indent.c_str()); - fprintf(out, "%s for (int i = 0; i < %s.length; i++) {\n", - indent.c_str(), tagName); - fprintf(out, "%s String str%d = (%s[i] == null) ? \"\" : %s[i];\n", - indent.c_str(), argIndex, tagName, tagName); - fprintf(out, - "%s int str%dlen = " - "str%d.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;\n", - indent.c_str(), argIndex, argIndex); - fprintf(out, - "%s attrSize += " - "LIST_TYPE_OVERHEAD + INT_TYPE_SIZE + STRING_TYPE_OVERHEAD + str%dlen;\n", - indent.c_str(), argIndex); - fprintf(out, "%s }\n", indent.c_str()); - fprintf(out, "%s needed += attrSize;\n", indent.c_str()); - break; - } - case JAVA_TYPE_KEY_VALUE_PAIR: - { - fprintf(out, - "%s // Calculate bytes needed by Key Value Pairs.\n", - indent.c_str()); - fprintf(out, - "%s final int count = valueMap.size();\n", indent.c_str()); - fprintf(out, - "%s android.util.SparseIntArray intMap = null;\n", indent.c_str()); - fprintf(out, - "%s android.util.SparseLongArray longMap = null;\n", indent.c_str()); - fprintf(out, - "%s android.util.SparseArray<String> stringMap = null;\n", - indent.c_str()); - fprintf(out, - "%s android.util.SparseArray<Float> floatMap = null;\n", indent.c_str()); - fprintf(out, "%s int keyValuePairSize = LIST_TYPE_OVERHEAD;\n", indent.c_str()); - fprintf(out, - "%s for (int i = 0; i < count; i++) {\n", indent.c_str()); - fprintf(out, - "%s final int key = valueMap.keyAt(i);\n", indent.c_str()); - fprintf(out, - "%s final Object value = valueMap.valueAt(i);\n", - indent.c_str()); - fprintf(out, - "%s if (value instanceof Integer) {\n", indent.c_str()); - fprintf(out, - "%s keyValuePairSize += LIST_TYPE_OVERHEAD\n", - indent.c_str()); - fprintf(out, - "%s + INT_TYPE_SIZE + INT_TYPE_SIZE;\n", - indent.c_str()); - fprintf(out, - "%s if (null == intMap) {\n", indent.c_str()); - fprintf(out, - "%s intMap = new android.util.SparseIntArray();\n", indent.c_str()); - fprintf(out, - "%s }\n", indent.c_str()); - fprintf(out, - "%s intMap.put(key, (Integer) value);\n", indent.c_str()); - fprintf(out, - "%s } else if (value instanceof Long) {\n", indent.c_str()); - fprintf(out, - "%s keyValuePairSize += LIST_TYPE_OVERHEAD\n", - indent.c_str()); - fprintf(out, - "%s + INT_TYPE_SIZE + LONG_TYPE_SIZE;\n", - indent.c_str()); - fprintf(out, - "%s if (null == longMap) {\n", indent.c_str()); - fprintf(out, - "%s longMap = new android.util.SparseLongArray();\n", indent.c_str()); - fprintf(out, - "%s }\n", indent.c_str()); - fprintf(out, - "%s longMap.put(key, (Long) value);\n", indent.c_str()); - fprintf(out, - "%s } else if (value instanceof String) {\n", indent.c_str()); - fprintf(out, - "%s final String str = (value == null) ? \"\" : " - "(String) value;\n", - indent.c_str()); - fprintf(out, - "%s final int len = " - "str.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;\n", - indent.c_str()); - fprintf(out, - "%s keyValuePairSize += LIST_TYPE_OVERHEAD + INT_TYPE_SIZE\n", - indent.c_str()); - fprintf(out, - "%s + STRING_TYPE_OVERHEAD + len;\n", - indent.c_str()); - fprintf(out, - "%s if (null == stringMap) {\n", indent.c_str()); - fprintf(out, - "%s stringMap = new android.util.SparseArray<>();\n", indent.c_str()); - fprintf(out, - "%s }\n", indent.c_str()); - fprintf(out, - "%s stringMap.put(key, str);\n", indent.c_str()); - fprintf(out, - "%s } else if (value instanceof Float) {\n", indent.c_str()); - fprintf(out, - "%s keyValuePairSize += LIST_TYPE_OVERHEAD\n", - indent.c_str()); - fprintf(out, - "%s + INT_TYPE_SIZE + FLOAT_TYPE_SIZE;\n", - indent.c_str()); - fprintf(out, - "%s if (null == floatMap) {\n", indent.c_str()); - fprintf(out, - "%s floatMap = new android.util.SparseArray<>();\n", indent.c_str()); - fprintf(out, - "%s }\n", indent.c_str()); - fprintf(out, - "%s floatMap.put(key, (Float) value);\n", indent.c_str()); - fprintf(out, - "%s }\n", indent.c_str()); - fprintf(out, - "%s }\n", indent.c_str()); - fprintf(out, "%s needed += keyValuePairSize;\n", indent.c_str()); - break; - } - default: - // Unsupported types: OBJECT, DOUBLE. - fprintf(stderr, "Module logging does not yet support Object and Double.\n"); - return 1; + case JAVA_TYPE_BOOLEAN: + case JAVA_TYPE_INT: + case JAVA_TYPE_FLOAT: + case JAVA_TYPE_ENUM: + fprintf(out, "%s needed += INT_TYPE_SIZE;\n", indent.c_str()); + break; + case JAVA_TYPE_LONG: + // Longs take 9 bytes, 1 for the type and 8 for the value. + fprintf(out, "%s needed += LONG_TYPE_SIZE;\n", indent.c_str()); + break; + case JAVA_TYPE_STRING: + // Strings take 5 metadata bytes + length of byte encoded string. + fprintf(out, "%s if (arg%d == null) {\n", indent.c_str(), argIndex); + fprintf(out, "%s arg%d = \"\";\n", indent.c_str(), argIndex); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, + "%s byte[] arg%dBytes = " + "arg%d.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n", + indent.c_str(), argIndex, argIndex); + fprintf(out, "%s needed += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n", + indent.c_str(), argIndex); + break; + case JAVA_TYPE_BYTE_ARRAY: + // Byte arrays take 5 metadata bytes + length of byte array. + fprintf(out, "%s if (arg%d == null) {\n", indent.c_str(), argIndex); + fprintf(out, "%s arg%d = new byte[0];\n", indent.c_str(), argIndex); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s needed += STRING_TYPE_OVERHEAD + arg%d.length;\n", + indent.c_str(), argIndex); + break; + case JAVA_TYPE_ATTRIBUTION_CHAIN: { + const char* uidName = attributionDecl.fields.front().name.c_str(); + const char* tagName = attributionDecl.fields.back().name.c_str(); + // Null checks on the params. + fprintf(out, "%s if (%s == null) {\n", indent.c_str(), uidName); + fprintf(out, "%s %s = new %s[0];\n", indent.c_str(), uidName, + java_type_name(attributionDecl.fields.front().javaType)); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s if (%s == null) {\n", indent.c_str(), tagName); + fprintf(out, "%s %s = new %s[0];\n", indent.c_str(), tagName, + java_type_name(attributionDecl.fields.back().javaType)); + fprintf(out, "%s }\n", indent.c_str()); + + // First check that the lengths of the uid and tag arrays are the + // same. + fprintf(out, "%s if (%s.length != %s.length) {\n", indent.c_str(), uidName, + tagName); + fprintf(out, "%s return;\n", indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s int attrSize = LIST_TYPE_OVERHEAD;\n", indent.c_str()); + fprintf(out, "%s for (int i = 0; i < %s.length; i++) {\n", indent.c_str(), + tagName); + fprintf(out, "%s String str%d = (%s[i] == null) ? \"\" : %s[i];\n", + indent.c_str(), argIndex, tagName, tagName); + fprintf(out, + "%s int str%dlen = " + "str%d.getBytes(java.nio.charset.StandardCharsets.UTF_8)." + "length;\n", + indent.c_str(), argIndex, argIndex); + fprintf(out, + "%s attrSize += " + "LIST_TYPE_OVERHEAD + INT_TYPE_SIZE + STRING_TYPE_OVERHEAD + " + "str%dlen;\n", + indent.c_str(), argIndex); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s needed += attrSize;\n", indent.c_str()); + break; + } + case JAVA_TYPE_KEY_VALUE_PAIR: { + fprintf(out, "%s // Calculate bytes needed by Key Value Pairs.\n", + indent.c_str()); + fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str()); + fprintf(out, "%s android.util.SparseIntArray intMap = null;\n", + indent.c_str()); + fprintf(out, "%s android.util.SparseLongArray longMap = null;\n", + indent.c_str()); + fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n", + indent.c_str()); + fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n", + indent.c_str()); + fprintf(out, "%s int keyValuePairSize = LIST_TYPE_OVERHEAD;\n", + indent.c_str()); + fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str()); + fprintf(out, "%s final int key = valueMap.keyAt(i);\n", indent.c_str()); + fprintf(out, "%s final Object value = valueMap.valueAt(i);\n", + indent.c_str()); + fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str()); + fprintf(out, "%s keyValuePairSize += LIST_TYPE_OVERHEAD\n", + indent.c_str()); + fprintf(out, "%s + INT_TYPE_SIZE + INT_TYPE_SIZE;\n", + indent.c_str()); + fprintf(out, "%s if (null == intMap) {\n", indent.c_str()); + fprintf(out, "%s intMap = new android.util.SparseIntArray();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s intMap.put(key, (Integer) value);\n", + indent.c_str()); + fprintf(out, "%s } else if (value instanceof Long) {\n", indent.c_str()); + fprintf(out, "%s keyValuePairSize += LIST_TYPE_OVERHEAD\n", + indent.c_str()); + fprintf(out, "%s + INT_TYPE_SIZE + LONG_TYPE_SIZE;\n", + indent.c_str()); + fprintf(out, "%s if (null == longMap) {\n", indent.c_str()); + fprintf(out, + "%s longMap = new " + "android.util.SparseLongArray();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s longMap.put(key, (Long) value);\n", indent.c_str()); + fprintf(out, "%s } else if (value instanceof String) {\n", + indent.c_str()); + fprintf(out, + "%s final String str = (value == null) ? \"\" : " + "(String) value;\n", + indent.c_str()); + fprintf(out, + "%s final int len = " + "str.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;\n", + indent.c_str()); + fprintf(out, + "%s keyValuePairSize += LIST_TYPE_OVERHEAD + " + "INT_TYPE_SIZE\n", + indent.c_str()); + fprintf(out, "%s + STRING_TYPE_OVERHEAD + len;\n", + indent.c_str()); + fprintf(out, "%s if (null == stringMap) {\n", indent.c_str()); + fprintf(out, + "%s stringMap = new " + "android.util.SparseArray<>();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s stringMap.put(key, str);\n", indent.c_str()); + fprintf(out, "%s } else if (value instanceof Float) {\n", + indent.c_str()); + fprintf(out, "%s keyValuePairSize += LIST_TYPE_OVERHEAD\n", + indent.c_str()); + fprintf(out, "%s + INT_TYPE_SIZE + FLOAT_TYPE_SIZE;\n", + indent.c_str()); + fprintf(out, "%s if (null == floatMap) {\n", indent.c_str()); + fprintf(out, + "%s floatMap = new " + "android.util.SparseArray<>();\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s floatMap.put(key, (Float) value);\n", + indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s }\n", indent.c_str()); + fprintf(out, "%s needed += keyValuePairSize;\n", indent.c_str()); + break; + } + default: + // Unsupported types: OBJECT, DOUBLE. + fprintf(stderr, "Module logging does not yet support Object and Double.\n"); + return 1; } argIndex++; } - // Now we have the size that is needed. Check for overflow and return if needed. + // Now we have the size that is needed. Check for overflow and return if + // needed. fprintf(out, "%s if (needed > MAX_EVENT_PAYLOAD) {\n", indent.c_str()); fprintf(out, "%s return;\n", indent.c_str()); fprintf(out, "%s }\n", indent.c_str()); @@ -279,7 +265,8 @@ int write_java_methods_q_schema( fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str()); // Write timestamp. - fprintf(out, "%s long elapsedRealtime = SystemClock.elapsedRealtimeNanos();\n", indent.c_str()); + fprintf(out, "%s long elapsedRealtime = SystemClock.elapsedRealtimeNanos();\n", + indent.c_str()); fprintf(out, "%s buff[pos] = LONG_TYPE;\n", indent.c_str()); fprintf(out, "%s copyLong(buff, pos + 1, elapsedRealtime);\n", indent.c_str()); fprintf(out, "%s pos += LONG_TYPE_SIZE;\n", indent.c_str()); @@ -291,77 +278,82 @@ int write_java_methods_q_schema( // Write the args. argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { switch (*arg) { - case JAVA_TYPE_BOOLEAN: - fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str()); - fprintf(out, "%s copyInt(buff, pos + 1, arg%d? 1 : 0);\n", - indent.c_str(), argIndex); - fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str()); - break; - case JAVA_TYPE_INT: - case JAVA_TYPE_ENUM: - fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str()); - fprintf(out, "%s copyInt(buff, pos + 1, arg%d);\n", indent.c_str(), argIndex); - fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str()); - break; - case JAVA_TYPE_FLOAT: - requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT; - fprintf(out, "%s buff[pos] = FLOAT_TYPE;\n", indent.c_str()); - fprintf(out, "%s copyFloat(buff, pos + 1, arg%d);\n", indent.c_str(), argIndex); - fprintf(out, "%s pos += FLOAT_TYPE_SIZE;\n", indent.c_str()); - break; - case JAVA_TYPE_LONG: - fprintf(out, "%s buff[pos] = LONG_TYPE;\n", indent.c_str()); - fprintf(out, "%s copyLong(buff, pos + 1, arg%d);\n", indent.c_str(), argIndex); - fprintf(out, "%s pos += LONG_TYPE_SIZE;\n", indent.c_str()); - break; - case JAVA_TYPE_STRING: - fprintf(out, "%s buff[pos] = STRING_TYPE;\n", indent.c_str()); - fprintf(out, "%s copyInt(buff, pos + 1, arg%dBytes.length);\n", - indent.c_str(), argIndex); - fprintf(out, "%s System.arraycopy(" - "arg%dBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%dBytes.length);\n", - indent.c_str(), argIndex, argIndex); - fprintf(out, "%s pos += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n", - indent.c_str(), argIndex); - break; - case JAVA_TYPE_BYTE_ARRAY: - fprintf(out, "%s buff[pos] = STRING_TYPE;\n", indent.c_str()); - fprintf(out, "%s copyInt(buff, pos + 1, arg%d.length);\n", - indent.c_str(), argIndex); - fprintf(out, "%s System.arraycopy(" - "arg%d, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%d.length);\n", - indent.c_str(), argIndex, argIndex); - fprintf(out, "%s pos += STRING_TYPE_OVERHEAD + arg%d.length;\n", - indent.c_str(), argIndex); - break; - case JAVA_TYPE_ATTRIBUTION_CHAIN: - { - requiredHelpers |= JAVA_MODULE_REQUIRES_ATTRIBUTION; - const char* uidName = attributionDecl.fields.front().name.c_str(); - const char* tagName = attributionDecl.fields.back().name.c_str(); - - fprintf(out, "%s writeAttributionChain(buff, pos, %s, %s);\n", indent.c_str(), - uidName, tagName); - fprintf(out, "%s pos += attrSize;\n", indent.c_str()); - break; - } - case JAVA_TYPE_KEY_VALUE_PAIR: - requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT; - requiredHelpers |= JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS; - fprintf(out, - "%s writeKeyValuePairs(buff, pos, (byte) count, intMap, longMap, " - "stringMap, floatMap);\n", - indent.c_str()); - fprintf(out, "%s pos += keyValuePairSize;\n", indent.c_str()); - break; - default: - // Unsupported types: OBJECT, DOUBLE. - fprintf(stderr, - "Object and Double are not supported in module logging"); - return 1; + case JAVA_TYPE_BOOLEAN: + fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str()); + fprintf(out, "%s copyInt(buff, pos + 1, arg%d? 1 : 0);\n", indent.c_str(), + argIndex); + fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str()); + break; + case JAVA_TYPE_INT: + case JAVA_TYPE_ENUM: + fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str()); + fprintf(out, "%s copyInt(buff, pos + 1, arg%d);\n", indent.c_str(), + argIndex); + fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str()); + break; + case JAVA_TYPE_FLOAT: + requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT; + fprintf(out, "%s buff[pos] = FLOAT_TYPE;\n", indent.c_str()); + fprintf(out, "%s copyFloat(buff, pos + 1, arg%d);\n", indent.c_str(), + argIndex); + fprintf(out, "%s pos += FLOAT_TYPE_SIZE;\n", indent.c_str()); + break; + case JAVA_TYPE_LONG: + fprintf(out, "%s buff[pos] = LONG_TYPE;\n", indent.c_str()); + fprintf(out, "%s copyLong(buff, pos + 1, arg%d);\n", indent.c_str(), + argIndex); + fprintf(out, "%s pos += LONG_TYPE_SIZE;\n", indent.c_str()); + break; + case JAVA_TYPE_STRING: + fprintf(out, "%s buff[pos] = STRING_TYPE;\n", indent.c_str()); + fprintf(out, "%s copyInt(buff, pos + 1, arg%dBytes.length);\n", + indent.c_str(), argIndex); + fprintf(out, + "%s System.arraycopy(" + "arg%dBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, " + "arg%dBytes.length);\n", + indent.c_str(), argIndex, argIndex); + fprintf(out, "%s pos += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n", + indent.c_str(), argIndex); + break; + case JAVA_TYPE_BYTE_ARRAY: + fprintf(out, "%s buff[pos] = STRING_TYPE;\n", indent.c_str()); + fprintf(out, "%s copyInt(buff, pos + 1, arg%d.length);\n", indent.c_str(), + argIndex); + fprintf(out, + "%s System.arraycopy(" + "arg%d, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%d.length);\n", + indent.c_str(), argIndex, argIndex); + fprintf(out, "%s pos += STRING_TYPE_OVERHEAD + arg%d.length;\n", + indent.c_str(), argIndex); + break; + case JAVA_TYPE_ATTRIBUTION_CHAIN: { + requiredHelpers |= JAVA_MODULE_REQUIRES_ATTRIBUTION; + const char* uidName = attributionDecl.fields.front().name.c_str(); + const char* tagName = attributionDecl.fields.back().name.c_str(); + + fprintf(out, "%s writeAttributionChain(buff, pos, %s, %s);\n", + indent.c_str(), uidName, tagName); + fprintf(out, "%s pos += attrSize;\n", indent.c_str()); + break; + } + case JAVA_TYPE_KEY_VALUE_PAIR: + requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT; + requiredHelpers |= JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS; + fprintf(out, + "%s writeKeyValuePairs(buff, pos, (byte) count, intMap, " + "longMap, " + "stringMap, floatMap);\n", + indent.c_str()); + fprintf(out, "%s pos += keyValuePairSize;\n", indent.c_str()); + break; + default: + // Unsupported types: OBJECT, DOUBLE. + fprintf(stderr, "Object and Double are not supported in module logging"); + return 1; } argIndex++; } @@ -376,11 +368,8 @@ int write_java_methods_q_schema( return 0; } -void write_java_helpers_for_q_schema_methods( - FILE* out, - const AtomDecl &attributionDecl, - const int requiredHelpers, - const string& indent) { +void write_java_helpers_for_q_schema_methods(FILE* out, const AtomDecl& attributionDecl, + const int requiredHelpers, const string& indent) { fprintf(out, "\n"); fprintf(out, "%s// Helper methods for copying primitives\n", indent.c_str()); fprintf(out, "%sprivate static void copyInt(byte[] buff, int pos, int val) {\n", @@ -420,8 +409,7 @@ void write_java_helpers_for_q_schema_methods( fprintf(out, "%sprivate static void writeAttributionChain(byte[] buff, int pos", indent.c_str()); for (auto chainField : attributionDecl.fields) { - fprintf(out, ", %s[] %s", - java_type_name(chainField.javaType), chainField.name.c_str()); + fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), chainField.name.c_str()); } fprintf(out, ") {\n"); @@ -437,8 +425,8 @@ void write_java_helpers_for_q_schema_methods( fprintf(out, "%s for (int i = 0; i < %s.length; i++) {\n", indent.c_str(), tagName); // Write the list begin. fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str()); - fprintf(out, "%s buff[pos + 1] = %lu;\n", - indent.c_str(), attributionDecl.fields.size()); + fprintf(out, "%s buff[pos + 1] = %lu;\n", indent.c_str(), + attributionDecl.fields.size()); fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str()); // Write the uid. @@ -447,18 +435,20 @@ void write_java_helpers_for_q_schema_methods( fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str()); // Write the tag. - fprintf(out, "%s String %sStr = (%s[i] == null) ? \"\" : %s[i];\n", - indent.c_str(), tagName, tagName, tagName); - fprintf(out, "%s byte[] %sByte = " + fprintf(out, "%s String %sStr = (%s[i] == null) ? \"\" : %s[i];\n", indent.c_str(), + tagName, tagName, tagName); + fprintf(out, + "%s byte[] %sByte = " "%sStr.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n", indent.c_str(), tagName, tagName); fprintf(out, "%s buff[pos] = STRING_TYPE;\n", indent.c_str()); fprintf(out, "%s copyInt(buff, pos + 1, %sByte.length);\n", indent.c_str(), tagName); - fprintf(out, "%s System.arraycopy(" + fprintf(out, + "%s System.arraycopy(" "%sByte, 0, buff, pos + STRING_TYPE_OVERHEAD, %sByte.length);\n", indent.c_str(), tagName, tagName); - fprintf(out, "%s pos += STRING_TYPE_OVERHEAD + %sByte.length;\n", - indent.c_str(), tagName); + fprintf(out, "%s pos += STRING_TYPE_OVERHEAD + %sByte.length;\n", indent.c_str(), + tagName); fprintf(out, "%s }\n", indent.c_str()); fprintf(out, "%s}\n", indent.c_str()); fprintf(out, "\n"); @@ -466,7 +456,8 @@ void write_java_helpers_for_q_schema_methods( if (requiredHelpers & JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS) { fprintf(out, - "%sprivate static void writeKeyValuePairs(byte[] buff, int pos, byte numPairs,\n", + "%sprivate static void writeKeyValuePairs(byte[] buff, int pos, " + "byte numPairs,\n", indent.c_str()); fprintf(out, "%s final android.util.SparseIntArray intMap,\n", indent.c_str()); fprintf(out, "%s final android.util.SparseLongArray longMap,\n", indent.c_str()); @@ -515,7 +506,9 @@ void write_java_helpers_for_q_schema_methods( fprintf(out, "%s }\n", indent.c_str()); // Write Strings. - fprintf(out, "%s final int stringMapSize = null == stringMap ? 0 : stringMap.size();\n", + fprintf(out, + "%s final int stringMapSize = null == stringMap ? 0 : " + "stringMap.size();\n", indent.c_str()); fprintf(out, "%s for (int i = 0; i < stringMapSize; i++) {\n", indent.c_str()); fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str()); @@ -523,7 +516,8 @@ void write_java_helpers_for_q_schema_methods( fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str()); fprintf(out, "%s final int key = stringMap.keyAt(i);\n", indent.c_str()); fprintf(out, "%s final String value = stringMap.valueAt(i);\n", indent.c_str()); - fprintf(out, "%s final byte[] valueBytes = " + fprintf(out, + "%s final byte[] valueBytes = " "value.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n", indent.c_str()); fprintf(out, "%s buff[pos] = INT_TYPE;\n", indent.c_str()); @@ -531,15 +525,19 @@ void write_java_helpers_for_q_schema_methods( fprintf(out, "%s pos += INT_TYPE_SIZE;\n", indent.c_str()); fprintf(out, "%s buff[pos] = STRING_TYPE;\n", indent.c_str()); fprintf(out, "%s copyInt(buff, pos + 1, valueBytes.length);\n", indent.c_str()); - fprintf(out, "%s System.arraycopy(" - "valueBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, valueBytes.length);\n", + fprintf(out, + "%s System.arraycopy(" + "valueBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, " + "valueBytes.length);\n", indent.c_str()); fprintf(out, "%s pos += STRING_TYPE_OVERHEAD + valueBytes.length;\n", indent.c_str()); fprintf(out, "%s }\n", indent.c_str()); // Write floats. - fprintf(out, "%s final int floatMapSize = null == floatMap ? 0 : floatMap.size();\n", + fprintf(out, + "%s final int floatMapSize = null == floatMap ? 0 : " + "floatMap.size();\n", indent.c_str()); fprintf(out, "%s for (int i = 0; i < floatMapSize; i++) {\n", indent.c_str()); fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str()); @@ -559,12 +557,11 @@ void write_java_helpers_for_q_schema_methods( } } -// This method is called in main.cpp to generate StatsLog for modules that's compatible with -// Q at compile-time. +// This method is called in main.cpp to generate StatsLog for modules that's +// compatible with Q at compile-time. int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms, - const AtomDecl &attributionDecl, - const string& javaClass, const string& javaPackage, - const bool supportWorkSource) { + const AtomDecl& attributionDecl, const string& javaClass, + const string& javaPackage, const bool supportWorkSource) { // Print prelude fprintf(out, "// This file is autogenerated\n"); fprintf(out, "\n"); @@ -590,8 +587,7 @@ int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms, int errors = 0; // Print write methods fprintf(out, " // Write methods\n"); - errors += write_java_methods_q_schema(out, atoms.signatureInfoMap, attributionDecl, - " "); + errors += write_java_methods_q_schema(out, atoms.signatureInfoMap, attributionDecl, " "); errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap); if (supportWorkSource) { errors += write_java_work_source_methods(out, atoms.signatureInfoMap); diff --git a/tools/stats_log_api_gen/java_writer_q.h b/tools/stats_log_api_gen/java_writer_q.h index 0f33b6cf94a1..f1cfc44494e5 100644 --- a/tools/stats_log_api_gen/java_writer_q.h +++ b/tools/stats_log_api_gen/java_writer_q.h @@ -16,14 +16,14 @@ #pragma once -#include "Collation.h" +#include <stdio.h> +#include <string.h> #include <map> #include <set> #include <vector> -#include <stdio.h> -#include <string.h> +#include "Collation.h" namespace android { namespace stats_log_api_gen { @@ -33,20 +33,15 @@ using namespace std; void write_java_q_logging_constants(FILE* out, const string& indent); int write_java_methods_q_schema( - FILE* out, - const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap, - const AtomDecl &attributionDecl, - const string& indent); + FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap, + const AtomDecl& attributionDecl, const string& indent); -void write_java_helpers_for_q_schema_methods( - FILE * out, - const AtomDecl &attributionDecl, - const int requiredHelpers, - const string& indent); +void write_java_helpers_for_q_schema_methods(FILE* out, const AtomDecl& attributionDecl, + const int requiredHelpers, const string& indent); int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms, - const AtomDecl &attributionDecl, const string& javaClass, - const string& javaPackage, const bool supportWorkSource); + const AtomDecl& attributionDecl, const string& javaClass, + const string& javaPackage, const bool supportWorkSource); } // namespace stats_log_api_gen } // namespace android diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 4f791a35be7b..fda57369d7bf 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -1,22 +1,21 @@ +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <map> +#include <set> +#include <vector> + #include "Collation.h" #include "atoms_info_writer.h" +#include "frameworks/base/cmds/statsd/src/atoms.pb.h" #include "java_writer.h" #include "java_writer_q.h" #include "native_writer.h" #include "utils.h" -#include "frameworks/base/cmds/statsd/src/atoms.pb.h" - -#include <map> -#include <set> -#include <vector> - -#include <getopt.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - using namespace google::protobuf; using namespace std; @@ -25,25 +24,34 @@ namespace stats_log_api_gen { using android::os::statsd::Atom; -static void -print_usage() -{ +static void print_usage() { fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n"); fprintf(stderr, "\n"); fprintf(stderr, "OPTIONS\n"); fprintf(stderr, " --cpp FILENAME the header file to output for write helpers\n"); fprintf(stderr, " --header FILENAME the cpp file to output for write helpers\n"); fprintf(stderr, - " --atomsInfoCpp FILENAME the header file to output for statsd metadata\n"); - fprintf(stderr, " --atomsInfoHeader FILENAME the cpp file to output for statsd metadata\n"); + " --atomsInfoCpp FILENAME the header file to output for " + "statsd metadata\n"); + fprintf(stderr, + " --atomsInfoHeader FILENAME the cpp file to output for statsd " + "metadata\n"); fprintf(stderr, " --help this message\n"); fprintf(stderr, " --java FILENAME the java file to output\n"); fprintf(stderr, " --module NAME optional, module name to generate outputs for\n"); - fprintf(stderr, " --namespace COMMA,SEP,NAMESPACE required for cpp/header with module\n"); - fprintf(stderr, " comma separated namespace of the files\n"); - fprintf(stderr," --importHeader NAME required for cpp/jni to say which header to import " + fprintf(stderr, + " --namespace COMMA,SEP,NAMESPACE required for cpp/header with " + "module\n"); + fprintf(stderr, + " comma separated namespace of " + "the files\n"); + fprintf(stderr, + " --importHeader NAME required for cpp/jni to say which header to " + "import " "for write helpers\n"); - fprintf(stderr," --atomsInfoImportHeader NAME required for cpp to say which header to import " + fprintf(stderr, + " --atomsInfoImportHeader NAME required for cpp to say which " + "header to import " "for statsd metadata\n"); fprintf(stderr, " --javaPackage PACKAGE the package for the java file.\n"); fprintf(stderr, " required for java with module\n"); @@ -51,17 +59,18 @@ print_usage() fprintf(stderr, " Optional for Java with module.\n"); fprintf(stderr, " Default is \"StatsLogInternal\"\n"); fprintf(stderr, " --supportQ Include runtime support for Android Q.\n"); - fprintf(stderr, " --worksource Include support for logging WorkSource objects.\n"); - fprintf(stderr, " --compileQ Include compile-time support for Android Q " + fprintf(stderr, + " --worksource Include support for logging WorkSource " + "objects.\n"); + fprintf(stderr, + " --compileQ Include compile-time support for Android Q " "(Java only).\n"); } /** * Do the argument parsing and execute the tasks. */ -static int -run(int argc, char const*const* argv) -{ +static int run(int argc, char const* const* argv) { string cppFilename; string headerFilename; string javaFilename; @@ -171,11 +180,8 @@ run(int argc, char const*const* argv) index++; } - if (cppFilename.size() == 0 - && headerFilename.size() == 0 - && javaFilename.size() == 0 - && atomsInfoHeaderFilename.size() == 0 - && atomsInfoCppFilename.size() == 0) { + if (cppFilename.size() == 0 && headerFilename.size() == 0 && javaFilename.size() == 0 && + atomsInfoHeaderFilename.size() == 0 && atomsInfoCppFilename.size() == 0) { print_usage(); return 1; } @@ -201,8 +207,8 @@ run(int argc, char const*const* argv) AtomDecl attributionDecl; vector<java_type_t> attributionSignature; - collate_atom(android::os::statsd::AttributionNode::descriptor(), - &attributionDecl, &attributionSignature); + collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl, + &attributionSignature); // Write the atoms info .cpp file if (atomsInfoCppFilename.size() != 0) { @@ -211,8 +217,8 @@ run(int argc, char const*const* argv) fprintf(stderr, "Unable to open file for write: %s\n", atomsInfoCppFilename.c_str()); return 1; } - errorCount = android::stats_log_api_gen::write_atoms_info_cpp( - out, atoms, cppNamespace, atomsInfoCppHeaderImport); + errorCount = android::stats_log_api_gen::write_atoms_info_cpp(out, atoms, cppNamespace, + atomsInfoCppHeaderImport); fclose(out); } @@ -227,7 +233,6 @@ run(int argc, char const*const* argv) fclose(out); } - // Write the .cpp file if (cppFilename.size() != 0) { FILE* out = fopen(cppFilename.c_str(), "w"); @@ -240,13 +245,14 @@ run(int argc, char const*const* argv) fprintf(stderr, "Must supply --namespace if supplying a specific module\n"); return 1; } - // If this is for a specific module, the header file to import must also be provided. + // If this is for a specific module, the header file to import must also be + // provided. if (moduleName != DEFAULT_MODULE_NAME && cppHeaderImport == DEFAULT_CPP_HEADER_IMPORT) { fprintf(stderr, "Must supply --headerImport if supplying a specific module\n"); return 1; } errorCount = android::stats_log_api_gen::write_stats_log_cpp( - out, atoms, attributionDecl, cppNamespace, cppHeaderImport, supportQ); + out, atoms, attributionDecl, cppNamespace, cppHeaderImport, supportQ); fclose(out); } @@ -261,8 +267,8 @@ run(int argc, char const*const* argv) if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) { fprintf(stderr, "Must supply --namespace if supplying a specific module\n"); } - errorCount = android::stats_log_api_gen::write_stats_log_header( - out, atoms, attributionDecl, cppNamespace); + errorCount = android::stats_log_api_gen::write_stats_log_header(out, atoms, attributionDecl, + cppNamespace); fclose(out); } @@ -291,8 +297,7 @@ run(int argc, char const*const* argv) if (compileQ) { errorCount = android::stats_log_api_gen::write_stats_log_java_q_for_module( - out, atoms, attributionDecl, javaClass, javaPackage, - supportWorkSource); + out, atoms, attributionDecl, javaClass, javaPackage, supportWorkSource); } else { errorCount = android::stats_log_api_gen::write_stats_log_java( out, atoms, attributionDecl, javaClass, javaPackage, supportQ, @@ -311,9 +316,7 @@ run(int argc, char const*const* argv) /** * Main. */ -int -main(int argc, char const*const* argv) -{ +int main(int argc, char const* const* argv) { GOOGLE_PROTOBUF_VERIFY_VERSION; return android::stats_log_api_gen::run(argc, argv); diff --git a/tools/stats_log_api_gen/native_writer.cpp b/tools/stats_log_api_gen/native_writer.cpp index 90dcae4197a2..0cf3225e7708 100644 --- a/tools/stats_log_api_gen/native_writer.cpp +++ b/tools/stats_log_api_gen/native_writer.cpp @@ -15,45 +15,38 @@ */ #include "native_writer.h" + #include "utils.h" namespace android { namespace stats_log_api_gen { -static void write_annotations( - FILE* out, int argIndex, - const FieldNumberToAnnotations& fieldNumberToAnnotations, - const string& methodPrefix, - const string& methodSuffix) { +static void write_annotations(FILE* out, int argIndex, + const FieldNumberToAnnotations& fieldNumberToAnnotations, + const string& methodPrefix, const string& methodSuffix) { auto fieldNumberToAnnotationsIt = fieldNumberToAnnotations.find(argIndex); if (fieldNumberToAnnotationsIt == fieldNumberToAnnotations.end()) { return; } - const set<shared_ptr<Annotation>>& annotations = - fieldNumberToAnnotationsIt->second; + const set<shared_ptr<Annotation>>& annotations = fieldNumberToAnnotationsIt->second; for (const shared_ptr<Annotation>& annotation : annotations) { // TODO(b/151744250): Group annotations for same atoms. // TODO(b/151786433): Write atom constant name instead of atom id literal. fprintf(out, " if (code == %d) {\n", annotation->atomId); - switch(annotation->type) { - // TODO(b/151776731): Check for reset state annotation and only include reset state - // when field value == default state annotation value. + switch (annotation->type) { + // TODO(b/151776731): Check for reset state annotation and only include + // reset state when field value == default state annotation value. case ANNOTATION_TYPE_INT: // TODO(b/151786433): Write annotation constant name instead of // annotation id literal. - fprintf(out, " %saddInt32Annotation(%s%d, %d);\n", - methodPrefix.c_str(), - methodSuffix.c_str(), - annotation->annotationId, - annotation->value.intValue); + fprintf(out, " %saddInt32Annotation(%s%d, %d);\n", methodPrefix.c_str(), + methodSuffix.c_str(), annotation->annotationId, annotation->value.intValue); break; case ANNOTATION_TYPE_BOOL: // TODO(b/151786433): Write annotation constant name instead of // annotation id literal. - fprintf(out, " %saddBoolAnnotation(%s%d, %s);\n", - methodPrefix.c_str(), - methodSuffix.c_str(), - annotation->annotationId, + fprintf(out, " %saddBoolAnnotation(%s%d, %s);\n", methodPrefix.c_str(), + methodSuffix.c_str(), annotation->annotationId, annotation->value.boolValue ? "true" : "false"); break; default: @@ -61,29 +54,28 @@ static void write_annotations( } fprintf(out, " }\n"); } - } static int write_native_stats_write_methods(FILE* out, const Atoms& atoms, - const AtomDecl& attributionDecl, const bool supportQ) { + const AtomDecl& attributionDecl, const bool supportQ) { fprintf(out, "\n"); for (auto signatureInfoMapIt = atoms.signatureInfoMap.begin(); - signatureInfoMapIt != atoms.signatureInfoMap.end(); signatureInfoMapIt++) { + signatureInfoMapIt != atoms.signatureInfoMap.end(); signatureInfoMapIt++) { vector<java_type_t> signature = signatureInfoMapIt->first; const FieldNumberToAnnotations& fieldNumberToAnnotations = signatureInfoMapIt->second; // Key value pairs not supported in native. if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) { continue; } - write_native_method_signature(out, "int stats_write", signature, - attributionDecl, " {"); + write_native_method_signature(out, "int stats_write", signature, attributionDecl, " {"); int argIndex = 1; if (supportQ) { fprintf(out, " StatsEventCompat event;\n"); fprintf(out, " event.setAtomId(code);\n"); + write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAnnotations, "event.", ""); for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + arg != signature.end(); arg++) { switch (*arg) { case JAVA_TYPE_ATTRIBUTION_CHAIN: { const char* uidName = attributionDecl.fields.front().name.c_str(); @@ -99,7 +91,7 @@ static int write_native_stats_write_methods(FILE* out, const Atoms& atoms, case JAVA_TYPE_BOOLEAN: fprintf(out, " event.writeBool(arg%d);\n", argIndex); break; - case JAVA_TYPE_INT: // Fall through. + case JAVA_TYPE_INT: // Fall through. case JAVA_TYPE_ENUM: fprintf(out, " event.writeInt32(arg%d);\n", argIndex); break; @@ -124,8 +116,10 @@ static int write_native_stats_write_methods(FILE* out, const Atoms& atoms, } else { fprintf(out, " AStatsEvent* event = AStatsEvent_obtain();\n"); fprintf(out, " AStatsEvent_setAtomId(event, code);\n"); + write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAnnotations, "AStatsEvent_", + "event, "); for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + arg != signature.end(); arg++) { switch (*arg) { case JAVA_TYPE_ATTRIBUTION_CHAIN: { const char* uidName = attributionDecl.fields.front().name.c_str(); @@ -140,13 +134,14 @@ static int write_native_stats_write_methods(FILE* out, const Atoms& atoms, case JAVA_TYPE_BYTE_ARRAY: fprintf(out, " AStatsEvent_writeByteArray(event, " - "reinterpret_cast<const uint8_t*>(arg%d.arg), arg%d.arg_length);\n", + "reinterpret_cast<const uint8_t*>(arg%d.arg), " + "arg%d.arg_length);\n", argIndex, argIndex); break; case JAVA_TYPE_BOOLEAN: fprintf(out, " AStatsEvent_writeBool(event, arg%d);\n", argIndex); break; - case JAVA_TYPE_INT: // Fall through. + case JAVA_TYPE_INT: // Fall through. case JAVA_TYPE_ENUM: fprintf(out, " AStatsEvent_writeInt32(event, arg%d);\n", argIndex); break; @@ -165,7 +160,7 @@ static int write_native_stats_write_methods(FILE* out, const Atoms& atoms, return 1; } write_annotations(out, argIndex, fieldNumberToAnnotations, "AStatsEvent_", - "event, "); + "event, "); argIndex++; } fprintf(out, " const int ret = AStatsEvent_write(event);\n"); @@ -178,10 +173,10 @@ static int write_native_stats_write_methods(FILE* out, const Atoms& atoms, } static void write_native_stats_write_non_chained_methods(FILE* out, const Atoms& atoms, - const AtomDecl& attributionDecl) { + const AtomDecl& attributionDecl) { fprintf(out, "\n"); for (auto signature_it = atoms.nonChainedSignatureInfoMap.begin(); - signature_it != atoms.nonChainedSignatureInfoMap.end(); signature_it++) { + signature_it != atoms.nonChainedSignatureInfoMap.end(); signature_it++) { vector<java_type_t> signature = signature_it->first; // Key value pairs not supported in native. if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) { @@ -189,7 +184,7 @@ static void write_native_stats_write_non_chained_methods(FILE* out, const Atoms& } write_native_method_signature(out, "int stats_write_non_chained", signature, - attributionDecl, " {"); + attributionDecl, " {"); vector<java_type_t> newSignature; @@ -212,17 +207,14 @@ static void write_native_stats_write_non_chained_methods(FILE* out, const Atoms& fprintf(out, "}\n\n"); } - } static void write_native_method_header( - FILE* out, - const string& methodName, + FILE* out, const string& methodName, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap, - const AtomDecl &attributionDecl) { - + const AtomDecl& attributionDecl) { for (auto signatureInfoMapIt = signatureInfoMap.begin(); - signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { + signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { vector<java_type_t> signature = signatureInfoMapIt->first; // Key value pairs not supported in native. @@ -233,9 +225,9 @@ static void write_native_method_header( } } -int write_stats_log_cpp(FILE *out, const Atoms &atoms, const AtomDecl &attributionDecl, - const string& cppNamespace, - const string& importHeader, const bool supportQ) { +int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl, + const string& cppNamespace, const string& importHeader, + const bool supportQ) { // Print prelude fprintf(out, "// This file is autogenerated\n"); fprintf(out, "\n"); @@ -260,8 +252,8 @@ int write_stats_log_cpp(FILE *out, const Atoms &atoms, const AtomDecl &attributi return 0; } -int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl, - const string& cppNamespace) { +int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl, + const string& cppNamespace) { // Print prelude fprintf(out, "// This file is autogenerated\n"); fprintf(out, "\n"); @@ -286,21 +278,18 @@ int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attrib fprintf(out, "//\n"); fprintf(out, "// Constants for enum values\n"); fprintf(out, "//\n\n"); - for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); - atom != atoms.decls.end(); atom++) { - + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); + atom++) { for (vector<AtomField>::const_iterator field = atom->fields.begin(); - field != atom->fields.end(); field++) { + field != atom->fields.end(); field++) { if (field->javaType == JAVA_TYPE_ENUM) { - fprintf(out, "// Values for %s.%s\n", atom->message.c_str(), - field->name.c_str()); + fprintf(out, "// Values for %s.%s\n", atom->message.c_str(), field->name.c_str()); for (map<int, string>::const_iterator value = field->enumValues.begin(); - value != field->enumValues.end(); value++) { + value != field->enumValues.end(); value++) { fprintf(out, "const int32_t %s__%s__%s = %d;\n", - make_constant_name(atom->message).c_str(), - make_constant_name(field->name).c_str(), - make_constant_name(value->second).c_str(), - value->first); + make_constant_name(atom->message).c_str(), + make_constant_name(field->name).c_str(), + make_constant_name(value->second).c_str(), value->first); } fprintf(out, "\n"); } @@ -325,8 +314,8 @@ int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attrib fprintf(out, "//\n"); fprintf(out, "// Write flattened methods\n"); fprintf(out, "//\n"); - write_native_method_header(out, "int stats_write_non_chained", - atoms.nonChainedSignatureInfoMap, attributionDecl); + write_native_method_header(out, "int stats_write_non_chained", atoms.nonChainedSignatureInfoMap, + attributionDecl); fprintf(out, "\n"); write_closing_namespace(out, cppNamespace); diff --git a/tools/stats_log_api_gen/native_writer.h b/tools/stats_log_api_gen/native_writer.h index 6e603259600d..264d4db29fc9 100644 --- a/tools/stats_log_api_gen/native_writer.h +++ b/tools/stats_log_api_gen/native_writer.h @@ -16,22 +16,22 @@ #pragma once -#include "Collation.h" - #include <stdio.h> #include <string.h> +#include "Collation.h" + namespace android { namespace stats_log_api_gen { using namespace std; -int write_stats_log_cpp(FILE *out, const Atoms &atoms, const AtomDecl &attributionDecl, - const string& cppNamespace, const string& importHeader, - const bool supportQ); +int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl, + const string& cppNamespace, const string& importHeader, + const bool supportQ); -int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl, - const string& cppNamespace); +int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl, + const string& cppNamespace); } // namespace stats_log_api_gen } // namespace android diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp index 5032ac088f4f..9878926bb045 100644 --- a/tools/stats_log_api_gen/test_collation.cpp +++ b/tools/stats_log_api_gen/test_collation.cpp @@ -15,11 +15,10 @@ */ #include <gtest/gtest.h> +#include <stdio.h> -#include "frameworks/base/tools/stats_log_api_gen/test.pb.h" #include "Collation.h" - -#include <stdio.h> +#include "frameworks/base/tools/stats_log_api_gen/test.pb.h" namespace android { namespace stats_log_api_gen { @@ -31,14 +30,13 @@ using std::vector; /** * Return whether the map contains a vector of the elements provided. */ -static bool -map_contains_vector(const map<vector<java_type_t>, FieldNumberToAnnotations>& s, int count, ...) -{ +static bool map_contains_vector(const map<vector<java_type_t>, FieldNumberToAnnotations>& s, + int count, ...) { va_list args; vector<java_type_t> v; va_start(args, count); - for (int i=0; i<count; i++) { + for (int i = 0; i < count; i++) { v.push_back((java_type_t)va_arg(args, int)); } va_end(args); @@ -49,34 +47,33 @@ map_contains_vector(const map<vector<java_type_t>, FieldNumberToAnnotations>& s, /** * Expect that the provided map contains the elements provided. */ -#define EXPECT_MAP_CONTAINS_SIGNATURE(s, ...) \ - do { \ - int count = sizeof((int[]){__VA_ARGS__})/sizeof(int); \ +#define EXPECT_MAP_CONTAINS_SIGNATURE(s, ...) \ + do { \ + int count = sizeof((int[]){__VA_ARGS__}) / sizeof(int); \ EXPECT_TRUE(map_contains_vector(s, count, __VA_ARGS__)); \ - } while(0) + } while (0) /** Expects that the provided atom has no enum values for any field. */ -#define EXPECT_NO_ENUM_FIELD(atom) \ - do { \ +#define EXPECT_NO_ENUM_FIELD(atom) \ + do { \ for (vector<AtomField>::const_iterator field = atom->fields.begin(); \ - field != atom->fields.end(); field++) { \ - EXPECT_TRUE(field->enumValues.empty()); \ - } \ - } while(0) + field != atom->fields.end(); field++) { \ + EXPECT_TRUE(field->enumValues.empty()); \ + } \ + } while (0) /** Expects that exactly one specific field has expected enum values. */ -#define EXPECT_HAS_ENUM_FIELD(atom, field_name, values) \ - do { \ +#define EXPECT_HAS_ENUM_FIELD(atom, field_name, values) \ + do { \ for (vector<AtomField>::const_iterator field = atom->fields.begin(); \ - field != atom->fields.end(); field++) { \ - if (field->name == field_name) { \ - EXPECT_EQ(field->enumValues, values); \ - } else { \ - EXPECT_TRUE(field->enumValues.empty()); \ - } \ - } \ - } while(0) - + field != atom->fields.end(); field++) { \ + if (field->name == field_name) { \ + EXPECT_EQ(field->enumValues, values); \ + } else { \ + EXPECT_TRUE(field->enumValues.empty()); \ + } \ + } \ + } while (0) /** * Test a correct collation, with all the types. @@ -95,23 +92,22 @@ TEST(CollationTest, CollateStats) { EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT, JAVA_TYPE_INT); // AllTypesAtom - EXPECT_MAP_CONTAINS_SIGNATURE( - atoms.signatureInfoMap, - JAVA_TYPE_ATTRIBUTION_CHAIN, // AttributionChain - JAVA_TYPE_FLOAT, // float - JAVA_TYPE_LONG, // int64 - JAVA_TYPE_LONG, // uint64 - JAVA_TYPE_INT, // int32 - JAVA_TYPE_LONG, // fixed64 - JAVA_TYPE_INT, // fixed32 - JAVA_TYPE_BOOLEAN, // bool - JAVA_TYPE_STRING, // string - JAVA_TYPE_INT, // uint32 - JAVA_TYPE_INT, // AnEnum - JAVA_TYPE_INT, // sfixed32 - JAVA_TYPE_LONG, // sfixed64 - JAVA_TYPE_INT, // sint32 - JAVA_TYPE_LONG // sint64 + EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, + JAVA_TYPE_ATTRIBUTION_CHAIN, // AttributionChain + JAVA_TYPE_FLOAT, // float + JAVA_TYPE_LONG, // int64 + JAVA_TYPE_LONG, // uint64 + JAVA_TYPE_INT, // int32 + JAVA_TYPE_LONG, // fixed64 + JAVA_TYPE_INT, // fixed32 + JAVA_TYPE_BOOLEAN, // bool + JAVA_TYPE_STRING, // string + JAVA_TYPE_INT, // uint32 + JAVA_TYPE_INT, // AnEnum + JAVA_TYPE_INT, // sfixed32 + JAVA_TYPE_LONG, // sfixed64 + JAVA_TYPE_INT, // sint32 + JAVA_TYPE_LONG // sint64 ); set<AtomDecl>::const_iterator atom = atoms.decls.begin(); @@ -156,7 +152,8 @@ TEST(CollationTest, NonMessageTypeFails) { } /** - * Test that atoms that have non-primitive types or repeated fields are rejected. + * Test that atoms that have non-primitive types or repeated fields are + * rejected. */ TEST(CollationTest, FailOnBadTypes) { Atoms atoms; @@ -170,18 +167,20 @@ TEST(CollationTest, FailOnBadTypes) { */ TEST(CollationTest, FailOnSkippedFieldsSingle) { Atoms atoms; - int errorCount = collate_atoms(BadSkippedFieldSingle::descriptor(), DEFAULT_MODULE_NAME, &atoms); + int errorCount = + collate_atoms(BadSkippedFieldSingle::descriptor(), DEFAULT_MODULE_NAME, &atoms); EXPECT_EQ(1, errorCount); } /** - * Test that atoms that skip field numbers (not in the first position, and multiple - * times) are rejected. + * Test that atoms that skip field numbers (not in the first position, and + * multiple times) are rejected. */ TEST(CollationTest, FailOnSkippedFieldsMultiple) { Atoms atoms; - int errorCount = collate_atoms(BadSkippedFieldMultiple::descriptor(), DEFAULT_MODULE_NAME, &atoms); + int errorCount = + collate_atoms(BadSkippedFieldMultiple::descriptor(), DEFAULT_MODULE_NAME, &atoms); EXPECT_EQ(2, errorCount); } @@ -191,11 +190,11 @@ TEST(CollationTest, FailOnSkippedFieldsMultiple) { * rejected. */ TEST(CollationTest, FailBadAttributionNodePosition) { - Atoms atoms; - int errorCount = - collate_atoms(BadAttributionNodePosition::descriptor(), DEFAULT_MODULE_NAME, &atoms); + Atoms atoms; + int errorCount = + collate_atoms(BadAttributionNodePosition::descriptor(), DEFAULT_MODULE_NAME, &atoms); - EXPECT_EQ(1, errorCount); + EXPECT_EQ(1, errorCount); } TEST(CollationTest, FailOnBadStateAtomOptions) { @@ -270,8 +269,8 @@ TEST(CollationTest, RecognizeModuleAtom) { const set<shared_ptr<Annotation>>& annotations = fieldNumberToAnnotations.at(1); EXPECT_EQ(2u, annotations.size()); for (const shared_ptr<Annotation> annotation : annotations) { - EXPECT_TRUE(annotation->annotationId == ANNOTATION_ID_IS_UID - || annotation->annotationId == ANNOTATION_ID_STATE_OPTION); + EXPECT_TRUE(annotation->annotationId == ANNOTATION_ID_IS_UID || + annotation->annotationId == ANNOTATION_ID_STATE_OPTION); if (ANNOTATION_ID_IS_UID == annotation->annotationId) { EXPECT_EQ(1, annotation->atomId); EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type); @@ -303,12 +302,11 @@ TEST(CollationTest, RecognizeModule1Atom) { EXPECT_EQ(1u, fieldNumberToAnnotations.size()); int fieldNumber = 1; EXPECT_NE(fieldNumberToAnnotations.end(), fieldNumberToAnnotations.find(fieldNumber)); - const set<shared_ptr<Annotation>>& annotations = - fieldNumberToAnnotations.at(fieldNumber); + const set<shared_ptr<Annotation>>& annotations = fieldNumberToAnnotations.at(fieldNumber); EXPECT_EQ(2u, annotations.size()); for (const shared_ptr<Annotation> annotation : annotations) { - EXPECT_TRUE(annotation->annotationId == ANNOTATION_ID_IS_UID - || annotation->annotationId == ANNOTATION_ID_STATE_OPTION); + EXPECT_TRUE(annotation->annotationId == ANNOTATION_ID_IS_UID || + annotation->annotationId == ANNOTATION_ID_STATE_OPTION); if (ANNOTATION_ID_IS_UID == annotation->annotationId) { EXPECT_EQ(1, annotation->atomId); EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type); diff --git a/tools/stats_log_api_gen/utils.cpp b/tools/stats_log_api_gen/utils.cpp index 7314127d5dfd..0262488e8501 100644 --- a/tools/stats_log_api_gen/utils.cpp +++ b/tools/stats_log_api_gen/utils.cpp @@ -22,9 +22,9 @@ namespace android { namespace stats_log_api_gen { static void build_non_chained_decl_map(const Atoms& atoms, - std::map<int, set<AtomDecl>::const_iterator>* decl_map) { + std::map<int, set<AtomDecl>::const_iterator>* decl_map) { for (set<AtomDecl>::const_iterator atom = atoms.non_chained_decls.begin(); - atom != atoms.non_chained_decls.end(); atom++) { + atom != atoms.non_chained_decls.end(); atom++) { decl_map->insert(std::make_pair(atom->code, atom)); } } @@ -36,7 +36,7 @@ string make_constant_name(const string& str) { string result; const int N = str.size(); bool underscore_next = false; - for (int i=0; i<N; i++) { + for (int i = 0; i < N; i++) { char c = str[i]; if (c >= 'A' && c <= 'Z') { if (underscore_next) { @@ -99,7 +99,8 @@ const char* java_type_name(java_type_t type) { } // Native -// Writes namespaces for the cpp and header files, returning the number of namespaces written. +// Writes namespaces for the cpp and header files, returning the number of +// namespaces written. void write_namespace(FILE* out, const string& cppNamespaces) { vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ","); for (string cppNamespace : cppNamespaceVec) { @@ -115,35 +116,31 @@ void write_closing_namespace(FILE* out, const string& cppNamespaces) { } } -static void write_cpp_usage( - FILE* out, const string& method_name, const string& atom_code_name, - const AtomDecl& atom, const AtomDecl &attributionDecl) { - fprintf(out, " * Usage: %s(StatsLog.%s", method_name.c_str(), - atom_code_name.c_str()); +static void write_cpp_usage(FILE* out, const string& method_name, const string& atom_code_name, + const AtomDecl& atom, const AtomDecl& attributionDecl) { + fprintf(out, " * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str()); - for (vector<AtomField>::const_iterator field = atom.fields.begin(); - field != atom.fields.end(); field++) { + for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end(); + field++) { if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { for (auto chainField : attributionDecl.fields) { if (chainField.javaType == JAVA_TYPE_STRING) { - fprintf(out, ", const std::vector<%s>& %s", - cpp_type_name(chainField.javaType), - chainField.name.c_str()); + fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType), + chainField.name.c_str()); } else { fprintf(out, ", const %s* %s, size_t %s_length", - cpp_type_name(chainField.javaType), - chainField.name.c_str(), chainField.name.c_str()); + cpp_type_name(chainField.javaType), chainField.name.c_str(), + chainField.name.c_str()); } } } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", const std::map<int, int32_t>& %s_int" - ", const std::map<int, int64_t>& %s_long" - ", const std::map<int, char const*>& %s_str" - ", const std::map<int, float>& %s_float", - field->name.c_str(), - field->name.c_str(), - field->name.c_str(), - field->name.c_str()); + fprintf(out, + ", const std::map<int, int32_t>& %s_int" + ", const std::map<int, int64_t>& %s_long" + ", const std::map<int, char const*>& %s_str" + ", const std::map<int, float>& %s_float", + field->name.c_str(), field->name.c_str(), field->name.c_str(), + field->name.c_str()); } else { fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str()); } @@ -162,8 +159,8 @@ void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& size_t i = 0; // Print atom constants - for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); - atom != atoms.decls.end(); atom++) { + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); + atom++) { string constant = make_constant_name(atom->name); fprintf(out, "\n"); fprintf(out, " /**\n"); @@ -173,7 +170,7 @@ void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code); if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) { write_cpp_usage(out, "stats_write_non_chained", constant, *non_chained_decl->second, - attributionDecl); + attributionDecl); } fprintf(out, " */\n"); char const* const comma = (i == atoms.decls.size() - 1) ? "" : ","; @@ -186,30 +183,30 @@ void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& } void write_native_method_signature(FILE* out, const string& methodName, - const vector<java_type_t>& signature, const AtomDecl& attributionDecl, - const string& closer) { + const vector<java_type_t>& signature, + const AtomDecl& attributionDecl, const string& closer) { fprintf(out, "%s(int32_t code", methodName.c_str()); int argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { for (auto chainField : attributionDecl.fields) { if (chainField.javaType == JAVA_TYPE_STRING) { - fprintf(out, ", const std::vector<%s>& %s", - cpp_type_name(chainField.javaType), - chainField.name.c_str()); + fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType), + chainField.name.c_str()); } else { - fprintf(out, ", const %s* %s, size_t %s_length", - cpp_type_name(chainField.javaType), - chainField.name.c_str(), chainField.name.c_str()); + fprintf(out, ", const %s* %s, size_t %s_length", + cpp_type_name(chainField.javaType), chainField.name.c_str(), + chainField.name.c_str()); } } } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", const std::map<int, int32_t>& arg%d_1, " - "const std::map<int, int64_t>& arg%d_2, " - "const std::map<int, char const*>& arg%d_3, " - "const std::map<int, float>& arg%d_4", - argIndex, argIndex, argIndex, argIndex); + fprintf(out, + ", const std::map<int, int32_t>& arg%d_1, " + "const std::map<int, int64_t>& arg%d_2, " + "const std::map<int, char const*>& arg%d_3, " + "const std::map<int, float>& arg%d_4", + argIndex, argIndex, argIndex, argIndex); } else { fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex); } @@ -219,27 +216,27 @@ void write_native_method_signature(FILE* out, const string& methodName, } void write_native_method_call(FILE* out, const string& methodName, - const vector<java_type_t>& signature, const AtomDecl& attributionDecl, int argIndex) { + const vector<java_type_t>& signature, const AtomDecl& attributionDecl, + int argIndex) { fprintf(out, "%s(code", methodName.c_str()); - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { - if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { - for (auto chainField : attributionDecl.fields) { - if (chainField.javaType == JAVA_TYPE_STRING) { - fprintf(out, ", %s", + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { + if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { + for (auto chainField : attributionDecl.fields) { + if (chainField.javaType == JAVA_TYPE_STRING) { + fprintf(out, ", %s", chainField.name.c_str()); + } else { + fprintf(out, ", %s, %s_length", chainField.name.c_str(), chainField.name.c_str()); - } else { - fprintf(out, ", %s, %s_length", - chainField.name.c_str(), chainField.name.c_str()); - } - } - } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex, - argIndex, argIndex, argIndex); - } else { - fprintf(out, ", arg%d", argIndex); - } - argIndex++; + } + } + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex, argIndex, argIndex, + argIndex); + } else { + fprintf(out, ", arg%d", argIndex); + } + argIndex++; } fprintf(out, ");\n"); } @@ -252,8 +249,8 @@ void write_java_atom_codes(FILE* out, const Atoms& atoms) { build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map); // Print constants for the atom codes. - for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); - atom != atoms.decls.end(); atom++) { + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); + atom++) { string constant = make_constant_name(atom->name); fprintf(out, "\n"); fprintf(out, " /**\n"); @@ -271,20 +268,19 @@ void write_java_atom_codes(FILE* out, const Atoms& atoms) { void write_java_enum_values(FILE* out, const Atoms& atoms) { fprintf(out, " // Constants for enum values.\n\n"); - for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); - atom != atoms.decls.end(); atom++) { + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end(); + atom++) { for (vector<AtomField>::const_iterator field = atom->fields.begin(); - field != atom->fields.end(); field++) { + field != atom->fields.end(); field++) { if (field->javaType == JAVA_TYPE_ENUM) { fprintf(out, " // Values for %s.%s\n", atom->message.c_str(), - field->name.c_str()); + field->name.c_str()); for (map<int, string>::const_iterator value = field->enumValues.begin(); - value != field->enumValues.end(); value++) { + value != field->enumValues.end(); value++) { fprintf(out, " public static final int %s__%s__%s = %d;\n", - make_constant_name(atom->message).c_str(), - make_constant_name(field->name).c_str(), - make_constant_name(value->second).c_str(), - value->first); + make_constant_name(atom->message).c_str(), + make_constant_name(field->name).c_str(), + make_constant_name(value->second).c_str(), value->first); } fprintf(out, "\n"); } @@ -293,11 +289,11 @@ void write_java_enum_values(FILE* out, const Atoms& atoms) { } void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name, - const AtomDecl& atom) { - fprintf(out, " * Usage: StatsLog.%s(StatsLog.%s", - method_name.c_str(), atom_code_name.c_str()); - for (vector<AtomField>::const_iterator field = atom.fields.begin(); - field != atom.fields.end(); field++) { + const AtomDecl& atom) { + fprintf(out, " * Usage: StatsLog.%s(StatsLog.%s", method_name.c_str(), + atom_code_name.c_str()); + for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end(); + field++) { if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { fprintf(out, ", android.os.WorkSource workSource"); } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) { @@ -312,16 +308,15 @@ void write_java_usage(FILE* out, const string& method_name, const string& atom_c } int write_java_non_chained_methods( - FILE* out, - const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap) { + FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap) { for (auto signatureInfoMapIt = signatureInfoMap.begin(); - signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { + signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { // Print method signature. fprintf(out, " public static void write_non_chained(int code"); vector<java_type_t> signature = signatureInfoMapIt->first; int argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { fprintf(stderr, "Non chained signatures should not have attribution chains.\n"); return 1; @@ -337,8 +332,8 @@ int write_java_non_chained_methods( fprintf(out, " write(code"); argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { // First two args are uid and tag of attribution chain. if (argIndex == 1) { fprintf(out, ", new int[] {arg%d}", argIndex); @@ -357,23 +352,24 @@ int write_java_non_chained_methods( } int write_java_work_source_methods( - FILE* out, - const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap) { + FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap) { fprintf(out, " // WorkSource methods.\n"); for (auto signatureInfoMapIt = signatureInfoMap.begin(); - signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { + signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { vector<java_type_t> signature = signatureInfoMapIt->first; // Determine if there is Attribution in this signature. int attributionArg = -1; int argIndexMax = 0; - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { argIndexMax++; if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { if (attributionArg > -1) { fprintf(stderr, "An atom contains multiple AttributionNode fields.\n"); fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n"); - fprintf(out, "\n// Invalid for WorkSource: more than one attribution chain.\n"); + fprintf(out, + "\n// Invalid for WorkSource: more than one attribution " + "chain.\n"); return 1; } attributionArg = argIndexMax; @@ -387,8 +383,8 @@ int write_java_work_source_methods( // Method header (signature) fprintf(out, " public static void write(int code"); int argIndex = 1; - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { + for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); + arg++) { if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { fprintf(out, ", android.os.WorkSource ws"); } else { @@ -398,36 +394,40 @@ int write_java_work_source_methods( } fprintf(out, ") {\n"); - // write_non_chained() component. TODO: Remove when flat uids are no longer needed. + // write_non_chained() component. TODO: Remove when flat uids are no longer + // needed. fprintf(out, " for (int i = 0; i < ws.size(); ++i) {\n"); fprintf(out, " write_non_chained(code"); for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) { if (argIndex == attributionArg) { fprintf(out, ", ws.getUid(i), ws.getPackageName(i)"); } else { - fprintf(out, ", arg%d", argIndex); + fprintf(out, ", arg%d", argIndex); } } fprintf(out, ");\n"); - fprintf(out, " }\n"); // close for-loop + fprintf(out, " }\n"); // close for-loop // write() component. - fprintf(out, " java.util.List<android.os.WorkSource.WorkChain> workChains = " + fprintf(out, + " java.util.List<android.os.WorkSource.WorkChain> workChains = " "ws.getWorkChains();\n"); fprintf(out, " if (workChains != null) {\n"); - fprintf(out, " for (android.os.WorkSource.WorkChain wc : workChains) {\n"); + fprintf(out, + " for (android.os.WorkSource.WorkChain wc : workChains) " + "{\n"); fprintf(out, " write(code"); for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) { if (argIndex == attributionArg) { fprintf(out, ", wc.getUids(), wc.getTags()"); } else { - fprintf(out, ", arg%d", argIndex); + fprintf(out, ", arg%d", argIndex); } } fprintf(out, ");\n"); - fprintf(out, " }\n"); // close for-loop - fprintf(out, " }\n"); // close if - fprintf(out, " }\n"); // close method + fprintf(out, " }\n"); // close for-loop + fprintf(out, " }\n"); // close if + fprintf(out, " }\n"); // close method } return 0; } diff --git a/tools/stats_log_api_gen/utils.h b/tools/stats_log_api_gen/utils.h index a6b3ef9fe99e..468f3233d77f 100644 --- a/tools/stats_log_api_gen/utils.h +++ b/tools/stats_log_api_gen/utils.h @@ -16,14 +16,14 @@ #pragma once -#include "Collation.h" +#include <stdio.h> +#include <string.h> #include <map> #include <set> #include <vector> -#include <stdio.h> -#include <string.h> +#include "Collation.h" namespace android { namespace stats_log_api_gen { @@ -52,11 +52,12 @@ void write_closing_namespace(FILE* out, const string& cppNamespaces); void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl); void write_native_method_signature(FILE* out, const string& methodName, - const vector<java_type_t>& signature, const AtomDecl& attributionDecl, - const string& closer); + const vector<java_type_t>& signature, + const AtomDecl& attributionDecl, const string& closer); void write_native_method_call(FILE* out, const string& methodName, - const vector<java_type_t>& signature, const AtomDecl& attributionDecl, int argIndex = 1); + const vector<java_type_t>& signature, const AtomDecl& attributionDecl, + int argIndex = 1); // Common Java helpers. void write_java_atom_codes(FILE* out, const Atoms& atoms); @@ -64,14 +65,13 @@ void write_java_atom_codes(FILE* out, const Atoms& atoms); void write_java_enum_values(FILE* out, const Atoms& atoms); void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name, - const AtomDecl& atom); + const AtomDecl& atom); -int write_java_non_chained_methods(FILE* out, const map<vector<java_type_t>, - FieldNumberToAnnotations>& signatureInfoMap); +int write_java_non_chained_methods( + FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap); int write_java_work_source_methods( - FILE* out, - const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap); + FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap); } // namespace stats_log_api_gen } // namespace android diff --git a/wifi/Android.bp b/wifi/Android.bp index 9d9a495d68ef..d0f1a26f7dbf 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -170,24 +170,23 @@ droidstubs { java_library { name: "framework-wifi-stubs-publicapi", srcs: [":framework-wifi-stubs-srcs-publicapi"], + defaults: ["framework-module-stubs-lib-defaults-publicapi"], + // TODO(b/151134996): remove this sdk_version: "current", - installable: false, } java_library { name: "framework-wifi-stubs-systemapi", srcs: [":framework-wifi-stubs-srcs-systemapi"], - sdk_version: "system_current", libs: ["framework-annotations-lib"], - installable: false, + defaults: ["framework-module-stubs-lib-defaults-systemapi"], } java_library { name: "framework-wifi-stubs-module_libs_api", srcs: [":framework-wifi-stubs-srcs-module_libs_api"], - sdk_version: "module_current", libs: ["framework-annotations-lib"], - installable: false, + defaults: ["framework-module-stubs-lib-defaults-module_libs_api"], } // defaults for tests that need to build against framework-wifi's @hide APIs diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index a269e17a8cff..457e0db9dc54 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -516,9 +516,6 @@ public final class SoftApConfiguration implements Parcelable { public WifiConfiguration toWifiConfiguration() { WifiConfiguration wifiConfig = new WifiConfiguration(); wifiConfig.SSID = mSsid; - if (mBssid != null) { - wifiConfig.BSSID = mBssid.toString(); - } wifiConfig.preSharedKey = mPassphrase; wifiConfig.hiddenSSID = mHiddenSsid; wifiConfig.apChannel = mChannel; @@ -662,8 +659,6 @@ public final class SoftApConfiguration implements Parcelable { /** * Specifies a BSSID for the AP. * <p> - * Only supported when configuring a local-only hotspot. - * <p> * <li>If not set, defaults to null.</li> * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is * responsible for avoiding collisions. diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index ba68d170364c..b110a6139429 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -2356,6 +2356,8 @@ public class WifiConfiguration implements Parcelable { sbuf.append(" lcuid=" + lastConnectUid); sbuf.append(" allowAutojoin=" + allowAutojoin); sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected); + sbuf.append(" mostRecentlyConnected=" + isMostRecentlyConnected); + sbuf.append(" "); if (this.lastConnected != 0) { @@ -2964,4 +2966,11 @@ public class WifiConfiguration implements Parcelable { return mPasspointUniqueId; } + /** + * If network is one of the most recently connected. + * For framework internal use only. Do not parcel. + * @hide + */ + public boolean isMostRecentlyConnected = false; + } 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> |