diff options
613 files changed, 18766 insertions, 11694 deletions
diff --git a/Android.bp b/Android.bp index 3f06ffd7ecd8..6653bd44966b 100644 --- a/Android.bp +++ b/Android.bp @@ -577,25 +577,6 @@ platform_compat_config { src: ":framework-annotation-proc", } -// A library including just UnsupportedAppUsage.java classes. -// -// Provided for target so that libraries can use it without depending on -// the whole of framework or the core platform API. -// -// Built for host so that the annotation processor can also use this annotation. -java_library { - name: "unsupportedappusage-annotation", - host_supported: true, - srcs: [ - "core/java/android/annotation/IntDef.java", - ], - static_libs: [ - "art.module.api.annotations", - ], - - sdk_version: "core_current", -} - // A temporary build target that is conditionally included on the bootclasspath if // android.test.base library has been removed and which provides support for // maintaining backwards compatibility for APKs that target pre-P and depend on @@ -722,6 +703,7 @@ filegroup { srcs: [ "core/java/android/annotation/StringDef.java", "core/java/android/net/annotations/PolicyDirection.java", + "core/java/com/android/internal/util/HexDump.java", "core/java/com/android/internal/util/IState.java", "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", @@ -970,7 +952,6 @@ filegroup { "core/java/android/content/pm/InstallationFileLocation.aidl", "core/java/android/content/pm/IDataLoaderStatusListener.aidl", "core/java/android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl", - "core/java/android/content/pm/NamedParcelFileDescriptor.aidl", ], path: "core/java", } @@ -1133,16 +1114,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/Android.mk b/Android.mk index aea0c951052f..3b307146770e 100644 --- a/Android.mk +++ b/Android.mk @@ -39,11 +39,6 @@ INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document)) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE)) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE)) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE)) -$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE):apistubs/android/public/api/android.txt) -$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE):apistubs/android/system/api/android.txt) -$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE):apistubs/android/test/api/android.txt) -$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_MODULE_LIB_API_FILE):apistubs/android/module-lib/api/android.txt) -$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_SERVER_API_FILE):apistubs/android/system-server/api/android.txt) # sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip. # So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to diff --git a/StubLibraries.bp b/StubLibraries.bp index 12f211df4549..ccd873352a33 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -115,6 +115,11 @@ droidstubs { baseline_file: "api/lint-baseline.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/public/api", + dest: "android.txt", + }, jdiff_enabled: true, } @@ -156,6 +161,11 @@ droidstubs { baseline_file: "api/system-lint-baseline.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system/api", + dest: "android.txt", + }, jdiff_enabled: true, } @@ -179,6 +189,11 @@ droidstubs { baseline_file: "api/test-lint-baseline.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/test/api", + dest: "android.txt", + }, } ///////////////////////////////////////////////////////////////////// @@ -214,6 +229,11 @@ droidstubs { baseline_file: "api/module-lib-lint-baseline.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/module-lib/api", + dest: "android.txt", + }, } diff --git a/apex/Android.bp b/apex/Android.bp index 88c43f984847..2f4294588d7a 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -60,6 +60,10 @@ stubs_defaults { removed_api_file: "api/removed.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/public/api", + }, } stubs_defaults { @@ -74,6 +78,40 @@ stubs_defaults { removed_api_file: "api/system-removed.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system/api", + }, +} + +java_defaults { + name: "framework-module-stubs-lib-defaults-publicapi", + installable: false, + sdk_version: "module_current", + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/public", + }, +} + +java_defaults { + name: "framework-module-stubs-lib-defaults-systemapi", + installable: false, + sdk_version: "module_current", + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system", + }, +} + +java_defaults { + name: "framework-module-stubs-lib-defaults-module_libs_api", + installable: false, + sdk_version: "module_current", + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/module-lib", + }, } // The defaults for module_libs comes in two parts - defaults for API checks @@ -93,6 +131,10 @@ stubs_defaults { removed_api_file: "api/module-lib-removed.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/module-lib/api", + }, } stubs_defaults { @@ -113,10 +155,18 @@ stubs_defaults { removed_api_file: "api/removed.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system-server/api", + }, } // Empty for now, but a convenient place to add rules for all // module java_library system_server stub libs. java_defaults { name: "service-module-stubs-defaults", + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system-server", + }, } 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..e472d052f32f 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())) @@ -1349,8 +1353,15 @@ public class BlobStoreManagerService extends SystemService { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "Caller is not allowed to call this; caller=" + Binder.getCallingUid()); - mHandler.post(PooledLambda.obtainRunnable(remoteCallback::sendResult, null) - .recycleOnUse()); + // We post messages back and forth between mHandler thread and mBackgroundHandler + // thread while committing a blob. We need to replicate the same pattern here to + // ensure pending messages have been handled. + mHandler.post(() -> { + mBackgroundHandler.post(() -> { + mHandler.post(PooledLambda.obtainRunnable(remoteCallback::sendResult, null) + .recycleOnUse()); + }); + }); } @Override 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/api/current.txt b/apex/media/framework/api/current.txt index 839fb5143196..9cec748e7b8e 100644 --- a/apex/media/framework/api/current.txt +++ b/apex/media/framework/api/current.txt @@ -29,7 +29,7 @@ package android.media { method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException; method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...); method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer); - method @Nullable public String getParserName(); + method @NonNull public String getParserName(); method @NonNull public static java.util.List<java.lang.String> getParserNames(@NonNull android.media.MediaFormat); method public void release(); method public void seek(@NonNull android.media.MediaParser.SeekPoint); @@ -65,6 +65,7 @@ package android.media { field public static final String PARSER_NAME_OGG = "android.media.mediaparser.OggParser"; field public static final String PARSER_NAME_PS = "android.media.mediaparser.PsParser"; field public static final String PARSER_NAME_TS = "android.media.mediaparser.TsParser"; + field public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN"; field public static final String PARSER_NAME_WAV = "android.media.mediaparser.WavParser"; } diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index 02c55b7c8df9..c0e3d55b45a6 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; @@ -450,6 +452,7 @@ public final class MediaParser { @StringDef( prefix = {"PARSER_NAME_"}, value = { + PARSER_NAME_UNKNOWN, PARSER_NAME_MATROSKA, PARSER_NAME_FMP4, PARSER_NAME_MP4, @@ -467,6 +470,7 @@ public final class MediaParser { }) public @interface ParserName {} + public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN"; public static final String PARSER_NAME_MATROSKA = "android.media.mediaparser.MatroskaParser"; public static final String PARSER_NAME_FMP4 = "android.media.mediaparser.FragmentedMp4Parser"; public static final String PARSER_NAME_MP4 = "android.media.mediaparser.Mp4Parser"; @@ -688,12 +692,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. @@ -763,14 +838,14 @@ public final class MediaParser { * Returns the name of the backing parser implementation. * * <p>If this instance was creating using {@link #createByName}, the provided name is returned. - * If this instance was created using {@link #create}, this method will return null until the - * first call to {@link #advance}, after which the name of the backing parser implementation is - * returned. + * If this instance was created using {@link #create}, this method will return {@link + * #PARSER_NAME_UNKNOWN} until the first call to {@link #advance}, after which the name of the + * backing parser implementation is returned. * * @return The name of the backing parser implementation, or null if the backing parser * implementation has not yet been selected. */ - @Nullable + @NonNull @ParserName public String getParserName() { return mExtractorName; @@ -807,7 +882,7 @@ public final class MediaParser { // TODO: Apply parameters when creating extractor instances. if (mExtractor == null) { - if (mExtractorName != null) { + if (!mExtractorName.equals(PARSER_NAME_UNKNOWN)) { mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance(); mExtractor.init(new ExtractorOutputAdapter()); } else { @@ -901,9 +976,7 @@ public final class MediaParser { mParserParameters = new HashMap<>(); mOutputConsumer = outputConsumer; mParserNamesPool = parserNamesPool; - if (!sniff) { - mExtractorName = parserNamesPool[0]; - } + mExtractorName = sniff ? PARSER_NAME_UNKNOWN : parserNamesPool[0]; mPositionHolder = new PositionHolder(); mDataSource = new InputReadingDataSource(); removePendingSeek(); diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp index 6d962008460f..793247e88614 100644 --- a/apex/permission/framework/Android.bp +++ b/apex/permission/framework/Android.bp @@ -31,6 +31,10 @@ java_library { "com.android.permission", "test_com.android.permission", ], + permitted_packages: [ + "android.permission", + "android.app.role", + ], hostdex: true, installable: true, visibility: [ @@ -84,20 +88,17 @@ droidstubs { java_library { name: "framework-permission-stubs-publicapi", srcs: [ ":framework-permission-stubs-srcs-publicapi" ], - sdk_version: "system_current", - installable: false, + defaults: ["framework-module-stubs-lib-defaults-publicapi"], } java_library { name: "framework-permission-stubs-systemapi", srcs: [ ":framework-permission-stubs-srcs-systemapi" ], - sdk_version: "system_current", - installable: false, + defaults: ["framework-module-stubs-lib-defaults-systemapi"], } java_library { name: "framework-permission-stubs-module_libs_api", srcs: [ ":framework-permission-stubs-srcs-module_libs_api" ], - sdk_version: "system_current", - installable: false, + defaults: ["framework-module-stubs-lib-defaults-module_libs_api"], } 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..d8eb2576f2be 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9,7 +9,6 @@ package android { ctor public Manifest.permission(); field public static final String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER"; field public static final String ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION"; - field public static final String ACCESS_CALL_AUDIO = "android.permission.ACCESS_CALL_AUDIO"; field public static final String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES"; field public static final String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION"; field public static final String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION"; @@ -289,7 +288,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 +327,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 = 16844309; // 0x1010615 field public static final int autoSizeMaxTextSize = 16844102; // 0x1010546 field public static final int autoSizeMinTextSize = 16844088; // 0x1010538 field public static final int autoSizePresetSizes = 16844087; // 0x1010537 @@ -1141,7 +1140,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 +7603,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; @@ -24358,7 +24357,7 @@ package android.media { } public final class AudioMetadata { - method @NonNull public static android.media.AudioMetadata.Map createMap(); + method @NonNull public static android.media.AudioMetadataMap createMap(); } public static class AudioMetadata.Format { @@ -24376,16 +24375,16 @@ package android.media { method @NonNull public Class<T> getValueClass(); } - public static interface AudioMetadata.Map extends android.media.AudioMetadata.ReadMap { + public interface AudioMetadataMap extends android.media.AudioMetadataReadMap { method @Nullable public <T> T remove(@NonNull android.media.AudioMetadata.Key<T>); method @Nullable public <T> T set(@NonNull android.media.AudioMetadata.Key<T>, @NonNull T); } - public static interface AudioMetadata.ReadMap { + public interface AudioMetadataReadMap { method public <T> boolean containsKey(@NonNull android.media.AudioMetadata.Key<T>); - method @NonNull public android.media.AudioMetadata.Map dup(); + method @NonNull public android.media.AudioMetadataMap dup(); method @Nullable public <T> T get(@NonNull android.media.AudioMetadata.Key<T>); - method public int size(); + method @IntRange(from=0) public int size(); } public final class AudioPlaybackCaptureConfiguration { @@ -24700,7 +24699,7 @@ package android.media { } public static interface AudioTrack.OnCodecFormatChangedListener { - method public void onCodecFormatChanged(@NonNull android.media.AudioTrack, @Nullable android.media.AudioMetadata.ReadMap); + method public void onCodecFormatChanged(@NonNull android.media.AudioTrack, @Nullable android.media.AudioMetadataReadMap); } public static interface AudioTrack.OnPlaybackPositionUpdateListener { @@ -25340,11 +25339,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); @@ -26400,7 +26400,7 @@ package android.media { method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException; method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...); method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer); - method @Nullable public String getParserName(); + method @NonNull public String getParserName(); method @NonNull public static java.util.List<java.lang.String> getParserNames(@NonNull android.media.MediaFormat); method public void release(); method public void seek(@NonNull android.media.MediaParser.SeekPoint); @@ -26436,6 +26436,7 @@ package android.media { field public static final String PARSER_NAME_OGG = "android.media.mediaparser.OggParser"; field public static final String PARSER_NAME_PS = "android.media.mediaparser.PsParser"; field public static final String PARSER_NAME_TS = "android.media.mediaparser.TsParser"; + field public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN"; field public static final String PARSER_NAME_WAV = "android.media.mediaparser.WavParser"; } @@ -30347,6 +30348,7 @@ package android.net { method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier(); method public boolean hasCapability(int); method public boolean hasTransport(int); + method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR; } @@ -37145,11 +37147,10 @@ package android.os { field public static final int EFFECT_TICK = 2; // 0x2 } - public static class VibrationEffect.Composition { - ctor public VibrationEffect.Composition(); - method @Nullable public android.os.VibrationEffect.Composition addPrimitive(int); - method @Nullable public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float); - method @Nullable public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int); + public static final class VibrationEffect.Composition { + method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int); + method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float); + method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int); method @NonNull public android.os.VibrationEffect compose(); field public static final int PRIMITIVE_CLICK = 1; // 0x1 field public static final int PRIMITIVE_QUICK_FALL = 6; // 0x6 @@ -37159,9 +37160,9 @@ package android.os { } public abstract class Vibrator { - method @Nullable public Boolean areAllEffectsSupported(@NonNull int...); - method public boolean areAllPrimitivesSupported(@NonNull int...); - method @Nullable public boolean[] areEffectsSupported(@NonNull int...); + method public final int areAllEffectsSupported(@NonNull int...); + method public final boolean areAllPrimitivesSupported(@NonNull int...); + method @NonNull public int[] areEffectsSupported(@NonNull int...); method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...); method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel(); method public abstract boolean hasAmplitudeControl(); @@ -37172,6 +37173,9 @@ package android.os { method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long[], int, android.media.AudioAttributes); method @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(android.os.VibrationEffect); method @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(android.os.VibrationEffect, android.media.AudioAttributes); + field public static final int VIBRATION_EFFECT_SUPPORT_NO = 2; // 0x2 + field public static final int VIBRATION_EFFECT_SUPPORT_UNKNOWN = 0; // 0x0 + field public static final int VIBRATION_EFFECT_SUPPORT_YES = 1; // 0x1 } public class WorkSource implements android.os.Parcelable { @@ -45922,13 +45926,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); @@ -46418,14 +46418,26 @@ package android.telephony { field public static final int BAND_46 = 46; // 0x2e field public static final int BAND_47 = 47; // 0x2f field public static final int BAND_48 = 48; // 0x30 + field public static final int BAND_49 = 49; // 0x31 field public static final int BAND_5 = 5; // 0x5 + field public static final int BAND_50 = 50; // 0x32 + field public static final int BAND_51 = 51; // 0x33 + field public static final int BAND_52 = 52; // 0x34 + field public static final int BAND_53 = 53; // 0x35 field public static final int BAND_6 = 6; // 0x6 field public static final int BAND_65 = 65; // 0x41 field public static final int BAND_66 = 66; // 0x42 field public static final int BAND_68 = 68; // 0x44 field public static final int BAND_7 = 7; // 0x7 field public static final int BAND_70 = 70; // 0x46 + field public static final int BAND_71 = 71; // 0x47 + field public static final int BAND_72 = 72; // 0x48 + field public static final int BAND_73 = 73; // 0x49 + field public static final int BAND_74 = 74; // 0x4a field public static final int BAND_8 = 8; // 0x8 + field public static final int BAND_85 = 85; // 0x55 + field public static final int BAND_87 = 87; // 0x57 + field public static final int BAND_88 = 88; // 0x58 field public static final int BAND_9 = 9; // 0x9 } @@ -46489,7 +46501,13 @@ package android.telephony { field public static final int BAND_83 = 83; // 0x53 field public static final int BAND_84 = 84; // 0x54 field public static final int BAND_86 = 86; // 0x56 + field public static final int BAND_89 = 89; // 0x59 field public static final int BAND_90 = 90; // 0x5a + field public static final int BAND_91 = 91; // 0x5b + field public static final int BAND_92 = 92; // 0x5c + field public static final int BAND_93 = 93; // 0x5d + field public static final int BAND_94 = 94; // 0x5e + field public static final int BAND_95 = 95; // 0x5f } public static final class AccessNetworkConstants.UtranBand { @@ -47777,7 +47795,7 @@ package android.telephony { method public String createAppSpecificSmsToken(android.app.PendingIntent); method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent); method public java.util.ArrayList<java.lang.String> divideMessage(String); - method @Deprecated public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); + method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); method @NonNull public android.os.Bundle getCarrierConfigValues(); method public static android.telephony.SmsManager getDefault(); method public static int getDefaultSmsSubscriptionId(); @@ -47787,7 +47805,7 @@ package android.telephony { method public int getSubscriptionId(); method public void injectSmsPdu(byte[], String, android.app.PendingIntent); method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent); - method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); + method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>); method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, long); method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String, @Nullable String); @@ -55743,7 +55761,7 @@ package android.view { field public int softInputMode; field @Deprecated public int systemUiVisibility; field public android.os.IBinder token; - field @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION, to="BASE_APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION, to="APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING, to="APPLICATION_STARTING"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION, to="DRAWN_APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, to="APPLICATION_PANEL"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA, to="APPLICATION_MEDIA"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL, to="APPLICATION_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=0x3ed, to="APPLICATION_ABOVE_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG, to="APPLICATION_ATTACHED_DIALOG"), @android.view.ViewDebug.IntToString(from=0x3ec, to="APPLICATION_MEDIA_OVERLAY"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR, to="STATUS_BAR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR, to="SEARCH_BAR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PHONE, to="PHONE"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, to="SYSTEM_ALERT"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_TOAST, to="TOAST"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, to="SYSTEM_OVERLAY"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE, to="PRIORITY_PHONE"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG, to="SYSTEM_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, to="KEYGUARD_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR, to="SYSTEM_ERROR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD, to="INPUT_METHOD"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, to="INPUT_METHOD_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_WALLPAPER, to="WALLPAPER"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, to="STATUS_BAR_PANEL"), @android.view.ViewDebug.IntToString(from=0x7df, to="SECURE_SYSTEM_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7e0, to="DRAG"), @android.view.ViewDebug.IntToString(from=0x7e1, to="STATUS_BAR_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=0x7e2, to="POINTER"), @android.view.ViewDebug.IntToString(from=0x7e3, to="NAVIGATION_BAR"), @android.view.ViewDebug.IntToString(from=0x7e4, to="VOLUME_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7e5, to="BOOT_PROGRESS"), @android.view.ViewDebug.IntToString(from=0x7e6, to="INPUT_CONSUMER"), @android.view.ViewDebug.IntToString(from=0x7e7, to="DREAM"), @android.view.ViewDebug.IntToString(from=0x7e8, to="NAVIGATION_BAR_PANEL"), @android.view.ViewDebug.IntToString(from=0x7ea, to="DISPLAY_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7eb, to="MAGNIFICATION_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7f5, to="PRESENTATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION, to="PRIVATE_PRESENTATION"), @android.view.ViewDebug.IntToString(from=0x7ef, to="VOICE_INTERACTION"), @android.view.ViewDebug.IntToString(from=0x7f1, to="VOICE_INTERACTION_STARTING"), @android.view.ViewDebug.IntToString(from=0x7f2, to="DOCK_DIVIDER"), @android.view.ViewDebug.IntToString(from=0x7f3, to="QS_DIALOG"), @android.view.ViewDebug.IntToString(from=0x7f4, to="SCREENSHOT"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, to="APPLICATION_OVERLAY")}) public int type; + field @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION, to="BASE_APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION, to="APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING, to="APPLICATION_STARTING"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION, to="DRAWN_APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, to="APPLICATION_PANEL"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA, to="APPLICATION_MEDIA"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL, to="APPLICATION_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=0x3ed, to="APPLICATION_ABOVE_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG, to="APPLICATION_ATTACHED_DIALOG"), @android.view.ViewDebug.IntToString(from=0x3ec, to="APPLICATION_MEDIA_OVERLAY"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR, to="STATUS_BAR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR, to="SEARCH_BAR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PHONE, to="PHONE"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, to="SYSTEM_ALERT"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_TOAST, to="TOAST"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, to="SYSTEM_OVERLAY"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE, to="PRIORITY_PHONE"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG, to="SYSTEM_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, to="KEYGUARD_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR, to="SYSTEM_ERROR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD, to="INPUT_METHOD"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, to="INPUT_METHOD_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_WALLPAPER, to="WALLPAPER"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, to="STATUS_BAR_PANEL"), @android.view.ViewDebug.IntToString(from=0x7df, to="SECURE_SYSTEM_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7e0, to="DRAG"), @android.view.ViewDebug.IntToString(from=0x7e1, to="STATUS_BAR_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=0x7e2, to="POINTER"), @android.view.ViewDebug.IntToString(from=0x7e3, to="NAVIGATION_BAR"), @android.view.ViewDebug.IntToString(from=0x7e4, to="VOLUME_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7e5, to="BOOT_PROGRESS"), @android.view.ViewDebug.IntToString(from=0x7e6, to="INPUT_CONSUMER"), @android.view.ViewDebug.IntToString(from=0x7e8, to="NAVIGATION_BAR_PANEL"), @android.view.ViewDebug.IntToString(from=0x7ea, to="DISPLAY_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7eb, to="MAGNIFICATION_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7f5, to="PRESENTATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION, to="PRIVATE_PRESENTATION"), @android.view.ViewDebug.IntToString(from=0x7ef, to="VOICE_INTERACTION"), @android.view.ViewDebug.IntToString(from=0x7f1, to="VOICE_INTERACTION_STARTING"), @android.view.ViewDebug.IntToString(from=0x7f2, to="DOCK_DIVIDER"), @android.view.ViewDebug.IntToString(from=0x7f3, to="QS_DIALOG"), @android.view.ViewDebug.IntToString(from=0x7f4, to="SCREENSHOT"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, to="APPLICATION_OVERLAY")}) public int type; field public float verticalMargin; field @android.view.ViewDebug.ExportedProperty public float verticalWeight; field public int windowAnimations; @@ -55752,8 +55770,8 @@ package android.view { } public final class WindowMetrics { - ctor public WindowMetrics(@NonNull android.util.Size, @NonNull android.view.WindowInsets); - method @NonNull public android.util.Size getSize(); + ctor public WindowMetrics(@NonNull android.graphics.Rect, @NonNull android.view.WindowInsets); + method @NonNull public android.graphics.Rect getBounds(); method @NonNull public android.view.WindowInsets getWindowInsets(); } diff --git a/api/system-current.txt b/api/system-current.txt index a1fd0dbd8df0..156d81e7ba2a 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -368,7 +368,6 @@ package android.app { method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int); field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover"; field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility"; - field public static final String OPSTR_ACCESS_CALL_AUDIO = "android:access_call_audio"; field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications"; field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn"; field public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot"; @@ -2030,10 +2029,10 @@ package android.content.pm { } public static class PackageInstaller.Session implements java.io.Closeable { - method public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]); + method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void commitTransferred(@NonNull android.content.IntentSender); - method @Nullable public android.content.pm.DataLoaderParams getDataLoaderParams(); - method public void removeFile(int, @NonNull String); + method @Nullable @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public android.content.pm.DataLoaderParams getDataLoaderParams(); + method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void removeFile(int, @NonNull String); } public static class PackageInstaller.SessionInfo implements android.os.Parcelable { @@ -2054,7 +2053,7 @@ package android.content.pm { public static class PackageInstaller.SessionParams implements android.os.Parcelable { method @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public void setAllocateAggressive(boolean); method @Deprecated public void setAllowDowngrade(boolean); - method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams); + method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.USE_INSTALLER_V2"}) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams); method public void setDontKillApp(boolean); method public void setEnableRollback(boolean); method public void setEnableRollback(boolean, int); @@ -4179,11 +4178,11 @@ package android.media { method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException; method public void clearAudioServerStateCallback(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); - method @IntRange(from=0) public int getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); + method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes); - method @IntRange(from=0) public int getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); + method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); @@ -4198,7 +4197,7 @@ package android.media { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException; method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException; method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int requestAudioFocus(@NonNull android.media.AudioFocusRequest, @Nullable android.media.audiopolicy.AudioPolicy); - method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) int); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long); method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes); @@ -4273,17 +4272,11 @@ package android.media { } public static class AudioTrack.TunerConfiguration { + ctor @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public AudioTrack.TunerConfiguration(@IntRange(from=1) int, @IntRange(from=1) int); method @IntRange(from=1) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getContentId(); method @IntRange(from=1) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getSyncId(); } - public static class AudioTrack.TunerConfiguration.Builder { - ctor public AudioTrack.TunerConfiguration.Builder(); - method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioTrack.TunerConfiguration build(); - method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioTrack.TunerConfiguration.Builder setContentId(@IntRange(from=1) int); - method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioTrack.TunerConfiguration.Builder setSyncId(@IntRange(from=1) int); - } - public class HwAudioSource { method public boolean isPlaying(); method public void start(); @@ -6061,7 +6054,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 @@ -6211,14 +6204,14 @@ package android.net { method public void onRemoveKeepalivePacketFilter(int); method public void onSaveAcceptUnvalidated(boolean); method public void onSignalStrengthThresholdsUpdated(@NonNull int[]); - method public void onStartSocketKeepalive(int, int, @NonNull android.net.KeepalivePacketData); + method public void onStartSocketKeepalive(int, @IntRange(from=10, to=3600) int, @NonNull android.net.KeepalivePacketData); method public void onStopSocketKeepalive(int); - method public void onValidationStatus(int, @Nullable String); + method public void onValidationStatus(int, @Nullable android.net.Uri); method @NonNull public android.net.Network register(); - method public void sendLinkProperties(@NonNull android.net.LinkProperties); - method public void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities); - method public void sendNetworkScore(int); - method public void sendSocketKeepaliveEvent(int, int); + method public final void sendLinkProperties(@NonNull android.net.LinkProperties); + method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities); + method public final void sendNetworkScore(@IntRange(from=0, to=99) int); + method public final void sendSocketKeepaliveEvent(int, int); method public void setConnected(); method public void unregister(); field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2 @@ -6236,7 +6229,7 @@ package android.net { field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR; } - public static class NetworkAgentConfig.Builder { + public static final class NetworkAgentConfig.Builder { ctor public NetworkAgentConfig.Builder(); method @NonNull public android.net.NetworkAgentConfig build(); method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean); @@ -6255,7 +6248,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); @@ -6304,7 +6297,6 @@ package android.net { public class NetworkRequest implements android.os.Parcelable { method @Nullable public String getRequestorPackageName(); method public int getRequestorUid(); - method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities); } public static class NetworkRequest.Builder { @@ -8395,12 +8387,12 @@ package android.os { public class RecoverySystem { method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void cancelScheduledUpdate(android.content.Context) throws java.io.IOException; - method @RequiresPermission(android.Manifest.permission.RECOVERY) public static boolean clearPrepareForUnattendedUpdate(@NonNull android.content.Context) throws java.io.IOException; + method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void clearPrepareForUnattendedUpdate(@NonNull android.content.Context) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void installPackage(android.content.Context, java.io.File, boolean) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void prepareForUnattendedUpdate(@NonNull android.content.Context, @NonNull String, @Nullable android.content.IntentSender) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException; - method @RequiresPermission(android.Manifest.permission.RECOVERY) public static boolean rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException; + method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException; method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException; method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException; @@ -12713,9 +12705,6 @@ package android.view.contentcapture { public final class ContentCaptureManager { method public boolean isContentCaptureFeatureEnabled(); - } - - public abstract class ContentCaptureSession implements java.lang.AutoCloseable { field public static final int NO_SESSION_ID = 0; // 0x0 } 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 f9711c3ce763..979f950f2770 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. @@ -2745,21 +2753,32 @@ message PhoneStateChanged { message BackGesture { enum BackType { - DEFAULT_BACK_TYPE = 0; - COMPLETED = 1; - COMPLETED_REJECTED = 2; // successful because coming from rejected area - INCOMPLETE_EXCLUDED = 3; // would have been successful but in the exclusion area - INCOMPLETE = 4; + DEFAULT_BACK_TYPE = 0; + COMPLETED = 1; + COMPLETED_REJECTED = 2; // successful because coming from rejected area + INCOMPLETE_EXCLUDED = 3; // would have been successful but in the exclusion area + INCOMPLETE = 4; // Unsuccessful, for reasons other than below. + INCOMPLETE_FAR_FROM_EDGE = 5; // Unsuccessful, far from the edge. + INCOMPLETE_MULTI_TOUCH = 6; // Unsuccessful, multi touch. + INCOMPLETE_LONG_PRESS = 7; // Unsuccessful, long press. + INCOMPLETE_VERTICAL_MOVE = 8; // Unsuccessful, move vertically. } optional BackType type = 1; - optional int32 y_coordinate = 2; // y coordinate for ACTION_DOWN event + optional int32 y_coordinate = 2 [deprecated = true]; // y coordinate for ACTION_DOWN event + optional int32 start_x = 4; // X coordinate for ACTION_DOWN event. + optional int32 start_y = 5; // Y coordinate for ACTION_DOWN event. + optional int32 end_x = 6; // X coordinate for ACTION_MOVE event. + optional int32 end_y = 7; // Y coordinate for ACTION_MOVE event. + optional int32 left_boundary = 8; // left edge width + left inset + optional int32 right_boundary = 9; // screen width - (right edge width + right inset) + enum WindowHorizontalLocation { DEFAULT_LOCATION = 0; LEFT = 1; RIGHT = 2; } - optional WindowHorizontalLocation x_location = 3; + optional WindowHorizontalLocation x_location = 3 [deprecated = true]; } message ExclusionRectStateChanged { @@ -4589,6 +4608,8 @@ message MobileBytesTransfer { optional int64 tx_bytes = 4; optional int64 tx_packets = 5; + + optional int32 rat_type = 6; } /** @@ -4611,6 +4632,8 @@ message MobileBytesTransferByFgBg { optional int64 tx_bytes = 5; optional int64 tx_packets = 6; + + optional int32 rat_type = 7; } /** @@ -8988,3 +9011,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/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp index 30c90b1e1f71..1d77513d9d33 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp +++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp @@ -130,15 +130,15 @@ bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int return false; } - android::os::IncidentReportRequest incidentReport; + AIncidentReportArgs* args = AIncidentReportArgs_init(); vector<uint8_t> protoData; getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, config.alert_description(), &protoData); - incidentReport.addHeader(protoData); + AIncidentReportArgs_addHeader(args, protoData.data(), protoData.size()); for (int i = 0; i < config.section_size(); i++) { - incidentReport.addSection(config.section(i)); + AIncidentReportArgs_addSection(args, config.section(i)); } uint8_t dest; @@ -152,13 +152,16 @@ bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int default: dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC; } - incidentReport.setPrivacyPolicy(dest); + AIncidentReportArgs_setPrivacyPolicy(args, dest); - incidentReport.setReceiverPackage(config.receiver_pkg()); + AIncidentReportArgs_setReceiverPackage(args, config.receiver_pkg().c_str()); - incidentReport.setReceiverClass(config.receiver_cls()); + AIncidentReportArgs_setReceiverClass(args, config.receiver_cls().c_str()); - return incidentReport.takeReport() == NO_ERROR; + int err = AIncidentReportArgs_takeReport(args); + AIncidentReportArgs_delete(args); + + return err == NO_ERROR; } } // namespace statsd 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/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index 0f39efd51b58..e58bbb7893d7 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -13,14 +13,17 @@ // limitations under the License. #include "src/metrics/EventMetricProducer.h" -#include "metrics_test_helper.h" -#include "tests/statsd_test_util.h" #include <gmock/gmock.h> #include <gtest/gtest.h> #include <stdio.h> + #include <vector> +#include "metrics_test_helper.h" +#include "stats_event.h" +#include "tests/statsd_test_util.h" + using namespace testing; using android::sp; using std::set; @@ -35,6 +38,22 @@ namespace statsd { const ConfigKey kConfigKey(0, 12345); +namespace { +void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeString(statsEvent, str.c_str()); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); +} +} // anonymous namespace + TEST(EventMetricProducerTest, TestNoCondition) { int64_t bucketStartTimeNs = 10000000000; int64_t eventStartTimeNs = bucketStartTimeNs + 1; @@ -43,8 +62,11 @@ TEST(EventMetricProducerTest, TestNoCondition) { EventMetric metric; metric.set_id(1); - LogEvent event1(1 /*tag id*/, bucketStartTimeNs + 1); - LogEvent event2(1 /*tag id*/, bucketStartTimeNs + 2); + LogEvent event1(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1); + + LogEvent event2(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 2); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -54,8 +76,17 @@ TEST(EventMetricProducerTest, TestNoCondition) { eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); - // TODO(b/110561136): get the report and check the content after the ProtoOutputStream change - // is done eventProducer.onDumpReport(); + // Check dump report content. + ProtoOutputStream output; + std::set<string> strSet; + eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/, + true /*erase data*/, FAST, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_event_metrics()); + EXPECT_EQ(2, report.event_metrics().data_size()); + EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos()); + EXPECT_EQ(bucketStartTimeNs + 2, report.event_metrics().data(1).elapsed_timestamp_nanos()); } TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) { @@ -67,8 +98,11 @@ TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) { metric.set_id(1); metric.set_condition(StringToId("SCREEN_ON")); - LogEvent event1(1, bucketStartTimeNs + 1); - LogEvent event2(1, bucketStartTimeNs + 10); + LogEvent event1(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1); + + LogEvent event2(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -81,51 +115,67 @@ TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) { eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); - // TODO: get the report and check the content after the ProtoOutputStream change is done. - // eventProducer.onDumpReport(); + // Check dump report content. + ProtoOutputStream output; + std::set<string> strSet; + eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/, + true /*erase data*/, FAST, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_event_metrics()); + EXPECT_EQ(1, report.event_metrics().data_size()); + EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos()); } -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; -// -// int tagId = 1; -// int conditionTagId = 2; -// -// EventMetric metric; -// metric.set_id(1); -// metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); -// MetricConditionLink* link = metric.add_links(); -// link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); -// buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what()); -// buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition()); -// -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// EXPECT_TRUE(event1.write("111")); -// event1.init(); -// ConditionKey key1; -// key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "111")}; -// -// LogEvent event2(tagId, bucketStartTimeNs + 10); -// EXPECT_TRUE(event2.write("222")); -// event2.init(); -// ConditionKey key2; -// key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")}; -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); -// -// EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); -// -// EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs); -// -// eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); -// eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); -// -// // TODO: get the report and check the content after the ProtoOutputStream change is done. -// // eventProducer.onDumpReport(); -//} +TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + + int tagId = 1; + int conditionTagId = 2; + + EventMetric metric; + metric.set_id(1); + metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); + MetricConditionLink* link = metric.add_links(); + link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); + buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what()); + buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition()); + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1, "111"); + ConditionKey key1; + key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = { + getMockedDimensionKey(conditionTagId, 2, "111")}; + + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10, "222"); + ConditionKey key2; + key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = { + getMockedDimensionKey(conditionTagId, 2, "222")}; + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + // Condition is false for first event. + EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); + // Condition is true for second event. + EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); + + EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs); + + eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); + eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); + + // Check dump report content. + ProtoOutputStream output; + std::set<string> strSet; + eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/, + true /*erase data*/, FAST, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_event_metrics()); + EXPECT_EQ(1, report.event_metrics().data_size()); + EXPECT_EQ(bucketStartTimeNs + 10, report.event_metrics().data(0).elapsed_timestamp_nanos()); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 609324e91daa..d372ffd7e689 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -26,6 +26,7 @@ #include "src/matchers/SimpleLogMatchingTracker.h" #include "src/metrics/MetricProducer.h" #include "src/stats_log_util.h" +#include "stats_event.h" #include "tests/statsd_test_util.h" using namespace testing; @@ -53,6 +54,28 @@ const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; +namespace { +shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t value1, string str1, + int32_t value2) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, value1); + AStatsEvent_writeString(statsEvent, str1.c_str()); + AStatsEvent_writeInt32(statsEvent, value2); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + + return logEvent; +} +} // anonymous namespace + /* * Tests that the first bucket works correctly */ @@ -88,769 +111,685 @@ TEST(GaugeMetricProducerTest, TestFirstBucket) { EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs()); } -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.mutable_gauge_fields_filter()->set_include_all(false); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); -// gaugeFieldMatcher->set_field(tagId); -// gaugeFieldMatcher->add_child()->set_field(1); -// gaugeFieldMatcher->add_child()->set_field(3); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); -// event->write(3); -// event->write("some value"); -// event->write(11); -// event->init(); -// data->push_back(event); -// return true; -// })); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, -// pullerManager); -// -// vector<shared_ptr<LogEvent>> allData; -// allData.clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); -// event->write(10); -// event->write("some value"); -// event->write(11); -// event->init(); -// allData.push_back(event); -// -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(10, it->mValue.int_value); -// it++; -// EXPECT_EQ(11, it->mValue.int_value); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -// EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms -// .front().mFields->begin()->mValue.int_value); -// -// allData.clear(); -// std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10); -// event2->write(24); -// event2->write("some value"); -// event2->write(25); -// event2->init(); -// allData.push_back(event2); -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(24, it->mValue.int_value); -// it++; -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(25, it->mValue.int_value); -// // One dimension. -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -// EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); -// it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(10L, it->mValue.int_value); -// it++; -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(11L, it->mValue.int_value); -// -// gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs); -// EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); -// // One dimension. -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -// EXPECT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size()); -// it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(24L, it->mValue.int_value); -// it++; -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(25L, it->mValue.int_value); -//} -// -//TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { -// sp<AlarmMonitor> alarmMonitor; -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.mutable_gauge_fields_filter()->set_include_all(true); -// -// Alert alert; -// alert.set_id(101); -// alert.set_metric_id(metricId); -// alert.set_trigger_if_sum_gt(25); -// alert.set_num_buckets(100); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs, -// bucketStartTimeNs, pullerManager); -// -// sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); -// EXPECT_TRUE(anomalyTracker != nullptr); -// -// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); -// event1->write(1); -// event1->write(10); -// event1->init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); -// EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); -// -// gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); -// EXPECT_EQ(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); -// // Partial buckets are not sent to anomaly tracker. -// EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -// -// // Create an event in the same partial bucket. -// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC); -// event2->write(1); -// event2->write(10); -// event2->init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); -// EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); -// // Partial buckets are not sent to anomaly tracker. -// EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -// -// // Next event should trigger creation of new bucket and send previous full bucket to anomaly -// // tracker. -// shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC); -// event3->write(1); -// event3->write(10); -// event3->init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); -// EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum); -// EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs); -// EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -// -// // Next event should trigger creation of new bucket. -// shared_ptr<LogEvent> event4 = -// make_shared<LogEvent>(tagId, bucketStartTimeNs + 125 * NS_PER_SEC); -// event4->write(1); -// event4->write(10); -// event4->init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); -// EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum); -// EXPECT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -//} -// -//TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); -// gaugeFieldMatcher->set_field(tagId); -// gaugeFieldMatcher->add_child()->set_field(2); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = -// new EventMatcherWizard({new SimpleLogMatchingTracker( -// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// .WillOnce(Return(false)) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventUpgradeTimeNs); -// event->write("some value"); -// event->write(2); -// event->init(); -// data->push_back(event); -// return true; -// })); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, -// bucketStartTimeNs, bucketStartTimeNs, pullerManager); -// -// vector<shared_ptr<LogEvent>> allData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); -// event->write("some value"); -// event->write(1); -// event->init(); -// allData.push_back(event); -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// -// gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); -// EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// -// allData.clear(); -// event = make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 1); -// event->write("some value"); -// event->write(3); -// event->init(); -// allData.push_back(event); -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); -// EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -//} -// -//TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.set_max_pull_delay_sec(INT_MAX); -// metric.set_split_bucket_for_app_upgrade(false); -// auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); -// gaugeFieldMatcher->set_field(tagId); -// gaugeFieldMatcher->add_child()->set_field(2); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false)); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, -// pullerManager); -// -// vector<shared_ptr<LogEvent>> allData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); -// event->write("some value"); -// event->write(1); -// event->init(); -// allData.push_back(event); -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// -// gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); -// EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -//} -// -//TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); -// gaugeFieldMatcher->set_field(tagId); -// gaugeFieldMatcher->add_child()->set_field(2); -// metric.set_condition(StringToId("SCREEN_ON")); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); -// event->write("some value"); -// event->write(100); -// event->init(); -// data->push_back(event); -// return true; -// })); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, -// logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, -// bucketStartTimeNs, bucketStartTimeNs, pullerManager); -// -// gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); -// -// vector<shared_ptr<LogEvent>> allData; -// allData.clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); -// event->write("some value"); -// event->write(110); -// event->init(); -// allData.push_back(event); -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); -// -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -// EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin() -// ->second.back() -// .mGaugeAtoms.front() -// .mFields->begin() -// ->mValue.int_value); -// -// gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10); -// gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -// EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); -// EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin() -// ->second.back() -// .mGaugeAtoms.front() -// .mFields->begin() -// ->mValue.int_value); -//} -// -//TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { -// const int conditionTag = 65; -// GaugeMetric metric; -// metric.set_id(1111111); -// metric.set_bucket(ONE_MINUTE); -// metric.mutable_gauge_fields_filter()->set_include_all(true); -// metric.set_condition(StringToId("APP_DIED")); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto dim = metric.mutable_dimensions_in_what(); -// dim->set_field(tagId); -// dim->add_child()->set_field(1); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// EXPECT_CALL(*wizard, query(_, _, _)) -// .WillRepeatedly( -// Invoke([](const int conditionIndex, const ConditionKey& conditionParameters, -// const bool isPartialLink) { -// int pos[] = {1, 0, 0}; -// Field f(conditionTag, pos, 0); -// HashableDimensionKey key; -// key.mutableValues()->emplace_back(f, Value((int32_t)1000000)); -// -// return ConditionState::kTrue; -// })); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); -// event->write(1000); -// event->write(100); -// event->init(); -// data->push_back(event); -// return true; -// })); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, -// logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, -// bucketStartTimeNs, bucketStartTimeNs, pullerManager); -// -// gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8); -// -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first; -// EXPECT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size()); -// EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value); -// -// EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); -// -// vector<shared_ptr<LogEvent>> allData; -// allData.clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); -// event->write(1000); -// event->write(110); -// event->init(); -// allData.push_back(event); -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); -// -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -//} -// -//TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { -// sp<AlarmMonitor> alarmMonitor; -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false)); -// -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); -// gaugeFieldMatcher->set_field(tagId); -// gaugeFieldMatcher->add_child()->set_field(2); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, -// pullerManager); -// -// Alert alert; -// alert.set_id(101); -// alert.set_metric_id(metricId); -// alert.set_trigger_if_sum_gt(25); -// alert.set_num_buckets(2); -// const int32_t refPeriodSec = 60; -// alert.set_refractory_period_secs(refPeriodSec); -// sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); -// -// int tagId = 1; -// std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); -// event1->write("some value"); -// event1->write(13); -// event1->init(); -// -// gaugeProducer.onDataPulled({event1}, /** succeed */ true, bucketStartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); -// -// std::shared_ptr<LogEvent> event2 = -// std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 20); -// event2->write("some value"); -// event2->write(15); -// event2->init(); -// -// gaugeProducer.onDataPulled({event2}, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), -// std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec); -// -// std::shared_ptr<LogEvent> event3 = -// std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10); -// event3->write("some value"); -// event3->write(26); -// event3->init(); -// -// gaugeProducer.onDataPulled({event3}, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), -// std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); -// -// // The event4 does not have the gauge field. Thus the current bucket value is 0. -// std::shared_ptr<LogEvent> event4 = -// std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10); -// event4->write("some value"); -// event4->init(); -// gaugeProducer.onDataPulled({event4}, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty()); -//} -// -//TEST(GaugeMetricProducerTest, TestPullOnTrigger) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); -// metric.mutable_gauge_fields_filter()->set_include_all(false); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); -// gaugeFieldMatcher->set_field(tagId); -// gaugeFieldMatcher->add_child()->set_field(1); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); -// event->write(4); -// event->init(); -// data->push_back(event); -// return true; -// })) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20); -// event->write(5); -// event->init(); -// data->push_back(event); -// return true; -// })) -// .WillOnce(Return(true)); -// -// int triggerId = 5; -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, -// pullerManager); -// -// vector<shared_ptr<LogEvent>> allData; -// -// EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); -// LogEvent trigger(triggerId, bucketStartTimeNs + 10); -// trigger.init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); -// trigger.setElapsedTimestampNs(bucketStartTimeNs + 20); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); -// trigger.setElapsedTimestampNs(bucket2StartTimeNs + 1); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -// EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size()); -// EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin() -// ->second.back() -// .mGaugeAtoms[0] -// .mFields->begin() -// ->mValue.int_value); -// EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin() -// ->second.back() -// .mGaugeAtoms[1] -// .mFields->begin() -// ->mValue.int_value); -//} -// -//TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); -// metric.mutable_gauge_fields_filter()->set_include_all(true); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto dimensionMatcher = metric.mutable_dimensions_in_what(); -// // use field 1 as dimension. -// dimensionMatcher->set_field(tagId); -// dimensionMatcher->add_child()->set_field(1); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3); -// event->write(3); -// event->write(4); -// event->init(); -// data->push_back(event); -// return true; -// })) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); -// event->write(4); -// event->write(5); -// event->init(); -// data->push_back(event); -// return true; -// })) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20); -// event->write(4); -// event->write(6); -// event->init(); -// data->push_back(event); -// return true; -// })) -// .WillOnce(Return(true)); -// -// int triggerId = 5; -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, -// pullerManager); -// -// vector<shared_ptr<LogEvent>> allData; -// -// LogEvent trigger(triggerId, bucketStartTimeNs + 3); -// trigger.init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// trigger.setElapsedTimestampNs(bucketStartTimeNs + 10); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); -// trigger.setElapsedTimestampNs(bucketStartTimeNs + 20); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); -// trigger.setElapsedTimestampNs(bucket2StartTimeNs + 1); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// -// EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.size()); -// auto bucketIt = gaugeProducer.mPastBuckets.begin(); -// EXPECT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size()); -// EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); -// EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); -// bucketIt++; -// EXPECT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size()); -// EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); -// EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); -// EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value); -//} -// -///* -// * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size -// * is smaller than the "min_bucket_size_nanos" specified in the metric config. -// */ -//TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(FIVE_MINUTES); -// metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); -// metric.set_min_bucket_size_nanos(10000000000); // 10 seconds -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// // Bucket start. -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs); -// event->write("field1"); -// event->write(10); -// event->init(); -// data->push_back(event); -// return true; -// })); -// -// int triggerId = 5; -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, -// pullerManager); -// -// LogEvent trigger(triggerId, bucketStartTimeNs + 3); -// trigger.init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// -// // Check dump report. -// ProtoOutputStream output; -// std::set<string> strSet; -// gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, -// true, FAST /* dump_latency */, &strSet, &output); -// -// StatsLogReport report = outputStreamToProto(&output); -// EXPECT_TRUE(report.has_gauge_metrics()); -// EXPECT_EQ(0, report.gauge_metrics().data_size()); -// EXPECT_EQ(1, report.gauge_metrics().skipped_size()); -// -// EXPECT_EQ(NanoToMillis(bucketStartTimeNs), -// report.gauge_metrics().skipped(0).start_bucket_elapsed_millis()); -// EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), -// report.gauge_metrics().skipped(0).end_bucket_elapsed_millis()); -// EXPECT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size()); -// -// auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0); -// EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason()); -// EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis()); -//} +TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_gauge_fields_filter()->set_include_all(false); + metric.set_max_pull_delay_sec(INT_MAX); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(1); + gaugeFieldMatcher->add_child()->set_field(3); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(makeLogEvent(tagId, bucketStartTimeNs + 10, 3, "some value", 11)); + return true; + })); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + allData.push_back(makeLogEvent(tagId, bucket2StartTimeNs + 1, 10, "some value", 11)); + + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(10, it->mValue.int_value); + it++; + EXPECT_EQ(11, it->mValue.int_value); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); + EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms.front() + .mFields->begin() + ->mValue.int_value); + + allData.clear(); + allData.push_back(makeLogEvent(tagId, bucket3StartTimeNs + 10, 24, "some value", 25)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(24, it->mValue.int_value); + it++; + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(25, it->mValue.int_value); + // One dimension. + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); + it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(10L, it->mValue.int_value); + it++; + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(11L, it->mValue.int_value); + + gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs); + EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); + // One dimension. + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); + EXPECT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size()); + it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(24L, it->mValue.int_value); + it++; + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(25L, it->mValue.int_value); +} + +TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { + sp<AlarmMonitor> alarmMonitor; + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_gauge_fields_filter()->set_include_all(true); + + Alert alert; + alert.set_id(101); + alert.set_metric_id(metricId); + alert.set_trigger_if_sum_gt(25); + alert.set_num_buckets(100); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, + -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); + EXPECT_TRUE(anomalyTracker != nullptr); + + LogEvent event1(/*uid=*/0, /*pid=*/0); + CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); + + gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + // Partial buckets are not sent to anomaly tracker. + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // Create an event in the same partial bucket. + LogEvent event2(/*uid=*/0, /*pid=*/0); + CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + // Partial buckets are not sent to anomaly tracker. + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // Next event should trigger creation of new bucket and send previous full bucket to anomaly + // tracker. + LogEvent event3(/*uid=*/0, /*pid=*/0); + CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // Next event should trigger creation of new bucket. + LogEvent event4(/*uid=*/0, /*pid=*/0); + CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 125 * NS_PER_SEC, 1, 10); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); + EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); +} + +TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_max_pull_delay_sec(INT_MAX); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(2); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Return(false)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, eventUpgradeTimeNs, 2)); + return true; + })); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + vector<shared_ptr<LogEvent>> allData; + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + + gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + + allData.clear(); + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 1, 3)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); +} + +TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_max_pull_delay_sec(INT_MAX); + metric.set_split_bucket_for_app_upgrade(false); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(2); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false)); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + vector<shared_ptr<LogEvent>> allData; + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + + gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); +} + +TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_max_pull_delay_sec(INT_MAX); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(2); + metric.set_condition(StringToId("SCREEN_ON")); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 100)); + return true; + })); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); + + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); + EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms.front() + .mFields->begin() + ->mValue.int_value); + + gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10); + gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms.front() + .mFields->begin() + ->mValue.int_value); +} + +TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { + const int conditionTag = 65; + GaugeMetric metric; + metric.set_id(1111111); + metric.set_bucket(ONE_MINUTE); + metric.mutable_gauge_fields_filter()->set_include_all(true); + metric.set_condition(StringToId("APP_DIED")); + metric.set_max_pull_delay_sec(INT_MAX); + auto dim = metric.mutable_dimensions_in_what(); + dim->set_field(tagId); + dim->add_child()->set_field(1); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + EXPECT_CALL(*wizard, query(_, _, _)) + .WillRepeatedly( + Invoke([](const int conditionIndex, const ConditionKey& conditionParameters, + const bool isPartialLink) { + int pos[] = {1, 0, 0}; + Field f(conditionTag, pos, 0); + HashableDimensionKey key; + key.mutableValues()->emplace_back(f, Value((int32_t)1000000)); + + return ConditionState::kTrue; + })); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 10, 1000, 100)); + return true; + })); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8); + + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first; + EXPECT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + + EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); + + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1000, 110)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); +} + +TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { + sp<AlarmMonitor> alarmMonitor; + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false)); + + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_max_pull_delay_sec(INT_MAX); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(2); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + Alert alert; + alert.set_id(101); + alert.set_metric_id(metricId); + alert.set_trigger_if_sum_gt(25); + alert.set_num_buckets(2); + const int32_t refPeriodSec = 60; + alert.set_refractory_period_secs(refPeriodSec); + sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); + + int tagId = 1; + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 13)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); + + std::shared_ptr<LogEvent> event2 = + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 20, 15); + + allData.clear(); + allData.push_back(event2); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), + std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec); + + allData.clear(); + allData.push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10, 26)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), + std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); + + // This event does not have the gauge field. Thus the current bucket value is 0. + allData.clear(); + allData.push_back(CreateNoValuesLogEvent(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty()); +} + +TEST(GaugeMetricProducerTest, TestPullOnTrigger) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); + metric.mutable_gauge_fields_filter()->set_include_all(false); + metric.set_max_pull_delay_sec(INT_MAX); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(1); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 4)); + return true; + })) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 5)); + return true; + })) + .WillOnce(Return(true)); + + int triggerId = 5; + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, + tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); + + LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); + triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); + triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size()); + EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms[0] + .mFields->begin() + ->mValue.int_value); + EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms[1] + .mFields->begin() + ->mValue.int_value); +} + +TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); + metric.mutable_gauge_fields_filter()->set_include_all(true); + metric.set_max_pull_delay_sec(INT_MAX); + auto dimensionMatcher = metric.mutable_dimensions_in_what(); + // use field 1 as dimension. + dimensionMatcher->set_field(tagId); + dimensionMatcher->add_child()->set_field(1); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 3, 3, 4)); + return true; + })) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 10, 4, 5)); + return true; + })) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 4, 6)); + return true; + })) + .WillOnce(Return(true)); + + int triggerId = 5; + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, + tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 10); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); + triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); + triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.size()); + auto bucketIt = gaugeProducer.mPastBuckets.begin(); + EXPECT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size()); + EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); + EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); + bucketIt++; + EXPECT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size()); + EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); + EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); + EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value); +} + +/* + * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size + * is smaller than the "min_bucket_size_nanos" specified in the metric config. + */ +TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(FIVE_MINUTES); + metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); + metric.set_min_bucket_size_nanos(10000000000); // 10 seconds + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // Bucket start. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 10)); + return true; + })); + + int triggerId = 5; + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, + tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + + // Check dump report. + ProtoOutputStream output; + std::set<string> strSet; + gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, true, + FAST /* dump_latency */, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_gauge_metrics()); + EXPECT_EQ(0, report.gauge_metrics().data_size()); + EXPECT_EQ(1, report.gauge_metrics().skipped_size()); + + EXPECT_EQ(NanoToMillis(bucketStartTimeNs), + report.gauge_metrics().skipped(0).start_bucket_elapsed_millis()); + EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), + report.gauge_metrics().skipped(0).end_bucket_elapsed_millis()); + EXPECT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size()); + + auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0); + EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason()); + EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis()); +} } // namespace statsd } // namespace os 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..8c8836b94f56 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -428,7 +428,7 @@ shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int return logEvent; } -// + void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1, int32_t value2) { AStatsEvent* statsEvent = AStatsEvent_obtain(); @@ -531,6 +531,18 @@ shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) { return logEvent; } +void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); +} + std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( uint64_t timestampNs, const android::view::DisplayStateEnum state) { AStatsEvent* statsEvent = AStatsEvent_obtain(); @@ -600,32 +612,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 +864,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..7c017554d511 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -187,6 +187,8 @@ void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTi std::shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs); +void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs); + // Create log event for screen state changed. std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( uint64_t timestampNs, const android::view::DisplayStateEnum state); @@ -195,14 +197,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 +251,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/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index c9673f59183a..1b941defba42 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -30,7 +30,6 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Singleton; -import android.window.ITaskOrganizerController; import java.util.List; @@ -159,24 +158,6 @@ public class ActivityTaskManager { } }; - /** @hide */ - @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public static ITaskOrganizerController getTaskOrganizerController() { - return ITaskOrganizerControllerSingleton.get(); - } - - private static final Singleton<ITaskOrganizerController> ITaskOrganizerControllerSingleton = - new Singleton<ITaskOrganizerController>() { - @Override - protected ITaskOrganizerController create() { - try { - return getService().getTaskOrganizerController(); - } catch (RemoteException e) { - return null; - } - } - }; - /** * Sets the windowing mode for a specific task. Only works on tasks of type * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} 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..a5dcefcf3ab7 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1088,8 +1088,9 @@ public class AppOpsManager { public static final int OP_ACTIVATE_PLATFORM_VPN = AppProtoEnums.APP_OP_ACTIVATE_PLATFORM_VPN; /** @hide */ public static final int OP_LOADER_USAGE_STATS = AppProtoEnums.APP_OP_LOADER_USAGE_STATS; - /** @hide Access telephony call audio */ - public static final int OP_ACCESS_CALL_AUDIO = AppProtoEnums.APP_OP_ACCESS_CALL_AUDIO; + + // App op deprecated/removed. + private static final int OP_DEPRECATED_1 = AppProtoEnums.APP_OP_DEPRECATED_1; /** @hide Auto-revoke app permissions if app is unused for an extended period */ public static final int OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = @@ -1396,9 +1397,6 @@ public class AppOpsManager { @SystemApi public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage"; - /** @hide Access telephony call audio */ - @SystemApi - public static final String OPSTR_ACCESS_CALL_AUDIO = "android:access_call_audio"; /** @hide Auto-revoke app permissions if app is unused for an extended period */ @SystemApi @@ -1498,7 +1496,6 @@ public class AppOpsManager { OP_MANAGE_EXTERNAL_STORAGE, OP_INTERACT_ACROSS_PROFILES, OP_LOADER_USAGE_STATS, - OP_ACCESS_CALL_AUDIO, OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, }; @@ -1608,7 +1605,7 @@ public class AppOpsManager { OP_INTERACT_ACROSS_PROFILES, //INTERACT_ACROSS_PROFILES OP_ACTIVATE_PLATFORM_VPN, // ACTIVATE_PLATFORM_VPN OP_LOADER_USAGE_STATS, // LOADER_USAGE_STATS - OP_ACCESS_CALL_AUDIO, // ACCESS_CALL_AUDIO + OP_DEPRECATED_1, // deprecated OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, //AUTO_REVOKE_PERMISSIONS_IF_UNUSED OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, //OP_AUTO_REVOKE_MANAGED_BY_INSTALLER }; @@ -1713,7 +1710,7 @@ public class AppOpsManager { OPSTR_INTERACT_ACROSS_PROFILES, OPSTR_ACTIVATE_PLATFORM_VPN, OPSTR_LOADER_USAGE_STATS, - OPSTR_ACCESS_CALL_AUDIO, + "", // deprecated OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER, }; @@ -1819,7 +1816,7 @@ public class AppOpsManager { "INTERACT_ACROSS_PROFILES", "ACTIVATE_PLATFORM_VPN", "LOADER_USAGE_STATS", - "ACCESS_CALL_AUDIO", + "deprecated", "AUTO_REVOKE_PERMISSIONS_IF_UNUSED", "AUTO_REVOKE_MANAGED_BY_INSTALLER", }; @@ -1926,7 +1923,7 @@ public class AppOpsManager { android.Manifest.permission.INTERACT_ACROSS_PROFILES, null, // no permission for OP_ACTIVATE_PLATFORM_VPN android.Manifest.permission.LOADER_USAGE_STATS, - Manifest.permission.ACCESS_CALL_AUDIO, + null, // deprecated operation null, // no permission for OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED null, // no permission for OP_AUTO_REVOKE_MANAGED_BY_INSTALLER }; @@ -2033,7 +2030,7 @@ public class AppOpsManager { null, // INTERACT_ACROSS_PROFILES null, // ACTIVATE_PLATFORM_VPN null, // LOADER_USAGE_STATS - null, // ACCESS_CALL_AUDIO + null, // deprecated operation null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED null, // AUTO_REVOKE_MANAGED_BY_INSTALLER }; @@ -2139,7 +2136,7 @@ public class AppOpsManager { null, // INTERACT_ACROSS_PROFILES null, // ACTIVATE_PLATFORM_VPN null, // LOADER_USAGE_STATS - null, // ACCESS_CALL_AUDIO + null, // deprecated operation null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED null, // AUTO_REVOKE_MANAGED_BY_INSTALLER }; @@ -2244,7 +2241,7 @@ public class AppOpsManager { AppOpsManager.MODE_DEFAULT, // INTERACT_ACROSS_PROFILES AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS - AppOpsManager.MODE_DEFAULT, // ACCESS_CALL_AUDIO + AppOpsManager.MODE_IGNORED, // deprecated operation AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED AppOpsManager.MODE_ALLOWED, // OP_AUTO_REVOKE_MANAGED_BY_INSTALLER }; @@ -2353,7 +2350,7 @@ public class AppOpsManager { false, // INTERACT_ACROSS_PROFILES false, // ACTIVATE_PLATFORM_VPN false, // LOADER_USAGE_STATS - false, // ACCESS_CALL_AUDIO + false, // deprecated operation false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED false, // AUTO_REVOKE_MANAGED_BY_INSTALLER }; @@ -8446,7 +8443,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..833bfed573b2 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. @@ -143,9 +144,6 @@ interface IActivityManager { void attachApplication(in IApplicationThread app, long startSeq); List<ActivityManager.RunningTaskInfo> getTasks(int maxNum); @UnsupportedAppUsage - List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, int ignoreActivityType, - int ignoreWindowingMode); - @UnsupportedAppUsage void moveTaskToFront(in IApplicationThread caller, in String callingPackage, int task, int flags, in Bundle options); @UnsupportedAppUsage diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index ab5c6788969a..8b8ebe80f01f 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -29,7 +29,6 @@ import android.app.IProcessObserver; import android.app.IRequestFinishCallback; import android.app.IServiceConnection; import android.app.IStopUserCallback; -import android.window.ITaskOrganizerController; import android.app.ITaskStackListener; import android.app.IUiAutomationConnection; import android.app.IUidObserver; @@ -69,9 +68,9 @@ import android.os.StrictMode; import android.os.WorkSource; import android.service.voice.IVoiceInteractionSession; import android.view.IRecentsAnimationRunner; -import android.window.ITaskOrganizer; import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationAdapter; +import android.window.IWindowOrganizerController; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; @@ -156,8 +155,8 @@ interface IActivityTaskManager { boolean removeTask(int taskId); void removeAllVisibleRecentTasks(); List<ActivityManager.RunningTaskInfo> getTasks(int maxNum); - List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, int ignoreActivityType, - int ignoreWindowingMode); + List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, + boolean filterOnlyVisibleRecents); boolean shouldUpRecreateTask(in IBinder token, in String destAffinity); boolean navigateUpTo(in IBinder token, in Intent target, int resultCode, in Intent resultData); @@ -340,10 +339,8 @@ interface IActivityTaskManager { in Rect tempDockedTaskInsetBounds, in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds); - /** - * Returns an interface enabling the management of task organizers. - */ - ITaskOrganizerController getTaskOrganizerController(); + /** Returns an interface enabling the management of window organizers. */ + IWindowOrganizerController getWindowOrganizerController(); /** * Sets whether we are currently in an interactive split screen resize operation where we 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/TaskEmbedder.java b/core/java/android/app/TaskEmbedder.java index 5ebcc46aa0d8..b8ad30840173 100644 --- a/core/java/android/app/TaskEmbedder.java +++ b/core/java/android/app/TaskEmbedder.java @@ -16,6 +16,7 @@ package android.app; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; @@ -291,7 +292,7 @@ public class TaskEmbedder { * @see #startActivity(PendingIntent) */ public void startActivity(@NonNull Intent intent) { - final ActivityOptions options = prepareActivityOptions(); + final ActivityOptions options = prepareActivityOptions(null); mContext.startActivity(intent, options.toBundle()); } @@ -304,7 +305,7 @@ public class TaskEmbedder { * @see #startActivity(PendingIntent) */ public void startActivity(@NonNull Intent intent, UserHandle user) { - final ActivityOptions options = prepareActivityOptions(); + final ActivityOptions options = prepareActivityOptions(null); mContext.startActivityAsUser(intent, options.toBundle(), user); } @@ -316,7 +317,7 @@ public class TaskEmbedder { * @see #startActivity(Intent) */ public void startActivity(@NonNull PendingIntent pendingIntent) { - final ActivityOptions options = prepareActivityOptions(); + final ActivityOptions options = prepareActivityOptions(null); try { pendingIntent.send(null /* context */, 0 /* code */, null /* intent */, null /* onFinished */, null /* handler */, null /* requiredPermission */, @@ -337,8 +338,7 @@ public class TaskEmbedder { */ public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent, @NonNull ActivityOptions options) { - - options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId()); + prepareActivityOptions(options); try { pendingIntent.send(mContext, 0 /* code */, fillInIntent, null /* onFinished */, null /* handler */, null /* requiredPermission */, @@ -364,21 +364,25 @@ public class TaskEmbedder { @NonNull ActivityOptions options, @Nullable Rect sourceBounds) { LauncherApps service = (LauncherApps) mContext.getSystemService(Context.LAUNCHER_APPS_SERVICE); - options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId()); + prepareActivityOptions(options); service.startShortcut(shortcut, sourceBounds, options.toBundle()); } /** - * Check if container is ready to launch and create {@link ActivityOptions} to target the - * virtual display. + * Check if container is ready to launch and modify {@param options} to target the virtual + * display, creating them if necessary. */ - private ActivityOptions prepareActivityOptions() { + private ActivityOptions prepareActivityOptions(ActivityOptions options) { if (mVirtualDisplay == null) { throw new IllegalStateException( "Trying to start activity before ActivityView is ready."); } - final ActivityOptions options = ActivityOptions.makeBasic(); + if (options == null) { + options = ActivityOptions.makeBasic(); + } options.setLaunchDisplayId(getDisplayId()); + options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + options.setTaskAlwaysOnTop(true); return options; } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 1b1568ac5a12..c5d343d168ca 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -178,6 +178,13 @@ public class WallpaperManager { public static final String COMMAND_DROP = "android.home.drop"; /** + * Command for {@link #sendWallpaperCommand}: reported when the wallpaper that was already + * set is re-applied by the user. + * @hide + */ + public static final String COMMAND_REAPPLY = "android.wallpaper.reapply"; + + /** * Extra passed back from setWallpaper() giving the new wallpaper's assigned ID. * @hide */ 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..10309a9b4a03 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. @@ -4424,11 +4424,13 @@ public class DevicePolicyManager { * the current factory reset protection (FRP) policy set previously by * {@link #setFactoryResetProtectionPolicy}. * <p> - * This method can also be called by the FRP management agent on device, in which case, - * it can pass {@code null} as the ComponentName. + * This method can also be called by the FRP management agent on device or with the permission + * {@link android.Manifest.permission#MASTER_CLEAR}, in which case, it can pass {@code null} + * as the ComponentName. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with or - * {@code null} if called by the FRP management agent on device. + * {@code null} if called by the FRP management agent on device or with the + * permission {@link android.Manifest.permission#MASTER_CLEAR}. * @return The current FRP policy object or {@code null} if no policy is set. * @throws SecurityException if {@code admin} is not a device owner, a profile owner of * an organization-owned device or the FRP management agent. @@ -11961,7 +11963,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/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java index 954db0459f99..aa94e817c152 100644 --- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java +++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java @@ -43,6 +43,12 @@ import java.util.List; * reset protection policy for the device by calling the {@code DevicePolicyManager} method * {@link DevicePolicyManager#setFactoryResetProtectionPolicy(ComponentName, * FactoryResetProtectionPolicy)}}. + * <p> + * Normally factory reset protection does not kick in if the device is factory reset via Settings. + * This is also the case when a device owner sets factory reset protection policy. However, + * when a profile owner of an organization-owned device sets factory reset protection policy that + * locks the device to specific accounts, the policy will take effect even if factory reset is + * performed from Settings. * * @see DevicePolicyManager#setFactoryResetProtectionPolicy * @see DevicePolicyManager#getFactoryResetProtectionPolicy @@ -236,4 +242,16 @@ public final class FactoryResetProtectionPolicy implements Parcelable { } } + /** + * Returns if the policy will result in factory reset protection being locked to + * admin-specified accounts. + * <p> + * When a device has a non-empty factory reset protection policy, trusted factory reset + * via Settings will no longer remove factory reset protection from the device. + * @hide + */ + public boolean isNotEmpty() { + return !mFactoryResetProtectionAccounts.isEmpty() && mFactoryResetProtectionEnabled; + } + } 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..9cf6569a6220 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,11 @@ 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) { + putExceptionInBundle(result, ContentResolver.REMOTE_CALLBACK_ERROR, e); + } callback.sendResult(result); } @@ -585,8 +590,12 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall public void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri, RemoteCallback callback) { final Bundle result = new Bundle(); - result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, - canonicalize(callingPkg, attributionTag, uri)); + try { + result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, + canonicalize(callingPkg, attributionTag, uri)); + } catch (Exception e) { + putExceptionInBundle(result, ContentResolver.REMOTE_CALLBACK_ERROR, e); + } callback.sendResult(result); } @@ -700,6 +709,22 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall return AppOpsManager.MODE_ALLOWED; } + + private void putExceptionInBundle(Bundle bundle, String key, 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())); + } + bundle.putByteArray(key, parcel.marshall()); + } finally { + parcel.recycle(); + } + } } boolean checkUser(int pid, int uid, Context context) { 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/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 179fc5c661a5..7578ede2648d 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -268,15 +268,17 @@ public class CrossProfileApps { } /** - * Returns whether the calling package can request user consent to interact across profiles. + * Returns whether the calling package can request to navigate the user to + * the relevant settings page to request user consent to interact across profiles. * - * <p>If {@code true}, user consent can be obtained via {@link + * <p>If {@code true}, the navigation intent can be obtained via {@link * #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts. * * <p>Specifically, returns whether the following are all true: * <ul> - * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li> + * <li>{@code UserManager#getEnabledProfileIds(int)} ()} returns at least one other profile for + * the calling user.</li> * <li>The calling app has requested</li> * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest. * <li>The calling package has either been whitelisted by default by the OEM or has been @@ -285,6 +287,10 @@ public class CrossProfileApps { * </li> * </ul> * + * <p>Note that in order for the user to be able to grant the consent, the requesting package + * must be whitelisted by the admin or the OEM and installed in the other profile. If this is + * not the case the user will be shown a message explaining why they can't grant the consent. + * * <p>Note that user consent could already be granted if given a return value of {@code true}. * The package's current ability to interact across profiles can be checked with {@link * #canInteractAcrossProfiles()}. @@ -422,6 +428,23 @@ public class CrossProfileApps { } /** + * Returns {@code true} if the given package has requested + * {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} and the user has at least one + * other profile in the same profile group. + * + * <p>This differs from {@link #canConfigureInteractAcrossProfiles(String)} since it will + * not return {@code false} if the app is not whitelisted or not installed in the other profile. + * + * @hide + */ + public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) { + try { + return mService.canUserAttemptToConfigureInteractAcrossProfiles(packageName); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + /** * For each of the packages defined in {@code previousCrossProfilePackages} but not included in * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission * #INTERACT_ACROSS_PROFILES} back to its default value if it can no longer be configured by @@ -464,6 +487,34 @@ public class CrossProfileApps { } } + /** + * Clears the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} back to + * its default value for every package on the device. + * + * <p>This method can be used to ensure that app-op state is not left around on existing users + * for previously-configured profiles. + * + * <p>If the caller does not have the {@link android.Manifest.permission + * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that + * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String, + * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}. + * + * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link + * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. + * + * @hide + */ + @RequiresPermission( + allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, + android.Manifest.permission.INTERACT_ACROSS_USERS}) + public void clearInteractAcrossProfilesAppOps() { + try { + mService.clearInteractAcrossProfilesAppOps(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private void verifyCanAccessUser(UserHandle userHandle) { if (!getTargetUserProfiles().contains(userHandle)) { throw new SecurityException("Not allowed to access " + userHandle); diff --git a/core/java/android/content/pm/DataLoaderParams.java b/core/java/android/content/pm/DataLoaderParams.java index 99c0907f1844..a791026d44cd 100644 --- a/core/java/android/content/pm/DataLoaderParams.java +++ b/core/java/android/content/pm/DataLoaderParams.java @@ -17,12 +17,8 @@ package android.content.pm; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.ComponentName; -import android.os.ParcelFileDescriptor; - -import java.util.Map; /** * This class represents the parameters used to configure a Data Loader. @@ -44,7 +40,7 @@ public class DataLoaderParams { */ public static final @NonNull DataLoaderParams forStreaming(@NonNull ComponentName componentName, @NonNull String arguments) { - return new DataLoaderParams(DataLoaderType.STREAMING, componentName, arguments, null); + return new DataLoaderParams(DataLoaderType.STREAMING, componentName, arguments); } /** @@ -55,29 +51,17 @@ public class DataLoaderParams { */ public static final @NonNull DataLoaderParams forIncremental( @NonNull ComponentName componentName, @NonNull String arguments) { - return new DataLoaderParams(DataLoaderType.INCREMENTAL, componentName, arguments, null); + return new DataLoaderParams(DataLoaderType.INCREMENTAL, componentName, arguments); } /** @hide */ public DataLoaderParams(@NonNull @DataLoaderType int type, @NonNull ComponentName componentName, - @NonNull String arguments, @Nullable Map<String, ParcelFileDescriptor> namedFds) { + @NonNull String arguments) { DataLoaderParamsParcel data = new DataLoaderParamsParcel(); data.type = type; data.packageName = componentName.getPackageName(); data.className = componentName.getClassName(); data.arguments = arguments; - if (namedFds == null || namedFds.isEmpty()) { - data.dynamicArgs = new NamedParcelFileDescriptor[0]; - } else { - data.dynamicArgs = new NamedParcelFileDescriptor[namedFds.size()]; - int i = 0; - for (Map.Entry<String, ParcelFileDescriptor> namedFd : namedFds.entrySet()) { - data.dynamicArgs[i] = new NamedParcelFileDescriptor(); - data.dynamicArgs[i].name = namedFd.getKey(); - data.dynamicArgs[i].fd = namedFd.getValue(); - i += 1; - } - } mData = data; } diff --git a/core/java/android/content/pm/DataLoaderParamsParcel.aidl b/core/java/android/content/pm/DataLoaderParamsParcel.aidl index e05843b4d4e9..d40012fd5718 100644 --- a/core/java/android/content/pm/DataLoaderParamsParcel.aidl +++ b/core/java/android/content/pm/DataLoaderParamsParcel.aidl @@ -17,7 +17,6 @@ package android.content.pm; import android.content.pm.DataLoaderType; -import android.content.pm.NamedParcelFileDescriptor; /** * Class for holding data loader configuration parameters. @@ -28,5 +27,4 @@ parcelable DataLoaderParamsParcel { @utf8InCpp String packageName; @utf8InCpp String className; @utf8InCpp String arguments; - NamedParcelFileDescriptor[] dynamicArgs; } diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl index 4cecb30990e6..e2850f111c4f 100644 --- a/core/java/android/content/pm/ICrossProfileApps.aidl +++ b/core/java/android/content/pm/ICrossProfileApps.aidl @@ -38,5 +38,7 @@ interface ICrossProfileApps { boolean canRequestInteractAcrossProfiles(in String callingPackage); void setInteractAcrossProfilesAppOp(in String packageName, int newMode); boolean canConfigureInteractAcrossProfiles(in String packageName); + boolean canUserAttemptToConfigureInteractAcrossProfiles(in String packageName); void resetInteractAcrossProfilesAppOps(in List<String> packageNames); + void clearInteractAcrossProfilesAppOps(); } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 50bee854c027..1e0b2e358e17 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1118,6 +1118,7 @@ public class PackageInstaller { * {@hide} */ @SystemApi + @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2) public @Nullable DataLoaderParams getDataLoaderParams() { try { DataLoaderParamsParcel data = mSession.getDataLoaderParams(); @@ -1157,6 +1158,7 @@ public class PackageInstaller { * {@hide} */ @SystemApi + @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2) public void addFile(@FileLocation int location, @NonNull String name, long lengthBytes, @NonNull byte[] metadata, @Nullable byte[] signature) { try { @@ -1180,6 +1182,7 @@ public class PackageInstaller { * {@hide} */ @SystemApi + @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2) public void removeFile(@FileLocation int location, @NonNull String name) { try { mSession.removeFile(location, name); @@ -1927,7 +1930,9 @@ public class PackageInstaller { * {@hide} */ @SystemApi - @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) + @RequiresPermission(allOf = { + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.USE_INSTALLER_V2}) public void setDataLoaderParams(@NonNull DataLoaderParams dataLoaderParams) { this.dataLoaderParams = dataLoaderParams; } 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/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 20ddba41bda1..1dadbda1918b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1837,6 +1837,12 @@ public class PackageParser { pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false); + final boolean isolatedSplits = sa.getBoolean( + com.android.internal.R.styleable.AndroidManifest_isolatedSplits, false); + if (isolatedSplits) { + pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING; + } + pkg.mCompileSdkVersion = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_compileSdkVersion, 0); pkg.applicationInfo.compileSdkVersion = pkg.mCompileSdkVersion; @@ -1912,10 +1918,6 @@ public class PackageParser { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE; } - if (sa.getBoolean(com.android.internal.R.styleable.AndroidManifest_isolatedSplits, false)) { - pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING; - } - // Resource boolean are -1, so 1 means we don't know the value. int supportsSmallScreens = 1; int supportsNormalScreens = 1; 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..894ad5584922 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; @@ -435,6 +433,10 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { R.styleable.AndroidManifest_compileSdkVersion, 0)); setCompileSdkVersionCodename(manifestArray.getNonConfigurationString( R.styleable.AndroidManifest_compileSdkVersionCodename, 0)); + + setIsolatedSplitLoading(manifestArray.getBoolean( + R.styleable.AndroidManifest_isolatedSplits, false)); + } } @@ -1089,8 +1091,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 +1250,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 +2026,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 +2501,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..e90ccdffa8a8 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -386,15 +386,14 @@ public class ParsingPackageUtils { return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME); } - TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest); + final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest); try { - boolean isCoreApp = parser.getAttributeBooleanValue(null, "coreApp", false); - - ParsingPackage pkg = mCallback.startParsingPackage(pkgName, apkPath, codePath, - manifestArray, isCoreApp); - - ParseResult<ParsingPackage> result = parseBaseApkTags(input, pkg, manifestArray, - res, parser, flags); + final boolean isCoreApp = + parser.getAttributeBooleanValue(null, "coreApp", false); + final ParsingPackage pkg = mCallback.startParsingPackage( + pkgName, apkPath, codePath, manifestArray, isCoreApp); + final ParseResult<ParsingPackage> result = + parseBaseApkTags(input, pkg, manifestArray, res, parser, flags); if (result.isError()) { return result; } @@ -620,14 +619,12 @@ public class ParsingPackageUtils { return sharedUserResult; } - pkg.setInstallLocation(anInt(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION, + pkg.setInstallLocation(anInteger(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION, R.styleable.AndroidManifest_installLocation, sa)) - .setTargetSandboxVersion(anInt(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX, + .setTargetSandboxVersion(anInteger(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX, R.styleable.AndroidManifest_targetSandboxVersion, sa)) /* Set the global "on SD card" flag */ - .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0) - .setIsolatedSplitLoading( - bool(false, R.styleable.AndroidManifest_isolatedSplits, sa)); + .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0); boolean foundApp = false; final int depth = parser.getDepth(); @@ -1829,8 +1826,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)) @@ -2659,6 +2655,10 @@ public class ParsingPackageUtils { return sa.getInt(attribute, defaultValue); } + private static int anInteger(int defaultValue, @StyleableRes int attribute, TypedArray sa) { + return sa.getInteger(attribute, defaultValue); + } + private static int anInt(@StyleableRes int attribute, TypedArray sa) { return sa.getInt(attribute, 0); } @@ -2689,6 +2689,6 @@ public class ParsingPackageUtils { boolean hasFeature(String feature); ParsingPackage startParsingPackage(String packageName, String baseCodePath, String codePath, - TypedArray manifestArray, boolean isCoreApp); + @NonNull TypedArray manifestArray, boolean isCoreApp); } } 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 c2ee21d5115d..782fff2f69e0 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -60,7 +60,6 @@ import android.text.method.MovementMethod; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; -import android.util.Size; import android.view.Gravity; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -878,7 +877,7 @@ public class InputMethodService extends AbstractInputMethodService { } private void notifyImeHidden() { - doHideWindow(); + requestHideSelf(0); } private void removeImeSurface() { @@ -1249,6 +1248,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. @@ -1479,8 +1479,8 @@ public class InputMethodService extends AbstractInputMethodService { */ public int getMaxWidth() { final WindowManager windowManager = getSystemService(WindowManager.class); - final Size windowSize = windowManager.getCurrentWindowMetrics().getSize(); - return windowSize.getWidth(); + final Rect windowBounds = windowManager.getCurrentWindowMetrics().getBounds(); + return windowBounds.width(); } /** diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 2a323e5ec97d..7332ede0b997 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -705,6 +705,36 @@ public class ConnectivityManager { @Deprecated public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused. + /** + * @deprecated Use {@link NetworkCapabilities} instead. + * @hide + */ + @Deprecated + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "TYPE_" }, value = { + TYPE_NONE, + TYPE_MOBILE, + TYPE_WIFI, + TYPE_MOBILE_MMS, + TYPE_MOBILE_SUPL, + TYPE_MOBILE_DUN, + TYPE_MOBILE_HIPRI, + TYPE_WIMAX, + TYPE_BLUETOOTH, + TYPE_DUMMY, + TYPE_ETHERNET, + TYPE_MOBILE_FOTA, + TYPE_MOBILE_IMS, + TYPE_MOBILE_CBS, + TYPE_WIFI_P2P, + TYPE_MOBILE_IA, + TYPE_MOBILE_EMERGENCY, + TYPE_PROXY, + TYPE_VPN, + TYPE_TEST + }) + public @interface LegacyNetworkType {} + // Deprecated constants for return values of startUsingNetworkFeature. They used to live // in com.android.internal.telephony.PhoneConstants until they were made inaccessible. private static final int DEPRECATED_PHONE_CONSTANT_APN_ALREADY_ACTIVE = 0; 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/LinkProperties.java b/core/java/android/net/LinkProperties.java index 2c356e43d9fe..7ff954bdc1d2 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -690,9 +690,9 @@ public final class LinkProperties implements Parcelable { route.getMtu()); } - private int findRouteIndexByDestination(RouteInfo route) { + private int findRouteIndexByRouteKey(RouteInfo route) { for (int i = 0; i < mRoutes.size(); i++) { - if (mRoutes.get(i).isSameDestinationAs(route)) { + if (mRoutes.get(i).getRouteKey().equals(route.getRouteKey())) { return i; } } @@ -701,11 +701,11 @@ public final class LinkProperties implements Parcelable { /** * Adds a {@link RouteInfo} to this {@code LinkProperties}, if a {@link RouteInfo} - * with the same destination exists with different properties (e.g., different MTU), - * it will be updated. If the {@link RouteInfo} had an interface name set and - * that differs from the interface set for this {@code LinkProperties} an - * {@link IllegalArgumentException} will be thrown. The proper - * course is to add either un-named or properly named {@link RouteInfo}. + * with the same {@link RouteInfo.RouteKey} with different properties + * (e.g., different MTU), it will be updated. If the {@link RouteInfo} had an + * interface name set and that differs from the interface set for this + * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown. + * The proper course is to add either un-named or properly named {@link RouteInfo}. * * @param route A {@link RouteInfo} to add to this object. * @return {@code true} was added or updated, false otherwise. @@ -719,7 +719,7 @@ public final class LinkProperties implements Parcelable { } route = routeWithInterface(route); - int i = findRouteIndexByDestination(route); + int i = findRouteIndexByRouteKey(route); if (i == -1) { // Route was not present. Add it. mRoutes.add(route); diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 5c754a1b9733..8119df921745 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -16,6 +16,8 @@ package android.net; +import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -32,18 +34,52 @@ import android.util.Log; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; /** - * A Utility class for handling for communicating between bearer-specific + * A utility class for handling for communicating between bearer-specific * code and ConnectivityService. * + * An agent manages the life cycle of a network. A network starts its + * life cycle when {@link register} is called on NetworkAgent. The network + * is then connecting. When full L3 connectivity has been established, + * the agent shoud call {@link setConnected} to inform the system that + * this network is ready to use. When the network disconnects its life + * ends and the agent should call {@link unregister}, at which point the + * system will clean up and free resources. + * Any reconnection becomes a new logical network, so after a network + * is disconnected the agent cannot be used any more. Network providers + * should create a new NetworkAgent instance to handle new connections. + * * A bearer may have more than one NetworkAgent if it can simultaneously * support separate networks (IMS / Internet / MMS Apns on cellular, or * perhaps connections with different SSID or P2P for Wi-Fi). * + * This class supports methods to start and stop sending keepalive packets. + * Keepalive packets are typically sent at periodic intervals over a network + * with NAT when there is no other traffic to avoid the network forcefully + * closing the connection. NetworkAgents that manage technologies that + * have hardware support for keepalive should implement the related + * methods to save battery life. NetworkAgent that cannot get support + * without waking up the CPU should not, as this would be prohibitive in + * terms of battery - these agents should simply not override the related + * methods, which results in the implementation returning + * {@link SocketKeepalive.ERROR_UNSUPPORTED} as appropriate. + * + * Keepalive packets need to be sent at relatively frequent intervals + * (a few seconds to a few minutes). As the contents of keepalive packets + * depend on the current network status, hardware needs to be configured + * to send them and has a limited amount of memory to do so. The HAL + * formalizes this as slots that an implementation can configure to send + * the correct packets. Devices typically have a small number of slots + * per radio technology, and the specific number of slots for each + * technology is specified in configuration files. + * {@see SocketKeepalive} for details. + * * @hide */ @SystemApi @@ -65,7 +101,7 @@ public abstract class NetworkAgent { private final String LOG_TAG; private static final boolean DBG = true; private static final boolean VDBG = false; - private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>(); + private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>(); private volatile long mLastBwRefreshTime = 0; private static final long BW_REFRESH_MIN_WIN_MS = 500; private boolean mBandwidthUpdateScheduled = false; @@ -74,6 +110,8 @@ public abstract class NetworkAgent { // into the internal API of ConnectivityService. @NonNull private NetworkInfo mNetworkInfo; + @NonNull + private final Object mRegisterLock = new Object(); /** * The ID of the {@link NetworkProvider} that created this object, or @@ -158,6 +196,14 @@ public abstract class NetworkAgent { */ public static final int VALIDATION_STATUS_NOT_VALID = 2; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "VALIDATION_STATUS_" }, value = { + VALIDATION_STATUS_VALID, + VALIDATION_STATUS_NOT_VALID + }) + public @interface ValidationStatus {} + // TODO: remove. /** @hide */ public static final int VALID_NETWORK = 1; @@ -202,7 +248,7 @@ public abstract class NetworkAgent { * Sent by ConnectivityService to the NetworkAgent to request that the specified packet be sent * periodically on the given interval. * - * arg1 = the slot number of the keepalive to start + * arg1 = the hardware slot number of the keepalive to start * arg2 = interval in seconds * obj = KeepalivePacketData object describing the data to be sent * @@ -214,7 +260,7 @@ public abstract class NetworkAgent { /** * Requests that the specified keepalive packet be stopped. * - * arg1 = slot number of the keepalive to stop. + * arg1 = hardware slot number of the keepalive to stop. * * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics. * @hide @@ -229,7 +275,7 @@ public abstract class NetworkAgent { * This is also sent by KeepaliveTracker to the app's {@link SocketKeepalive}, * so that the app's {@link SocketKeepalive.Callback} methods can be called. * - * arg1 = slot number of the keepalive + * arg1 = hardware slot number of the keepalive * arg2 = error code * @hide */ @@ -259,7 +305,7 @@ public abstract class NetworkAgent { * remote site will send ACK packets in response to the keepalive packets, the firmware also * needs to be configured to properly filter the ACKs to prevent the system from waking up. * This does not happen with UDP, so this message is TCP-specific. - * arg1 = slot number of the keepalive to filter for. + * arg1 = hardware slot number of the keepalive to filter for. * obj = the keepalive packet to send repeatedly. * @hide */ @@ -268,7 +314,7 @@ public abstract class NetworkAgent { /** * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}. - * arg1 = slot number of the keepalive packet filter to remove. + * arg1 = hardware slot number of the keepalive packet filter to remove. * @hide */ public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17; @@ -441,7 +487,15 @@ public abstract class NetworkAgent { + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl); } - onValidationStatus(msg.arg1 /* status */, redirectUrl); + Uri uri = null; + try { + if (null != redirectUrl) { + uri = Uri.parse(redirectUrl); + } + } catch (Exception e) { + Log.wtf(LOG_TAG, "Surprising URI : " + redirectUrl, e); + } + onValidationStatus(msg.arg1 /* status */, uri); break; } case CMD_SAVE_ACCEPT_UNVALIDATED: { @@ -489,19 +543,29 @@ public abstract class NetworkAgent { /** * Register this network agent with ConnectivityService. + * + * This method can only be called once per network agent. + * * @return the Network associated with this network agent (which can also be obtained later * by calling getNetwork() on this agent). + * @throws IllegalStateException thrown by the system server if this network agent is + * already registered. */ @NonNull public Network register() { if (VDBG) log("Registering NetworkAgent"); final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context .getSystemService(Context.CONNECTIVITY_SERVICE); - mNetwork = cm.registerNetworkAgent(new Messenger(mHandler), - new NetworkInfo(mInitialConfiguration.info), - mInitialConfiguration.properties, mInitialConfiguration.capabilities, - mInitialConfiguration.score, mInitialConfiguration.config, providerId); - mInitialConfiguration = null; // All this memory can now be GC'd + synchronized (mRegisterLock) { + if (mNetwork != null) { + throw new IllegalStateException("Agent already registered"); + } + mNetwork = cm.registerNetworkAgent(new Messenger(mHandler), + new NetworkInfo(mInitialConfiguration.info), + mInitialConfiguration.properties, mInitialConfiguration.capabilities, + mInitialConfiguration.score, mInitialConfiguration.config, providerId); + mInitialConfiguration = null; // All this memory can now be GC'd + } return mNetwork; } @@ -544,13 +608,14 @@ public abstract class NetworkAgent { * Must be called by the agent when the network's {@link LinkProperties} change. * @param linkProperties the new LinkProperties. */ - public void sendLinkProperties(@NonNull LinkProperties linkProperties) { + public final void sendLinkProperties(@NonNull LinkProperties linkProperties) { Objects.requireNonNull(linkProperties); queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties)); } /** * Inform ConnectivityService that this agent has now connected. + * Call {@link #unregister} to disconnect. */ public void setConnected() { if (mIsLegacy) { @@ -569,8 +634,7 @@ public abstract class NetworkAgent { */ public void unregister() { if (mIsLegacy) { - throw new UnsupportedOperationException( - "Legacy agents can't call unregister."); + throw new UnsupportedOperationException("Legacy agents can't call unregister."); } mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null); queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo); @@ -626,7 +690,7 @@ public abstract class NetworkAgent { * @hide TODO: expose something better. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public void sendNetworkInfo(NetworkInfo networkInfo) { + public final void sendNetworkInfo(NetworkInfo networkInfo) { if (!mIsLegacy) { throw new UnsupportedOperationException("Only legacy agents can call sendNetworkInfo."); } @@ -637,7 +701,7 @@ public abstract class NetworkAgent { * Must be called by the agent when the network's {@link NetworkCapabilities} change. * @param networkCapabilities the new NetworkCapabilities. */ - public void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) { + public final void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) { Objects.requireNonNull(networkCapabilities); mBandwidthUpdatePending.set(false); mLastBwRefreshTime = System.currentTimeMillis(); @@ -647,9 +711,10 @@ public abstract class NetworkAgent { /** * Must be called by the agent to update the score of this network. - * @param score the new score. + * + * @param score the new score, between 0 and 99. */ - public void sendNetworkScore(int score) { + public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) { if (score < 0) { throw new IllegalArgumentException("Score must be >= 0"); } @@ -737,11 +802,11 @@ public abstract class NetworkAgent { * subsequent attempts to validate connectivity that fail. * * @param status one of {@code VALIDATION_STATUS_VALID} or {@code VALIDATION_STATUS_NOT_VALID}. - * @param redirectUrl If Internet connectivity is being redirected (e.g., on a captive portal), + * @param redirectUri If Internet connectivity is being redirected (e.g., on a captive portal), * this is the destination the probes are being redirected to, otherwise {@code null}. */ - public void onValidationStatus(int status, @Nullable String redirectUrl) { - networkStatus(status, redirectUrl); + public void onValidationStatus(@ValidationStatus int status, @Nullable Uri redirectUri) { + networkStatus(status, redirectUri.toString()); } /** @hide TODO delete once subclasses have moved to onValidationStatus */ protected void networkStatus(int status, String redirectUrl) { @@ -770,7 +835,12 @@ public abstract class NetworkAgent { * @param intervalSeconds the interval between packets * @param packet the packet to send. */ - public void onStartSocketKeepalive(int slot, int intervalSeconds, + // seconds is from SocketKeepalive.MIN_INTERVAL_SEC to MAX_INTERVAL_SEC, but these should + // not be exposed as constants because they may change in the future (API guideline 4.8) + // and should have getters if exposed at all. Getters can't be used in the annotation, + // so the values unfortunately need to be copied. + public void onStartSocketKeepalive(int slot, + @IntRange(from = 10, to = 3600) int intervalSeconds, @NonNull KeepalivePacketData packet) { Message msg = mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, slot, intervalSeconds, packet); @@ -801,9 +871,11 @@ public abstract class NetworkAgent { * Must be called by the agent when a socket keepalive event occurs. * * @param slot the hardware slot on which the event occurred. - * @param event the event that occurred. + * @param event the event that occurred, as one of the SocketKeepalive.ERROR_* + * or SocketKeepalive.SUCCESS constants. */ - public void sendSocketKeepaliveEvent(int slot, int event) { + public final void sendSocketKeepaliveEvent(int slot, + @SocketKeepalive.KeepaliveEvent int event) { queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, event); } /** @hide TODO delete once callers have moved to sendSocketKeepaliveEvent */ @@ -845,9 +917,18 @@ public abstract class NetworkAgent { } /** - * Called by ConnectivityService to inform this network transport of signal strength thresholds + * Called by ConnectivityService to inform this network agent of signal strength thresholds * that when crossed should trigger a system wakeup and a NetworkCapabilities update. * + * When the system updates the list of thresholds that should wake up the CPU for a + * given agent it will call this method on the agent. The agent that implement this + * should implement it in hardware so as to ensure the CPU will be woken up on breach. + * Agents are expected to react to a breach by sending an updated NetworkCapabilities + * object with the appropriate signal strength to sendNetworkCapabilities. + * + * The specific units are bearer-dependent. See details on the units and requests in + * {@link NetworkCapabilities.Builder#setSignalStrength}. + * * @param thresholds the array of thresholds that should trigger wakeups. */ public void onSignalStrengthThresholdsUpdated(@NonNull int[] thresholds) { diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java index ca9328a713f0..fee868a93be4 100644 --- a/core/java/android/net/NetworkAgentConfig.java +++ b/core/java/android/net/NetworkAgentConfig.java @@ -155,6 +155,7 @@ public final class NetworkAgentConfig implements Parcelable { /** * @return the legacy type */ + @ConnectivityManager.LegacyNetworkType public int getLegacyType() { return legacyType; } @@ -206,7 +207,7 @@ public final class NetworkAgentConfig implements Parcelable { /** * Builder class to facilitate constructing {@link NetworkAgentConfig} objects. */ - public static class Builder { + public static final class Builder { private final NetworkAgentConfig mConfig = new NetworkAgentConfig(); /** 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/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 964f13f39ec6..798856d13b1d 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -473,9 +473,7 @@ public class NetworkRequest implements Parcelable { * * @param nc Capabilities that should satisfy this NetworkRequest. null capabilities do not * satisfy any request. - * @hide */ - @SystemApi public boolean satisfiedBy(@Nullable NetworkCapabilities nc) { return networkCapabilities.satisfiedByNetworkCapabilities(nc); } diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index fec2df412adb..dbdaa4c2da67 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -26,6 +26,7 @@ import android.net.util.NetUtils; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.util.Pair; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -527,23 +528,27 @@ public final class RouteInfo implements Parcelable { } /** - * Compares this RouteInfo object against the specified object and indicates if the - * destinations of both routes are equal. - * @return {@code true} if the route destinations are equal, {@code false} otherwise. + * A helper class that contains the destination and the gateway in a {@code RouteInfo}, + * used by {@link ConnectivityService#updateRoutes} or + * {@link LinkProperties#addRoute} to calculate the list to be updated. * * @hide */ - public boolean isSameDestinationAs(@Nullable Object obj) { - if (this == obj) return true; - - if (!(obj instanceof RouteInfo)) return false; - - RouteInfo target = (RouteInfo) obj; - - if (Objects.equals(mDestination, target.getDestination())) { - return true; + public static class RouteKey extends Pair<IpPrefix, InetAddress> { + RouteKey(@NonNull IpPrefix destination, @Nullable InetAddress gateway) { + super(destination, gateway); } - return false; + } + + /** + * Get {@code RouteKey} of this {@code RouteInfo}. + * @return a {@code RouteKey} object. + * + * @hide + */ + @NonNull + public RouteKey getRouteKey() { + return new RouteKey(mDestination, mGateway); } /** diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java index fc9a8f63c131..8ff8f4c48c32 100644 --- a/core/java/android/net/SocketKeepalive.java +++ b/core/java/android/net/SocketKeepalive.java @@ -109,6 +109,16 @@ public abstract class SocketKeepalive implements AutoCloseable { }) public @interface ErrorCode {} + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + SUCCESS, + ERROR_INVALID_LENGTH, + ERROR_UNSUPPORTED, + ERROR_INSUFFICIENT_RESOURCES + }) + public @interface KeepaliveEvent {} + /** * The minimum interval in seconds between keepalive packet transmissions. * diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl index 84013e7ebc88..615ae6593330 100644 --- a/core/java/android/os/IVibratorService.aidl +++ b/core/java/android/os/IVibratorService.aidl @@ -28,7 +28,7 @@ interface IVibratorService boolean registerVibratorStateListener(in IVibratorStateListener listener); boolean unregisterVibratorStateListener(in IVibratorStateListener listener); boolean hasAmplitudeControl(); - boolean[] areEffectsSupported(in int[] effectIds); + int[] areEffectsSupported(in int[] effectIds); boolean[] arePrimitivesSupported(in int[] primitiveIds); boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, in VibrationEffect effect, in VibrationAttributes attributes); 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/Process.java b/core/java/android/os/Process.java index b7b3c4fc8add..5d2c9d18c00c 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -607,6 +607,9 @@ public class Process { * started. * @param pkgDataInfoMap Map from related package names to private data directory * volume UUID and inode number. + * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory + * volume UUID and inode number. + * @param bindMountAppsData whether zygote needs to mount CE and DE data. * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data. * @param zygoteArgs Additional arguments to supply to the zygote process. * @return An object that describes the result of the attempt to start the process. @@ -631,13 +634,17 @@ public class Process { @Nullable long[] disabledCompatChanges, @Nullable Map<String, Pair<String, Long>> pkgDataInfoMap, + @Nullable Map<String, Pair<String, Long>> + whitelistedDataInfoMap, + boolean bindMountAppsData, boolean bindMountAppStorageDirs, @Nullable String[] zygoteArgs) { return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges, - pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs); + pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData, + bindMountAppStorageDirs, zygoteArgs); } /** @hide */ @@ -661,7 +668,8 @@ public class Process { runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, packageName, /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false, - disabledCompatChanges, /* pkgDataInfoMap */ null, false, zygoteArgs); + disabledCompatChanges, /* pkgDataInfoMap */ null, + /* whitelistedDataInfoMap */ null, false, false, zygoteArgs); } /** diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index d60820ef0f57..8cdcd49cb2cc 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -665,15 +665,17 @@ public class RecoverySystem { * the preparation for unattended update is reset. * * @param context the Context to use. - * @throws IOException if there were any errors setting up unattended update + * @throws IOException if there were any errors clearing the unattended update state * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.RECOVERY) - public static boolean clearPrepareForUnattendedUpdate(@NonNull Context context) + public static void clearPrepareForUnattendedUpdate(@NonNull Context context) throws IOException { RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); - return rs.clearLskf(); + if (!rs.clearLskf()) { + throw new IOException("could not reset unattended update state"); + } } /** @@ -684,21 +686,22 @@ public class RecoverySystem { * @param context the Context to use. * @param updateToken the token used to call {@link #prepareForUnattendedUpdate} before * @param reason the reboot reason to give to the {@link PowerManager} - * @throws IOException if there were any errors setting up unattended update - * @return false if the reboot couldn't proceed because the device wasn't ready for an + * @throws IOException if the reboot couldn't proceed because the device wasn't ready for an * unattended reboot or if the {@code updateToken} did not match the previously * given token * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.RECOVERY) - public static boolean rebootAndApply(@NonNull Context context, @NonNull String updateToken, + public static void rebootAndApply(@NonNull Context context, @NonNull String updateToken, @NonNull String reason) throws IOException { if (updateToken == null) { throw new NullPointerException("updateToken == null"); } RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); - return rs.rebootWithLskf(updateToken, reason); + if (!rs.rebootWithLskf(updateToken, reason)) { + throw new IOException("system not prepared to apply update"); + } } /** diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java index da20c7f2ae70..2dba8dce62da 100644 --- a/core/java/android/os/SystemVibrator.java +++ b/core/java/android/os/SystemVibrator.java @@ -21,12 +21,13 @@ import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.media.AudioAttributes; -import android.os.IVibratorStateListener; import android.util.ArrayMap; import android.util.Log; + import com.android.internal.annotations.GuardedBy; -import java.util.concurrent.Executor; + import java.util.Objects; +import java.util.concurrent.Executor; /** * Vibrator implementation that controls the main system vibrator. @@ -238,13 +239,13 @@ public class SystemVibrator extends Vibrator { } @Override - public boolean[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) { + public int[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) { try { return mService.areEffectsSupported(effectIds); } catch (RemoteException e) { Log.w(TAG, "Failed to query effect support"); + throw e.rethrowAsRuntimeException(); } - return new boolean[effectIds.length]; } @Override @@ -254,8 +255,8 @@ public class SystemVibrator extends Vibrator { return mService.arePrimitivesSupported(primitiveIds); } catch (RemoteException e) { Log.w(TAG, "Failed to query effect support"); + throw e.rethrowAsRuntimeException(); } - return new boolean[primitiveIds.length]; } diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index aa89b515adc6..ca861577ab37 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -961,7 +961,7 @@ public abstract class VibrationEffect implements Parcelable { * * @see VibrationEffect#startComposition() */ - public static class Composition { + public static final class Composition { /** @hide */ @IntDef(prefix = { "PRIMITIVE_" }, value = { PRIMITIVE_CLICK, @@ -1020,6 +1020,8 @@ public abstract class VibrationEffect implements Parcelable { private ArrayList<PrimitiveEffect> mEffects = new ArrayList<>(); + Composition() { } + /** * Add a haptic primitive to the end of the current composition. * @@ -1030,7 +1032,7 @@ public abstract class VibrationEffect implements Parcelable { * * @return The {@link Composition} object to enable adding multiple primitives in one chain. */ - @Nullable + @NonNull public Composition addPrimitive(@Primitive int primitiveId) { addPrimitive(primitiveId, /*scale*/ 1.0f, /*delay*/ 0); return this; @@ -1046,7 +1048,7 @@ public abstract class VibrationEffect implements Parcelable { * * @return The {@link Composition} object to enable adding multiple primitives in one chain. */ - @Nullable + @NonNull public Composition addPrimitive(@Primitive int primitiveId, @FloatRange(from = 0f, to = 1f) float scale) { addPrimitive(primitiveId, scale, /*delay*/ 0); @@ -1058,11 +1060,11 @@ public abstract class VibrationEffect implements Parcelable { * * @param primitiveId The primitive to add * @param scale The scale to apply to the intensity of the primitive. - * @param delay The amount of time, in milliseconds, to wait before playing the prior + * @param delay The amount of time, in milliseconds, to wait between playing the prior * primitive and this one * @return The {@link Composition} object to enable adding multiple primitives in one chain. */ - @Nullable + @NonNull public Composition addPrimitive(@Primitive int primitiveId, @FloatRange(from = 0f, to = 1f) float scale, @IntRange(from = 0) int delay) { mEffects.add(new PrimitiveEffect(checkPrimitive(primitiveId), scale, delay)); diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index d4da7a84d2a1..86d009e8607e 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -32,6 +32,7 @@ import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; import java.util.concurrent.Executor; /** @@ -72,6 +73,37 @@ public abstract class Vibrator { */ public static final int VIBRATION_INTENSITY_HIGH = 3; + /** + * Vibration effect support: unknown + * + * The hardware doesn't report it's supported effects, so we can't determine whether the + * effect is supported or not. + */ + public static final int VIBRATION_EFFECT_SUPPORT_UNKNOWN = 0; + + /** + * Vibration effect support: supported + * + * This effect is supported by the underlying hardware. + */ + public static final int VIBRATION_EFFECT_SUPPORT_YES = 1; + + /** + * Vibration effect support: unsupported + * + * This effect is <b>not</b> supported by the underlying hardware. + */ + public static final int VIBRATION_EFFECT_SUPPORT_NO = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"VIBRATION_EFFECT_SUPPORT_"}, value = { + VIBRATION_EFFECT_SUPPORT_UNKNOWN, + VIBRATION_EFFECT_SUPPORT_YES, + VIBRATION_EFFECT_SUPPORT_NO, + }) + public @interface VibrationEffectSupport {} + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"VIBRATION_INTENSITY_"}, value = { @@ -318,47 +350,61 @@ public abstract class Vibrator { /** * Query whether the vibrator supports the given effects. * - * If the returned array is {@code null}, the hardware doesn't support querying its supported - * effects. It may support any or all effects, but there's no way to programmatically know - * whether a {@link #vibrate} call will be successful. + * Not all hardware reports its effect capabilities, so the system may not necessarily know + * whether an effect is supported or not. * - * If the returned array is non-null, then it will be the same length as the query array and - * the value at a given index will contain whether the effect at that same index in the - * querying array is supported or not. + * The returned array will be the same length as the query array and the value at a given index + * will contain {@link #VIBRATION_EFFECT_SUPPORT_YES} if the effect at that same index in the + * querying array is supported, {@link #VIBRATION_EFFECT_SUPPORT_NO} if it isn't supported, or + * {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN} if the system can't determine whether it's + * supported or not. * * @param effectIds Which effects to query for. - * @return Whether the effects are supported. Null when the hardware doesn't tell us what it - * supports. + * @return An array containing the systems current knowledge about whether the given effects + * are supported or not. */ - @Nullable - public boolean[] areEffectsSupported( + @NonNull + @VibrationEffectSupport + public int[] areEffectsSupported( @NonNull @VibrationEffect.EffectType int... effectIds) { - return new boolean[effectIds.length]; + final int[] support = new int[effectIds.length]; + Arrays.fill(support, VIBRATION_EFFECT_SUPPORT_NO); + return support; } /** * Query whether the vibrator supports all of the given effects. * - * If the result is {@code null}, the hardware doesn't support querying its supported - * effects. It may support any or all effects, but there's no way to programmatically know - * whether a {@link #vibrate} call will be successful. + * Not all hardware reports its effect capabilities, so the system may not necessarily know + * whether an effect is supported or not. * - * If the returned array is non-null, then it will return whether all of the effects are + * If the result is {@link #VIBRATION_EFFECT_SUPPORT_YES}, all effects in the query are * supported by the hardware. * + * If the result is {@link #VIBRATION_EFFECT_SUPPORT_NO}, at least one of the effects in the + * query is not supported. + * + * If the result is {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN}, the system doesn't know whether + * all of the effects are supported. It may support any or all of the queried effects, + * but there's no way to programmatically know whether a {@link #vibrate} call will successfully + * cause a vibration. It's guaranteed, however, that none of the queried effects are + * definitively unsupported by the hardware. + * * @param effectIds Which effects to query for. - * @return Whether the effects are supported. {@code null} when the hardware doesn't tell us - * what it supports. + * @return Whether all of the effects are supported. */ - @Nullable - public Boolean areAllEffectsSupported( + @VibrationEffectSupport + public final int areAllEffectsSupported( @NonNull @VibrationEffect.EffectType int... effectIds) { - for (boolean supported : areEffectsSupported(effectIds)) { - if (!supported) { - return false; + int support = VIBRATION_EFFECT_SUPPORT_YES; + for (int supported : areEffectsSupported(effectIds)) { + if (supported == VIBRATION_EFFECT_SUPPORT_NO) { + return VIBRATION_EFFECT_SUPPORT_NO; + } else if (supported == VIBRATION_EFFECT_SUPPORT_UNKNOWN) { + support = VIBRATION_EFFECT_SUPPORT_UNKNOWN; } } - return true; + return support; } @@ -384,7 +430,7 @@ public abstract class Vibrator { * @param primitiveIds Which primitives to query for. * @return Whether primitives effects are supported. */ - public boolean areAllPrimitivesSupported( + public final boolean areAllPrimitivesSupported( @NonNull @VibrationEffect.Composition.Primitive int... primitiveIds) { for (boolean supported : arePrimitivesSupported(primitiveIds)) { if (!supported) { diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 5f3f14facd75..a4c99c006d80 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -333,6 +333,9 @@ public class ZygoteProcess { * started. * @param pkgDataInfoMap Map from related package names to private data directory * volume UUID and inode number. + * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory + * volume UUID and inode number. + * @param bindMountAppsData whether zygote needs to mount CE and DE data. * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data. * * @param zygoteArgs Additional arguments to supply to the Zygote process. @@ -355,6 +358,9 @@ public class ZygoteProcess { @Nullable long[] disabledCompatChanges, @Nullable Map<String, Pair<String, Long>> pkgDataInfoMap, + @Nullable Map<String, Pair<String, Long>> + whitelistedDataInfoMap, + boolean bindMountAppsData, boolean bindMountAppStorageDirs, @Nullable String[] zygoteArgs) { // TODO (chriswailes): Is there a better place to check this value? @@ -367,7 +373,8 @@ public class ZygoteProcess { runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false, packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges, - pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs); + pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData, + bindMountAppStorageDirs, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); @@ -608,6 +615,9 @@ public class ZygoteProcess { * @param disabledCompatChanges a list of disabled compat changes for the process being started. * @param pkgDataInfoMap Map from related package names to private data directory volume UUID * and inode number. + * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory + * volume UUID and inode number. + * @param bindMountAppsData whether zygote needs to mount CE and DE data. * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data. * @param extraArgs Additional arguments to supply to the zygote process. * @return An object that describes the result of the attempt to start the process. @@ -631,6 +641,9 @@ public class ZygoteProcess { @Nullable long[] disabledCompatChanges, @Nullable Map<String, Pair<String, Long>> pkgDataInfoMap, + @Nullable Map<String, Pair<String, Long>> + whitelistedDataInfoMap, + boolean bindMountAppsData, boolean bindMountAppStorageDirs, @Nullable String[] extraArgs) throws ZygoteStartFailedEx { @@ -728,11 +741,33 @@ public class ZygoteProcess { } argsForZygote.add(sb.toString()); } + if (whitelistedDataInfoMap != null && whitelistedDataInfoMap.size() > 0) { + StringBuilder sb = new StringBuilder(); + sb.append(Zygote.WHITELISTED_DATA_INFO_MAP); + sb.append("="); + boolean started = false; + for (Map.Entry<String, Pair<String, Long>> entry : whitelistedDataInfoMap.entrySet()) { + if (started) { + sb.append(','); + } + started = true; + sb.append(entry.getKey()); + sb.append(','); + sb.append(entry.getValue().first); + sb.append(','); + sb.append(entry.getValue().second); + } + argsForZygote.add(sb.toString()); + } if (bindMountAppStorageDirs) { argsForZygote.add(Zygote.BIND_MOUNT_APP_STORAGE_DIRS); } + if (bindMountAppsData) { + argsForZygote.add(Zygote.BIND_MOUNT_APP_DATA_DIRS); + } + if (disabledCompatChanges != null && disabledCompatChanges.length > 0) { StringBuilder sb = new StringBuilder(); sb.append("--disabled-compat-changes="); @@ -1291,6 +1326,7 @@ public class ZygoteProcess { true /* startChildZygote */, null /* packageName */, ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */, null /* disabledCompatChanges */, null /* pkgDataInfoMap */, + null /* whitelistedDataInfoMap */, false /* bindMountAppsData*/, /* bindMountAppStorageDirs */ false, extraArgs); } catch (ZygoteStartFailedEx ex) { 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 a28ea899694c..530585d86465 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"; @@ -6596,11 +6589,9 @@ public final class Settings { "accessibility_shortcut_target_service"; /** - * Setting specifying the accessibility services, accessibility shortcut targets, - * or features to be toggled via the accessibility button in the navigation bar. - * - * <p> This is a colon-separated string list which contains the flattened - * {@link ComponentName} and the class name of a system class implementing a supported + * Setting specifying the accessibility service or feature to be toggled via the + * accessibility button in the navigation bar. This is either a flattened + * {@link ComponentName} or the class name of a system class implementing a supported * accessibility feature. * @hide */ @@ -6609,15 +6600,14 @@ public final class Settings { /** * Setting specifying the accessibility services, accessibility shortcut targets, - * or features to be toggled via the long press accessibility button in the navigation bar. + * or features to be toggled via the accessibility button in the navigation bar. * * <p> This is a colon-separated string list which contains the flattened * {@link ComponentName} and the class name of a system class implementing a supported * accessibility feature. * @hide */ - public static final String ACCESSIBILITY_BUTTON_LONG_PRESS_TARGETS = - "accessibility_button_long_press_targets"; + public static final String ACCESSIBILITY_BUTTON_TARGETS = "accessibility_button_targets"; /** * The system class name of magnification controller which is a target to be toggled via @@ -6782,8 +6772,8 @@ public final class Settings { * zoom in the display content and is targeted to low vision users. The current * magnification scale is controlled by {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}. * - * @deprecated Use {@link #ACCESSIBILITY_BUTTON_TARGET_COMPONENT} instead. - * {@link #ACCESSIBILITY_BUTTON_TARGET_COMPONENT} holds the magnification system class name + * @deprecated Use {@link #ACCESSIBILITY_BUTTON_TARGETS} instead. + * {@link #ACCESSIBILITY_BUTTON_TARGETS} holds the magnification system class name * when navigation bar magnification is enabled. * @hide */ @@ -8581,6 +8571,16 @@ public final class Settings { public static final String QS_TILES = "sysui_qs_tiles"; /** + * Whether this user has enabled Quick controls. + * + * 0 indicates disabled and 1 indicates enabled. A non existent value should be treated as + * enabled. + * + * @hide + */ + public static final String CONTROLS_ENABLED = "controls_enabled"; + + /** * Specifies whether the web action API is enabled. * * @hide @@ -10338,6 +10338,15 @@ public final class Settings { public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled"; /** + * 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 + */ + public static final String WIFI_MIGRATION_COMPLETED = "wifi_migration_completed"; + + /** * Value to specify whether network quality scores and badging should be shown in the UI. * * Type: int (0 for false, 1 for true) 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/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index cecfe24613a4..61744e423b5b 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -18,7 +18,7 @@ package android.service.contentcapture; import static android.view.contentcapture.ContentCaptureHelper.sDebug; import static android.view.contentcapture.ContentCaptureHelper.sVerbose; import static android.view.contentcapture.ContentCaptureHelper.toList; -import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID; +import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java index 9accf5b0abf5..4262c4022131 100644 --- a/core/java/android/service/controls/ControlsProviderService.java +++ b/core/java/android/service/controls/ControlsProviderService.java @@ -304,12 +304,11 @@ public abstract class ControlsProviderService extends Service { Preconditions.checkNotNull(context); Preconditions.checkNotNull(componentName); Preconditions.checkNotNull(control); - final ComponentName sysuiComponent = ComponentName.unflattenFromString( - context.getResources().getString( - com.android.internal.R.string.config_systemUIServiceComponent)); + final String controlsPackage = context.getString( + com.android.internal.R.string.config_controlsPackage); Intent intent = new Intent(ACTION_ADD_CONTROL); intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName); - intent.setPackage(sysuiComponent.getPackageName()); + intent.setPackage(controlsPackage); if (isStatelessControl(control)) { intent.putExtra(EXTRA_CONTROL, control); } else { diff --git a/core/java/android/service/controls/actions/ControlAction.java b/core/java/android/service/controls/actions/ControlAction.java index 37a75f0e9e5a..10f526d6565c 100644 --- a/core/java/android/service/controls/actions/ControlAction.java +++ b/core/java/android/service/controls/actions/ControlAction.java @@ -136,7 +136,8 @@ public abstract class ControlAction { /** * Response code for the {@code consumer} in * {@link ControlsProviderService#performControlAction} indicating that in order for the action - * to be performed, acknowledgment from the user is required. + * to be performed, acknowledgment from the user is required. Any non-empty string returned + * from {@link #getChallengeValue} shall be treated as a positive acknowledgment. */ public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 3; /** diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java index 0170726b31d6..c047dc0d07c7 100644 --- a/core/java/android/service/dataloader/DataLoaderService.java +++ b/core/java/android/service/dataloader/DataLoaderService.java @@ -29,7 +29,6 @@ import android.content.pm.IDataLoader; import android.content.pm.IDataLoaderStatusListener; import android.content.pm.InstallationFile; import android.content.pm.InstallationFileParcel; -import android.content.pm.NamedParcelFileDescriptor; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.util.ExceptionUtils; @@ -133,16 +132,6 @@ public abstract class DataLoaderService extends Service { } } } - if (params.dynamicArgs != null) { - NamedParcelFileDescriptor[] fds = params.dynamicArgs; - for (NamedParcelFileDescriptor nfd : fds) { - try { - nfd.fd.close(); - } catch (IOException e) { - Slog.e(TAG, "Failed to close DynamicArgs parcel file descriptor " + e); - } - } - } } } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index e70311fb9429..c9be1c1fabb2 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -15,8 +15,6 @@ */ package android.service.dreams; -import static android.view.WindowManager.LayoutParams.TYPE_DREAM; - import android.annotation.IdRes; import android.annotation.LayoutRes; import android.annotation.NonNull; @@ -1071,7 +1069,6 @@ public class DreamService extends Service implements Window.Callback { private void onWindowCreated(Window w) { mWindow = w; mWindow.setCallback(this); - mWindow.setType(TYPE_DREAM); mWindow.requestFeature(Window.FEATURE_NO_TITLE); WindowManager.LayoutParams lp = mWindow.getAttributes(); 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/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java index 9a555c161300..2c260f6b8cbe 100644 --- a/core/java/android/util/proto/ProtoOutputStream.java +++ b/core/java/android/util/proto/ProtoOutputStream.java @@ -201,7 +201,9 @@ public final class ProtoOutputStream extends ProtoStream { } /** - * Returns the uncompressed buffer size + * Returns the total size of the data that has been written, after full + * protobuf encoding has occurred. + * * @return the uncompressed buffer size */ public int getRawSize() { @@ -2271,9 +2273,12 @@ public final class ProtoOutputStream extends ProtoStream { } /** - * Write a field tag to the stream. + * Write an individual field tag by hand. + * + * @see See <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf + * Encoding</a> for details on the structure of how tags and data are written. */ - public void writeTag(int id, int wireType) { + public void writeTag(int id, @WireType int wireType) { mBuffer.writeRawVarint32((id << FIELD_ID_SHIFT) | wireType); } diff --git a/core/java/android/util/proto/ProtoStream.java b/core/java/android/util/proto/ProtoStream.java index 4969d8a7fbe7..1940da907973 100644 --- a/core/java/android/util/proto/ProtoStream.java +++ b/core/java/android/util/proto/ProtoStream.java @@ -16,9 +16,14 @@ package android.util.proto; +import android.annotation.IntDef; +import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.Nullable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Base utility class for protobuf streams. * @@ -30,6 +35,71 @@ import android.annotation.Nullable; public class ProtoStream { /** + * A protobuf wire type. All application-level types are represented using + * varint, fixed64, length-delimited and fixed32 wire types. The start-group + * and end-group types are unused in modern protobuf versions (proto2 and proto3), + * but are included here for completeness. + * + * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf + * Encoding</a> + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + WIRE_TYPE_VARINT, + WIRE_TYPE_FIXED64, + WIRE_TYPE_LENGTH_DELIMITED, + WIRE_TYPE_START_GROUP, + WIRE_TYPE_END_GROUP, + WIRE_TYPE_FIXED32 + }) + public @interface WireType {} + + /** + * Application-level protobuf field types, as would be used in a .proto file. + * + * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf + * Encoding</a> + */ + @Retention(RetentionPolicy.SOURCE) + @LongDef({ + FIELD_TYPE_UNKNOWN, + FIELD_TYPE_DOUBLE, + FIELD_TYPE_FLOAT, + FIELD_TYPE_INT64, + FIELD_TYPE_UINT64, + FIELD_TYPE_INT32, + FIELD_TYPE_FIXED64, + FIELD_TYPE_FIXED32, + FIELD_TYPE_BOOL, + FIELD_TYPE_STRING, + FIELD_TYPE_MESSAGE, + FIELD_TYPE_BYTES, + FIELD_TYPE_UINT32, + FIELD_TYPE_ENUM, + FIELD_TYPE_SFIXED32, + FIELD_TYPE_SFIXED64, + FIELD_TYPE_SINT32, + FIELD_TYPE_SINT64, + }) + public @interface FieldType {} + + + /** + * Represents the cardinality of a protobuf field. + * + * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf + * Encoding</a> + */ + @Retention(RetentionPolicy.SOURCE) + @LongDef({ + FIELD_COUNT_UNKNOWN, + FIELD_COUNT_SINGLE, + FIELD_COUNT_REPEATED, + FIELD_COUNT_PACKED, + }) + public @interface FieldCount {} + + /** * Number of bits to shift the field number to form a tag. * * <pre> @@ -128,7 +198,7 @@ public class ProtoStream { public static final long FIELD_TYPE_MASK = 0x0ffL << FIELD_TYPE_SHIFT; /** - * Not a real wire type. + * Not a real field type. * @hide */ public static final long FIELD_TYPE_UNKNOWN = 0; @@ -378,7 +448,7 @@ public class ProtoStream { /** * Get the developer-usable name of a field type. */ - public static @Nullable String getFieldTypeString(long fieldType) { + public static @Nullable String getFieldTypeString(@FieldType long fieldType) { int index = ((int) ((fieldType & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) - 1; if (index >= 0 && index < FIELD_TYPE_NAMES.length) { return FIELD_TYPE_NAMES[index]; @@ -405,7 +475,7 @@ public class ProtoStream { /** * Get the developer-usable name of a wire type. */ - public static @Nullable String getWireTypeString(int wireType) { + public static @Nullable String getWireTypeString(@WireType int wireType) { switch (wireType) { case WIRE_TYPE_VARINT: return "Varint"; diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java index 95b2c7019dbc..30e3ec135065 100644 --- a/core/java/android/view/BatchedInputEventReceiver.java +++ b/core/java/android/view/BatchedInputEventReceiver.java @@ -35,7 +35,7 @@ public class BatchedInputEventReceiver extends InputEventReceiver { } @Override - public void onBatchedInputEventPending() { + public void onBatchedInputEventPending(int source) { scheduleBatchedInput(); } diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index dffcafe1de0e..0ccb1e055c46 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -57,8 +57,8 @@ import java.util.List; * <li>The application display area specifies the part of the display that may contain * an application window, excluding the system decorations. The application display area may * be smaller than the real display area because the system subtracts the space needed - * for decor elements such as the status bar. Use {@link WindowMetrics#getSize()} to query the - * application window size.</li> + * for decor elements such as the status bar. Use {@link WindowMetrics#getBounds()} to query the + * application window bounds.</li> * <li>The real display area specifies the part of the display that contains content * including the system decorations. Even so, the real display area may be smaller than the * physical size of the display if the window manager is emulating a smaller display @@ -673,7 +673,7 @@ public final class Display { * * @param outSize A {@link Point} object to receive the size information. * @deprecated Use {@link WindowManager#getCurrentWindowMetrics()} to obtain an instance of - * {@link WindowMetrics} and use {@link WindowMetrics#getSize()} instead. + * {@link WindowMetrics} and use {@link WindowMetrics#getBounds()} instead. */ @Deprecated public void getSize(Point outSize) { @@ -689,7 +689,7 @@ public final class Display { * Gets the size of the display as a rectangle, in pixels. * * @param outSize A {@link Rect} object to receive the size information. - * @deprecated Use {@link WindowMetrics#getSize()} to get the dimensions of the application + * @deprecated Use {@link WindowMetrics#getBounds()} to get the dimensions of the application * window area. */ @Deprecated @@ -755,7 +755,7 @@ public final class Display { } /** - * @deprecated Use {@link WindowMetrics#getSize()} instead. + * @deprecated Use {@link WindowMetrics#getBounds#width()} instead. */ @Deprecated public int getWidth() { @@ -766,7 +766,7 @@ public final class Display { } /** - * @deprecated Use {@link WindowMetrics#getSize()} instead. + * @deprecated Use {@link WindowMetrics#getBounds()#height()} instead. */ @Deprecated public int getHeight() { @@ -1105,7 +1105,7 @@ public final class Display { * </p> * * @param outMetrics A {@link DisplayMetrics} object to receive the metrics. - * @deprecated Use {@link WindowMetrics#getSize()} to get the dimensions of the application + * @deprecated Use {@link WindowMetrics#getBounds()} to get the dimensions of the application * window area, and {@link Configuration#densityDpi} to get the current density. */ @Deprecated diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index dfe89a37a229..45e51f756489 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -318,8 +318,7 @@ interface IWindowSession { * Called when the client has changed the local insets state, and now the server should reflect * that new state. */ - void insetsModified(IWindow window, in InsetsState state); - + oneway void insetsModified(IWindow window, in InsetsState state); /** * Called when the system gesture exclusion has changed. diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index 7986ceb988db..25a4108c612c 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -147,8 +147,9 @@ public abstract class InputEventReceiver { * samples until the recipient calls {@link #consumeBatchedInputEvents} or * an event is received that ends the batch and causes it to be consumed * immediately (such as a pointer up event). + * @param source The source of the batched event. */ - public void onBatchedInputEventPending() { + public void onBatchedInputEventPending(int source) { consumeBatchedInputEvents(-1); } @@ -219,13 +220,6 @@ public abstract class InputEventReceiver { onInputEvent(event); } - // Called from native code. - @SuppressWarnings("unused") - @UnsupportedAppUsage - private void dispatchBatchedInputEventPending() { - onBatchedInputEventPending(); - } - /** * Factory for InputEventReceiver */ diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index f827eda07901..07b6cc1c22d2 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -196,11 +196,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll if (mCancelled || mFinished) { return; } + mShownOnFinish = shown; setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */); mFinished = true; mListener.onFinished(this); - - mShownOnFinish = shown; } @Override @@ -259,7 +258,6 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll return state.calculateInsets(frame, null /* ignoringVisibilityState */, false /* isScreenRound */, false /* alwaysConsumeSystemBars */, null /* displayCutout */, - null /* legacyContentInsets */, null /* legacyStableInsets */, LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/, 0 /* legacySystemUiFlags */, typeSideMap) .getInsets(mTypes); @@ -301,7 +299,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll .withAlpha(side == ISIDE_FLOATING ? 1 : alpha) .withMatrix(mTmpMatrix) .withVisibility(side == ISIDE_FLOATING - ? state.getSource(source.getType()).isVisible() + ? mShownOnFinish : inset != 0 /* visible */) .build(); surfaceParams.add(params); diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 048c0e26cdb8..40a460dfece0 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -16,7 +16,9 @@ package android.view; +import static android.view.InsetsState.ITYPE_CAPTION_BAR; 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 +28,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 +38,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; @@ -363,9 +361,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private final Runnable mAnimCallback; - private final Rect mLastLegacyContentInsets = new Rect(); - private final Rect mLastLegacyStableInsets = new Rect(); - /** Pending control request that is waiting on IME to be ready to be shown */ private PendingControlRequest mPendingImeControlRequest; @@ -373,6 +368,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private int mLastLegacySystemUiFlags; private DisplayCutout mLastDisplayCutout; private boolean mStartingAnimation; + private int mCaptionInsetsHeight = 0; private SyncRtSurfaceTransactionApplier mApplier; @@ -435,8 +431,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/, mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(), - mLastDisplayCutout, mLastLegacyContentInsets, mLastLegacyStableInsets, - mLastLegacySoftInputMode, mLastLegacySystemUiFlags, null /* typeSideMap */); + mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacySystemUiFlags, + null /* typeSideMap */); mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets, mUnmodifiableTmpRunningAnims); @@ -466,44 +462,76 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting public boolean onStateChanged(InsetsState state) { - if (mState.equals(state) && mLastDispachedState.equals(state)) { + boolean localStateChanged = !mState.equals(state, true /* excludingCaptionInsets */) + || !captionInsetsUnchanged(); + if (!localStateChanged && mLastDispachedState.equals(state)) { return false; } - mState.set(state); + updateState(state); mLastDispachedState.set(state, true /* copySources */); applyLocalVisibilityOverride(); - mViewRoot.notifyInsetsChanged(); - if (!mState.equals(mLastDispachedState)) { + if (localStateChanged) { + mViewRoot.notifyInsetsChanged(); + } + if (!mState.equals(mLastDispachedState, true /* excludingCaptionInsets */)) { sendStateToWindowManager(); } 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()); + } + } + if (mCaptionInsetsHeight != 0) { + mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top, + mFrame.right, mFrame.top + mCaptionInsetsHeight)); + } + } + + private boolean captionInsetsUnchanged() { + if (mState.peekSource(ITYPE_CAPTION_BAR) == null + && mCaptionInsetsHeight == 0) { + return true; + } + if (mState.peekSource(ITYPE_CAPTION_BAR) != null + && mCaptionInsetsHeight + == mState.peekSource(ITYPE_CAPTION_BAR).getFrame().height()) { + return true; + } + return false; + } + /** * @see InsetsState#calculateInsets */ @VisibleForTesting public WindowInsets calculateInsets(boolean isScreenRound, - boolean alwaysConsumeSystemBars, DisplayCutout cutout, Rect legacyContentInsets, - Rect legacyStableInsets, int legacySoftInputMode, int legacySystemUiFlags) { - mLastLegacyContentInsets.set(legacyContentInsets); - mLastLegacyStableInsets.set(legacyStableInsets); + boolean alwaysConsumeSystemBars, DisplayCutout cutout, + int legacySoftInputMode, int legacySystemUiFlags) { mLastLegacySoftInputMode = legacySoftInputMode; mLastLegacySystemUiFlags = legacySystemUiFlags; mLastDisplayCutout = cutout; mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/, - isScreenRound, alwaysConsumeSystemBars, cutout, legacyContentInsets, - legacyStableInsets, legacySoftInputMode, legacySystemUiFlags, + isScreenRound, alwaysConsumeSystemBars, cutout, + legacySoftInputMode, legacySystemUiFlags, null /* typeSideMap */); return mLastInsets; } /** - * @see InsetsState#calculateVisibleInsets(Rect, Rect, int) + * @see InsetsState#calculateVisibleInsets(Rect, int) */ - public Rect calculateVisibleInsets(Rect legacyVisibleInsets, - @SoftInputModeFlags int softInputMode) { - return mState.calculateVisibleInsets(mFrame, legacyVisibleInsets, softInputMode); + public Rect calculateVisibleInsets(@SoftInputModeFlags int softInputMode) { + return mState.calculateVisibleInsets(mFrame, softInputMode); } /** @@ -861,8 +889,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; } } @@ -949,12 +984,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation InsetsState tmpState = new InsetsState(); for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); + if (consumer.getType() == ITYPE_CAPTION_BAR) continue; if (consumer.getControl() != null) { tmpState.addSource(mState.getSource(consumer.getType())); } } - // TODO: Put this on a dispatcher thread. try { mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, tmpState); } catch (RemoteException e) { @@ -1091,6 +1126,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } @Override + public void setCaptionInsetsHeight(int height) { + mCaptionInsetsHeight = height; + } + + @Override public void setSystemBarsBehavior(@Behavior int behavior) { mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED; if (mViewRoot.mWindowAttributes.insetsFlags.behavior != behavior) { diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 294faaf0b5c8..033ccef3666d 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.InsetsState.ITYPE_CAPTION_BAR; import static android.view.InsetsState.ITYPE_IME; import android.annotation.NonNull; @@ -118,6 +119,12 @@ public class InsetsSource implements Parcelable { if (!getIntersection(frame, relativeFrame, mTmpFrame)) { return Insets.NONE; } + // During drag-move and drag-resizing, the caption insets position may not get updated + // before the app frame get updated. To layout the app content correctly during drag events, + // we always return the insets with the corresponding height covering the top. + if (getType() == ITYPE_CAPTION_BAR) { + return Insets.of(0, frame.height(), 0, 0); + } // TODO: Currently, non-floating IME always intersects at bottom due to issues with cutout. // However, we should let the policy decide from the server. 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/InsetsState.java b/core/java/android/view/InsetsState.java index c2ad74a566e9..c5154662928e 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -45,6 +45,8 @@ import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; +import com.android.internal.annotations.VisibleForTesting; + import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -160,7 +162,6 @@ public class InsetsState implements Parcelable { */ public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState, boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout, - @Nullable Rect legacyContentInsets, @Nullable Rect legacyStableInsets, int legacySoftInputMode, int legacySystemUiFlags, @Nullable @InternalInsetsSide SparseIntArray typeSideMap) { Insets[] typeInsetsMap = new Insets[Type.SIZE]; @@ -168,11 +169,6 @@ public class InsetsState implements Parcelable { boolean[] typeVisibilityMap = new boolean[SIZE]; final Rect relativeFrame = new Rect(frame); final Rect relativeFrameMax = new Rect(frame); - if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL - && legacyContentInsets != null && legacyStableInsets != null) { - WindowInsets.assignCompatInsets(typeInsetsMap, legacyContentInsets); - WindowInsets.assignCompatInsets(typeMaxInsetsMap, legacyStableInsets); - } for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { InsetsSource source = mSources.get(type); if (source == null) { @@ -217,12 +213,7 @@ public class InsetsState implements Parcelable { && (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0); } - public Rect calculateVisibleInsets(Rect frame, Rect legacyVisibleInsets, - @SoftInputModeFlags int softInputMode) { - if (sNewInsetsMode == NEW_INSETS_MODE_NONE) { - return legacyVisibleInsets; - } - + public Rect calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) { Insets insets = Insets.NONE; for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { InsetsSource source = mSources.get(type); @@ -509,6 +500,19 @@ public class InsetsState implements Parcelable { @Override public boolean equals(Object o) { + return equals(o, false); + } + + /** + * An equals method can exclude the caption insets. This is useful because we assemble the + * caption insets information on the client side, and when we communicate with server, it's + * excluded. + * @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but + * ignore the caption insets source value. + * @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise. + */ + @VisibleForTesting + public boolean equals(Object o, boolean excludingCaptionInsets) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } @@ -517,11 +521,24 @@ public class InsetsState implements Parcelable { if (!mDisplayFrame.equals(state.mDisplayFrame)) { return false; } - if (mSources.size() != state.mSources.size()) { + int size = mSources.size(); + int otherSize = state.mSources.size(); + if (excludingCaptionInsets) { + if (mSources.get(ITYPE_CAPTION_BAR) != null) { + size--; + } + if (state.mSources.get(ITYPE_CAPTION_BAR) != null) { + otherSize--; + } + } + if (size != otherSize) { return false; } for (int i = mSources.size() - 1; i >= 0; i--) { InsetsSource source = mSources.valueAt(i); + if (excludingCaptionInsets) { + if (source.getType() == ITYPE_CAPTION_BAR) continue; + } InsetsSource otherSource = state.mSources.get(source.getType()); if (otherSource == null) { return false; diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java index 229ee03521bc..a106b2c4726d 100644 --- a/core/java/android/view/PendingInsetsController.java +++ b/core/java/android/view/PendingInsetsController.java @@ -42,6 +42,7 @@ public class PendingInsetsController implements WindowInsetsController { private InsetsController mReplayedInsetsController; private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners = new ArrayList<>(); + private int mCaptionInsetsHeight = 0; @Override public void show(int types) { @@ -80,6 +81,11 @@ public class PendingInsetsController implements WindowInsetsController { } @Override + public void setCaptionInsetsHeight(int height) { + mCaptionInsetsHeight = height; + } + + @Override public void setSystemBarsBehavior(int behavior) { if (mReplayedInsetsController != null) { mReplayedInsetsController.setSystemBarsBehavior(behavior); @@ -134,6 +140,9 @@ public class PendingInsetsController implements WindowInsetsController { if (mAppearanceMask != 0) { controller.setSystemBarsAppearance(mAppearance, mAppearanceMask); } + if (mCaptionInsetsHeight != 0) { + controller.setCaptionInsetsHeight(mCaptionInsetsHeight); + } int size = mRequests.size(); for (int i = 0; i < size; i++) { mRequests.get(i).replay(controller); 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/view/View.java b/core/java/android/view/View.java index 708a09467247..da186087a34a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -80,6 +80,7 @@ import android.graphics.drawable.GradientDrawable; import android.hardware.display.DisplayManagerGlobal; import android.net.Uri; import android.os.Build; +import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -3474,7 +3475,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Flag indicating the field should not have yellow highlight when autofilled. */ - private static final int PFLAG4_AUTOFILL_HIDE_HIGHLIGHT = 0x100; + private static final int PFLAG4_AUTOFILL_HIDE_HIGHLIGHT = 0x200; /* End of masks for mPrivateFlags4 */ @@ -9581,18 +9582,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // First check if context has client, so it saves a service lookup when it doesn't if (mContext.getContentCaptureOptions() == null) return; - // Then check if it's enabled in the context... - final ContentCaptureManager ccm = ai != null ? ai.getContentCaptureManager(mContext) - : mContext.getSystemService(ContentCaptureManager.class); - if (ccm == null || !ccm.isContentCaptureEnabled()) return; - - // ... and finally at the view level - // NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled() - if (!isImportantForContentCapture()) return; - - ContentCaptureSession session = getContentCaptureSession(); - if (session == null) return; - if (appeared) { if (!isLaidOut() || getVisibility() != VISIBLE || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) { @@ -9601,21 +9590,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, + isLaidOut() + ", visibleToUser=" + isVisibleToUser() + ", visible=" + (getVisibility() == VISIBLE) + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) + & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0)); + & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0)); } return; } - setNotifiedContentCaptureAppeared(); - - if (ai != null) { - ai.delayNotifyContentCaptureEvent(session, this, appeared); - } else { - if (DEBUG_CONTENT_CAPTURE) { - Log.w(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on appeared for " + this); - } - } } else { if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0 || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) { @@ -9624,12 +9604,32 @@ public class View implements Drawable.Callback, KeyEvent.Callback, + isLaidOut() + ", visibleToUser=" + isVisibleToUser() + ", visible=" + (getVisibility() == VISIBLE) + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) + & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0)); + & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0)); } return; } + } + + ContentCaptureSession session = getContentCaptureSession(); + if (session == null) return; + + // ... and finally at the view level + // NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled() + if (!isImportantForContentCapture()) return; + + if (appeared) { + setNotifiedContentCaptureAppeared(); + + if (ai != null) { + ai.delayNotifyContentCaptureEvent(session, this, appeared); + } else { + if (DEBUG_CONTENT_CAPTURE) { + Log.w(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on appeared for " + this); + } + } + } else { mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED; mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; @@ -28746,7 +28746,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * of the screen decorations, these are the current insets for the * content of the window. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.Q, + publicAlternatives = "Use {@link WindowInsets#getInsets(int)}") final Rect mContentInsets = new Rect(); /** @@ -28754,7 +28755,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * of the screen decorations, these are the current insets for the * actual visible parts of the window. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.Q, + publicAlternatives = "Use {@link WindowInsets#getInsets(int)}") final Rect mVisibleInsets = new Rect(); /** @@ -28762,9 +28764,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * of the screen decorations, these are the current insets for the * stable system windows. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.Q, + publicAlternatives = "Use {@link WindowInsets#getInsets(int)}") final Rect mStableInsets = new Rect(); + /** + * Current caption insets to the display coordinate. + */ + final Rect mCaptionInsets = new Rect(); + final DisplayCutout.ParcelableWrapper mDisplayCutout = new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT); diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 04260c47eda3..4bea623716dc 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -23,11 +23,11 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Rect; import android.os.Build; import android.os.RemoteException; import android.provider.Settings; import android.util.DisplayMetrics; -import android.util.Size; import android.util.SparseArray; import android.util.TypedValue; @@ -410,8 +410,8 @@ public class ViewConfiguration { // Size of the screen in bytes, in ARGB_8888 format final WindowManager windowManager = context.getSystemService(WindowManager.class); - final Size maxWindowSize = windowManager.getMaximumWindowMetrics().getSize(); - mMaximumDrawingCacheSize = 4 * maxWindowSize.getWidth() * maxWindowSize.getHeight(); + final Rect maxWindowBounds = windowManager.getMaximumWindowMetrics().getBounds(); + mMaximumDrawingCacheSize = 4 * maxWindowBounds.width() * maxWindowBounds.height(); mOverscrollDistance = (int) (sizeAndDensity * OVERSCROLL_DISTANCE + 0.5f); mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 218c9fb2fd4f..51304dcfe8cb 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -158,6 +158,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.os.SomeArgs; +import com.android.internal.policy.DecorView; import com.android.internal.policy.PhoneFallbackEventHandler; import com.android.internal.util.Preconditions; import com.android.internal.view.BaseSurfaceHolder; @@ -504,7 +505,6 @@ public final class ViewRootImpl implements ViewParent, int mPendingInputEventCount; boolean mProcessInputEventsScheduled; boolean mUnbufferedInputDispatch; - boolean mUnbufferedInputDispatchBySource; @InputSourceClass int mUnbufferedInputSource = SOURCE_CLASS_NONE; @@ -548,13 +548,11 @@ public final class ViewRootImpl implements ViewParent, boolean mAddedTouchMode; final Rect mTmpFrame = new Rect(); + final Rect mTmpRect = new Rect(); // These are accessed by multiple threads. final Rect mWinFrame; // frame given by window manager. - final Rect mPendingVisibleInsets = new Rect(); - final Rect mPendingStableInsets = new Rect(); - final Rect mPendingContentInsets = new Rect(); final Rect mPendingBackDropFrame = new Rect(); final DisplayCutout.ParcelableWrapper mPendingDisplayCutout = new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT); @@ -563,10 +561,6 @@ public final class ViewRootImpl implements ViewParent, final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets = new ViewTreeObserver.InternalInsetsInfo(); - final Rect mDispatchContentInsets = new Rect(); - final Rect mDispatchStableInsets = new Rect(); - DisplayCutout mDispatchDisplayCutout = DisplayCutout.NO_CUTOUT; - private WindowInsets mLastWindowInsets; // Insets types hidden by legacy window flags or system UI flags. @@ -1020,10 +1014,7 @@ public final class ViewRootImpl implements ViewParent, if (mTranslator != null) { mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets); } - mPendingContentInsets.set(mAttachInfo.mContentInsets); - mPendingStableInsets.set(mAttachInfo.mStableInsets); mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout); - mPendingVisibleInsets.set(0, 0, 0, 0); mAttachInfo.mAlwaysConsumeSystemBars = (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) != 0; mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars; @@ -1872,9 +1863,6 @@ public final class ViewRootImpl implements ViewParent, mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); - if (!mUnbufferedInputDispatch && !mUnbufferedInputDispatchBySource) { - scheduleConsumeBatchedInput(); - } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } @@ -2204,45 +2192,20 @@ public final class ViewRootImpl implements ViewParent, /* package */ WindowInsets getWindowInsets(boolean forceConstruct) { if (mLastWindowInsets == null || forceConstruct) { - mDispatchContentInsets.set(mAttachInfo.mContentInsets); - mDispatchStableInsets.set(mAttachInfo.mStableInsets); - mDispatchDisplayCutout = mAttachInfo.mDisplayCutout.get(); - - Rect contentInsets = mDispatchContentInsets; - Rect stableInsets = mDispatchStableInsets; - DisplayCutout displayCutout = mDispatchDisplayCutout; - // For dispatch we preserve old logic, but for direct requests from Views we allow to - // immediately use pending insets. This is such that getRootWindowInsets returns the - // result from the layout hint before we ran a traversal shortly after adding a window. - if (!forceConstruct - && (!mPendingContentInsets.equals(contentInsets) || - !mPendingStableInsets.equals(stableInsets) || - !mPendingDisplayCutout.get().equals(displayCutout))) { - contentInsets = mPendingContentInsets; - stableInsets = mPendingStableInsets; - displayCutout = mPendingDisplayCutout.get(); - } - contentInsets = ensureInsetsNonNegative(contentInsets, "content"); - stableInsets = ensureInsetsNonNegative(stableInsets, "stable"); mLastWindowInsets = mInsetsController.calculateInsets( mContext.getResources().getConfiguration().isScreenRound(), - mAttachInfo.mAlwaysConsumeSystemBars, displayCutout, - contentInsets, stableInsets, mWindowAttributes.softInputMode, - (mWindowAttributes.systemUiVisibility + mAttachInfo.mAlwaysConsumeSystemBars, mPendingDisplayCutout.get(), + mWindowAttributes.softInputMode, (mWindowAttributes.systemUiVisibility | mWindowAttributes.subtreeSystemUiVisibility)); - } - return mLastWindowInsets; - } - private Rect ensureInsetsNonNegative(Rect insets, String kind) { - if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) { - Log.wtf(mTag, "Negative " + kind + "Insets: " + insets + ", mFirst=" + mFirst); - return new Rect(Math.max(0, insets.left), - Math.max(0, insets.top), - Math.max(0, insets.right), - Math.max(0, insets.bottom)); + Rect visibleInsets = mInsetsController.calculateVisibleInsets( + mWindowAttributes.softInputMode); + + mAttachInfo.mVisibleInsets.set(visibleInsets); + mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect()); + mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect()); } - return insets; + return mLastWindowInsets; } public void dispatchApplyInsets(View host) { @@ -2259,6 +2222,19 @@ public final class ViewRootImpl implements ViewParent, Trace.traceEnd(Trace.TRACE_TAG_VIEW); } + private boolean updateCaptionInsets() { + if (!(mView instanceof DecorView)) return false; + final int captionInsetsHeight = ((DecorView) mView).getCaptionInsetsHeight(); + final Rect captionFrame = new Rect(); + if (captionInsetsHeight != 0) { + captionFrame.set(mWinFrame.left, mWinFrame.top, mWinFrame.right, + mWinFrame.top + captionInsetsHeight); + } + if (mAttachInfo.mCaptionInsets.equals(captionFrame)) return false; + mAttachInfo.mCaptionInsets.set(captionFrame); + return true; + } + private boolean shouldDispatchCutout() { return mWindowAttributes.layoutInDisplayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS @@ -2266,12 +2242,6 @@ public final class ViewRootImpl implements ViewParent, == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; } - private void updateVisibleInsets() { - Rect visibleInsets = mInsetsController.calculateVisibleInsets(mPendingVisibleInsets, - mWindowAttributes.softInputMode); - mAttachInfo.mVisibleInsets.set(visibleInsets); - } - @VisibleForTesting public InsetsController getInsetsController() { return mInsetsController; @@ -2407,7 +2377,7 @@ public final class ViewRootImpl implements ViewParent, // Execute enqueued actions on every traversal in case a detached view enqueued an action getRunQueue().executeActions(mAttachInfo.mHandler); - boolean insetsChanged = false; + boolean cutoutChanged = false; boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw); if (layoutRequested) { @@ -2420,22 +2390,8 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mInTouchMode = !mAddedTouchMode; ensureTouchModeLocally(mAddedTouchMode); } else { - if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) { - insetsChanged = true; - } - if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) { - insetsChanged = true; - } if (!mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout)) { - insetsChanged = true; - } - if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) { - updateVisibleInsets(); - if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: " - + mAttachInfo.mVisibleInsets); - } - if (mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars) { - insetsChanged = true; + cutoutChanged = true; } if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { @@ -2498,7 +2454,6 @@ public final class ViewRootImpl implements ViewParent, } if (mApplyInsetsRequested) { - updateVisibleInsets(); dispatchApplyInsets(host); if (mLayoutRequested) { // Short-circuit catching a new layout request here, so @@ -2566,8 +2521,8 @@ public final class ViewRootImpl implements ViewParent, controlInsetsForCompatibility(params); } - if (mFirst || windowShouldResize || insetsChanged || - viewVisibilityChanged || params != null || mForceNextWindowRelayout) { + if (mFirst || windowShouldResize || viewVisibilityChanged || cutoutChanged || params != null + || mForceNextWindowRelayout) { mForceNextWindowRelayout = false; if (isViewVisible) { @@ -2589,7 +2544,7 @@ public final class ViewRootImpl implements ViewParent, } boolean hwInitialized = false; - boolean contentInsetsChanged = false; + boolean dispatchApplyInsets = false; boolean hadSurface = mSurface.isValid(); try { @@ -2612,9 +2567,6 @@ public final class ViewRootImpl implements ViewParent, relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString() - + " content=" + mPendingContentInsets.toShortString() - + " visible=" + mPendingVisibleInsets.toShortString() - + " stable=" + mPendingStableInsets.toShortString() + " cutout=" + mPendingDisplayCutout.get().toString() + " surface=" + mSurface); @@ -2631,14 +2583,7 @@ public final class ViewRootImpl implements ViewParent, updatedConfiguration = true; } - contentInsetsChanged = !mPendingContentInsets.equals( - mAttachInfo.mContentInsets); - final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals( - mAttachInfo.mVisibleInsets); - final boolean stableInsetsChanged = !mPendingStableInsets.equals( - mAttachInfo.mStableInsets); - final boolean cutoutChanged = !mPendingDisplayCutout.equals( - mAttachInfo.mDisplayCutout); + cutoutChanged = !mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout); surfaceSizeChanged = (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0; final boolean alwaysConsumeSystemBarsChanged = @@ -2649,42 +2594,28 @@ public final class ViewRootImpl implements ViewParent, surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId()) && mSurface.isValid(); - if (contentInsetsChanged) { - mAttachInfo.mContentInsets.set(mPendingContentInsets); - if (DEBUG_LAYOUT) Log.v(mTag, "Content insets changing to: " - + mAttachInfo.mContentInsets); - } - if (stableInsetsChanged) { - mAttachInfo.mStableInsets.set(mPendingStableInsets); - if (DEBUG_LAYOUT) Log.v(mTag, "Decor insets changing to: " - + mAttachInfo.mStableInsets); - // Need to relayout with content insets. - contentInsetsChanged = true; - } if (cutoutChanged) { mAttachInfo.mDisplayCutout.set(mPendingDisplayCutout); if (DEBUG_LAYOUT) { Log.v(mTag, "DisplayCutout changing to: " + mAttachInfo.mDisplayCutout); } // Need to relayout with content insets. - contentInsetsChanged = true; + dispatchApplyInsets = true; } if (alwaysConsumeSystemBarsChanged) { mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars; - contentInsetsChanged = true; + dispatchApplyInsets = true; } - if (contentInsetsChanged || mLastSystemUiVisibility != + if (updateCaptionInsets()) { + dispatchApplyInsets = true; + } + if (dispatchApplyInsets || mLastSystemUiVisibility != mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested) { mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; dispatchApplyInsets(host); // We applied insets so force contentInsetsChanged to ensure the // hierarchy is measured below. - contentInsetsChanged = true; - } - if (visibleInsetsChanged) { - updateVisibleInsets(); - if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: " - + mAttachInfo.mVisibleInsets); + dispatchApplyInsets = true; } if (colorModeChanged && mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mThreadedRenderer.setWideGamut( @@ -2775,7 +2706,8 @@ public final class ViewRootImpl implements ViewParent, && mWinFrame.height() == mPendingBackDropFrame.height(); // TODO: Need cutout? startDragResizing(mPendingBackDropFrame, !backdropSizeMatchesFrame, - mPendingVisibleInsets, mPendingStableInsets, mResizeMode); + mLastWindowInsets.getSystemWindowInsets().toRect(), + mLastWindowInsets.getStableInsets().toRect(), mResizeMode); } else { // We shouldn't come here, but if we come we should end the resize. endDragResizing(); @@ -2866,7 +2798,7 @@ public final class ViewRootImpl implements ViewParent, boolean focusChangedDueToTouchMode = ensureTouchModeLocally( (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0); if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() - || mHeight != host.getMeasuredHeight() || contentInsetsChanged || + || mHeight != host.getMeasuredHeight() || dispatchApplyInsets || updatedConfiguration) { int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); @@ -2875,7 +2807,7 @@ public final class ViewRootImpl implements ViewParent, + mWidth + " measuredWidth=" + host.getMeasuredWidth() + " mHeight=" + mHeight + " measuredHeight=" + host.getMeasuredHeight() - + " coveredInsetsChanged=" + contentInsetsChanged); + + " dispatchApplyInsets=" + dispatchApplyInsets); // Ask host how big it wants to be performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); @@ -4921,12 +4853,9 @@ public final class ViewRootImpl implements ViewParent, // Recycled in the fall through... SomeArgs args = (SomeArgs) msg.obj; if (mWinFrame.equals(args.arg1) - && mPendingContentInsets.equals(args.arg2) - && mPendingStableInsets.equals(args.arg6) && mPendingDisplayCutout.get().equals(args.arg9) - && mPendingVisibleInsets.equals(args.arg3) && mPendingBackDropFrame.equals(args.arg8) - && args.arg4 == null + && mLastReportedMergedConfiguration.equals(args.arg4) && args.argi1 == 0 && mDisplay.getDisplayId() == args.argi3) { break; @@ -4954,16 +4883,10 @@ public final class ViewRootImpl implements ViewParent, } final boolean framesChanged = !mWinFrame.equals(args.arg1) - || !mPendingContentInsets.equals(args.arg2) - || !mPendingStableInsets.equals(args.arg6) - || !mPendingDisplayCutout.get().equals(args.arg9) - || !mPendingVisibleInsets.equals(args.arg3); + || !mPendingDisplayCutout.get().equals(args.arg9); setFrame((Rect) args.arg1); - mPendingContentInsets.set((Rect) args.arg2); - mPendingStableInsets.set((Rect) args.arg6); mPendingDisplayCutout.set((DisplayCutout) args.arg9); - mPendingVisibleInsets.set((Rect) args.arg3); mPendingBackDropFrame.set((Rect) args.arg8); mForceNextWindowRelayout = args.argi1 != 0; mPendingAlwaysConsumeSystemBars = args.argi2 != 0; @@ -7417,10 +7340,9 @@ public final class ViewRootImpl implements ViewParent, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, - mTmpFrame, mPendingContentInsets, mPendingVisibleInsets, - mPendingStableInsets, mPendingBackDropFrame, mPendingDisplayCutout, - mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mSurfaceSize, - mBlastSurfaceControl); + mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame, + mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, + mSurfaceSize, mBlastSurfaceControl); if (mSurfaceControl.isValid()) { if (!mUseBLASTAdapter) { mSurface.copyFrom(mSurfaceControl); @@ -7441,9 +7363,6 @@ public final class ViewRootImpl implements ViewParent, if (mTranslator != null) { mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame); - mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets); - mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets); - mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets); } setFrame(mTmpFrame); mInsetsController.onStateChanged(mTempInsets); @@ -8156,7 +8075,6 @@ public final class ViewRootImpl implements ViewParent, @Override public void onInputEvent(InputEvent event) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility"); - processUnbufferedRequest(event); List<InputEvent> processedEvents; try { processedEvents = @@ -8181,12 +8099,18 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void onBatchedInputEventPending() { - if (mUnbufferedInputDispatch || mUnbufferedInputDispatchBySource) { - super.onBatchedInputEventPending(); - } else { - scheduleConsumeBatchedInput(); + public void onBatchedInputEventPending(int source) { + final boolean unbuffered = mUnbufferedInputDispatch + || (source & mUnbufferedInputSource) != SOURCE_CLASS_NONE; + if (unbuffered) { + if (mConsumeBatchedInputScheduled) { + unscheduleConsumeBatchedInput(); + } + // Consume event immediately if unbuffered input dispatch has been requested. + consumeBatchedInputEvents(-1); + return; } + scheduleConsumeBatchedInput(); } @Override @@ -8199,17 +8123,6 @@ public final class ViewRootImpl implements ViewParent, unscheduleConsumeBatchedInput(); super.dispose(); } - - private void processUnbufferedRequest(InputEvent event) { - if (!(event instanceof MotionEvent)) { - return; - } - mUnbufferedInputDispatchBySource = - (event.getSource() & mUnbufferedInputSource) != SOURCE_CLASS_NONE; - if (mUnbufferedInputDispatchBySource && mConsumeBatchedInputScheduled) { - scheduleConsumeBatchedInputImmediately(); - } - } } WindowInputEventReceiver mInputEventReceiver; diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 0c5c18316e61..ae9afabad533 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -49,9 +49,6 @@ import android.transition.Transition; import android.transition.TransitionManager; import android.util.Pair; import android.view.View.OnApplyWindowInsetsListener; -import android.view.ViewGroup.LayoutParams; -import android.view.WindowInsets.Side.InsetsSide; -import android.view.WindowInsets.Type.InsetsType; import android.view.accessibility.AccessibilityEvent; import java.util.Collections; @@ -323,7 +320,7 @@ public abstract class Window { @UnsupportedAppUsage private boolean mDestroyed; - private boolean mOverlayWithDecorCaptionEnabled = false; + private boolean mOverlayWithDecorCaptionEnabled = true; private boolean mCloseOnSwipeEnabled = false; // The current window attributes. diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index fde184ccfb0e..9b2a6cbce48f 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -17,6 +17,7 @@ package android.view; +import static android.view.WindowInsets.Type.CAPTION_BAR; import static android.view.WindowInsets.Type.DISPLAY_CUTOUT; import static android.view.WindowInsets.Type.FIRST; import static android.view.WindowInsets.Type.IME; diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index 0282ecac8920..439223cf568b 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -196,6 +196,15 @@ public interface WindowInsetsController { @Appearance int getSystemBarsAppearance(); /** + * Notify the caption insets height change. The information will be used on the client side to, + * make sure the InsetsState has the correct caption insets. + * + * @param height the height of caption bar insets. + * @hide + */ + void setCaptionInsetsHeight(int height); + + /** * Controls the behavior of system bars. * * @param behavior Determines how the bars behave when being hidden by the application. diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 77ce5c1b1ddc..cc380f32297e 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -781,8 +781,6 @@ public interface WindowManager extends ViewManager { to = "BOOT_PROGRESS"), @ViewDebug.IntToString(from = TYPE_INPUT_CONSUMER, to = "INPUT_CONSUMER"), - @ViewDebug.IntToString(from = TYPE_DREAM, - to = "DREAM"), @ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL, to = "NAVIGATION_BAR_PANEL"), @ViewDebug.IntToString(from = TYPE_DISPLAY_OVERLAY, @@ -1105,13 +1103,6 @@ public interface WindowManager extends ViewManager { public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22; /** - * Window type: Dreams (screen saver) window, just above keyguard. - * In multiuser systems shows only on the owning user's window. - * @hide - */ - public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23; - - /** * Window type: Navigation bar panel (when navigation bar is distinct from status bar) * In multiuser systems shows on all users' windows. * @hide @@ -1180,8 +1171,9 @@ public interface WindowManager extends ViewManager { public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35; /** - * Window type: shares similar characteristics with {@link #TYPE_DREAM}. The layer is + * Window type: shows directly above the keyguard. The layer is * reserved for screenshot region selection. These windows must not take input focus. + * In multiuser systems shows only on the owning user's window. * @hide */ public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36; diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 4050da1b5cb1..316a5f2c88d2 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -35,7 +35,6 @@ import android.graphics.Region; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; -import android.util.Size; import com.android.internal.os.IResultReceiver; @@ -220,7 +219,7 @@ public final class WindowManagerImpl implements WindowManager { final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext; final Rect bounds = getCurrentBounds(context); - return new WindowMetrics(toSize(bounds), computeWindowInsets(bounds)); + return new WindowMetrics(bounds, computeWindowInsets(bounds)); } private static Rect getCurrentBounds(Context context) { @@ -232,11 +231,7 @@ public final class WindowManagerImpl implements WindowManager { @Override public WindowMetrics getMaximumWindowMetrics() { final Rect maxBounds = getMaximumBounds(); - return new WindowMetrics(toSize(maxBounds), computeWindowInsets(maxBounds)); - } - - private Size toSize(Rect frame) { - return new Size(frame.width(), frame.height()); + return new WindowMetrics(maxBounds, computeWindowInsets(maxBounds)); } private Rect getMaximumBounds() { @@ -278,7 +273,7 @@ public final class WindowManagerImpl implements WindowManager { if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) { return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/, isScreenRound, alwaysConsumeSystemBars, displayCutout.get(), - systemWindowInsets, stableInsets, SOFT_INPUT_ADJUST_NOTHING, + SOFT_INPUT_ADJUST_NOTHING, SYSTEM_UI_FLAG_VISIBLE, null /* typeSideMap */); } else { return new WindowInsets.Builder() diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java index ab5a06eb99de..86ef87997a07 100644 --- a/core/java/android/view/WindowMetrics.java +++ b/core/java/android/view/WindowMetrics.java @@ -18,10 +18,10 @@ package android.view; import android.annotation.NonNull; import android.graphics.Point; -import android.util.Size; +import android.graphics.Rect; /** - * Metrics about a Window, consisting of the size and {@link WindowInsets}. + * Metrics about a Window, consisting of the bounds and {@link WindowInsets}. * <p> * This is usually obtained from {@link WindowManager#getCurrentWindowMetrics()} and * {@link WindowManager#getMaximumWindowMetrics()}. @@ -31,21 +31,22 @@ import android.util.Size; * @see WindowManager#getMaximumWindowMetrics() */ public final class WindowMetrics { - private final @NonNull Size mSize; + private final @NonNull Rect mBounds; private final @NonNull WindowInsets mWindowInsets; - public WindowMetrics(@NonNull Size size, @NonNull WindowInsets windowInsets) { - mSize = size; + public WindowMetrics(@NonNull Rect bounds, @NonNull WindowInsets windowInsets) { + mBounds = bounds; mWindowInsets = windowInsets; } /** - * Returns the size of the window. + * Returns the bounds of the area associated with this window or visual context. * <p> - * <b>Note that this reports a different size than {@link Display#getSize(Point)}.</b> - * This method reports the window size including all system bars area, while - * {@link Display#getSize(Point)} reports the area excluding navigation bars and display cutout - * areas. The value reported by {@link Display#getSize(Point)} can be obtained by using: + * <b>Note that the size of the reported bounds can have different size than + * {@link Display#getSize(Point)}.</b> This method reports the window size including all system + * bar areas, while {@link Display#getSize(Point)} reports the area excluding navigation bars + * and display cutout areas. The value reported by {@link Display#getSize(Point)} can be + * obtained by using: * <pre class="prettyprint"> * final WindowMetrics metrics = windowManager.getCurrentMetrics(); * // Gets all excluding insets @@ -66,16 +67,16 @@ public final class WindowMetrics { * </pre> * </p> * - * @return window size in pixel. + * @return window bounds in pixels. */ - public @NonNull Size getSize() { - return mSize; + public @NonNull Rect getBounds() { + return mBounds; } /** - * Returns the {@link WindowInsets} of the window. + * Returns the {@link WindowInsets} of the area associated with this window or visual context. * - * @return the {@link WindowInsets} of the window. + * @return the {@link WindowInsets} of the visual area. */ public @NonNull WindowInsets getWindowInsets() { return mWindowInsets; diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 39a9ed4a82e7..267a5a6561af 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -1242,9 +1242,10 @@ public final class AutofillManager { if (mLastAutofilledData.containsKey(id)) { value = view.getAutofillValue(); valueWasRead = true; + final boolean hideHighlight = mLastAutofilledData.keySet().size() == 1; if (Objects.equals(mLastAutofilledData.get(id), value)) { - view.setAutofilled(true, false); + view.setAutofilled(true, hideHighlight); } else { view.setAutofilled(false, false); mLastAutofilledData.remove(id); diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java index 8d3dc83bca0c..2ead352fd199 100644 --- a/core/java/android/view/autofill/AutofillPopupWindow.java +++ b/core/java/android/view/autofill/AutofillPopupWindow.java @@ -25,7 +25,6 @@ import android.os.IBinder; import android.os.RemoteException; import android.transition.Transition; import android.util.Log; -import android.util.Size; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewTreeObserver; @@ -129,10 +128,10 @@ public class AutofillPopupWindow extends PopupWindow { // Gravity.BOTTOM because PopupWindow base class does not expose computeGravity(). final WindowManager windowManager = anchor.getContext() .getSystemService(WindowManager.class); - final Size windowSize = windowManager.getCurrentWindowMetrics().getSize(); - width = windowSize.getWidth(); + final Rect windowBounds = windowManager.getCurrentWindowMetrics().getBounds(); + width = windowBounds.width(); if (height != LayoutParams.MATCH_PARENT) { - offsetY = windowSize.getHeight() - height; + offsetY = windowBounds.height() - height; } actualAnchor = anchor; } else if (virtualBounds != null) { diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java index 1692051924f4..b84cb88ccd84 100644 --- a/core/java/android/view/contentcapture/ContentCaptureContext.java +++ b/core/java/android/view/contentcapture/ContentCaptureContext.java @@ -15,7 +15,7 @@ */ package android.view.contentcapture; -import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID; +import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID; import android.annotation.IntDef; import android.annotation.NonNull; diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index ea34d948c91a..f49b1beee8ad 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -16,7 +16,7 @@ package android.view.contentcapture; import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString; -import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID; +import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID; import android.annotation.IntDef; import android.annotation.NonNull; diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index b9889276ae0b..954b83b6f9ee 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -235,6 +235,13 @@ public final class ContentCaptureManager { public static final int RESULT_CODE_SECURITY_EXCEPTION = -1; /** + * ID used to indicate that a session does not exist + * @hide + */ + @SystemApi + public static final int NO_SESSION_ID = 0; + + /** * Timeout for calls to system_server. */ private static final int SYNC_CALLS_TIMEOUT_MS = 5000; diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 012f5e6d3507..39c7210d8dac 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -17,12 +17,12 @@ package android.view.contentcapture; import static android.view.contentcapture.ContentCaptureHelper.sDebug; import static android.view.contentcapture.ContentCaptureHelper.sVerbose; +import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID; import android.annotation.CallSuper; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.graphics.Insets; import android.util.DebugUtils; import android.util.Log; @@ -53,13 +53,6 @@ public abstract class ContentCaptureSession implements AutoCloseable { private static final Random sIdGenerator = new Random(); /** - * ID used to indicate that a session does not exist - * @hide - */ - @SystemApi - public static final int NO_SESSION_ID = 0; - - /** * Initial state, when there is no session. * * @hide 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 b2da23949005..fcf4830fba60 100644 --- a/core/java/android/window/ITaskOrganizer.aidl +++ b/core/java/android/window/ITaskOrganizer.aidl @@ -1,19 +1,18 @@ -/* //device/java/android/android/view/ITaskOrganizer.aidl -** -** Copyright 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. -*/ +/* + * 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 android.window; @@ -26,14 +25,8 @@ import android.window.IWindowContainer; * {@hide} */ oneway interface ITaskOrganizer { - void taskAppeared(in ActivityManager.RunningTaskInfo taskInfo); - void taskVanished(in ActivityManager.RunningTaskInfo taskInfo); - - /** - * Called upon completion of - * ActivityTaskManagerService#applyTaskOrganizerTransaction - */ - void transactionReady(int id, in SurfaceControl.Transaction t); + 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/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl index e8af8e196bed..a8b6aae80dce 100644 --- a/core/java/android/window/ITaskOrganizerController.aidl +++ b/core/java/android/window/ITaskOrganizerController.aidl @@ -36,20 +36,6 @@ interface ITaskOrganizerController { */ void unregisterTaskOrganizer(ITaskOrganizer organizer); - /** - * Apply multiple WindowContainer operations at once. - * @param organizer If non-null this transaction will use the synchronization - * scheme described in BLASTSyncEngine.java. The SurfaceControl transaction - * containing the effects of this WindowContainer transaction will be passed - * to the organizers Transaction ready callback. If null the transaction - * will apply with non particular synchronization constraints (other than - * it will all apply at once). - * @return If organizer was non-null returns an ID for the sync operation which will - * later be passed to transactionReady. This lets TaskOrganizer implementations - * differentiate overlapping sync operations. - */ - int applyContainerTransaction(in WindowContainerTransaction t, ITaskOrganizer organizer); - /** Creates a persistent root task in WM for a particular windowing-mode. */ ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode); diff --git a/core/java/android/window/IWindowContainerTransactionCallback.aidl b/core/java/android/window/IWindowContainerTransactionCallback.aidl new file mode 100644 index 000000000000..0579932f0c5f --- /dev/null +++ b/core/java/android/window/IWindowContainerTransactionCallback.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.view.SurfaceControl; + +/** + * See WindowOrganizer#applyTransaction. + * {@hide} + */ +oneway interface IWindowContainerTransactionCallback { + /** Called upon completion of WindowOrganizer#applyTransaction */ + void transactionReady(int id, in SurfaceControl.Transaction t); +} diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl new file mode 100644 index 000000000000..7f4b26dba479 --- /dev/null +++ b/core/java/android/window/IWindowOrganizerController.aidl @@ -0,0 +1,50 @@ +/** + * 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.IDisplayAreaOrganizerController; +import android.window.ITaskOrganizerController; +import android.window.IWindowContainerTransactionCallback; +import android.window.WindowContainerTransaction; + +/** @hide */ +interface IWindowOrganizerController { + + /** + * Apply multiple WindowContainer operations at once. + * @param t The transaction to apply. + */ + void applyTransaction(in WindowContainerTransaction t); + + /** + * Apply multiple WindowContainer operations at once. + * @param t The transaction to apply. + * @param callback This transaction will use the synchronization scheme described in + * BLASTSyncEngine.java. The SurfaceControl transaction containing the effects of this + * WindowContainer transaction will be passed to this callback when ready. + * @return An ID for the sync operation which will later be passed to transactionReady callback. + * This lets the caller differentiate overlapping sync operations. + */ + int applySyncTransaction(in WindowContainerTransaction t, + in IWindowContainerTransactionCallback callback); + + /** @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 new file mode 100644 index 000000000000..4bd5b29f98ac --- /dev/null +++ b/core/java/android/window/WindowOrganizer.java @@ -0,0 +1,201 @@ +/* + * 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.annotation.RequiresPermission; +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.os.RemoteException; +import android.util.Singleton; + +import java.util.List; + +/** + * Class for organizing specific types of windows like Tasks and DisplayAreas + * + * @hide + */ +public class WindowOrganizer { + + /** + * Apply multiple WindowContainer operations at once. + * @param t The transaction to apply. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public static void applyTransaction(WindowContainerTransaction t) throws RemoteException { + getWindowOrganizerController().applyTransaction(t); + } + + /** + * Apply multiple WindowContainer operations at once. + * @param t The transaction to apply. + * @param callback This transaction will use the synchronization scheme described in + * BLASTSyncEngine.java. The SurfaceControl transaction containing the effects of this + * WindowContainer transaction will be passed to this callback when ready. + * @return An ID for the sync operation which will later be passed to transactionReady callback. + * This lets the caller differentiate overlapping sync operations. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public static int applySyncTransaction(WindowContainerTransaction t, + IWindowContainerTransactionCallback callback) throws RemoteException { + return getWindowOrganizerController().applySyncTransaction(t, callback); + } + + /** @hide */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + private static IWindowOrganizerController getWindowOrganizerController() { + return IWindowOrganizerControllerSingleton.get(); + } + + private static final Singleton<IWindowOrganizerController> IWindowOrganizerControllerSingleton = + new Singleton<IWindowOrganizerController>() { + @Override + protected IWindowOrganizerController create() { + try { + return ActivityTaskManager.getService().getWindowOrganizerController(); + } catch (RemoteException e) { + return null; + } + } + }; + + public static class TaskOrganizer { + + /** + * Register a TaskOrganizer to manage tasks as they enter the given windowing mode. + * If there was already a TaskOrganizer for this windowing mode it will be evicted + * and receive taskVanished callbacks in the process. + */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public static void registerOrganizer(ITaskOrganizer organizer, int windowingMode) + throws RemoteException { + getController().registerTaskOrganizer(organizer, windowingMode); + } + + /** Unregisters a previously registered task organizer. */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public static void unregisterOrganizer(ITaskOrganizer organizer) throws RemoteException { + getController().unregisterTaskOrganizer(organizer); + } + + /** Creates a persistent root task in WM for a particular windowing-mode. */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public static ActivityManager.RunningTaskInfo createRootTask( + int displayId, int windowingMode) throws RemoteException { + return getController().createRootTask(displayId, windowingMode); + } + + /** Deletes a persistent root task in WM */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public static boolean deleteRootTask(IWindowContainer task) throws RemoteException { + return getController().deleteRootTask(task); + } + + /** Gets direct child tasks (ordered from top-to-bottom) */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public static List<ActivityManager.RunningTaskInfo> getChildTasks(IWindowContainer parent, + int[] activityTypes) throws RemoteException { + return getController().getChildTasks(parent, activityTypes); + } + + /** Gets all root tasks on a display (ordered from top-to-bottom) */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public static List<ActivityManager.RunningTaskInfo> getRootTasks( + int displayId, int[] activityTypes) throws RemoteException { + return getController().getRootTasks(displayId, activityTypes); + } + + /** Get the root task which contains the current ime target */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public static IWindowContainer getImeTarget(int display) throws RemoteException { + return getController().getImeTarget(display); + } + + /** + * Set's the root task to launch new tasks into on a display. {@code null} means no launch + * root and thus new tasks just end up directly on the display. + */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) + public static void setLaunchRoot(int displayId, IWindowContainer root) + throws RemoteException { + getController().setLaunchRoot(displayId, root); + } + + private static ITaskOrganizerController getController() { + return ITaskOrganizerControllerSingleton.get(); + } + + private static final Singleton<ITaskOrganizerController> ITaskOrganizerControllerSingleton = + new Singleton<ITaskOrganizerController>() { + @Override + protected ITaskOrganizerController create() { + try { + return getWindowOrganizerController().getTaskOrganizerController(); + } catch (RemoteException e) { + return null; + } + } + }; + } + + /** 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/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index ae9ce655ff3a..c53516389cd9 100644 --- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -387,9 +387,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { resetViewVisibilitiesForWorkProfileEmptyState(emptyStateView); emptyStateView.setVisibility(View.VISIBLE); - ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon); - icon.setImageResource(iconRes); - TextView title = emptyStateView.findViewById(R.id.resolver_empty_state_title); title.setText(titleRes); @@ -401,9 +398,17 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { subtitle.setVisibility(View.GONE); } + ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon); Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button); - button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE); - button.setOnClickListener(buttonOnClick); + if (!getContext().getResources().getBoolean(R.bool.resolver_landscape_phone)) { + icon.setVisibility(View.VISIBLE); + icon.setImageResource(iconRes); + button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE); + button.setOnClickListener(buttonOnClick); + } else { + icon.setVisibility(View.GONE); + button.setVisibility(View.GONE); + } activeListAdapter.markTabLoaded(); } diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java index c40864131a2e..51b13345a6c3 100644 --- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java @@ -169,13 +169,41 @@ public class AccessibilityButtonChooserActivity extends Activity { private static List<AccessibilityButtonTarget> getInstalledServiceTargets( @NonNull Context context) { final List<AccessibilityButtonTarget> targets = new ArrayList<>(); - targets.addAll(getAccessibilityServiceTargets(context)); - targets.addAll(getAccessibilityActivityTargets(context)); + targets.addAll(getAccessibilityFilteredTargets(context)); targets.addAll(getWhiteListingServiceTargets(context)); return targets; } + private static List<AccessibilityButtonTarget> getAccessibilityFilteredTargets( + @NonNull Context context) { + final List<AccessibilityButtonTarget> serviceTargets = + getAccessibilityServiceTargets(context); + final List<AccessibilityButtonTarget> activityTargets = + getAccessibilityActivityTargets(context); + + for (AccessibilityButtonTarget activityTarget : activityTargets) { + serviceTargets.removeIf(serviceTarget -> { + final ComponentName serviceComponentName = + ComponentName.unflattenFromString(serviceTarget.getId()); + final ComponentName activityComponentName = + ComponentName.unflattenFromString(activityTarget.getId()); + final boolean isSamePackageName = activityComponentName.getPackageName().equals( + serviceComponentName.getPackageName()); + final boolean isSameLabel = activityTarget.getLabel().equals( + serviceTarget.getLabel()); + + return isSamePackageName && isSameLabel; + }); + } + + final List<AccessibilityButtonTarget> targets = new ArrayList<>(); + targets.addAll(serviceTargets); + targets.addAll(activityTargets); + + return targets; + } + private static List<AccessibilityButtonTarget> getAccessibilityServiceTargets( @NonNull Context context) { final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index a1a434d3bc64..dca682e4ee29 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -2674,7 +2674,7 @@ public class ChooserActivity extends ResolverActivity implements */ private boolean shouldShowStickyContentPreview() { return shouldShowStickyContentPreviewNoOrientationCheck() - && getResources().getBoolean(R.bool.sharesheet_show_content_preview); + && !getResources().getBoolean(R.bool.resolver_landscape_phone); } private boolean shouldShowStickyContentPreviewNoOrientationCheck() { diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index ff03f1a1a2ab..c75898994f3e 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -205,9 +205,15 @@ public final class Zygote { /** List of packages with the same uid, and its app data info: volume uuid and inode. */ public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map"; + /** List of whitelisted packages and its app data info: volume uuid and inode. */ + public static final String WHITELISTED_DATA_INFO_MAP = "--whitelisted-data-info-map"; + /** Bind mount app storage dirs to lower fs not via fuse */ public static final String BIND_MOUNT_APP_STORAGE_DIRS = "--bind-mount-storage-dirs"; + /** Bind mount app storage dirs to lower fs not via fuse */ + public static final String BIND_MOUNT_APP_DATA_DIRS = "--bind-mount-data-dirs"; + /** * An extraArg passed when a zygote process is forking a child-zygote, specifying a name * in the abstract socket namespace. This socket name is what the new child zygote @@ -313,6 +319,8 @@ public final class Zygote { * @param isTopApp true if the process is for top (high priority) application. * @param pkgDataInfoList A list that stores related packages and its app data * info: volume uuid and inode. + * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps. + * @param bindMountAppDataDirs True if the zygote needs to mount data dirs. * @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs. * * @return 0 if this is the child, pid of the child @@ -321,13 +329,15 @@ public final class Zygote { static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir, - boolean isTopApp, String[] pkgDataInfoList, boolean bindMountAppStorageDirs) { + boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList, + boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) { ZygoteHooks.preFork(); int pid = nativeForkAndSpecialize( uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp, - pkgDataInfoList, bindMountAppStorageDirs); + pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs, + bindMountAppStorageDirs); if (pid == 0) { // Note that this event ends at the end of handleChildProc, Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); @@ -344,6 +354,7 @@ public final class Zygote { int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp, String[] pkgDataInfoList, + String[] whitelistedDataInfoList, boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs); /** @@ -371,15 +382,19 @@ public final class Zygote { * volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name, * app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid, * app_b_ce_inode, ...]; + * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps. + * @param bindMountAppDataDirs True if the zygote needs to mount data dirs. * @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs. */ private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp, - String[] pkgDataInfoList, boolean bindMountAppStorageDirs) { + String[] pkgDataInfoList, String[] whitelistedDataInfoList, + boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) { nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, startChildZygote, instructionSet, appDataDir, isTopApp, - pkgDataInfoList, bindMountAppStorageDirs); + pkgDataInfoList, whitelistedDataInfoList, + bindMountAppDataDirs, bindMountAppStorageDirs); // Note that this event ends at the end of handleChildProc. Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); @@ -399,7 +414,8 @@ public final class Zygote { private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp, - String[] pkgDataInfoList, boolean bindMountAppStorageDirs); + String[] pkgDataInfoList, String[] whitelistedDataInfoList, + boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs); /** * Called to do any initialization before starting an application. @@ -724,7 +740,8 @@ public final class Zygote { args.mRuntimeFlags, rlimits, args.mMountExternal, args.mSeInfo, args.mNiceName, args.mStartChildZygote, args.mInstructionSet, args.mAppDataDir, args.mIsTopApp, - args.mPkgDataInfoList, args.mBindMountAppStorageDirs); + args.mPkgDataInfoList, args.mWhitelistedDataInfoList, + args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java index 1a63765fcaa6..94c1f71a26db 100644 --- a/core/java/com/android/internal/os/ZygoteArguments.java +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -227,11 +227,22 @@ class ZygoteArguments { String[] mPkgDataInfoList; /** + * A list that stores all whitelisted app data info: volume uuid and inode. + * Null if it does need to do app data isolation. + */ + String[] mWhitelistedDataInfoList; + + /** * @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS */ boolean mBindMountAppStorageDirs; /** + * @see Zygote#BIND_MOUNT_APP_DATA_DIRS + */ + boolean mBindMountAppDataDirs; + + /** * Constructs instance and parses args * * @param args zygote command-line args @@ -452,8 +463,12 @@ class ZygoteArguments { } } else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) { mPkgDataInfoList = getAssignmentList(arg); + } else if (arg.startsWith(Zygote.WHITELISTED_DATA_INFO_MAP)) { + mWhitelistedDataInfoList = getAssignmentList(arg); } else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) { mBindMountAppStorageDirs = true; + } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) { + mBindMountAppDataDirs = true; } else { break; } diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index bc8dfd4aa402..6e880d43b73e 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -258,7 +258,8 @@ class ZygoteConnection { parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote, parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp, - parsedArgs.mPkgDataInfoList, parsedArgs.mBindMountAppStorageDirs); + parsedArgs.mPkgDataInfoList,parsedArgs.mWhitelistedDataInfoList, + parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs); try { if (pid == 0) { diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 51b73fc674e7..d2508f3616e4 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -78,7 +78,6 @@ import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.InputQueue; import android.view.InsetsState; -import android.view.InsetsController; import android.view.InsetsState.InternalInsetsType; import android.view.KeyEvent; import android.view.KeyboardShortcutGroup; @@ -1174,6 +1173,12 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset, animate && !disallowAnimate, mForceWindowDrawsBarBackgrounds, state); + + if (mHasCaption) { + final int captionColor = calculateStatusBarColor(); + mDecorCaptionView.getCaption().setBackgroundColor(captionColor); + updateDecorCaptionShade(); + } } // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or @@ -1355,7 +1360,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind : state.attributes.isPresent(insetsState, mWindow.getAttributes().flags, force); boolean show = state.attributes.isVisible(state.present, color, mWindow.getAttributes().flags, force); - boolean showView = show && !isResizing() && size > 0; + boolean showView = show && !isResizing() && !mHasCaption && size > 0; boolean visibilityChanged = false; View view = state.view; @@ -2021,6 +2026,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind if (getForeground() != null) { drawableChanged(); } + getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight()); } } @@ -2094,6 +2100,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind mDecorCaptionView.onConfigurationChanged(displayWindowDecor); enableCaption(displayWindowDecor); } + getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight()); } void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { @@ -2182,11 +2189,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind inflater = inflater.from(context); final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption, null); - setDecorCaptionShade(context, view); + setDecorCaptionShade(view); return view; } - private void setDecorCaptionShade(Context context, DecorCaptionView view) { + private void setDecorCaptionShade(DecorCaptionView view) { final int shade = mWindow.getDecorCaptionShade(); switch (shade) { case DECOR_CAPTION_SHADE_LIGHT: @@ -2196,15 +2203,10 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind setDarkDecorCaptionShade(view); break; default: { - TypedValue value = new TypedValue(); - context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true); - // We invert the shade depending on brightness of the theme. Dark shade for light - // theme and vice versa. Thanks to this the buttons should be visible on the - // background. - if (Color.luminance(value.data) < 0.5) { - setLightDecorCaptionShade(view); - } else { + if ((getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0) { setDarkDecorCaptionShade(view); + } else { + setLightDecorCaptionShade(view); } break; } @@ -2213,7 +2215,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind void updateDecorCaptionShade() { if (mDecorCaptionView != null) { - setDecorCaptionShade(getContext(), mDecorCaptionView); + setDecorCaptionShade(mDecorCaptionView); } } @@ -2484,6 +2486,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } /** + * @hide + * @return the height of insets covering the top of window content area. + */ + public int getCaptionInsetsHeight() { + if (!mWindow.isOverlayWithDecorCaptionEnabled()) return 0; + return getCaptionHeight(); + } + + /** * Converts a DIP measure into physical pixels. * @param dip The dip value. * @return Returns the number of pixels. diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 0aeaa47ba3d8..980943ebea5a 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -22,14 +22,10 @@ import android.annotation.Nullable; import android.annotation.StyleRes; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; -import android.graphics.Point; import android.graphics.Rect; -import android.util.Size; -import android.view.Display; import android.view.Gravity; import android.view.View; import android.view.WindowManager; -import android.view.WindowMetrics; import android.widget.PopupWindow.OnDismissListener; import com.android.internal.view.menu.MenuPresenter.Callback; @@ -227,9 +223,9 @@ public class MenuPopupHelper implements MenuHelper { @NonNull private MenuPopup createPopup() { final WindowManager windowManager = mContext.getSystemService(WindowManager.class); - final Size maxWindowSize = windowManager.getMaximumWindowMetrics().getSize(); + final Rect maxWindowBounds = windowManager.getMaximumWindowMetrics().getBounds(); - final int smallestWidth = Math.min(maxWindowSize.getWidth(), maxWindowSize.getHeight()); + final int smallestWidth = Math.min(maxWindowBounds.width(), maxWindowBounds.height()); final int minSmallestWidthCascading = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.cascading_menus_min_smallest_width); final boolean enableCascadingSubmenus = smallestWidth >= minSmallestWidthCascading; diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 4e7ae8a235e7..bedf55d52391 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -93,7 +93,6 @@ public class ConversationLayout extends FrameLayout private MessagingLinearLayout mMessagingLinearLayout; private boolean mShowHistoricMessages; private ArrayList<MessagingGroup> mGroups = new ArrayList<>(); - private TextView mTitleView; private int mLayoutColor; private int mSenderTextColor; private int mMessageTextColor; @@ -108,8 +107,14 @@ public class ConversationLayout extends FrameLayout private boolean mIsCollapsed; private ImageResolver mImageResolver; private ImageView mConversationIcon; + private View mConversationIconContainer; + private int mConversationIconTopPadding; + private int mConversationIconTopPaddingExpandedGroup; + private int mConversationIconTopPaddingNoAppName; + private int mExpandedGroupMessagePaddingNoAppName; private TextView mConversationText; private View mConversationIconBadge; + private ImageView mConversationIconBadgeBg; private Icon mLargeIcon; private View mExpandButtonContainer; private ViewGroup mExpandButtonAndContentContainer; @@ -117,11 +122,12 @@ public class ConversationLayout extends FrameLayout private MessagingLinearLayout mImageMessageContainer; private int mExpandButtonExpandedTopMargin; private int mBadgedSideMargins; - private int mIconSizeBadged; - private int mIconSizeCentered; + private int mConversationAvatarSize; + private int mConversationAvatarSizeExpanded; private CachingIconView mIcon; private View mImportanceRingView; - private int mExpandedGroupTopMargin; + private int mExpandedGroupSideMargin; + private int mExpandedGroupSideMarginFacePile; private View mConversationFacePile; private int mNotificationBackgroundColor; private CharSequence mFallbackChatName; @@ -133,7 +139,12 @@ public class ConversationLayout extends FrameLayout private boolean mExpandable = true; private int mContentMarginEnd; private Rect mMessagingClipRect; - private TextView mAppName; + private ObservableTextView mAppName; + private boolean mAppNameGone; + private int mFacePileAvatarSize; + private int mFacePileAvatarSizeExpandedGroup; + private int mFacePileProtectionWidth; + private int mFacePileProtectionWidthExpanded; public ConversationLayout(@NonNull Context context) { super(context); @@ -165,14 +176,15 @@ public class ConversationLayout extends FrameLayout int size = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels); mMessagingClipRect = new Rect(0, 0, size, size); setMessagingClippingDisabled(false); - mTitleView = findViewById(R.id.title); mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setAntiAlias(true); mConversationIcon = findViewById(R.id.conversation_icon); + mConversationIconContainer = findViewById(R.id.conversation_icon_container); mIcon = findViewById(R.id.icon); mImportanceRingView = findViewById(R.id.conversation_icon_badge_ring); mConversationIconBadge = findViewById(R.id.conversation_icon_badge); + mConversationIconBadgeBg = findViewById(R.id.conversation_icon_badge_bg); mIcon.setOnVisibilityChangedListener((visibility) -> { // Always keep the badge visibility in sync with the icon. This is necessary in cases // Where the icon is being hidden externally like in group children. @@ -192,18 +204,40 @@ public class ConversationLayout extends FrameLayout R.dimen.notification_content_margin_end); mBadgedSideMargins = getResources().getDimensionPixelSize( R.dimen.conversation_badge_side_margin); - mIconSizeBadged = getResources().getDimensionPixelSize( - R.dimen.conversation_icon_size_badged); - mIconSizeCentered = getResources().getDimensionPixelSize( - R.dimen.conversation_icon_size_centered); - mExpandedGroupTopMargin = getResources().getDimensionPixelSize( - R.dimen.conversation_icon_margin_top_centered); + mConversationAvatarSize = getResources().getDimensionPixelSize( + R.dimen.conversation_avatar_size); + mConversationAvatarSizeExpanded = getResources().getDimensionPixelSize( + R.dimen.conversation_avatar_size_group_expanded); + mConversationIconTopPadding = getResources().getDimensionPixelSize( + R.dimen.conversation_icon_container_top_padding); + mConversationIconTopPaddingExpandedGroup = getResources().getDimensionPixelSize( + R.dimen.conversation_icon_container_top_padding_small_avatar); + mConversationIconTopPaddingNoAppName = getResources().getDimensionPixelSize( + R.dimen.conversation_icon_container_top_padding_no_app_name); + mExpandedGroupMessagePaddingNoAppName = getResources().getDimensionPixelSize( + R.dimen.expanded_group_conversation_message_padding_without_app_name); + mExpandedGroupSideMargin = getResources().getDimensionPixelSize( + R.dimen.conversation_badge_side_margin_group_expanded); + mExpandedGroupSideMarginFacePile = getResources().getDimensionPixelSize( + R.dimen.conversation_badge_side_margin_group_expanded_face_pile); mConversationFacePile = findViewById(R.id.conversation_face_pile); + mFacePileAvatarSize = getResources().getDimensionPixelSize( + R.dimen.conversation_face_pile_avatar_size); + mFacePileAvatarSizeExpandedGroup = getResources().getDimensionPixelSize( + R.dimen.conversation_face_pile_avatar_size_group_expanded); + mFacePileProtectionWidth = getResources().getDimensionPixelSize( + R.dimen.conversation_face_pile_protection_width); + mFacePileProtectionWidthExpanded = getResources().getDimensionPixelSize( + R.dimen.conversation_face_pile_protection_width_expanded); mFallbackChatName = getResources().getString( R.string.conversation_title_fallback_one_to_one); mFallbackGroupChatName = getResources().getString( R.string.conversation_title_fallback_group_chat); mAppName = findViewById(R.id.app_name_text); + mAppNameGone = mAppName.getVisibility() == GONE; + mAppName.setOnVisibilityChangedListener((visibility) -> { + onAppNameVisibilityChanged(); + }); } @RemotableViewMethod @@ -234,7 +268,7 @@ public class ConversationLayout extends FrameLayout mIsCollapsed = isCollapsed; mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE); updateExpandButton(); - updateContentPaddings(); + updateContentEndPaddings(); } @RemotableViewMethod @@ -354,21 +388,17 @@ public class ConversationLayout extends FrameLayout } } } else { - if (mIsCollapsed) { - if (mLargeIcon != null) { - mConversationIcon.setVisibility(VISIBLE); - mConversationFacePile.setVisibility(GONE); - mConversationIcon.setImageIcon(mLargeIcon); - } else { - mConversationIcon.setVisibility(GONE); - // This will also inflate it! - mConversationFacePile.setVisibility(VISIBLE); - mConversationFacePile = findViewById(R.id.conversation_face_pile); - bindFacePile(); - } - } else { + if (mLargeIcon != null) { + mConversationIcon.setVisibility(VISIBLE); mConversationFacePile.setVisibility(GONE); + mConversationIcon.setImageIcon(mLargeIcon); + } else { mConversationIcon.setVisibility(GONE); + // This will also inflate it! + mConversationFacePile.setVisibility(VISIBLE); + // rebind the value to the inflated view instead of the stub + mConversationFacePile = findViewById(R.id.conversation_face_pile); + bindFacePile(); } } if (TextUtils.isEmpty(conversationText)) { @@ -384,9 +414,10 @@ public class ConversationLayout extends FrameLayout && TextUtils.equals(conversationText, messageSender); messagingGroup.setCanHideSenderIfFirst(canHide); } + updateAppName(); updateIconPositionAndSize(); updateImageMessages(); - updateAppName(); + updatePaddingsBasedOnContentAvailability(); } private void updateImageMessages() { @@ -425,7 +456,7 @@ public class ConversationLayout extends FrameLayout private void bindFacePile() { // Let's bind the face pile - View bottomBackground = mConversationFacePile.findViewById( + ImageView bottomBackground = mConversationFacePile.findViewById( R.id.conversation_face_pile_bottom_background); applyNotificationBackgroundColor(bottomBackground); ImageView bottomView = mConversationFacePile.findViewById( @@ -463,6 +494,38 @@ public class ConversationLayout extends FrameLayout secondLastIcon = createAvatarSymbol("", "", mLayoutColor); } topView.setImageIcon(secondLastIcon); + + int conversationAvatarSize; + int facepileAvatarSize; + int facePileBackgroundSize; + if (mIsCollapsed) { + conversationAvatarSize = mConversationAvatarSize; + facepileAvatarSize = mFacePileAvatarSize; + facePileBackgroundSize = facepileAvatarSize + 2 * mFacePileProtectionWidth; + } else { + conversationAvatarSize = mConversationAvatarSizeExpanded; + facepileAvatarSize = mFacePileAvatarSizeExpandedGroup; + facePileBackgroundSize = facepileAvatarSize + 2 * mFacePileProtectionWidthExpanded; + } + LayoutParams layoutParams = (LayoutParams) mConversationIcon.getLayoutParams(); + layoutParams.width = conversationAvatarSize; + layoutParams.height = conversationAvatarSize; + mConversationFacePile.setLayoutParams(layoutParams); + + layoutParams = (LayoutParams) bottomView.getLayoutParams(); + layoutParams.width = facepileAvatarSize; + layoutParams.height = facepileAvatarSize; + bottomView.setLayoutParams(layoutParams); + + layoutParams = (LayoutParams) topView.getLayoutParams(); + layoutParams.width = facepileAvatarSize; + layoutParams.height = facepileAvatarSize; + topView.setLayoutParams(layoutParams); + + layoutParams = (LayoutParams) bottomBackground.getLayoutParams(); + layoutParams.width = facePileBackgroundSize; + layoutParams.height = facePileBackgroundSize; + bottomBackground.setLayoutParams(layoutParams); } private void updateAppName() { @@ -477,30 +540,61 @@ public class ConversationLayout extends FrameLayout * update the icon position and sizing */ private void updateIconPositionAndSize() { - int gravity; - int marginStart; - int marginTop; - int iconSize; + int sidemargin; + int conversationAvatarSize; if (mIsOneToOne || mIsCollapsed) { - // Badged format - gravity = Gravity.LEFT; - marginStart = mBadgedSideMargins; - marginTop = mBadgedSideMargins; - iconSize = mIconSizeBadged; + sidemargin = mBadgedSideMargins; + conversationAvatarSize = mConversationAvatarSize; } else { - gravity = Gravity.CENTER_HORIZONTAL; - marginStart = 0; - marginTop = mExpandedGroupTopMargin; - iconSize = mIconSizeCentered; + sidemargin = mConversationFacePile.getVisibility() == VISIBLE + ? mExpandedGroupSideMarginFacePile + : mExpandedGroupSideMargin; + conversationAvatarSize = mConversationAvatarSizeExpanded; } LayoutParams layoutParams = (LayoutParams) mConversationIconBadge.getLayoutParams(); - layoutParams.gravity = gravity; - layoutParams.topMargin = marginTop; - layoutParams.setMarginStart(marginStart); - layoutParams.width = iconSize; - layoutParams.height = iconSize; + layoutParams.topMargin = sidemargin; + layoutParams.setMarginStart(sidemargin); mConversationIconBadge.setLayoutParams(layoutParams); + + if (mConversationIcon.getVisibility() == VISIBLE) { + layoutParams = (LayoutParams) mConversationIcon.getLayoutParams(); + layoutParams.width = conversationAvatarSize; + layoutParams.height = conversationAvatarSize; + mConversationIcon.setLayoutParams(layoutParams); + } + } + + private void updatePaddingsBasedOnContentAvailability() { + int containerTopPadding; + int messagingPadding = 0; + if (mIsOneToOne || mIsCollapsed) { + containerTopPadding = mConversationIconTopPadding; + } else { + if (mAppName.getVisibility() != GONE) { + // The app name is visible, let's center outselves in the two lines + containerTopPadding = mConversationIconTopPaddingExpandedGroup; + } else { + // App name is gone, let's center ourselves int he one remaining line + containerTopPadding = mConversationIconTopPaddingNoAppName; + + // The app name is gone and we're a group, we'll need to add some extra padding + // to the messages, since otherwise it will overlap with the group + messagingPadding = mExpandedGroupMessagePaddingNoAppName; + } + } + + mConversationIconContainer.setPaddingRelative( + mConversationIconContainer.getPaddingStart(), + containerTopPadding, + mConversationIconContainer.getPaddingEnd(), + mConversationIconContainer.getPaddingBottom()); + + mMessagingLinearLayout.setPaddingRelative( + mMessagingLinearLayout.getPaddingStart(), + messagingPadding, + mMessagingLinearLayout.getPaddingEnd(), + mMessagingLinearLayout.getPaddingBottom()); } @RemotableViewMethod @@ -678,11 +772,11 @@ public class ConversationLayout extends FrameLayout @RemotableViewMethod public void setNotificationBackgroundColor(int color) { mNotificationBackgroundColor = color; - applyNotificationBackgroundColor(mConversationIconBadge); + applyNotificationBackgroundColor(mConversationIconBadgeBg); } - private void applyNotificationBackgroundColor(View view) { - view.setBackgroundTintList(ColorStateList.valueOf(mNotificationBackgroundColor)); + private void applyNotificationBackgroundColor(ImageView view) { + view.setImageTintList(ColorStateList.valueOf(mNotificationBackgroundColor)); } @RemotableViewMethod @@ -922,7 +1016,7 @@ public class ConversationLayout extends FrameLayout mExpandButtonContainer.setContentDescription(mContext.getText(contentDescriptionId)); } - private void updateContentPaddings() { + private void updateContentEndPaddings() { // Let's make sure the conversation header can't run into the expand button when we're // collapsed and update the paddings of the content @@ -951,6 +1045,14 @@ public class ConversationLayout extends FrameLayout mContentContainer.getPaddingBottom()); } + private void onAppNameVisibilityChanged() { + boolean appNameGone = mAppName.getVisibility() == GONE; + if (appNameGone != mAppNameGone) { + mAppNameGone = appNameGone; + updatePaddingsBasedOnContentAvailability(); + } + } + public void updateExpandability(boolean expandable, @Nullable OnClickListener onClickListener) { mExpandable = expandable; if (expandable) { @@ -960,7 +1062,7 @@ public class ConversationLayout extends FrameLayout // TODO: handle content paddings to end of layout mExpandButtonContainer.setVisibility(GONE); } - updateContentPaddings(); + updateContentEndPaddings(); } @Override diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java index b5d787c24fbd..7a01024ffc36 100644 --- a/core/java/com/android/internal/widget/DecorCaptionView.java +++ b/core/java/com/android/internal/widget/DecorCaptionView.java @@ -17,7 +17,6 @@ package com.android.internal.widget; import android.content.Context; -import android.graphics.Color; import android.graphics.Rect; import android.os.RemoteException; import android.util.AttributeSet; @@ -53,8 +52,7 @@ import java.util.ArrayList; * <li>..</li> * </ul> * - * Although this ViewGroup has only two direct sub-Views, its behavior is more complex due to - * overlaying caption on the content and drawing. + * Here describe the behavior of overlaying caption on the content and drawing. * * First, no matter where the content View gets added, it will always be the first child and the * caption will be the second. This way the caption will always be drawn on top of the content when @@ -66,11 +64,9 @@ import java.util.ArrayList; * <li>DecorCaptionView.onInterceptTouchEvent() will try intercepting the touch events if the * down action is performed on top close or maximize buttons; the reason for that is we want these * buttons to always work.</li> - * <li>The content View will receive the touch event. Mind that content is actually underneath the - * caption, so we need to introduce our own dispatch ordering. We achieve this by overriding - * {@link #buildTouchDispatchChildList()}.</li> - * <li>If the touch event is not consumed by the content View, it will go to the caption View - * and the dragging logic will be executed.</li> + * <li>The caption view will try to consume the event to apply the dragging logic.</li> + * <li>If the touch event is not consumed by the caption, the content View will receive the touch + * event</li> * </ul> */ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener, @@ -137,11 +133,6 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener, mOwner = owner; mShow = show; mOverlayWithAppContent = owner.isOverlayWithDecorCaptionEnabled(); - if (mOverlayWithAppContent) { - // The caption is covering the content, so we make its background transparent to make - // the content visible. - mCaption.setBackgroundColor(Color.TRANSPARENT); - } updateCaptionVisibility(); // By changing the outline provider to BOUNDS, the window can remove its // background without removing the shadow. @@ -236,18 +227,6 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener, } @Override - public ArrayList<View> buildTouchDispatchChildList() { - mTouchDispatchList.ensureCapacity(3); - if (mCaption != null) { - mTouchDispatchList.add(mCaption); - } - if (mContent != null) { - mTouchDispatchList.add(mContent); - } - return mTouchDispatchList; - } - - @Override public boolean shouldDelayChildPressedState() { return false; } diff --git a/core/java/com/android/internal/widget/ObservableTextView.java b/core/java/com/android/internal/widget/ObservableTextView.java new file mode 100644 index 000000000000..1f3c296f0b60 --- /dev/null +++ b/core/java/com/android/internal/widget/ObservableTextView.java @@ -0,0 +1,66 @@ +/* + * 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.internal.widget; + +import android.annotation.Nullable; +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.RemoteViews; +import android.widget.TextView; + +import java.util.function.Consumer; + +/** + * A text view whose visibility can be observed. + */ +@RemoteViews.RemoteView +public class ObservableTextView extends TextView { + + private Consumer<Integer> mOnVisibilityChangedListener; + + public ObservableTextView(Context context) { + super(context); + } + + public ObservableTextView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public ObservableTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public ObservableTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onVisibilityChanged(View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + if (changedView == this) { + if (mOnVisibilityChangedListener != null) { + mOnVisibilityChangedListener.accept(visibility); + } + } + } + + public void setOnVisibilityChangedListener(Consumer<Integer> listener) { + mOnVisibilityChangedListener = listener; + } +} 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/jni/android_media_AudioProductStrategies.cpp b/core/jni/android_media_AudioProductStrategies.cpp index 17a02b24c697..34be2a52344d 100644 --- a/core/jni/android_media_AudioProductStrategies.cpp +++ b/core/jni/android_media_AudioProductStrategies.cpp @@ -85,10 +85,23 @@ static jint convertAudioProductStrategiesFromNative( jStrategyId = static_cast<jint>(strategy.getId()); // Audio Attributes Group array - std::map<int, std::vector<AudioAttributes> > groups; + int attrGroupIndex = 0; + std::map<int /**attributesGroupIndex*/, std::vector<AudioAttributes> > groups; for (const auto &attr : strategy.getAudioAttributes()) { - int attrGroupId = attr.getGroupId(); - groups[attrGroupId].push_back(attr); + int groupId = attr.getGroupId(); + int streamType = attr.getStreamType(); + const auto &iter = std::find_if(begin(groups), end(groups), + [groupId, streamType](const auto &iter) { + const auto &frontAttr = iter.second.front(); + return frontAttr.getGroupId() == groupId && frontAttr.getStreamType() == streamType; + }); + // Same Volume Group Id and same stream type + if (iter != end(groups)) { + groups[iter->first].push_back(attr); + } else { + // Add a new Group of AudioAttributes for this product strategy + groups[attrGroupIndex++].push_back(attr); + } } numAttributesGroups = groups.size(); @@ -97,7 +110,7 @@ static jint convertAudioProductStrategiesFromNative( for (const auto &iter : groups) { std::vector<AudioAttributes> audioAttributesGroups = iter.second; jint numAttributes = audioAttributesGroups.size(); - jint jGroupId = iter.first; + jint jGroupId = audioAttributesGroups.front().getGroupId(); jint jLegacyStreamType = audioAttributesGroups.front().getStreamType(); jStatus = JNIAudioAttributeHelper::getJavaArray(env, &jAudioAttributes, numAttributes); diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index cb5a332c6e85..bf3fc5704739 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -214,12 +214,8 @@ static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& valu // ---------------------------------------------------------------------------- -static std::unique_ptr<DynamicLibManager> sDynamicLibManager = - std::make_unique<DynamicLibManager>(); - // Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. struct GuardedAssetManager : public ::AAssetManager { - GuardedAssetManager() : guarded_assetmanager(sDynamicLibManager.get()) {} Guarded<AssetManager2> guarded_assetmanager; }; diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 649e5d2f49fe..cc94d6ff5d67 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -49,7 +49,7 @@ static struct { jmethodID dispatchInputEvent; jmethodID onFocusEvent; - jmethodID dispatchBatchedInputEventPending; + jmethodID onBatchedInputEventPending; } gInputEventReceiverClassInfo; @@ -242,37 +242,40 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, status_t status = mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent); - if (status) { - if (status == WOULD_BLOCK) { - if (!skipCallbacks && !mBatchedInputEventPending - && mInputConsumer.hasPendingBatch()) { - // There is a pending batch. Come back later. + if (status != OK && status != WOULD_BLOCK) { + ALOGE("channel '%s' ~ Failed to consume input event. status=%d", + getInputChannelName().c_str(), status); + return status; + } + + if (status == WOULD_BLOCK) { + if (!skipCallbacks && !mBatchedInputEventPending && mInputConsumer.hasPendingBatch()) { + // There is a pending batch. Come back later. + if (!receiverObj.get()) { + receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal)); if (!receiverObj.get()) { - receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal)); - if (!receiverObj.get()) { - ALOGW("channel '%s' ~ Receiver object was finalized " - "without being disposed.", getInputChannelName().c_str()); - return DEAD_OBJECT; - } + ALOGW("channel '%s' ~ Receiver object was finalized " + "without being disposed.", + getInputChannelName().c_str()); + return DEAD_OBJECT; } + } - mBatchedInputEventPending = true; - if (kDebugDispatchCycle) { - ALOGD("channel '%s' ~ Dispatching batched input event pending notification.", - getInputChannelName().c_str()); - } - env->CallVoidMethod(receiverObj.get(), - gInputEventReceiverClassInfo.dispatchBatchedInputEventPending); - if (env->ExceptionCheck()) { - ALOGE("Exception dispatching batched input events."); - mBatchedInputEventPending = false; // try again later - } + mBatchedInputEventPending = true; + if (kDebugDispatchCycle) { + ALOGD("channel '%s' ~ Dispatching batched input event pending notification.", + getInputChannelName().c_str()); + } + + env->CallVoidMethod(receiverObj.get(), + gInputEventReceiverClassInfo.onBatchedInputEventPending, + mInputConsumer.getPendingBatchSource()); + if (env->ExceptionCheck()) { + ALOGE("Exception dispatching batched input events."); + mBatchedInputEventPending = false; // try again later } - return OK; } - ALOGE("channel '%s' ~ Failed to consume input event. status=%d", - getInputChannelName().c_str(), status); - return status; + return OK; } assert(inputEvent); @@ -441,8 +444,9 @@ int register_android_view_InputEventReceiver(JNIEnv* env) { "dispatchInputEvent", "(ILandroid/view/InputEvent;)V"); gInputEventReceiverClassInfo.onFocusEvent = GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onFocusEvent", "(ZZ)V"); - gInputEventReceiverClassInfo.dispatchBatchedInputEventPending = GetMethodIDOrDie(env, - gInputEventReceiverClassInfo.clazz, "dispatchBatchedInputEventPending", "()V"); + gInputEventReceiverClassInfo.onBatchedInputEventPending = + GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onBatchedInputEventPending", + "(I)V"); return res; } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index ea3c0fa9fc3c..aa2d1b5fa02b 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -110,7 +110,6 @@ using android::base::StringAppendF; using android::base::StringPrintf; using android::base::WriteStringToFile; using android::base::GetBoolProperty; -using android::base::GetProperty; #define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \ append(StringPrintf(__VA_ARGS__)) @@ -170,18 +169,6 @@ static int gSystemServerSocketFd = -1; static constexpr int DEFAULT_DATA_DIR_PERMISSION = 0751; -/** - * Property to control if app data isolation is enabled. - */ -static const std::string ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY = - "persist.zygote.app_data_isolation"; - -/** - * Property to enable app data isolation for sdcard obb or data in vold. - */ -static const std::string ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY = - "persist.sys.vold_app_data_isolation_enabled"; - static constexpr const uint64_t UPPER_HALF_WORD_MASK = 0xFFFF'FFFF'0000'0000; static constexpr const uint64_t LOWER_HALF_WORD_MASK = 0x0000'0000'FFFF'FFFF; @@ -1319,20 +1306,13 @@ static void relabelAllDirs(const char* path, security_context_t context, fail_fn * be decrypted after storage is decrypted. * */ -static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list, - uid_t uid, const char* process_name, jstring managed_nice_name, - fail_fn_t fail_fn) { +static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_data_info_list, + uid_t uid, const char* process_name, + jstring managed_nice_name, fail_fn_t fail_fn) { const userid_t userId = multiuser_get_user_id(uid); - auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1); - - int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0; - // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode> - if ((size % 3) != 0) { - fail_fn(CREATE_ERROR("Wrong pkg_inode_list size %d", size)); - } - ensureInAppMountNamespace(fail_fn); + int size = merged_data_info_list.size(); // Mount tmpfs on all possible data directories, so app no longer see the original apps data. char internalCePath[PATH_MAX]; @@ -1377,14 +1357,10 @@ static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list, bool legacySymlinkCreated = false; for (int i = 0; i < size; i += 3) { - jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i)); - std::string packageName = extract_fn(package_str).value(); + std::string const & packageName = merged_data_info_list[i]; + std::string const & volUuid = merged_data_info_list[i + 1]; + std::string const & inode = merged_data_info_list[i + 2]; - jstring vol_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i + 1)); - std::string volUuid = extract_fn(vol_str).value(); - - jstring inode_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i + 2)); - std::string inode = extract_fn(inode_str).value(); std::string::size_type sz; long long ceDataInode = std::stoll(inode, &sz); @@ -1482,6 +1458,48 @@ static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list, freecon(dataDataContext); } +static void insertPackagesToMergedList(JNIEnv* env, + std::vector<std::string>& merged_data_info_list, + jobjectArray data_info_list, const char* process_name, + jstring managed_nice_name, fail_fn_t fail_fn) { + + auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1); + + int size = (data_info_list != nullptr) ? env->GetArrayLength(data_info_list) : 0; + // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode> + if ((size % 3) != 0) { + fail_fn(CREATE_ERROR("Wrong data_info_list size %d", size)); + } + + for (int i = 0; i < size; i += 3) { + jstring package_str = (jstring) (env->GetObjectArrayElement(data_info_list, i)); + std::string packageName = extract_fn(package_str).value(); + merged_data_info_list.push_back(packageName); + + jstring vol_str = (jstring) (env->GetObjectArrayElement(data_info_list, i + 1)); + std::string volUuid = extract_fn(vol_str).value(); + merged_data_info_list.push_back(volUuid); + + jstring inode_str = (jstring) (env->GetObjectArrayElement(data_info_list, i + 2)); + std::string inode = extract_fn(inode_str).value(); + merged_data_info_list.push_back(inode); + } +} + +static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list, + jobjectArray whitelisted_data_info_list, uid_t uid, const char* process_name, + jstring managed_nice_name, fail_fn_t fail_fn) { + + ensureInAppMountNamespace(fail_fn); + std::vector<std::string> merged_data_info_list; + insertPackagesToMergedList(env, merged_data_info_list, pkg_data_info_list, + process_name, managed_nice_name, fail_fn); + insertPackagesToMergedList(env, merged_data_info_list, whitelisted_data_info_list, + process_name, managed_nice_name, fail_fn); + + isolateAppData(env, merged_data_info_list, uid, process_name, managed_nice_name, fail_fn); +} + /** * Like isolateAppData(), isolate jit profile directories, so apps don't see what * other apps are installed by reading content inside /data/misc/profiles/cur. @@ -1594,7 +1612,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, jstring managed_nice_name, bool is_system_server, bool is_child_zygote, jstring managed_instruction_set, jstring managed_app_data_dir, bool is_top_app, - jobjectArray pkg_data_info_list, bool mount_storage_dirs) { + jobjectArray pkg_data_info_list, + jobjectArray whitelisted_data_info_list, + bool mount_data_dirs, bool mount_storage_dirs) { const char* process_name = is_system_server ? "system_server" : "zygote"; auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1); auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1); @@ -1628,9 +1648,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, // give a null in same_uid_pkgs and private_volumes so they don't need app data isolation. // Isolated process / webview / app zygote should be gated by SELinux and file permission // so they can't even traverse CE / DE directories. - if (pkg_data_info_list != nullptr - && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) { - isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn); + if (mount_data_dirs) { + isolateAppData(env, pkg_data_info_list, whitelisted_data_info_list, + uid, process_name, managed_nice_name, fail_fn); isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn); } if ((mount_external != MOUNT_EXTERNAL_INSTALLER) && mount_storage_dirs) { @@ -2003,7 +2023,8 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( jint mount_external, jstring se_info, jstring nice_name, jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, - jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) { + jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, + jboolean mount_data_dirs, jboolean mount_storage_dirs) { jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote); if (UNLIKELY(managed_fds_to_close == nullptr)) { @@ -2041,6 +2062,8 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE, instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list, + whitelisted_data_info_list, + mount_data_dirs == JNI_TRUE, mount_storage_dirs == JNI_TRUE); } return pid; @@ -2076,7 +2099,8 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( permitted_capabilities, effective_capabilities, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true, false, nullptr, nullptr, /* is_top_app= */ false, - /* pkg_data_info_list */ nullptr, false); + /* pkg_data_info_list */ nullptr, + /* whitelisted_data_info_list */ nullptr, false, false); } else if (pid > 0) { // The zygote process checks whether the child process has died or not. ALOGI("System server process %d has been created", pid); @@ -2206,15 +2230,16 @@ static void com_android_internal_os_Zygote_nativeSpecializeAppProcess( jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, - jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) { + jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, + jboolean mount_data_dirs, jboolean mount_storage_dirs) { jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote); SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities, mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE, instruction_set, app_data_dir, - is_top_app == JNI_TRUE, pkg_data_info_list, - mount_storage_dirs == JNI_TRUE); + is_top_app == JNI_TRUE, pkg_data_info_list, whitelisted_data_info_list, + mount_data_dirs == JNI_TRUE, mount_storage_dirs == JNI_TRUE); } /** @@ -2408,7 +2433,7 @@ static jint com_android_internal_os_Zygote_nativeParseSigChld(JNIEnv* env, jclas static const JNINativeMethod gMethods[] = { {"nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/" - "String;Z[Ljava/lang/String;Z)I", + "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I", (void*)com_android_internal_os_Zygote_nativeForkAndSpecialize}, {"nativeForkSystemServer", "(II[II[[IJJ)I", (void*)com_android_internal_os_Zygote_nativeForkSystemServer}, @@ -2421,7 +2446,7 @@ static const JNINativeMethod gMethods[] = { {"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap}, {"nativeSpecializeAppProcess", "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/" - "String;Z[Ljava/lang/String;Z)V", + "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V", (void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess}, {"nativeInitNativeState", "(Z)V", (void*)com_android_internal_os_Zygote_nativeInitNativeState}, diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto index 42437d5b44a0..563ef145b79c 100644 --- a/core/proto/android/app/enums.proto +++ b/core/proto/android/app/enums.proto @@ -203,9 +203,7 @@ enum AppOpEnum { APP_OP_INTERACT_ACROSS_PROFILES = 93; APP_OP_ACTIVATE_PLATFORM_VPN = 94; APP_OP_LOADER_USAGE_STATS = 95; - APP_OP_ACCESS_CALL_AUDIO = 96; + APP_OP_DEPRECATED_1 = 96 [deprecated = true]; APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97; APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98; } - - diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index fc6e639f0aa0..ab57e3dcdc53 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2653,4 +2653,20 @@ enum PageId { // CATEGORY: SETTINGS // OS: R ADB_WIRELESS_DEVICE_DETAILS = 1836; + + // Open: Settings > Sound > Do Not Disturb > People > Conversations + // OS: R + DND_CONVERSATIONS = 1837; + + // Open: Settings > Sound > Do Not Disturb > People > Calls + // OS: R + DND_CALLS = 1838; + + // 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/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index a3313b21131d..075aa97edb58 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -77,7 +77,7 @@ message SecureSettingsProto { optional SettingProto interactive_ui_timeout_ms = 33 [ (android.privacy).dest = DEST_AUTOMATIC ]; // Settings for magnification mode optional SettingProto accessibility_magnification_mode = 34 [ (android.privacy).dest = DEST_AUTOMATIC ]; - optional SettingProto button_long_press_targets = 35 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto button_targets = 35 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Accessibility accessibility = 2; @@ -180,6 +180,14 @@ message SecureSettingsProto { optional SettingProto cmas_additional_broadcast_pkg = 14 [ (android.privacy).dest = DEST_AUTOMATIC ]; repeated SettingProto completed_categories = 15; optional SettingProto connectivity_release_pending_intent_delay_ms = 16 [ (android.privacy).dest = DEST_AUTOMATIC ]; + + message Controls { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + } + optional Controls controls = 79; + optional SettingProto device_paired = 17 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto dialer_default_application = 18 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto display_density_forced = 19 [ (android.privacy).dest = DEST_AUTOMATIC ]; @@ -580,5 +588,5 @@ message SecureSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 79; + // Next tag = 80; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 84d9ce288069..eae614546dfb 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1213,15 +1213,6 @@ android:description="@string/permdesc_acceptHandovers" android:protectionLevel="dangerous" /> - <!-- Allows an application assigned to the Dialer role to be granted access to the telephony - call audio streams, both TX and RX. - <p>Protection level: signature|appop - --> - <permission android:name="android.permission.ACCESS_CALL_AUDIO" - android.label="@string/permlab_accessCallAudio" - android:description="@string/permdesc_accessCallAudio" - android:protectionLevel="signature|appop" /> - <!-- ====================================================================== --> <!-- Permissions for accessing the device microphone --> <!-- ====================================================================== --> @@ -3654,6 +3645,16 @@ <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES" android:protectionLevel="signature|privileged" /> + <!-- Allows an application to use the package installer v2 APIs. + <p>The package installer v2 APIs are still a work in progress and we're + currently validating they work in all scenarios. + <p>Not for use by third-party applications. + TODO(b/152310230): remove this permission once the APIs are confirmed to be sufficient. + @hide + --> + <permission android:name="com.android.permission.USE_INSTALLER_V2" + android:protectionLevel="signature|verifier" /> + <!-- @SystemApi @TestApi Allows an application to clear user data. <p>Not for use by third-party applications @hide diff --git a/core/res/res/drawable/resolver_turn_on_work_button_ripple_background.xml b/core/res/res/drawable/resolver_turn_on_work_button_ripple_background.xml new file mode 100644 index 000000000000..786f5e676c36 --- /dev/null +++ b/core/res/res/drawable/resolver_turn_on_work_button_ripple_background.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?attr/colorAccent"> + <item android:id="@android:id/mask"> + <shape android:shape="rectangle"> + <solid android:color="?attr/colorControlHighlight" /> + <corners android:radius="?attr/buttonCornerRadius" /> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/core/res/res/layout/conversation_face_pile_layout.xml b/core/res/res/layout/conversation_face_pile_layout.xml index 1db38702f926..528562534aab 100644 --- a/core/res/res/layout/conversation_face_pile_layout.xml +++ b/core/res/res/layout/conversation_face_pile_layout.xml @@ -23,21 +23,25 @@ > <ImageView android:id="@+id/conversation_face_pile_top" - android:layout_width="36dp" - android:layout_height="36dp" + android:layout_width="@dimen/messaging_avatar_size" + android:layout_height="@dimen/messaging_avatar_size" android:scaleType="centerCrop" android:layout_gravity="end|top" /> <FrameLayout - android:id="@+id/conversation_face_pile_bottom_background" - android:layout_width="40dp" - android:layout_height="40dp" - android:layout_gravity="start|bottom" - android:background="@drawable/conversation_badge_background"> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="start|bottom"> + <ImageView + android:id="@+id/conversation_face_pile_bottom_background" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/conversation_badge_background" + /> <ImageView android:id="@+id/conversation_face_pile_bottom" - android:layout_width="36dp" - android:layout_height="36dp" + android:layout_width="@dimen/messaging_avatar_size" + android:layout_height="@dimen/messaging_avatar_size" android:scaleType="centerCrop" android:layout_gravity="center" /> diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml index 7bf13ec4ad65..7cadecbbdb91 100644 --- a/core/res/res/layout/notification_template_material_conversation.xml +++ b/core/res/res/layout/notification_template_material_conversation.xml @@ -25,6 +25,7 @@ > <FrameLayout + android:id="@+id/conversation_icon_container" android:layout_width="@dimen/conversation_content_start" android:layout_height="wrap_content" android:gravity="start|top" @@ -175,7 +176,7 @@ </LinearLayout> <!-- App Name --> - <TextView + <com.android.internal.widget.ObservableTextView android:id="@+id/app_name_text" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml index af803fcf09cc..c7e1a21aa642 100644 --- a/core/res/res/layout/resolver_empty_states.xml +++ b/core/res/res/layout/resolver_empty_states.xml @@ -22,11 +22,11 @@ android:orientation="vertical" android:gravity="center_horizontal" android:visibility="gone" + android:paddingTop="48dp" android:paddingStart="24dp" android:paddingEnd="24dp"> <ImageView android:id="@+id/resolver_empty_state_icon" - android:layout_marginTop="48dp" android:layout_width="24dp" android:layout_height="24dp" android:layout_centerHorizontal="true" /> @@ -57,11 +57,11 @@ android:text="@string/resolver_switch_on_work" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="@null" android:fontFamily="@string/config_headlineFontFamilyMedium" android:textSize="14sp" android:textColor="?attr/colorAccent" - android:layout_centerHorizontal="true" /> + android:layout_centerHorizontal="true" + android:background="@drawable/resolver_turn_on_work_button_ripple_background"/> <ProgressBar android:id="@+id/resolver_empty_state_progress" style="@style/Widget.Material.Light.ProgressBar" diff --git a/core/res/res/values-h480dp/bools.xml b/core/res/res/values-h480dp/bools.xml index 65e3ae6e71ec..7896d9bfe2b1 100644 --- a/core/res/res/values-h480dp/bools.xml +++ b/core/res/res/values-h480dp/bools.xml @@ -16,5 +16,5 @@ --> <resources> - <bool name="sharesheet_show_content_preview">true</bool> + <bool name="resolver_landscape_phone">false</bool> </resources>
\ No newline at end of file diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index d6e200afc029..2a41542d38e4 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1827,19 +1827,14 @@ <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}. --> + <!-- @hide no longer used, kept to preserve padding --> <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/bools.xml b/core/res/res/values/bools.xml index c5127dccdae7..fe296c704095 100644 --- a/core/res/res/values/bools.xml +++ b/core/res/res/values/bools.xml @@ -29,5 +29,5 @@ <p>The main purpose is for OEMs to customize the rendering of the lockscreen, setting this to true should come with customized drawables. --> <bool name="use_lock_pattern_drawable">false</bool> - <bool name="sharesheet_show_content_preview">false</bool> + <bool name="resolver_landscape_phone">true</bool> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index e3a73377e365..acda77f185fd 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2779,6 +2779,10 @@ <string name="config_systemUIServiceComponent" translatable="false" >com.android.systemui/com.android.systemui.SystemUIService</string> + <!-- Package handling Quick controls --> + <string name="config_controlsPackage" translatable="false" + >com.android.systemui</string> + <!-- Keyguard component --> <string name="config_keyguardComponent" translatable="false" >com.android.systemui/com.android.systemui.keyguard.KeyguardService</string> @@ -4424,4 +4428,7 @@ <!-- Set to true to enable the user switcher on the keyguard. --> <bool name="config_keyguardUserSwitcher">false</bool> + <!-- Set to true to make assistant show in front of the dream/screensaver. --> + <bool name="config_assistantOnTopOfDream">false</bool> + </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index f2e25998e0ae..7edb86b2db18 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -694,10 +694,31 @@ <dimen name="conversation_badge_side_margin">36dp</dimen> <!-- size of the notification badge when applied to the conversation icon --> <dimen name="conversation_icon_size_badged">20dp</dimen> - <!-- size of the notification badge when centered in a conversation --> - <dimen name="conversation_icon_size_centered">26dp</dimen> - <!-- margin on the top when the icon is centered for group conversations --> - <dimen name="conversation_icon_margin_top_centered">12dp</dimen> + <!-- size of the conversation avatar in an expanded group --> + <dimen name="conversation_avatar_size_group_expanded">@dimen/messaging_avatar_size</dimen> + <!-- size of the face pile icons --> + <dimen name="conversation_face_pile_avatar_size">@dimen/messaging_avatar_size</dimen> + <!-- size of the face pile icons when the group is expanded --> + <dimen name="conversation_face_pile_avatar_size_group_expanded">25dp</dimen> + <!-- Side margins of the conversation badge in relation to the conversation icon when the group is expanded--> + <dimen name="conversation_badge_side_margin_group_expanded">22dp</dimen> + <!-- Side margins of the conversation badge in relation to the conversation icon when the group is expanded--> + <dimen name="conversation_badge_side_margin_group_expanded_face_pile">18dp</dimen> + <!-- The width of the protection of the face pile layout--> + <dimen name="conversation_face_pile_protection_width">2dp</dimen> + <!-- The width of the protection of the face pile layout when expanded--> + <dimen name="conversation_face_pile_protection_width_expanded">1dp</dimen> + <!-- The padding of the expanded message container when the app name is gone--> + <dimen name="expanded_group_conversation_message_padding_without_app_name">14dp</dimen> + + <!-- The top padding of the conversation icon container in the regular state--> + <dimen name="conversation_icon_container_top_padding">12dp</dimen> + + <!-- The top padding of the conversation icon container when there's no app name present in a group--> + <dimen name="conversation_icon_container_top_padding_no_app_name">9dp</dimen> + + <!-- The top padding of the conversation icon container when the avatar is small--> + <dimen name="conversation_icon_container_top_padding_small_avatar">17.5dp</dimen> <!-- The padding of the conversation header when expanded. This is calculated from the expand button size + notification_content_margin_end --> <dimen name="conversation_header_expanded_padding_end">38dp</dimen> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index f02d54f48d29..738688b36257 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3014,8 +3014,9 @@ <!-- @hide @SystemApi --> <public name="minExtensionVersion" /> <public name="allowNativeHeapPointerTagging" /> - <public name="requestAutoRevokePermissionsExemption" /> - <public name="allowAutoRevokePermissionsExemption" /> + <!-- @hide no longer used, kept to preserve padding --> + <public name="allowAutoRevokePermissionsExemption"/> + <public name="autoRevokePermissions" /> <public name="preserveLegacyExternalStorage" /> <public name="mimeGroup" /> <public name="gwpAsanMode" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 7ec7eccd7f8d..2ae9d994484a 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -416,10 +416,10 @@ <!-- Content title for a notification. This notification indicates that the device owner has changed the location settings. [CHAR LIMIT=NONE] --> - <string name="location_changed_notification_title">Location settings changed by your admin</string> + <string name="location_changed_notification_title">Apps can access your location</string> <!-- Content text for a notification. Tapping opens device location settings. [CHAR LIMIT=NONE] --> - <string name="location_changed_notification_text">Tap to see your location settings.</string> + <string name="location_changed_notification_text">Contact your IT admin to learn more</string> <!-- Feature Id for Country Detector. [CHAR LIMIT=NONE]--> <string name="country_detector">Country Detector</string> @@ -1138,17 +1138,17 @@ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_accessFineLocation">access precise location only in the foreground</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_accessFineLocation">This app can get your exact location only when it is in the foreground. Location services must be turned on and available on your device for the app to be able to use them. This may increase battery consumption.</string> + <string name="permdesc_accessFineLocation">This app can get your precise location from location services while the app is in use. Location services for your device must be turned on for the app to get location. This may increase battery usage.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_accessCoarseLocation">access approximate location only in the foreground</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_accessCoarseLocation">This app can get your approximate location only when it is in the foreground. Location services must be turned on and available on your device for the app to be able to use them.</string> + <string name="permdesc_accessCoarseLocation">This app can get your approximate location from location services while the app is in use. Location services for your device must be turned on for the app to get location.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_accessBackgroundLocation">access location in the background</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_accessBackgroundLocation">This app can access location while running in the background, in addition to foreground location access.</string> + <string name="permdesc_accessBackgroundLocation">This app can access location at any time, even while the app is not in use.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_modifyAudioSettings">change your audio settings</string> @@ -5482,11 +5482,6 @@ <!-- Error message. This text lets the user know that their current personal apps can't open this specific content. [CHAR LIMIT=NONE] --> <string name="resolver_no_personal_apps_available_resolve">No personal apps can open this content</string> - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> - <string name="permlab_accessCallAudio">Record or play audio in telephony calls</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> - <string name="permdesc_accessCallAudio">Allows this app, when assigned as default dialer application, to record or play audio in telephony calls.</string> - <!-- Icc depersonalization related strings --> <!-- Label text for PIN entry widget on SIM Network Depersonalization panel [CHAR LIMIT=none] --> <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY">SIM network unlock PIN</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 163501a5985e..04c6a41833df 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -365,6 +365,7 @@ <java-symbol type="bool" name="config_hasRecents" /> <java-symbol type="string" name="config_recentsComponentName" /> <java-symbol type="string" name="config_systemUIServiceComponent" /> + <java-symbol type="string" name="config_controlsPackage" /> <java-symbol type="string" name="config_screenRecorderComponent" /> <java-symbol type="string" name="config_somnambulatorComponent" /> <java-symbol type="string" name="config_screenshotServiceComponent" /> @@ -3874,15 +3875,25 @@ <java-symbol type="id" name="conversation_text" /> <java-symbol type="id" name="message_icon_container" /> <java-symbol type="id" name="conversation_image_message_container" /> + <java-symbol type="id" name="conversation_icon_container" /> <java-symbol type="dimen" name="conversation_expand_button_top_margin_expanded" /> <java-symbol type="dimen" name="messaging_group_singleline_sender_padding_end" /> <java-symbol type="dimen" name="conversation_badge_side_margin" /> - <java-symbol type="dimen" name="conversation_icon_size_badged" /> - <java-symbol type="dimen" name="conversation_icon_size_centered" /> - <java-symbol type="dimen" name="conversation_icon_margin_top_centered" /> + <java-symbol type="dimen" name="conversation_avatar_size" /> + <java-symbol type="dimen" name="conversation_avatar_size_group_expanded" /> + <java-symbol type="dimen" name="conversation_face_pile_avatar_size" /> + <java-symbol type="dimen" name="conversation_face_pile_avatar_size_group_expanded" /> + <java-symbol type="dimen" name="conversation_face_pile_protection_width" /> + <java-symbol type="dimen" name="conversation_face_pile_protection_width_expanded" /> + <java-symbol type="dimen" name="conversation_badge_side_margin_group_expanded" /> + <java-symbol type="dimen" name="conversation_badge_side_margin_group_expanded_face_pile" /> <java-symbol type="dimen" name="conversation_content_start" /> + <java-symbol type="dimen" name="expanded_group_conversation_message_padding_without_app_name" /> <java-symbol type="dimen" name="messaging_layout_margin_end" /> <java-symbol type="dimen" name="conversation_header_expanded_padding_end" /> + <java-symbol type="dimen" name="conversation_icon_container_top_padding" /> + <java-symbol type="dimen" name="conversation_icon_container_top_padding_small_avatar" /> + <java-symbol type="dimen" name="conversation_icon_container_top_padding_no_app_name" /> <java-symbol type="layout" name="notification_template_material_conversation" /> <java-symbol type="layout" name="conversation_face_pile_layout" /> @@ -3919,7 +3930,7 @@ <java-symbol type="dimen" name="resolver_empty_state_height" /> <java-symbol type="dimen" name="resolver_empty_state_height_with_tabs" /> <java-symbol type="dimen" name="resolver_max_collapsed_height_with_tabs" /> - <java-symbol type="bool" name="sharesheet_show_content_preview" /> + <java-symbol type="bool" name="resolver_landscape_phone" /> <java-symbol type="dimen" name="resolver_tab_text_size" /> <!-- Toast message for background started foreground service while-in-use permission restriction feature --> @@ -3943,4 +3954,7 @@ <!-- Set to true to enable the user switcher on the keyguard. --> <java-symbol type="bool" name="config_keyguardUserSwitcher" /> + + <!-- Set to true to make assistant show in front of the dream/screensaver. --> + <java-symbol type="bool" name="config_assistantOnTopOfDream"/> </resources> 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/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java index 79b803a0cda6..0cd0643ee8c0 100644 --- a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java +++ b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java @@ -25,13 +25,13 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.graphics.Color; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.util.Log; -import android.util.Size; import android.view.Display; import android.view.Gravity; import android.view.View; @@ -119,15 +119,15 @@ public class TestService extends Service { @Override public void showApplicationOverlay() throws RemoteException { final WindowManager wm = mOverlayContext.getSystemService(WindowManager.class); - final Size size = wm.getCurrentWindowMetrics().getSize(); + final Rect bounds = wm.getCurrentWindowMetrics().getBounds(); final WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams( TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); - wmlp.width = size.getWidth() / 2; - wmlp.height = size.getHeight() / 2; + wmlp.width = bounds.width() / 2; + wmlp.height = bounds.height() / 2; wmlp.gravity = Gravity.CENTER | Gravity.LEFT; wmlp.setTitle(TAG); diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java index 1737bd0fa20b..f48e66681cc7 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,10 +237,28 @@ public class ContentResolverTest { } @Test + public void testGetType_providerException() { + String type = + mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote/error")); + assertThat(type).isNull(); + } + + @Test public void testCanonicalize() { Uri canonical = mResolver.canonicalize( Uri.parse("content://android.content.FakeProviderRemote/something")); assertThat(canonical).isEqualTo( Uri.parse("content://android.content.FakeProviderRemote/canonical")); } + + @Test + public void testCanonicalize_providerException() { + try { + mResolver.canonicalize( + Uri.parse("content://android.content.FakeProviderRemote/error")); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected + } + } } diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java index 1d7ba5d9be46..8bc56607f555 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"; } @@ -57,6 +60,9 @@ public class FakeProviderRemote extends ContentProvider { @Override public Uri canonicalize(Uri uri) { + if (uri.getPath() != null && uri.getPath().contains("error")) { + throw new IllegalArgumentException("Expected exception"); + } return new Uri.Builder().scheme(uri.getScheme()).authority(uri.getAuthority()) .appendPath("canonical").build(); } 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/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java index 13000e943141..f4ebe2f9a755 100644 --- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java +++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java @@ -64,8 +64,7 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) public class ControlProviderServiceTest { - private static final ComponentName TEST_SYSUI_COMPONENT = - ComponentName.unflattenFromString("sysui/.test.cls"); + private static final String TEST_CONTROLS_PACKAGE = "sysui"; private static final ComponentName TEST_COMPONENT = ComponentName.unflattenFromString("test.pkg/.test.cls"); @@ -97,8 +96,8 @@ public class ControlProviderServiceTest { when(mSubscriber.asBinder()).thenCallRealMethod(); when(mSubscriber.queryLocalInterface(any())).thenReturn(mSubscriber); - when(mResources.getString(com.android.internal.R.string.config_systemUIServiceComponent)) - .thenReturn(TEST_SYSUI_COMPONENT.flattenToString()); + when(mResources.getString(com.android.internal.R.string.config_controlsPackage)) + .thenReturn(TEST_CONTROLS_PACKAGE); when(mContext.getResources()).thenReturn(mResources); Bundle b = new Bundle(); @@ -252,7 +251,7 @@ public class ControlProviderServiceTest { eq(Manifest.permission.BIND_CONTROLS)); Intent intent = mIntentArgumentCaptor.getValue(); assertEquals(ControlsProviderService.ACTION_ADD_CONTROL, intent.getAction()); - assertEquals(TEST_SYSUI_COMPONENT.getPackageName(), intent.getPackage()); + assertEquals(TEST_CONTROLS_PACKAGE, intent.getPackage()); assertEquals(TEST_COMPONENT, intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME)); assertTrue(equals(control, intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL))); diff --git a/core/tests/coretests/src/android/util/GridScenario.java b/core/tests/coretests/src/android/util/GridScenario.java index 4809a213ab48..e7ee1cd59c7c 100644 --- a/core/tests/coretests/src/android/util/GridScenario.java +++ b/core/tests/coretests/src/android/util/GridScenario.java @@ -233,7 +233,7 @@ public abstract class GridScenario extends Activity { // turn off title bar requestWindowFeature(Window.FEATURE_NO_TITLE); - mScreenHeight = getWindowManager().getCurrentWindowMetrics().getSize().getHeight(); + mScreenHeight = getWindowManager().getCurrentWindowMetrics().getBounds().height(); final Params params = new Params(); init(params); diff --git a/core/tests/coretests/src/android/util/ListScenario.java b/core/tests/coretests/src/android/util/ListScenario.java index d4e5a438d855..74dc4b4b34a1 100644 --- a/core/tests/coretests/src/android/util/ListScenario.java +++ b/core/tests/coretests/src/android/util/ListScenario.java @@ -306,7 +306,7 @@ public abstract class ListScenario extends Activity { requestWindowFeature(Window.FEATURE_NO_TITLE); - mScreenHeight = getWindowManager().getCurrentWindowMetrics().getSize().getHeight(); + mScreenHeight = getWindowManager().getCurrentWindowMetrics().getBounds().height(); final Params params = createParams(); init(params); diff --git a/core/tests/coretests/src/android/util/ScrollViewScenario.java b/core/tests/coretests/src/android/util/ScrollViewScenario.java index 2c0aa7362dfa..ab1a642a9327 100644 --- a/core/tests/coretests/src/android/util/ScrollViewScenario.java +++ b/core/tests/coretests/src/android/util/ScrollViewScenario.java @@ -239,7 +239,7 @@ public abstract class ScrollViewScenario extends Activity { // for test stability, turn off title bar requestWindowFeature(Window.FEATURE_NO_TITLE); - int screenHeight = getWindowManager().getCurrentWindowMetrics().getSize().getHeight() + int screenHeight = getWindowManager().getCurrentWindowMetrics().getBounds().height() - 25; mLinearLayout = new LinearLayout(this); mLinearLayout.setOrientation(LinearLayout.VERTICAL); diff --git a/core/tests/coretests/src/android/view/BigCache.java b/core/tests/coretests/src/android/view/BigCache.java index e465a859218a..3038e79abd72 100644 --- a/core/tests/coretests/src/android/view/BigCache.java +++ b/core/tests/coretests/src/android/view/BigCache.java @@ -17,8 +17,8 @@ package android.view; import android.app.Activity; +import android.graphics.Rect; import android.os.Bundle; -import android.util.Size; import android.widget.LinearLayout; import android.widget.ScrollView; @@ -39,9 +39,9 @@ public class BigCache extends Activity { ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); final int cacheSize = ViewConfiguration.getMaximumDrawingCacheSize(); - final Size windowSize = getWindowManager().getCurrentWindowMetrics().getSize(); - final int screenWidth = windowSize.getWidth(); - final int screenHeight = windowSize.getHeight(); + final Rect windowBounds = getWindowManager().getCurrentWindowMetrics().getBounds(); + final int screenWidth = windowBounds.width(); + final int screenHeight = windowBounds.height(); final View tiny = new View(this); tiny.setId(R.id.a); diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java index 7f0e0d2f54c7..03aba25bf9f7 100644 --- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java @@ -74,7 +74,7 @@ public class ImeInsetsSourceConsumerTest { false, new DisplayCutout( Insets.of(10, 10, 10, 10), rect, rect, rect, rect), - rect, rect, SOFT_INPUT_ADJUST_RESIZE, 0); + SOFT_INPUT_ADJUST_RESIZE, 0); mImeConsumer = new ImeInsetsSourceConsumer( new InsetsState(), Transaction::new, mController); }); diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index b449bb00a85d..cbb379bf8207 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -21,6 +21,7 @@ import static android.view.InsetsController.ANIMATION_TYPE_NONE; import static android.view.InsetsController.ANIMATION_TYPE_SHOW; import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED; import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY; +import static android.view.InsetsState.ITYPE_CAPTION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; @@ -31,6 +32,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; @@ -87,7 +89,6 @@ import java.util.concurrent.CountDownLatch; @Presubmit @RunWith(AndroidJUnit4.class) public class InsetsControllerTest { - private InsetsController mController; private SurfaceSession mSession = new SurfaceSession(); private SurfaceControl mLeash; @@ -162,7 +163,7 @@ public class InsetsControllerTest { false, new DisplayCutout( Insets.of(10, 10, 10, 10), rect, rect, rect, rect), - rect, rect, SOFT_INPUT_ADJUST_RESIZE, 0); + SOFT_INPUT_ADJUST_RESIZE, 0); mController.onFrameChanged(new Rect(0, 0, 100, 100)); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -638,6 +639,52 @@ 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(); + } + + @Test + public void testCaptionInsetsStateAssemble() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + mController.onFrameChanged(new Rect(0, 0, 100, 300)); + final InsetsState state = new InsetsState(mController.getState(), true); + final Rect captionFrame = new Rect(0, 0, 100, 100); + mController.setCaptionInsetsHeight(100); + mController.onStateChanged(state); + final InsetsState currentState = new InsetsState(mController.getState()); + // The caption bar source should be synced with the info in mAttachInfo. + assertEquals(captionFrame, currentState.peekSource(ITYPE_CAPTION_BAR).getFrame()); + assertTrue(currentState.equals(state, true /* excludingCaptionInsets*/)); + mController.setCaptionInsetsHeight(0); + mController.onStateChanged(state); + // The caption bar source should not be there at all, because we don't add empty + // caption to the state from the server. + assertNull(mController.getState().peekSource(ITYPE_CAPTION_BAR)); + }); + } + 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/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 1d8e0a3186e8..2884777fc997 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -73,7 +73,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_IME).setVisible(true); SparseIntArray typeSideMap = new SparseIntArray(); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, - false, DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, 0, typeSideMap); + false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, typeSideMap); assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets()); assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all())); assertEquals(ISIDE_TOP, typeSideMap.get(ITYPE_STATUS_BAR)); @@ -92,7 +92,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_IME).setFrame(new Rect(0, 100, 100, 300)); mState.getSource(ITYPE_IME).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, - false, DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, 0, null); + false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, null); assertEquals(100, insets.getStableInsetBottom()); assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(Type.systemBars())); assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets()); @@ -111,7 +111,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300)); mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, - false, DisplayCutout.NO_CUTOUT, null, null, 0, 0, null); + false, DisplayCutout.NO_CUTOUT, 0, 0, null); assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets()); assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars())); assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars())); @@ -127,7 +127,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300)); mState.getSource(ITYPE_IME).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, - false, DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING, 0, null); + false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, null); assertEquals(0, insets.getSystemWindowInsetBottom()); assertEquals(100, insets.getInsets(ime()).bottom); assertTrue(insets.isVisible(ime())); @@ -143,16 +143,45 @@ public class InsetsStateTest { mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300)); mState.getSource(ITYPE_IME).setVisible(true); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, - false, DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING, + false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, SYSTEM_UI_FLAG_LAYOUT_STABLE, null); assertEquals(100, insets.getSystemWindowInsetTop()); insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false, - DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_NOTHING, + DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0 /* legacySystemUiFlags */, null); assertEquals(0, insets.getSystemWindowInsetTop()); } } + + @Test + public void testCalculateInsets_captionStatusBarOverlap() throws Exception { + try (InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(ITYPE_STATUS_BAR).setVisible(true); + mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300)); + mState.getSource(ITYPE_CAPTION_BAR).setVisible(true); + + Rect visibleInsets = mState.calculateVisibleInsets( + new Rect(0, 0, 100, 400), SOFT_INPUT_ADJUST_NOTHING); + assertEquals(new Rect(0, 300, 0, 0), visibleInsets); + } + } + + @Test + public void testCalculateInsets_captionBarOffset() throws Exception { + try (InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300)); + mState.getSource(ITYPE_CAPTION_BAR).setVisible(true); + + Rect visibleInsets = mState.calculateVisibleInsets( + new Rect(0, 0, 150, 400), SOFT_INPUT_ADJUST_NOTHING); + assertEquals(new Rect(0, 300, 0, 0), visibleInsets); + } + } + @Test public void testStripForDispatch() { mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100)); @@ -161,7 +190,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_IME).setVisible(true); mState.removeSource(ITYPE_IME); WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false, - DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, 0, null); + DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, null); assertEquals(0, insets.getSystemWindowInsetBottom()); } @@ -255,7 +284,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300)); mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true); Rect visibleInsets = mState.calculateVisibleInsets( - new Rect(0, 0, 100, 300), new Rect(), SOFT_INPUT_ADJUST_PAN); + new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_PAN); assertEquals(new Rect(0, 100, 0, 100), visibleInsets); } } @@ -273,7 +302,7 @@ public class InsetsStateTest { mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300)); mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true); Rect visibleInsets = mState.calculateVisibleInsets( - new Rect(0, 0, 100, 300), new Rect(), SOFT_INPUT_ADJUST_NOTHING); + new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_NOTHING); assertEquals(new Rect(0, 100, 0, 0), visibleInsets); } } diff --git a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java index 039387c85b11..46e55faae8b6 100644 --- a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java +++ b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java @@ -22,7 +22,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import android.util.Size; +import android.graphics.Rect; import android.widget.TextView; import androidx.test.filters.LargeTest; @@ -55,9 +55,9 @@ public class ScaleGestureDetectorTest { // Specify start and end coordinates with respect to the window size. final WindowManager wm = mScaleGestureActivity.getSystemService(WindowManager.class); - final Size windowSize = wm.getCurrentWindowMetrics().getSize(); - final int windowWidth = windowSize.getWidth(); - final int windowHeight = windowSize.getHeight(); + final Rect windowBounds = wm.getCurrentWindowMetrics().getBounds(); + final int windowWidth = windowBounds.width(); + final int windowHeight = windowBounds.height(); // Obtain coordinates to perform pinch and zoom from the center, to 75% of the display. final int centerX = windowWidth / 2; diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java index fa6886075bfd..74524bf6d76f 100644 --- a/core/tests/coretests/src/android/view/WindowMetricsTest.java +++ b/core/tests/coretests/src/android/view/WindowMetricsTest.java @@ -22,10 +22,10 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static org.junit.Assert.assertTrue; import android.content.Context; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Handler; import android.platform.test.annotations.Presubmit; -import android.util.Size; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; @@ -87,10 +87,12 @@ public class WindowMetricsTest { private static void verifyMetricsSanity(WindowMetrics currentMetrics, WindowMetrics maxMetrics) { - Size currentSize = currentMetrics.getSize(); - Size maxSize = maxMetrics.getSize(); + Rect currentBounds = currentMetrics.getBounds(); + Rect maxBounds = maxMetrics.getBounds(); - assertTrue(maxSize.getWidth() >= currentSize.getWidth()); - assertTrue(maxSize.getHeight() >= currentSize.getHeight()); + assertTrue(maxBounds.width() >= currentBounds.width()); + assertTrue(maxBounds.height() >= currentBounds.height()); + assertTrue(maxBounds.left >= 0); + assertTrue(maxBounds.top >= 0); } } 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/view/menu/ContextMenuTest.java b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java index d5825e20163c..4bd9ccd8d4d3 100644 --- a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java +++ b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java @@ -16,9 +16,9 @@ package android.view.menu; +import android.graphics.Rect; import android.test.ActivityInstrumentationTestCase; import android.util.PollingCheck; -import android.util.Size; import android.view.View; import android.view.WindowManager; import android.widget.espresso.ContextMenuUtils; @@ -81,8 +81,8 @@ public class ContextMenuTest extends ActivityInstrumentationTestCase<ContextMenu */ private int getMinScreenDimension() { final WindowManager windowManager = getActivity().getSystemService(WindowManager.class); - final Size maxWindowSize = windowManager.getMaximumWindowMetrics().getSize(); - return Math.min(maxWindowSize.getWidth(), maxWindowSize.getHeight()); + final Rect maxWindowBounds = windowManager.getMaximumWindowMetrics().getBounds(); + return Math.min(maxWindowBounds.width(), maxWindowBounds.height()); } /** diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java index f81964c9cdf9..4bfffd72d835 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; @@ -36,7 +37,11 @@ import static org.junit.Assert.assertTrue; import android.app.Activity; import android.app.Instrumentation; import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; 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; @@ -44,7 +49,7 @@ import android.view.MotionEvent; import android.view.View; import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; +import androidx.test.filters.MediumTest; import androidx.test.filters.Suppress; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; @@ -63,7 +68,8 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicLong; @RunWith(AndroidJUnit4.class) -@SmallTest +@MediumTest +@Presubmit public class EditorCursorDragTest { private static final String LOG_TAG = EditorCursorDragTest.class.getSimpleName(); @@ -488,6 +494,39 @@ public class EditorCursorDragTest { simulateDrag(tv, events, true); } + @Suppress // b/152574363 + @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" diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java index 3dc001d68a02..ec75e40f1334 100644 --- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java +++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java @@ -22,6 +22,7 @@ import static junit.framework.Assert.assertTrue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import android.platform.test.annotations.Presubmit; import android.view.InputDevice; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -36,6 +37,7 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) @SmallTest +@Presubmit public class EditorTouchStateTest { private EditorTouchState mTouchState; diff --git a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java index 8e90a824c873..d51cc328e4fb 100644 --- a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java +++ b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java @@ -111,7 +111,7 @@ public class ListOfInternalSelectionViews extends Activity { protected void onCreate(Bundle icicle) { super.onCreate(icicle); - mScreenHeight = getWindowManager().getCurrentWindowMetrics().getSize().getHeight(); + mScreenHeight = getWindowManager().getCurrentWindowMetrics().getBounds().height(); Bundle extras = getIntent().getExtras(); if (extras != null) { diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java index fd1dbfc63708..5cedd13533e7 100644 --- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java +++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java @@ -106,8 +106,8 @@ public class GridTouchVerticalSpacingStackFromBottomTest extends ActivityInstrum int firstTop = firstChild.getTop(); - int windowHeight = mActivity.getWindowManager().getCurrentWindowMetrics().getSize() - .getHeight(); + int windowHeight = mActivity.getWindowManager().getCurrentWindowMetrics().getBounds() + .height(); int distance = TouchUtils.dragViewBy(this, firstChild, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, (int) (windowHeight * 0.75f)); diff --git a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java index e6195b147045..5cca766e1b1e 100644 --- a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java +++ b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java @@ -66,7 +66,7 @@ public class AdjacentListsWithAdjacentISVsInside extends Activity { super.onCreate(savedInstanceState); final int desiredHeight = - (int) (0.8 * getWindowManager().getCurrentWindowMetrics().getSize().getHeight()); + (int) (0.8 * getWindowManager().getCurrentWindowMetrics().getBounds().height()); mLeftListView = new ListView(this); mLeftListView.setAdapter(new AdjacentISVAdapter(desiredHeight)); 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/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index d8bd81e80ed5..59bdf3dad43e 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -27,6 +27,10 @@ applications that come with the platform <permission name="android.permission.INTERACT_ACROSS_USERS" /> </privapp-permissions> + <privapp-permissions package="com.android.angle"> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> + <privapp-permissions package="com.android.apps.tag"> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> </privapp-permissions> @@ -255,6 +259,7 @@ applications that come with the platform </privapp-permissions> <privapp-permissions package="com.android.networkstack.tethering"> + <permission name="android.permission.BLUETOOTH_PRIVILEGED" /> <permission name="android.permission.MANAGE_USB"/> <permission name="android.permission.MODIFY_PHONE_STATE"/> <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/> @@ -398,6 +403,10 @@ applications that come with the platform <permission name="android.permission.REGISTER_STATS_PULL_ATOM"/> <!-- Permission required for testing system audio effect APIs. --> <permission name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"/> + <!-- Permissions required for CTS test - TunerTest --> + <permission name="android.permission.ACCESS_TV_DESCRAMBLER" /> + <permission name="android.permission.ACCESS_TV_TUNER" /> + <permission name="android.permission.TUNER_RESOURCE_ACCESS" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> 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/androidfw/Android.bp b/libs/androidfw/Android.bp index f87f98a59a12..02c85aa34f4b 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -44,7 +44,6 @@ cc_library { "AttributeResolution.cpp", "ChunkIterator.cpp", "ConfigDescription.cpp", - "DynamicLibManager.cpp", "Idmap.cpp", "LoadedArsc.cpp", "Locale.cpp", diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index f20e18453f8b..eaf452b5fa71 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -25,7 +25,6 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" -#include "androidfw/DynamicLibManager.h" #include "androidfw/ResourceUtils.h" #include "androidfw/Util.h" #include "utils/ByteOrder.h" @@ -67,12 +66,7 @@ struct FindEntryResult { StringPoolRef entry_string_ref; }; -AssetManager2::AssetManager2() : dynamic_lib_manager_(std::make_unique<DynamicLibManager>()) { - memset(&configuration_, 0, sizeof(configuration_)); -} - -AssetManager2::AssetManager2(DynamicLibManager* dynamic_lib_manager) - : dynamic_lib_manager_(dynamic_lib_manager) { +AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -91,6 +85,9 @@ void AssetManager2::BuildDynamicRefTable() { package_groups_.clear(); package_ids_.fill(0xff); + // A mapping from apk assets path to the runtime package id of its first loaded package. + std::unordered_map<std::string, uint8_t> apk_assets_package_ids; + // Overlay resources are not directly referenced by an application so their resource ids // can change throughout the application's lifetime. Assign overlay package ids last. std::vector<const ApkAssets*> sorted_apk_assets(apk_assets_); @@ -98,37 +95,25 @@ void AssetManager2::BuildDynamicRefTable() { return !a->IsOverlay(); }); - std::unordered_map<std::string, uint8_t> apk_assets_package_ids; - std::unordered_map<std::string, uint8_t> package_name_package_ids; - - // Assign stable package ids to application packages. - uint8_t next_available_package_id = 0U; - for (const auto& apk_assets : sorted_apk_assets) { - for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) { - uint8_t package_id = package->GetPackageId(); - if (package->IsOverlay()) { - package_id = GetDynamicLibManager()->FindUnassignedId(next_available_package_id); - next_available_package_id = package_id + 1; - } else if (package->IsDynamic()) { - package_id = GetDynamicLibManager()->GetAssignedId(package->GetPackageName()); - } - - // Map the path of the apk assets to the package id of its first loaded package. - apk_assets_package_ids[apk_assets->GetPath()] = package_id; - - // Map the package name of the package to the first loaded package with that package id. - package_name_package_ids[package->GetPackageName()] = package_id; - } + // The assets cookie must map to the position of the apk assets in the unsorted apk assets list. + std::unordered_map<const ApkAssets*, ApkAssetsCookie> apk_assets_cookies; + apk_assets_cookies.reserve(apk_assets_.size()); + for (size_t i = 0, n = apk_assets_.size(); i < n; i++) { + apk_assets_cookies[apk_assets_[i]] = static_cast<ApkAssetsCookie>(i); } - const int apk_assets_count = apk_assets_.size(); - for (int i = 0; i < apk_assets_count; i++) { - const auto& apk_assets = apk_assets_[i]; - for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) { - const auto package_id_entry = package_name_package_ids.find(package->GetPackageName()); - CHECK(package_id_entry != package_name_package_ids.end()) - << "no package id assgined to package " << package->GetPackageName(); - const uint8_t package_id = package_id_entry->second; + // 0x01 is reserved for the android package. + int next_package_id = 0x02; + for (const ApkAssets* apk_assets : sorted_apk_assets) { + const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); + for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) { + // Get the package ID or assign one if a shared library. + int package_id; + if (package->IsDynamic()) { + package_id = next_package_id++; + } else { + package_id = package->GetPackageId(); + } // Add the mapping for package ID to index if not present. uint8_t idx = package_ids_[package_id]; @@ -162,7 +147,7 @@ void AssetManager2::BuildDynamicRefTable() { target_package_group.overlays_.push_back( ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, overlay_table.get()), - static_cast<ApkAssetsCookie>(i)}); + apk_assets_cookies[apk_assets]}); } } @@ -174,7 +159,7 @@ void AssetManager2::BuildDynamicRefTable() { // Add the package and to the set of packages with the same ID. package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); - package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i)); + package_group->cookies_.push_back(apk_assets_cookies[apk_assets]); // Add the package name -> build time ID mappings. for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) { @@ -182,6 +167,8 @@ void AssetManager2::BuildDynamicRefTable() { package_group->dynamic_ref_table->mEntries.replaceValueFor( package_name, static_cast<uint8_t>(entry.package_id)); } + + apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id)); } } @@ -1329,16 +1316,6 @@ uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const return 0; } -DynamicLibManager* AssetManager2::GetDynamicLibManager() const { - auto dynamic_lib_manager = - std::get_if<std::unique_ptr<DynamicLibManager>>(&dynamic_lib_manager_); - if (dynamic_lib_manager) { - return (*dynamic_lib_manager).get(); - } else { - return *std::get_if<DynamicLibManager*>(&dynamic_lib_manager_); - } -} - std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); } diff --git a/libs/androidfw/DynamicLibManager.cpp b/libs/androidfw/DynamicLibManager.cpp deleted file mode 100644 index 895b7695bf26..000000000000 --- a/libs/androidfw/DynamicLibManager.cpp +++ /dev/null @@ -1,34 +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. - */ - -#include "androidfw/DynamicLibManager.h" - -namespace android { - -uint8_t DynamicLibManager::GetAssignedId(const std::string& library_package_name) { - auto lib_entry = shared_lib_package_ids_.find(library_package_name); - if (lib_entry != shared_lib_package_ids_.end()) { - return lib_entry->second; - } - - return shared_lib_package_ids_[library_package_name] = next_package_id_++; -} - -uint8_t DynamicLibManager::FindUnassignedId(uint8_t start_package_id) { - return (start_package_id < next_package_id_) ? next_package_id_ : start_package_id; -} - -} // namespace android diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index b2cec2a42994..e21abade99a4 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -27,7 +27,6 @@ #include "androidfw/ApkAssets.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" -#include "androidfw/DynamicLibManager.h" #include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" @@ -95,7 +94,6 @@ class AssetManager2 { }; AssetManager2(); - explicit AssetManager2(DynamicLibManager* dynamic_lib_manager); // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets // are not owned by the AssetManager, and must have a longer lifetime. @@ -126,6 +124,9 @@ class AssetManager2 { // This may be nullptr if the APK represented by `cookie` has no resource table. std::shared_ptr<const DynamicRefTable> GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; + // Retrieve the assigned package id of the package if loaded into this AssetManager + uint8_t GetAssignedPackageId(const LoadedPackage* package) const; + // Returns a string representation of the overlayable API of a package. bool GetOverlayablesToString(const android::StringPiece& package_name, std::string* out) const; @@ -370,11 +371,6 @@ class AssetManager2 { // been seen while traversing bag parents. const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids); - // Retrieve the assigned package id of the package if loaded into this AssetManager - uint8_t GetAssignedPackageId(const LoadedPackage* package) const; - - DynamicLibManager* GetDynamicLibManager() const; - // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector<const ApkAssets*> apk_assets_; @@ -393,9 +389,6 @@ class AssetManager2 { // may need to be purged. ResTable_config configuration_; - // Component responsible for assigning package ids to shared libraries. - std::variant<std::unique_ptr<DynamicLibManager>, DynamicLibManager*> dynamic_lib_manager_; - // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_; diff --git a/libs/androidfw/include/androidfw/DynamicLibManager.h b/libs/androidfw/include/androidfw/DynamicLibManager.h deleted file mode 100644 index 1ff7079573d2..000000000000 --- a/libs/androidfw/include/androidfw/DynamicLibManager.h +++ /dev/null @@ -1,48 +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. - */ - -#ifndef ANDROIDFW_DYNAMICLIBMANAGER_H -#define ANDROIDFW_DYNAMICLIBMANAGER_H - -#include <string> -#include <unordered_map> - -#include "android-base/macros.h" - -namespace android { - -// Manages assigning resource ids for dynamic resources. -class DynamicLibManager { - public: - DynamicLibManager() = default; - - // Retrieves the assigned package id for the library. - uint8_t GetAssignedId(const std::string& library_package_name); - - // Queries in ascending order for the first available package id that is not currently assigned to - // a library. - uint8_t FindUnassignedId(uint8_t start_package_id); - - private: - DISALLOW_COPY_AND_ASSIGN(DynamicLibManager); - - uint8_t next_package_id_ = 0x02; - std::unordered_map<std::string, uint8_t> shared_lib_package_ids_; -}; - -} // namespace android - -#endif //ANDROIDFW_DYNAMICLIBMANAGER_H diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h index 8891512958f0..64924f433245 100644 --- a/libs/androidfw/include/androidfw/MutexGuard.h +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -47,8 +47,7 @@ class Guarded { static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer"); public: - template <typename ...Args> - explicit Guarded(Args&& ...args) : guarded_(std::forward<Args>(args)...) { + explicit Guarded() : guarded_() { } template <typename U = T> diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index ac32699c6dfd..8c255d16fe1f 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -17,9 +17,9 @@ #include "androidfw/AssetManager2.h" #include "androidfw/AssetManager.h" -#include "android-base/logging.h" - #include "TestHelpers.h" +#include "android-base/file.h" +#include "android-base/logging.h" #include "androidfw/ResourceUtils.h" #include "data/appaslib/R.h" #include "data/basic/R.h" @@ -45,37 +45,43 @@ namespace android { class AssetManager2Test : public ::testing::Test { public: void SetUp() override { - basic_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + // Move to the test data directory so the idmap can locate the overlay APK. + std::string original_path = base::GetExecutableDirectory(); + chdir(GetTestDataPath().c_str()); + + basic_assets_ = ApkAssets::Load("basic/basic.apk"); ASSERT_NE(nullptr, basic_assets_); - basic_de_fr_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic_de_fr.apk"); + basic_de_fr_assets_ = ApkAssets::Load("basic/basic_de_fr.apk"); ASSERT_NE(nullptr, basic_de_fr_assets_); - style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + style_assets_ = ApkAssets::Load("styles/styles.apk"); ASSERT_NE(nullptr, style_assets_); - lib_one_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_one/lib_one.apk"); + lib_one_assets_ = ApkAssets::Load("lib_one/lib_one.apk"); ASSERT_NE(nullptr, lib_one_assets_); - lib_two_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_two/lib_two.apk"); + lib_two_assets_ = ApkAssets::Load("lib_two/lib_two.apk"); ASSERT_NE(nullptr, lib_two_assets_); - libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk"); + libclient_assets_ = ApkAssets::Load("libclient/libclient.apk"); ASSERT_NE(nullptr, libclient_assets_); - appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk", - PROPERTY_DYNAMIC); + appaslib_assets_ = ApkAssets::Load("appaslib/appaslib.apk", PROPERTY_DYNAMIC); ASSERT_NE(nullptr, appaslib_assets_); - system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", - PROPERTY_SYSTEM); + system_assets_ = ApkAssets::Load("system/system.apk", PROPERTY_SYSTEM); ASSERT_NE(nullptr, system_assets_); - app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk"); + app_assets_ = ApkAssets::Load("app/app.apk"); ASSERT_THAT(app_assets_, NotNull()); - overlayable_assets_ = ApkAssets::Load(GetTestDataPath() + "/overlayable/overlayable.apk"); + overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap"); + ASSERT_NE(nullptr, overlay_assets_); + + overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk"); ASSERT_THAT(overlayable_assets_, NotNull()); + chdir(original_path.c_str()); } protected: @@ -88,6 +94,7 @@ class AssetManager2Test : public ::testing::Test { std::unique_ptr<const ApkAssets> appaslib_assets_; std::unique_ptr<const ApkAssets> system_assets_; std::unique_ptr<const ApkAssets> app_assets_; + std::unique_ptr<const ApkAssets> overlay_assets_; std::unique_ptr<const ApkAssets> overlayable_assets_; }; @@ -216,23 +223,24 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); } -TEST_F(AssetManager2Test, AssignsUnchangingPackageIdToSharedLibrary) { - DynamicLibManager lib_manager; - AssetManager2 assetmanager(&lib_manager); +TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) { + AssetManager2 assetmanager; assetmanager.SetApkAssets( - {lib_one_assets_.get(), lib_two_assets_.get(), libclient_assets_.get()}); - - AssetManager2 assetmanager2(&lib_manager); - assetmanager2.SetApkAssets( - {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + {overlayable_assets_.get(), overlay_assets_.get(), lib_one_assets_.get()}); - uint32_t res_id = assetmanager.GetResourceId("com.android.lib_one:string/foo"); - ASSERT_NE(0U, res_id); + auto apk_assets = assetmanager.GetApkAssets(); + ASSERT_EQ(3, apk_assets.size()); + ASSERT_EQ(overlayable_assets_.get(), apk_assets[0]); + ASSERT_EQ(overlay_assets_.get(), apk_assets[1]); + ASSERT_EQ(lib_one_assets_.get(), apk_assets[2]); - uint32_t res_id_2 = assetmanager2.GetResourceId("com.android.lib_one:string/foo"); - ASSERT_NE(0U, res_id_2); + auto get_first_package_id = [&assetmanager](const ApkAssets* apkAssets) -> uint8_t { + return assetmanager.GetAssignedPackageId(apkAssets->GetLoadedArsc()->GetPackages()[0].get()); + }; - ASSERT_EQ(res_id, res_id_2); + ASSERT_EQ(get_first_package_id(overlayable_assets_.get()), 0x7f); + ASSERT_EQ(get_first_package_id(overlay_assets_.get()), 0x03); + ASSERT_EQ(get_first_package_id(lib_one_assets_.get()), 0x02); } TEST_F(AssetManager2Test, GetSharedLibraryResourceName) { @@ -770,7 +778,6 @@ TEST_F(AssetManager2Test, GetOverlayablesToString) { ASSERT_EQ(api.find("not_overlayable"), std::string::npos); ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"), std::string::npos); - } } // namespace android diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp index 512b8c439dcf..af6411011411 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -93,6 +93,7 @@ cc_library_shared { cc_test { name: "libincident_test", + test_config: "AndroidTest.xml", defaults: ["libincidentpriv_defaults"], test_suites: ["device-tests"], @@ -104,7 +105,6 @@ cc_test { srcs: [ "tests/IncidentReportArgs_test.cpp", "tests/IncidentReportRequest_test.cpp", - "tests/c_api_compile_test.c", ], shared_libs: [ diff --git a/libs/incident/AndroidTest.xml b/libs/incident/AndroidTest.xml new file mode 100644 index 000000000000..7c0b04471d13 --- /dev/null +++ b/libs/incident/AndroidTest.xml @@ -0,0 +1,28 @@ +<?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. +--> +<configuration description="Config for libincident_test"> + <option name="test-suite-tag" value="device-tests" /> + <option name="config-descriptor:metadata" key="component" value="misc" /> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="libincident_test->/data/local/tmp/libincident_test" /> + </target_preparer> + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="libincident_test" /> + </test> +</configuration> + diff --git a/libs/incident/TEST_MAPPING b/libs/incident/TEST_MAPPING new file mode 100644 index 000000000000..59ebe7664b5f --- /dev/null +++ b/libs/incident/TEST_MAPPING @@ -0,0 +1,10 @@ +{ + "presubmit": [ + { + "name": "libincident_test" + }, + { + "name": "GtsLibIncidentTests" + } + ] +} diff --git a/libs/incident/include/incident/incident_report.h b/libs/incident/include/incident/incident_report.h index 49fe5b9b73b4..4fbac9681214 100644 --- a/libs/incident/include/incident/incident_report.h +++ b/libs/incident/include/incident/incident_report.h @@ -18,16 +18,12 @@ * @file incident_report.h */ -#ifndef ANDROID_INCIDENT_INCIDENT_REPORT_H -#define ANDROID_INCIDENT_INCIDENT_REPORT_H +#pragma once #include <stdbool.h> +#include <stdint.h> #if __cplusplus -#include <set> -#include <string> -#include <vector> - extern "C" { #endif // __cplusplus @@ -125,68 +121,5 @@ int AIncidentReportArgs_takeReport(AIncidentReportArgs* args); #if __cplusplus } // extern "C" - -namespace android { -namespace os { - -class IncidentReportRequest { -public: - inline IncidentReportRequest() { - mImpl = AIncidentReportArgs_init(); - } - - inline IncidentReportRequest(const IncidentReportRequest& that) { - mImpl = AIncidentReportArgs_clone(that.mImpl); - } - - inline ~IncidentReportRequest() { - AIncidentReportArgs_delete(mImpl); - } - - inline AIncidentReportArgs* getImpl() { - return mImpl; - } - - inline void setAll(bool all) { - AIncidentReportArgs_setAll(mImpl, all); - } - - inline void setPrivacyPolicy(int privacyPolicy) { - AIncidentReportArgs_setPrivacyPolicy(mImpl, privacyPolicy); - } - - inline void addSection(int section) { - AIncidentReportArgs_addSection(mImpl, section); - } - - inline void setReceiverPackage(const std::string& pkg) { - AIncidentReportArgs_setReceiverPackage(mImpl, pkg.c_str()); - }; - - inline void setReceiverClass(const std::string& cls) { - AIncidentReportArgs_setReceiverClass(mImpl, cls.c_str()); - }; - - inline void addHeader(const std::vector<uint8_t>& headerProto) { - AIncidentReportArgs_addHeader(mImpl, headerProto.data(), headerProto.size()); - }; - - inline void addHeader(const uint8_t* buf, size_t size) { - AIncidentReportArgs_addHeader(mImpl, buf, size); - }; - - // returns a status_t - inline int takeReport() { - return AIncidentReportArgs_takeReport(mImpl); - } - -private: - AIncidentReportArgs* mImpl; -}; - -} // namespace os -} // namespace android - #endif // __cplusplus -#endif // ANDROID_INCIDENT_INCIDENT_REPORT_H diff --git a/libs/incident/tests/IncidentReportRequest_test.cpp b/libs/incident/tests/IncidentReportRequest_test.cpp index 6d218b6682a3..5619bb6c2220 100644 --- a/libs/incident/tests/IncidentReportRequest_test.cpp +++ b/libs/incident/tests/IncidentReportRequest_test.cpp @@ -17,9 +17,67 @@ #include <gtest/gtest.h> -namespace android { -namespace os { -namespace statsd { +#include <vector> +#include <string> + +using namespace std; +using namespace android::os; + +class IncidentReportRequest { +public: + inline IncidentReportRequest() { + mImpl = AIncidentReportArgs_init(); + } + + inline IncidentReportRequest(const IncidentReportRequest& that) { + mImpl = AIncidentReportArgs_clone(that.mImpl); + } + + inline ~IncidentReportRequest() { + AIncidentReportArgs_delete(mImpl); + } + + inline AIncidentReportArgs* getImpl() { + return mImpl; + } + + inline void setAll(bool all) { + AIncidentReportArgs_setAll(mImpl, all); + } + + inline void setPrivacyPolicy(int privacyPolicy) { + AIncidentReportArgs_setPrivacyPolicy(mImpl, privacyPolicy); + } + + inline void addSection(int section) { + AIncidentReportArgs_addSection(mImpl, section); + } + + inline void setReceiverPackage(const string& pkg) { + AIncidentReportArgs_setReceiverPackage(mImpl, pkg.c_str()); + }; + + inline void setReceiverClass(const string& cls) { + AIncidentReportArgs_setReceiverClass(mImpl, cls.c_str()); + }; + + inline void addHeader(const vector<uint8_t>& headerProto) { + AIncidentReportArgs_addHeader(mImpl, headerProto.data(), headerProto.size()); + }; + + inline void addHeader(const uint8_t* buf, size_t size) { + AIncidentReportArgs_addHeader(mImpl, buf, size); + }; + + // returns a status_t + inline int takeReport() { + return AIncidentReportArgs_takeReport(mImpl); + } + +private: + AIncidentReportArgs* mImpl; +}; + TEST(IncidentReportRequestTest, testWrite) { IncidentReportRequest request; @@ -60,6 +118,3 @@ TEST(IncidentReportRequestTest, testWrite) { EXPECT_EQ(headers, args->headers()); } -} // namespace statsd -} // namespace os -} // namespace android diff --git a/libs/incident/tests/c_api_compile_test.c b/libs/incident/tests/c_api_compile_test.c deleted file mode 100644 index e1620dfe3280..000000000000 --- a/libs/incident/tests/c_api_compile_test.c +++ /dev/null @@ -1,11 +0,0 @@ -#include <stdio.h> -#include <incident/incident_report.h> - -/* - * This file ensures that incident/incident_report.h actually compiles with C, - * since there is no other place in the tree that actually uses it from C. - */ -int not_called() { - return 0; -} - 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/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index 9950f05560d7..db2a1e8b6e7c 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -432,7 +432,7 @@ public final class AudioDeviceInfo { * @return An array of supported encapsulation modes for the device. This * may be an empty array if no encapsulation modes are supported. */ - public @NonNull int[] getEncapsulationModes() { + public @NonNull @AudioTrack.EncapsulationMode int[] getEncapsulationModes() { // Implement a getter in r-dev or r-tv-dev as needed. return new int[0]; // be careful of returning a copy of any internal data. } @@ -451,7 +451,7 @@ public final class AudioDeviceInfo { * @return An array of supported encapsulation metadata types for the device. This * may be an empty array if no metadata types are supported. */ - public @NonNull int[] getEncapsulationMetadataTypes() { + public @NonNull @AudioTrack.EncapsulationMetadataType int[] getEncapsulationMetadataTypes() { // Implement a getter in r-dev or r-tv-dev as needed. return new int[0]; // be careful of returning a copy of any internal data. } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 383202b20550..ba8f7e60e268 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 { @@ -4768,7 +4769,7 @@ public class AudioManager { * opened on that device. * * @param device an instance of {@link AudioDeviceInfo} returned from {@link getDevices()}. - * @param delayMs delay in milliseconds desired. This should be in range of {@code 0} + * @param delayMillis delay in milliseconds desired. This should be in range of {@code 0} * to the value returned by {@link #getMaxAdditionalOutputDeviceDelay()}. * @return true if successful, false if the device does not support output device delay * or the delay is not in range of {@link #getMaxAdditionalOutputDeviceDelay()}. @@ -4776,7 +4777,7 @@ public class AudioManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay( - @NonNull AudioDeviceInfo device, @IntRange(from = 0) int delayMs) { + @NonNull AudioDeviceInfo device, @IntRange(from = 0) long delayMillis) { Objects.requireNonNull(device); // Implement the setter in r-dev or r-tv-dev as needed. return false; @@ -4792,7 +4793,7 @@ public class AudioManager { */ @SystemApi @IntRange(from = 0) - public int getAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) { + public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) { Objects.requireNonNull(device); // Implement the getter in r-dev or r-tv-dev as needed. return 0; @@ -4810,7 +4811,7 @@ public class AudioManager { */ @SystemApi @IntRange(from = 0) - public int getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) { + public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) { Objects.requireNonNull(device); // Implement the getter in r-dev or r-tv-dev as needed. return 0; diff --git a/media/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java index 1a9517cafdde..c91ff0d099cf 100644 --- a/media/java/android/media/AudioMetadata.java +++ b/media/java/android/media/AudioMetadata.java @@ -79,96 +79,11 @@ public final class AudioMetadata { } /** - * A read only {@code Map} interface of {@link Key} value pairs. - * - * <p>Using a {@link Key} interface, the map looks up the corresponding value.</p> - */ - public interface ReadMap { - /** - * Returns true if the key exists in the map. - * - * @param key interface for requesting the value. - * @param <T> type of value. - * @return true if key exists in the Map. - */ - <T> boolean containsKey(@NonNull Key<T> key); - - /** - * Returns a copy of the map. - * - * This is intended for safe conversion between a {@link ReadMap} - * interface and a {@link Map} interface. - * Currently only simple objects are used for key values which - * means a shallow copy is sufficient. - * - * @return a Map copied from the existing map. - */ - @NonNull - Map dup(); // lint checker doesn't like clone(). - - /** - * Returns the value associated with the key. - * - * @param key interface for requesting the value. - * @param <T> type of value. - * @return returns the value of associated with key or null if it doesn't exist. - */ - @Nullable - <T> T get(@NonNull Key<T> key); - - /** - * Returns a {@code Set} of keys associated with the map. - * @hide - */ - @NonNull - Set<Key<?>> keySet(); - - /** - * Returns the number of elements in the map. - */ - int size(); - } - - /** - * A writeable {@link Map} interface of {@link Key} value pairs. - * This interface is not guaranteed to be thread-safe - * unless the supplier for the {@code Map} states it as thread safe. - */ - // TODO: Create a wrapper like java.util.Collections.synchronizedMap? - public interface Map extends ReadMap { - /** - * Removes the value associated with the key. - * @param key interface for storing the value. - * @param <T> type of value. - * @return the value of the key, null if it doesn't exist. - */ - @Nullable - <T> T remove(@NonNull Key<T> key); - - /** - * Sets a value for the key. - * - * @param key interface for storing the value. - * @param <T> type of value. - * @param value a non-null value of type T. - * @return the previous value associated with key or null if it doesn't exist. - */ - // See automatic Kotlin overloading for Java interoperability. - // https://kotlinlang.org/docs/reference/java-interop.html#operators - // See also Kotlin set for overloaded operator indexing. - // https://kotlinlang.org/docs/reference/operator-overloading.html#indexed - // Also the Kotlin mutable-list set. - // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/set.html - @Nullable - <T> T set(@NonNull Key<T> key, @NonNull T value); - } - - /** - * Creates a {@link Map} suitable for adding keys. - * @return an empty {@link Map} instance. + * Creates a {@link AudioMetadataMap} suitable for adding keys. + * @return an empty {@link AudioMetadataMap} instance. */ @NonNull - public static Map createMap() { + public static AudioMetadataMap createMap() { return new BaseMap(); } @@ -339,7 +254,7 @@ public final class AudioMetadata { * It is possible to require the keys to be of a certain class * before allowing a set or get operation. */ - public static class BaseMap implements Map { + public static class BaseMap implements AudioMetadataMap { @Override public <T> boolean containsKey(@NonNull Key<T> key) { Pair<Key<?>, Object> valuePair = mHashMap.get(pairFromKey(key)); @@ -348,7 +263,7 @@ public final class AudioMetadata { @Override @NonNull - public Map dup() { + public AudioMetadataMap dup() { BaseMap map = new BaseMap(); map.mHashMap.putAll(this.mHashMap); return map; diff --git a/media/java/android/media/AudioMetadataMap.java b/media/java/android/media/AudioMetadataMap.java new file mode 100644 index 000000000000..196193174754 --- /dev/null +++ b/media/java/android/media/AudioMetadataMap.java @@ -0,0 +1,59 @@ +/* + * 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.media; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +/** + * AudioMetadataMap is a writeable {@code Map}-style + * interface of {@link AudioMetadata.Key} value pairs. + * This interface is not guaranteed to be thread-safe + * unless the underlying implementation for the {@code AudioMetadataMap} + * states it as thread safe. + * + * {@see AudioMetadataReadMap} + */ +// TODO: Create a wrapper like java.util.Collections.synchronizedMap? + +public interface AudioMetadataMap extends AudioMetadataReadMap { + /** + * Removes the value associated with the key. + * @param key interface for storing the value. + * @param <T> type of value. + * @return the value of the key, null if it doesn't exist. + */ + @Nullable + <T> T remove(@NonNull AudioMetadata.Key<T> key); + + /** + * Sets a value for the key. + * + * @param key interface for storing the value. + * @param <T> type of value. + * @param value a non-null value of type T. + * @return the previous value associated with key or null if it doesn't exist. + */ + // See automatic Kotlin overloading for Java interoperability. + // https://kotlinlang.org/docs/reference/java-interop.html#operators + // See also Kotlin set for overloaded operator indexing. + // https://kotlinlang.org/docs/reference/operator-overloading.html#indexed + // Also the Kotlin mutable-list set. + // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/set.html + @Nullable + <T> T set(@NonNull AudioMetadata.Key<T> key, @NonNull T value); +} diff --git a/media/java/android/media/AudioMetadataReadMap.java b/media/java/android/media/AudioMetadataReadMap.java new file mode 100644 index 000000000000..e74242a292d4 --- /dev/null +++ b/media/java/android/media/AudioMetadataReadMap.java @@ -0,0 +1,81 @@ +/* + * 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.media; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.util.Set; + +/** + * A read only {@code Map}-style interface of {@link AudioMetadata.Key} value pairs used + * for {@link AudioMetadata}. + * + * <p>Using a {@link AudioMetadata.Key} interface, + * this map looks up the corresponding value. + * Read-only maps are thread-safe for lookup, but the underlying object + * values may need their own thread protection if mutable.</p> + * + * {@see AudioMetadataMap} + */ +public interface AudioMetadataReadMap { + /** + * Returns true if the key exists in the map. + * + * @param key interface for requesting the value. + * @param <T> type of value. + * @return true if key exists in the Map. + */ + <T> boolean containsKey(@NonNull AudioMetadata.Key<T> key); + + /** + * Returns a copy of the map. + * + * This is intended for safe conversion between a {@link AudioMetadataReadMap} + * interface and a {@link AudioMetadataMap} interface. + * Currently only simple objects are used for key values which + * means a shallow copy is sufficient. + * + * @return a Map copied from the existing map. + */ + @NonNull + AudioMetadataMap dup(); // lint checker doesn't like clone(). + + /** + * Returns the value associated with the key. + * + * @param key interface for requesting the value. + * @param <T> type of value. + * @return returns the value of associated with key or null if it doesn't exist. + */ + @Nullable + <T> T get(@NonNull AudioMetadata.Key<T> key); + + /** + * Returns a {@code Set} of keys associated with the map. + * @hide + */ + @NonNull + Set<AudioMetadata.Key<?>> keySet(); + + /** + * Returns the number of elements in the map. + */ + @IntRange(from = 0) + int size(); +} 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/AudioTrack.java b/media/java/android/media/AudioTrack.java index d17e42996726..1d229b80cb2c 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -918,7 +918,29 @@ public class AudioTrack extends PlayerBase private final int mContentId; private final int mSyncId; - private TunerConfiguration(int contentId, int syncId) { + /** + * Constructs a TunerConfiguration instance for use in {@link AudioTrack.Builder} + * + * @param contentId selects the audio stream to use. + * The contentId may be obtained from + * {@link android.media.tv.tuner.filter.Filter#getId()}. + * This is always a positive number. + * @param syncId selects the clock to use for synchronization + * of audio with other streams such as video. + * The syncId may be obtained from + * {@link android.media.tv.tuner.Tuner#getAvSyncHwId()}. + * This is always a positive number. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public TunerConfiguration( + @IntRange(from = 1) int contentId, @IntRange(from = 1)int syncId) { + if (contentId < 1) { + throw new IllegalArgumentException( + "contentId " + contentId + " must be positive"); + } + if (syncId < 1) { + throw new IllegalArgumentException("syncId " + syncId + " must be positive"); + } mContentId = contentId; mSyncId = syncId; } @@ -938,73 +960,6 @@ public class AudioTrack extends PlayerBase public @IntRange(from = 1) int getSyncId() { return mSyncId; // The Builder ensures this is > 0. } - - /** - * Builder class for {@link AudioTrack.TunerConfiguration} objects. - */ - public static class Builder { - private int mContentId; - private int mSyncId; - - /** - * Sets the contentId from the Tuner filter. - * - * @param contentId selects the audio stream to use. - * The contentId may be obtained from - * {@link android.media.tv.tuner.filter.Filter#getId()}. - * This is always a positive number. - * - * @return the same Builder instance. - */ - @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public @NonNull Builder setContentId(@IntRange(from = 1) int contentId) { - if (contentId < 1) { - throw new IllegalArgumentException( - "contentId " + contentId + " must be positive"); - } - mContentId = contentId; - return this; - } - - /** - * Sets the syncId from the Tuner filter. - * - * @param syncId selects the clock to use for synchronization - * of audio with other streams such as video. - * The syncId may be obtained from - * {@link android.media.tv.tuner.Tuner#getAvSyncHwId()}. - * This is always a positive number. - * - * @return the same Builder instance. - */ - @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public @NonNull Builder setSyncId(@IntRange(from = 1) int syncId) { - if (syncId < 1) { - throw new IllegalArgumentException("syncId " + syncId + " must be positive"); - } - mSyncId = syncId; - return this; - } - - /** - * Builds a {@link AudioTrack.TunerConfiguration} instance initialized with - * the parameters set on this {@code Builder}. - * - * @return a new successfully initialized {@link AudioTrack.TunerConfiguration}. - * @throws UnsupportedOperationException if the parameters set on the - * {@code Builder} are incompatible. - */ - @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public @NonNull TunerConfiguration build() { - if (mContentId < 1 || mSyncId < 1) { - throw new UnsupportedOperationException( - "mContentId " + mContentId - + " mSyncId " + mSyncId - + " must be set"); - } - return new TunerConfiguration(mContentId, mSyncId); - } - } } /** @@ -3673,7 +3628,7 @@ public class AudioTrack extends PlayerBase // OnCodecFormatChangedListener notifications uses an instance // of ListenerList to manage its listeners. - private final Utils.ListenerList<AudioMetadata.ReadMap> mCodecFormatChangedListeners = + private final Utils.ListenerList<AudioMetadataReadMap> mCodecFormatChangedListeners = new Utils.ListenerList(); /** @@ -3684,13 +3639,13 @@ public class AudioTrack extends PlayerBase * Called when the compressed codec format changes. * * @param audioTrack is the {@code AudioTrack} instance associated with the codec. - * @param info is a {@link AudioMetadata.ReadMap} of values which contains decoded format + * @param info is a {@link AudioMetadataReadMap} of values which contains decoded format * changes reported by the codec. Not all hardware * codecs indicate codec format changes. Acceptable keys are taken from * {@code AudioMetadata.Format.KEY_*} range, with the associated value type. */ void onCodecFormatChanged( - @NonNull AudioTrack audioTrack, @Nullable AudioMetadata.ReadMap info); + @NonNull AudioTrack audioTrack, @Nullable AudioMetadataReadMap info); } /** @@ -3708,7 +3663,7 @@ public class AudioTrack extends PlayerBase mCodecFormatChangedListeners.add( listener, /* key for removal */ executor, - (int eventCode, AudioMetadata.ReadMap readMap) -> { + (int eventCode, AudioMetadataReadMap readMap) -> { // eventCode is unused by this implementation. listener.onCodecFormatChanged(this, readMap); } @@ -4067,7 +4022,7 @@ public class AudioTrack extends PlayerBase ByteBuffer buffer = (ByteBuffer) obj; buffer.order(ByteOrder.nativeOrder()); buffer.rewind(); - AudioMetadata.ReadMap audioMetaData = AudioMetadata.fromByteBuffer(buffer); + AudioMetadataReadMap audioMetaData = AudioMetadata.fromByteBuffer(buffer); if (audioMetaData == null) { Log.e(TAG, "Unable to get audio metadata from byte buffer"); return; 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/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 998561316dd4..e5ad569bb24f 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -663,8 +663,8 @@ public final class MediaRoute2Info implements Parcelable { } /** - * Constructor for builder to create {@link MediaRoute2Info} with - * existing {@link MediaRoute2Info} instance. + * Constructor for builder to create {@link MediaRoute2Info} with existing + * {@link MediaRoute2Info} instance. * * @param routeInfo the existing instance to copy data from. */ @@ -690,6 +690,38 @@ public final class MediaRoute2Info implements Parcelable { } /** + * Constructor for builder to create {@link MediaRoute2Info} with existing + * {@link MediaRoute2Info} instance and replace ID with the given {@code id}. + * + * @param id The ID of the new route. Must not be empty. + * @param routeInfo the existing instance to copy data from. + * @hide + */ + public Builder(@NonNull String id, @NonNull MediaRoute2Info routeInfo) { + if (TextUtils.isEmpty(id)) { + throw new IllegalArgumentException("id must not be empty"); + } + Objects.requireNonNull(routeInfo, "routeInfo must not be null"); + + mId = id; + mName = routeInfo.mName; + mFeatures = new ArrayList<>(routeInfo.mFeatures); + mType = routeInfo.mType; + mIsSystem = routeInfo.mIsSystem; + mIconUri = routeInfo.mIconUri; + mDescription = routeInfo.mDescription; + mConnectionState = routeInfo.mConnectionState; + mClientPackageName = routeInfo.mClientPackageName; + mVolumeHandling = routeInfo.mVolumeHandling; + mVolumeMax = routeInfo.mVolumeMax; + mVolume = routeInfo.mVolume; + if (routeInfo.mExtras != null) { + mExtras = new Bundle(routeInfo.mExtras); + } + mProviderId = routeInfo.mProviderId; + } + + /** * Adds a feature for the route. * @param feature a feature that the route has. May be one of predefined features * such as {@link #FEATURE_LIVE_AUDIO}, {@link #FEATURE_LIVE_VIDEO} or diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java index f9dbc50e20cf..090f78e4e4f7 100644 --- a/media/java/android/media/audiopolicy/AudioProductStrategy.java +++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java @@ -374,8 +374,8 @@ public final class AudioProductStrategy implements Parcelable { if (refAttr.equals(sDefaultAttributes)) { return false; } - return ((refAttr.getUsage() == AudioAttributes.USAGE_UNKNOWN) - || (attr.getUsage() == refAttr.getUsage())) + return ((refAttr.getSystemUsage() == AudioAttributes.USAGE_UNKNOWN) + || (attr.getSystemUsage() == refAttr.getSystemUsage())) && ((refAttr.getContentType() == AudioAttributes.CONTENT_TYPE_UNKNOWN) || (attr.getContentType() == refAttr.getContentType())) && ((refAttr.getAllFlags() == 0) diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index b943fe589270..d654b45da092 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -22,7 +22,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; +import android.media.tv.tuner.Tuner; import android.media.tv.tuner.Tuner.Result; +import android.media.tv.tuner.TunerUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -178,13 +180,14 @@ public class Filter implements AutoCloseable { */ public static final int STATUS_OVERFLOW = Constants.DemuxFilterStatus.OVERFLOW; - private long mNativeContext; private FilterCallback mCallback; private Executor mExecutor; private final int mId; private int mMainType; private int mSubtype; + private Filter mSource; + private boolean mStarted; private native int nativeConfigureFilter( int type, int subType, FilterConfiguration settings); @@ -202,6 +205,9 @@ public class Filter implements AutoCloseable { } private void onFilterStatus(int status) { + if (mCallback != null && mExecutor != null) { + mExecutor.execute(() -> mCallback.onFilterStatusChanged(this, status)); + } } private void onFilterEvent(FilterEvent[] events) { @@ -266,10 +272,18 @@ public class Filter implements AutoCloseable { * @param source the filter instance which provides data input. Switch to * use demux as data source if the filter instance is NULL. * @return result status of the operation. + * @throws IllegalStateException if the data source has been set. */ @Result public int setDataSource(@Nullable Filter source) { - return nativeSetDataSource(source); + if (mSource != null) { + throw new IllegalStateException("Data source is existing"); + } + int res = nativeSetDataSource(source); + if (res == Tuner.RESULT_SUCCESS) { + mSource = source; + } + return res; } /** @@ -328,6 +342,9 @@ public class Filter implements AutoCloseable { */ @Override public void close() { - nativeClose(); + int res = nativeClose(); + if (res != Tuner.RESULT_SUCCESS) { + TunerUtils.throwExceptionForResult(res, "Failed to close filter."); + } } } diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 95ce07d3c55e..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 //////////////////////////////////////////////////////////////////////////////// @@ -2448,11 +2485,11 @@ static DemuxFilterSettings getFilterConfiguration( return filterSettings; } -static int copyData(JNIEnv *env, sp<Filter> filter, jbyteArray buffer, jint offset, int size) { - ALOGD("copyData, size=%d, offset=%d", size, offset); +static jint copyData(JNIEnv *env, sp<Filter> filter, jbyteArray buffer, jlong offset, jlong size) { + ALOGD("copyData, size=%ld, offset=%ld", (long) size, (long) offset); - int available = filter->mFilterMQ->availableToRead(); - ALOGD("copyData, available=%d", available); + jlong available = filter->mFilterMQ->availableToRead(); + ALOGD("copyData, available=%ld", (long) available); size = std::min(size, available); jboolean isCopy; @@ -2474,7 +2511,7 @@ static int copyData(JNIEnv *env, sp<Filter> filter, jbyteArray buffer, jint offs return size; } -static int android_media_tv_Tuner_configure_filter( +static jint android_media_tv_Tuner_configure_filter( JNIEnv *env, jobject filter, int type, int subtype, jobject settings) { ALOGD("configure filter type=%d, subtype=%d", type, subtype); sp<Filter> filterSp = getFilter(env, filter); @@ -2485,9 +2522,14 @@ static int android_media_tv_Tuner_configure_filter( } DemuxFilterSettings filterSettings = getFilterConfiguration(env, type, subtype, settings); Result res = iFilterSp->configure(filterSettings); + + if (res != Result::SUCCESS) { + return (jint) res; + } + MQDescriptorSync<uint8_t> filterMQDesc; - if (res == Result::SUCCESS && filterSp->mFilterMQ == NULL) { - Result getQueueDescResult = Result::UNKNOWN_ERROR; + Result getQueueDescResult = Result::UNKNOWN_ERROR; + if (filterSp->mFilterMQ == NULL) { iFilterSp->getQueueDesc( [&](Result r, const MQDescriptorSync<uint8_t>& desc) { filterMQDesc = desc; @@ -2500,59 +2542,97 @@ static int android_media_tv_Tuner_configure_filter( filterSp->mFilterMQ->getEventFlagWord(), &(filterSp->mFilterMQEventFlag)); } } - return (int)res; + return (jint) getQueueDescResult; } -static int android_media_tv_Tuner_get_filter_id(JNIEnv*, jobject) { - return 0; +static jint android_media_tv_Tuner_get_filter_id(JNIEnv* env, jobject filter) { + sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); + if (iFilterSp == NULL) { + ALOGD("Failed to get filter ID: filter not found"); + return (int) Result::INVALID_STATE; + } + Result res; + uint32_t id; + iFilterSp->getId( + [&](Result r, uint32_t filterId) { + res = r; + id = filterId; + }); + if (res != Result::SUCCESS) { + return (jint) Constant::INVALID_FILTER_ID; + } + return (jint) id; } -static int android_media_tv_Tuner_set_filter_data_source(JNIEnv*, jobject, jobject) { - return 0; +static jint android_media_tv_Tuner_set_filter_data_source( + JNIEnv* env, jobject filter, jobject srcFilter) { + sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); + if (iFilterSp == NULL) { + ALOGD("Failed to set filter data source: filter not found"); + return (jint) Result::INVALID_STATE; + } + Result r; + if (srcFilter == NULL) { + r = iFilterSp->setDataSource(NULL); + } else { + sp<IFilter> srcSp = getFilter(env, srcFilter)->getIFilter(); + if (iFilterSp == NULL) { + ALOGD("Failed to set filter data source: src filter not found"); + return (jint) Result::INVALID_STATE; + } + r = iFilterSp->setDataSource(srcSp); + } + return (jint) r; } -static int android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) { - sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); - if (filterSp == NULL) { +static jint android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) { + sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); + if (iFilterSp == NULL) { ALOGD("Failed to start filter: filter not found"); - return false; + return (jint) Result::INVALID_STATE; } - Result r = filterSp->start(); - return (int) r; + Result r = iFilterSp->start(); + return (jint) r; } -static int android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) { - sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); - if (filterSp == NULL) { +static jint android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) { + sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); + if (iFilterSp == NULL) { ALOGD("Failed to stop filter: filter not found"); - return false; + return (jint) Result::INVALID_STATE; } - Result r = filterSp->stop(); - return (int) r; + Result r = iFilterSp->stop(); + return (jint) r; } -static int android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) { - sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); - if (filterSp == NULL) { +static jint android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) { + sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); + if (iFilterSp == NULL) { ALOGD("Failed to flush filter: filter not found"); - return false; + return (jint) Result::INVALID_STATE; } - Result r = filterSp->flush(); - return (int) r; + Result r = iFilterSp->flush(); + return (jint) r; } -static int android_media_tv_Tuner_read_filter_fmq( +static jint android_media_tv_Tuner_read_filter_fmq( JNIEnv *env, jobject filter, jbyteArray buffer, jlong offset, jlong size) { sp<Filter> filterSp = getFilter(env, filter); if (filterSp == NULL) { ALOGD("Failed to read filter FMQ: filter not found"); - return 0; + return (jint) Result::INVALID_STATE; } return copyData(env, filterSp, buffer, offset, size); } -static int android_media_tv_Tuner_close_filter(JNIEnv*, jobject) { - return 0; +static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) { + sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); + if (iFilterSp == NULL) { + ALOGD("Failed to close filter: filter not found"); + return (jint) Result::INVALID_STATE; + } + Result r = iFilterSp->close(); + return (jint) r; } static sp<TimeFilter> getTimeFilter(JNIEnv *env, jobject filter) { @@ -2660,8 +2740,8 @@ static int android_media_tv_Tuner_add_pid( if (descramblerSp == NULL) { return false; } - sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); - Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), filterSp); + sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); + Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), iFilterSp); return (int)result; } @@ -2671,8 +2751,8 @@ static int android_media_tv_Tuner_remove_pid( if (descramblerSp == NULL) { return false; } - sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); - Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), filterSp); + sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); + Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), iFilterSp); return (int)result; } @@ -2696,27 +2776,28 @@ 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) { sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr(); - sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); - if (dvrSp == NULL || filterSp == NULL) { + sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); + if (dvrSp == NULL || iFilterSp == NULL) { return false; } - Result result = dvrSp->attachFilter(filterSp); + Result result = dvrSp->attachFilter(iFilterSp); return (int) result; } static int android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobject filter) { sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr(); - sp<IFilter> filterSp = getFilter(env, filter)->getIFilter(); - if (dvrSp == NULL || filterSp == NULL) { + sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); + if (dvrSp == NULL || iFilterSp == NULL) { return false; } - Result result = dvrSp->detachFilter(filterSp); + Result result = dvrSp->detachFilter(iFilterSp); return (int) result; } 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/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java index 25220951dd7b..daeb731ad457 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java @@ -2210,14 +2210,14 @@ public class CameraTestUtils extends Assert { } public static Size getPreviewSizeBound(WindowManager windowManager, Size bound) { - Size windowSize = windowManager.getCurrentWindowMetrics().getSize(); + Rect windowBounds = windowManager.getCurrentWindowMetrics().getBounds(); - int width = windowSize.getWidth(); - int height = windowSize.getHeight(); + int width = windowBounds.width(); + int height = windowBounds.height(); if (height > width) { height = width; - width = windowSize.getHeight(); + width = windowBounds.height(); } if (bound.getWidth() <= width && diff --git a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java index d4eb2a9c75d0..a2da23e918d2 100644 --- a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java +++ b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java @@ -28,11 +28,11 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.IBinder; import android.os.Message; -import android.util.Size; import android.util.Slog; import android.view.Display; import android.view.ViewGroup; @@ -134,8 +134,8 @@ public class FakeApp extends Application { } lp.width = ViewGroup.LayoutParams.MATCH_PARENT; lp.height = ViewGroup.LayoutParams.MATCH_PARENT; - Size maxWindowSize = wm.getMaximumWindowMetrics().getSize(); - int maxSize = Math.max(maxWindowSize.getWidth(), maxWindowSize.getHeight()); + Rect maxWindowBounds = wm.getMaximumWindowMetrics().getBounds(); + int maxSize = Math.max(maxWindowBounds.width(), maxWindowBounds.height()); maxSize *= 2; lp.x = maxSize; lp.y = maxSize; diff --git a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeBackgroundService.java b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeBackgroundService.java index df00eee63b50..e2120f80f1c9 100644 --- a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeBackgroundService.java +++ b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeBackgroundService.java @@ -19,22 +19,22 @@ package com.android.fakeoemfeatures; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; -import java.util.ArrayList; -import java.util.Random; - import android.app.Dialog; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.IBinder; import android.os.Message; -import android.util.Size; import android.view.Display; import android.view.ViewGroup; import android.view.WindowManager; +import java.util.ArrayList; +import java.util.Random; + public class FakeBackgroundService extends Service { final ArrayList<int[]> mAllocs = new ArrayList<int[]>(); @@ -99,8 +99,8 @@ public class FakeBackgroundService extends Service { // Create an instance of WindowManager that is adjusted to the area of the display dedicated // for windows with type TYPE_APPLICATION_OVERLAY. final WindowManager wm = windowContext.getSystemService(WindowManager.class); - Size maxWindowSize = wm.getMaximumWindowMetrics().getSize(); - int maxSize = Math.max(maxWindowSize.getWidth(), maxWindowSize.getHeight()); + Rect maxWindowBounds = wm.getMaximumWindowMetrics().getBounds(); + int maxSize = Math.max(maxWindowBounds.width(), maxWindowBounds.height()); maxSize *= 2; lp.x = maxSize; lp.y = maxSize; diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index a2fa46130076..a95677d0202f 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -406,6 +406,10 @@ public class PackageInstallerActivity extends AlertActivity { mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); mOk.setEnabled(false); + + if (!mOk.isInTouchMode()) { + mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus(); + } } /** diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 73f08c68fdb2..3f59f896a82e 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1305,4 +1305,44 @@ <string name="delete_blob_text">Delete shared data</string> <!-- Shared data: confirmation dialog text when attempting delete some shared data [CHAR LIMIT=NONE] --> <string name="delete_blob_confirmation_text">Are you sure you want to delete this shared data?</string> + + <!-- Summary for add user entry in the choice dialog [CHAR LIMIT=none] --> + <string name="user_add_user_item_summary">Users have their own apps and content</string> + <!-- Summary for add restricted profile entry in the choice dialog [CHAR LIMIT=none] --> + <string name="user_add_profile_item_summary">You can restrict access to apps and content from your account</string> + <!-- Button text for adding a regular user [CHAR LIMIT=25] --> + <string name="user_add_user_item_title">User</string> + <!-- Button text for adding a restricted profile [CHAR LIMIT=25] --> + <string name="user_add_profile_item_title">Restricted profile</string> + <!-- Title for add user confirmation dialog [CHAR LIMIT=30] --> + <string name="user_add_user_title">Add new user?</string> + <!-- Message for add user confirmation dialog - long version. [CHAR LIMIT=none] --> + <string name="user_add_user_message_long">You can share this device with other people by creating additional users. Each user has their own space, which they can customize with apps, wallpaper, and so on. Users can also adjust device settings like Wi\u2011Fi that affect everyone.\n\nWhen you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users. Accessibility settings and services may not transfer to the new user.</string> + <!-- Message for add user confirmation dialog - short version. [CHAR LIMIT=none] --> + <string name="user_add_user_message_short">When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users. </string> + <!-- Title of dialog to setup a new user [CHAR LIMIT=30] --> + <string name="user_setup_dialog_title">Set up user now?</string> + <!-- Message in dialog to setup a new user after creation [CHAR LIMIT=none] --> + <string name="user_setup_dialog_message">Make sure the person is available to take the device and set up their space</string> + <!-- Message in dialog to setup a new restricted profile after creation [CHAR LIMIT=none] --> + <string name="user_setup_profile_dialog_message">Set up profile now?</string> + <!-- Button text to setup the new user now [CHAR LIMIT=25] --> + <string name="user_setup_button_setup_now">Set up now</string> + <!-- Button text to setup the new user later [CHAR LIMIT=25] --> + <string name="user_setup_button_setup_later">Not now</string> + <!-- Title for add user type dialog [CHAR LIMIT=45] --> + <string name="user_add_user_type_title">Add</string> + <!-- User details new user name [CHAR LIMIT=30] --> + <string name="user_new_user_name">New user</string> + <!-- User details new restricted profile name [CHAR LIMIT=30] --> + <string name="user_new_profile_name">New profile</string> + <!-- Text shown for title of user info setting [CHAR LIMIT=20]--> + <string name="user_info_settings_title">User info</string> + <!-- Text shown for title of profile info setting [CHAR LIMIT=20]--> + <string name="profile_info_settings_title">Profile info</string> + <!-- User settings warning that restricted profile needs a screen lock [CHAR LIMIT=NONE] --> + <string name="user_need_lock_message">Before you can create a restricted profile, you\u2019ll need to set up a screen lock to protect your apps and personal data.</string> + <!-- User settings dialog button to set screen lock [CHAR LIMIT=25] --> + <string name="user_set_lock_button">Set lock</string> + </resources> 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/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java index c34c365c1bfa..7ef080178a2f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java @@ -169,6 +169,22 @@ public class MetricsFeatureProvider { sourceMetricsCategory); } + /** + * Logs an event when the intent is started by Profile select dialog. + * + * @return true if the intent is loggable, otherwise false + */ + public boolean logStartedIntentWithProfile(Intent intent, int sourceMetricsCategory, + boolean isWorkProfile) { + if (intent == null) { + return false; + } + final ComponentName cn = intent.getComponent(); + final String key = cn != null ? cn.flattenToString() : intent.getAction(); + return logSettingsTileClick(key + (isWorkProfile ? "/work" : "/personal"), + sourceMetricsCategory); + } + private boolean logSettingsTileClick(String logKey, int sourceMetricsCategory) { if (TextUtils.isEmpty(logKey)) { // Not loggable diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 9ae9b4a46227..df0de68b1fb9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -66,11 +66,12 @@ public class LocalMediaManager implements BluetoothCallback { private LocalBluetoothManager mLocalBluetoothManager; private InfoMediaManager mInfoMediaManager; private String mPackageName; + private MediaDevice mOnTransferBluetoothDevice; @VisibleForTesting - List<MediaDevice> mMediaDevices = new ArrayList<>(); + List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>(); @VisibleForTesting - List<MediaDevice> mDisconnectedMediaDevices = new ArrayList<>(); + List<MediaDevice> mDisconnectedMediaDevices = new CopyOnWriteArrayList<>(); @VisibleForTesting MediaDevice mPhoneDevice; @VisibleForTesting @@ -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; } @@ -206,6 +207,7 @@ public class LocalMediaManager implements BluetoothCallback { public void stopScan() { mInfoMediaManager.unregisterCallback(mMediaDeviceCallback); mInfoMediaManager.stopScan(); + unRegisterDeviceAttributeChangeCallback(); } /** @@ -389,35 +391,41 @@ public class LocalMediaManager implements BluetoothCallback { mCurrentConnectedDevice = infoMediaDevice != null ? infoMediaDevice : updateCurrentConnectedDevice(); dispatchDeviceListUpdate(); + if (mOnTransferBluetoothDevice != null && mOnTransferBluetoothDevice.isConnected()) { + connectDevice(mOnTransferBluetoothDevice); + mOnTransferBluetoothDevice = null; + } } private List<MediaDevice> buildDisconnectedBluetoothDevice() { - for (MediaDevice device : mDisconnectedMediaDevices) { - ((BluetoothMediaDevice) device).getCachedDevice() - .unregisterCallback(mDeviceAttributeChangeCallback); - } - mDisconnectedMediaDevices.clear(); final List<BluetoothDevice> bluetoothDevices = mBluetoothAdapter.getMostRecentlyConnectedDevices(); final CachedBluetoothDeviceManager cachedDeviceManager = mLocalBluetoothManager.getCachedDeviceManager(); + final List<CachedBluetoothDevice> cachedBluetoothDeviceList = new ArrayList<>(); for (BluetoothDevice device : bluetoothDevices) { final CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(device); if (cachedDevice != null) { if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED && !cachedDevice.isConnected()) { - final MediaDevice mediaDevice = new BluetoothMediaDevice(mContext, - cachedDevice, - null, null, mPackageName); - if (!mMediaDevices.contains(mediaDevice)) { - cachedDevice.registerCallback(mDeviceAttributeChangeCallback); - mDisconnectedMediaDevices.add(mediaDevice); - } + cachedBluetoothDeviceList.add(cachedDevice); } } } + + unRegisterDeviceAttributeChangeCallback(); + mDisconnectedMediaDevices.clear(); + for (CachedBluetoothDevice cachedDevice : cachedBluetoothDeviceList) { + final MediaDevice mediaDevice = new BluetoothMediaDevice(mContext, + cachedDevice, + null, null, mPackageName); + if (!mMediaDevices.contains(mediaDevice)) { + cachedDevice.registerCallback(mDeviceAttributeChangeCallback); + mDisconnectedMediaDevices.add(mediaDevice); + } + } return new ArrayList<>(mDisconnectedMediaDevices); } @@ -440,8 +448,9 @@ public class LocalMediaManager implements BluetoothCallback { MediaDevice connectDevice = getMediaDeviceById(mMediaDevices, id); connectDevice = connectDevice != null ? connectDevice : updateCurrentConnectedDevice(); - connectDevice.setState(MediaDeviceState.STATE_CONNECTED); - + if (connectDevice != null) { + connectDevice.setState(MediaDeviceState.STATE_CONNECTED); + } if (connectDevice == mCurrentConnectedDevice) { Log.d(TAG, "onConnectedDeviceChanged() this device all ready connected!"); return; @@ -467,6 +476,12 @@ public class LocalMediaManager implements BluetoothCallback { } } + private void unRegisterDeviceAttributeChangeCallback() { + for (MediaDevice device : mDisconnectedMediaDevices) { + ((BluetoothMediaDevice) device).getCachedDevice() + .unregisterCallback(mDeviceAttributeChangeCallback); + } + } /** * Callback for notifying device information updating diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index 8bf48e59165e..c713d7813a54 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -17,6 +17,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; @@ -36,7 +37,10 @@ import com.android.settingslib.R; import java.util.List; -public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { +/** + * Track status of Wi-Fi for the Sys UI. + */ +public class WifiStatusTracker { private final Context mContext; private final WifiNetworkScoreCache mWifiNetworkScoreCache; private final WifiManager mWifiManager; @@ -55,8 +59,9 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { .clearCapabilities() .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build(); - private final ConnectivityManager.NetworkCallback mNetworkCallback = new ConnectivityManager - .NetworkCallback() { + private final NetworkCallback mNetworkCallback = new NetworkCallback() { + // Note: onCapabilitiesChanged is guaranteed to be called "immediately" after onAvailable + // and onLinkPropertiesChanged. @Override public void onCapabilitiesChanged( Network network, NetworkCapabilities networkCapabilities) { @@ -64,11 +69,35 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { mCallback.run(); } }; + private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback() { + @Override + public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { + // network is now the default network, and its capabilities are nc. + // This method will always be called immediately after the network becomes the + // default, in addition to any time the capabilities change while the network is + // the default. + mDefaultNetwork = network; + mDefaultNetworkCapabilities = nc; + updateStatusLabel(); + mCallback.run(); + } + @Override + public void onLost(Network network) { + // The system no longer has a default network. + mDefaultNetwork = null; + mDefaultNetworkCapabilities = null; + updateStatusLabel(); + mCallback.run(); + } + }; + private Network mDefaultNetwork = null; + private NetworkCapabilities mDefaultNetworkCapabilities = null; private final Runnable mCallback; private WifiInfo mWifiInfo; public boolean enabled; public boolean isCaptivePortal; + public boolean isDefaultNetwork; public int state; public boolean connected; public String ssid; @@ -94,11 +123,13 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { mWifiNetworkScoreCache.registerListener(mCacheListener); mConnectivityManager.registerNetworkCallback( mNetworkRequest, mNetworkCallback, mHandler); + mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler); } else { mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, mWifiNetworkScoreCache); mWifiNetworkScoreCache.unregisterListener(); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); + mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback); } } @@ -154,8 +185,17 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { } private void updateStatusLabel() { - final NetworkCapabilities networkCapabilities - = mConnectivityManager.getNetworkCapabilities(mWifiManager.getCurrentNetwork()); + NetworkCapabilities networkCapabilities; + final Network currentWifiNetwork = mWifiManager.getCurrentNetwork(); + if (currentWifiNetwork != null && currentWifiNetwork.equals(mDefaultNetwork)) { + // Wifi is connected and the default network. + isDefaultNetwork = true; + networkCapabilities = mDefaultNetworkCapabilities; + } else { + isDefaultNetwork = false; + networkCapabilities = mConnectivityManager.getNetworkCapabilities( + mWifiManager.getCurrentNetwork()); + } isCaptivePortal = false; if (networkCapabilities != null) { if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java index ed0857ca21dc..204a93333d81 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java @@ -164,6 +164,38 @@ public class MetricsFeatureProviderTest { } @Test + public void logStartedIntentWithProfile_isPersonalProfile_shouldTagPersonal() { + final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls")); + + final boolean loggable = mProvider.logStartedIntentWithProfile(intent, + MetricsEvent.SETTINGS_GESTURES, false); + + assertThat(loggable).isTrue(); + verify(mLogWriter).action( + MetricsEvent.SETTINGS_GESTURES, + MetricsEvent.ACTION_SETTINGS_TILE_CLICK, + SettingsEnums.PAGE_UNKNOWN, + "pkg/cls/personal", + 0); + } + + @Test + public void logStartedIntentWithProfile_isWorkProfile_shouldTagWork() { + final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls")); + + final boolean loggable = mProvider.logStartedIntentWithProfile(intent, + MetricsEvent.SETTINGS_GESTURES, true); + + assertThat(loggable).isTrue(); + verify(mLogWriter).action( + MetricsEvent.SETTINGS_GESTURES, + MetricsEvent.ACTION_SETTINGS_TILE_CLICK, + SettingsEnums.PAGE_UNKNOWN, + "pkg/cls/work", + 0); + } + + @Test public void getAttribution_noActivity_shouldReturnUnknown() { assertThat(mProvider.getAttribution(null /* activity */)) .isEqualTo(SettingsEnums.PAGE_UNKNOWN); 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 559187d2c38c..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, @@ -450,6 +451,12 @@ public class LocalMediaManagerTest { } @Test + public void onConnectedDeviceChanged_nullConnectedDevice_noException() { + mLocalMediaManager.registerCallback(mCallback); + mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2); + } + + @Test public void onDeviceAttributesChanged_shouldDispatchDeviceListUpdate() { mLocalMediaManager.registerCallback(mCallback); @@ -559,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..736e995451cd 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, @@ -97,6 +96,7 @@ public class SecureSettings { Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, Settings.Secure.QS_TILES, + Settings.Secure.CONTROLS_ENABLED, Settings.Secure.DOZE_ENABLED, Settings.Secure.DOZE_ALWAYS_ON, Settings.Secure.DOZE_PICK_UP_GESTURE, @@ -164,6 +164,6 @@ public class SecureSettings { Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT, Settings.Secure.PEOPLE_STRIP, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, - Settings.Secure.ACCESSIBILITY_BUTTON_LONG_PRESS_TARGETS, + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 4d33b627f4e2..b413e8e9dda2 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; @@ -78,11 +78,8 @@ public class SecureSettingsValidators { ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR); // technically either ComponentName or class name, but there's proper value // validation at callsites, so allow any non-null string - VALIDATORS.put( - Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, - ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR); + VALIDATORS.put(Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, value -> value != null); 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( @@ -144,6 +141,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.QS_TILES, TILE_LIST_VALIDATOR); + VALIDATORS.put(Secure.CONTROLS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DOZE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DOZE_ALWAYS_ON, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DOZE_PICK_UP_GESTURE, BOOLEAN_VALIDATOR); @@ -249,7 +247,7 @@ public class SecureSettingsValidators { Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)); VALIDATORS.put( - Secure.ACCESSIBILITY_BUTTON_LONG_PRESS_TARGETS, + Secure.ACCESSIBILITY_BUTTON_TARGETS, ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index af74121a11c9..8a7b9134a5d9 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, @@ -1814,8 +1811,8 @@ class SettingsProtoDumpUtil { Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, SecureSettingsProto.Accessibility.ACCESSIBILITY_MAGNIFICATION_MODE); dumpSetting(s, p, - Settings.Secure.ACCESSIBILITY_BUTTON_LONG_PRESS_TARGETS, - SecureSettingsProto.Accessibility.BUTTON_LONG_PRESS_TARGETS); + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, + SecureSettingsProto.Accessibility.BUTTON_TARGETS); p.end(accessibilityToken); final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP); @@ -1975,6 +1972,13 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, SecureSettingsProto.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS); + + final long controlsToken = p.start(SecureSettingsProto.CONTROLS); + dumpSetting(s, p, + Settings.Secure.CONTROLS_ENABLED, + SecureSettingsProto.Controls.ENABLED); + p.end(controlsToken); + dumpSetting(s, p, Settings.Secure.DEVICE_PAIRED, SecureSettingsProto.DEVICE_PAIRED); 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/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 24cc3c901920..a36949b9e1ff 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -540,6 +540,7 @@ public class SettingsBackupTest { Settings.Global.WIFI_FREQUENCY_BAND, Settings.Global.WIFI_IDLE_MS, Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT, + Settings.Global.WIFI_MIGRATION_COMPLETED, Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS, Settings.Global.WIFI_NETWORK_SHOW_RSSI, Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 0230970cfa83..4771c4139a5b 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -81,6 +81,8 @@ <uses-permission android:name="android.permission.READ_INPUT_STATE" /> <uses-permission android:name="android.permission.SET_ORIENTATION" /> <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> + <!-- TODO(b/152310230): remove once APIs are confirmed to be sufficient --> + <uses-permission android:name="com.android.permission.USE_INSTALLER_V2" /> <uses-permission android:name="android.permission.MOVE_PACKAGE" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> @@ -284,6 +286,11 @@ <!-- Permission required for testing system audio effect APIs. --> <uses-permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"/> + <!-- Permissions required for CTS test - TunerTest --> + <uses-permission android:name="android.permission.ACCESS_TV_DESCRAMBLER" /> + <uses-permission android:name="android.permission.ACCESS_TV_TUNER" /> + <uses-permission android:name="android.permission.TUNER_RESOURCE_ACCESS" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 64e52376effd..a1376c3203f5 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; @@ -1803,13 +1808,13 @@ public class BugreportProgressService extends Service { * Current value of progress (in percentage) of the bugreport generation as * displayed by the UI. */ - AtomicInteger progress; + AtomicInteger progress = new AtomicInteger(0); /** * Last value of progress (in percentage) of the bugreport generation for which * system notification was updated. */ - AtomicInteger lastProgress; + AtomicInteger lastProgress = new AtomicInteger(0); /** * Time of the last progress update. @@ -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/drawable/control_background_ripple.xml b/packages/SystemUI/res/drawable/control_background_ripple.xml new file mode 100644 index 000000000000..37914e272811 --- /dev/null +++ b/packages/SystemUI/res/drawable/control_background_ripple.xml @@ -0,0 +1,23 @@ +<!-- + ~ 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. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask"> + <color android:color="@android:color/white" /> + </item> + <item android:drawable="@drawable/control_background" /> +</ripple>
\ No newline at end of file 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_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml index 7708b8e9db6c..b83e500fbaeb 100644 --- a/packages/SystemUI/res/layout/controls_base_item.xml +++ b/packages/SystemUI/res/layout/controls_base_item.xml @@ -30,8 +30,8 @@ <ImageView android:id="@+id/icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_width="@dimen/control_icon_size" + android:layout_height="@dimen/control_icon_size" android:paddingTop="@dimen/control_padding_adjustment" android:clickable="false" android:focusable="false" @@ -50,6 +50,7 @@ app:layout_constraintBottom_toBottomOf="@+id/icon" app:layout_constraintStart_toEndOf="@+id/icon" /> + <TextView android:id="@+id/status_extra" android:layout_width="wrap_content" @@ -64,7 +65,7 @@ <TextView android:id="@+id/title" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="@style/TextAppearance.Control.Title" android:paddingLeft="@dimen/control_padding_adjustment" @@ -73,12 +74,20 @@ android:focusable="false" android:maxLines="1" android:ellipsize="end" - app:layout_constraintBottom_toTopOf="@+id/subtitle" - app:layout_constraintStart_toStartOf="parent" /> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toTopOf="@id/barrier"/> + + <androidx.constraintlayout.widget.Barrier + android:id="@+id/barrier" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:barrierDirection="top" + app:constraint_referenced_ids="subtitle,favorite" /> <TextView android:id="@+id/subtitle" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" android:textAppearance="@style/TextAppearance.Control.Subtitle" android:paddingLeft="@dimen/control_padding_adjustment" @@ -88,24 +97,22 @@ android:focusable="false" android:maxLines="1" android:ellipsize="end" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toStartOf="@id/favorite" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent"/> + /> - <FrameLayout - android:id="@+id/favorite_container" + <CheckBox + android:id="@+id/favorite" android:visibility="gone" - android:layout_width="48dp" - android:layout_height="48dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|end" + android:button="@drawable/controls_btn_star" + android:layout_marginTop="4dp" + android:layout_marginStart="4dp" + app:layout_constraintStart_toEndOf="@id/subtitle" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintBottom_toBottomOf="parent"> - - <CheckBox - android:id="@+id/favorite" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="bottom|end" - android:button="@drawable/controls_btn_star"/> - </FrameLayout> - + app:layout_constraintBottom_toBottomOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout> 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-television/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml new file mode 100644 index 000000000000..6da0c693f389 --- /dev/null +++ b/packages/SystemUI/res/values-television/dimens.xml @@ -0,0 +1,20 @@ +<?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 + --> +<resources> + <!-- Opacity at which the background for the shutdown UI will be drawn. --> + <item name="shutdown_scrim_behind_alpha" format="float" type="dimen">1.0</item> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-television/styles.xml b/packages/SystemUI/res/values-television/styles.xml index b59f0072b8c3..b01c5d88e3b3 100644 --- a/packages/SystemUI/res/values-television/styles.xml +++ b/packages/SystemUI/res/values-television/styles.xml @@ -17,4 +17,9 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog" /> <style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert" /> + + <style name="Animation.ShutdownUi"> + <item name="android:windowEnterAnimation">@null</item> + <item name="android:windowExitAnimation">@null</item> + </style> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index e45cbecd3aa1..864442ecd0c5 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 --> @@ -1225,6 +1225,7 @@ <dimen name="controls_top_margin">44dp</dimen> <dimen name="control_header_text_size">22sp</dimen> <dimen name="control_text_size">14sp</dimen> + <dimen name="control_icon_size">24dp</dimen> <dimen name="control_spacing">4dp</dimen> <dimen name="control_list_divider">1dp</dimen> <dimen name="control_corner_radius">12dp</dimen> @@ -1265,4 +1266,7 @@ <dimen name="screenrecord_status_icon_radius">5dp</dimen> <dimen name="kg_user_switcher_text_size">16sp</dimen> + + <!-- Opacity at which the background for the shutdown UI will be drawn. --> + <item name="shutdown_scrim_behind_alpha" format="float" type="dimen">0.95</item> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index f71c0b39c62a..8a3a16e9a6cf 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -374,9 +374,31 @@ <string name="biometric_dialog_wrong_password">Wrong password</string> <!-- Error string shown when the user enters too many incorrect attempts [CHAR LIMIT=120]--> <string name="biometric_dialog_credential_too_many_attempts">Too many incorrect attempts.\nTry again in <xliff:g id="number">%d</xliff:g> seconds.</string> + <!-- Error string shown when the user enters an incorrect PIN/pattern/password and it counts towards the max attempts before the data on the device is wiped. [CHAR LIMIT=NONE]--> <string name="biometric_dialog_credential_attempts_before_wipe">Try again. Attempt <xliff:g id="attempts" example="1">%1$d</xliff:g> of <xliff:g id="max_attempts" example="3">%2$d</xliff:g>.</string> + <!-- Title of a dialog shown when the user only has one attempt left to provide the correct PIN/pattern/password before the device, one of its users, or a work profile is wiped. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_attempt_before_wipe_dialog_title">Your data will be deleted</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct lock pattern before the device is wiped. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_pattern_attempt_before_wipe_device">If you enter an incorrect pattern on the next attempt, this device\u2019s data will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct PIN before the device is wiped. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_pin_attempt_before_wipe_device">If you enter an incorrect PIN on the next attempt, this device\u2019s data will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct password before the device is wiped. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_password_attempt_before_wipe_device">If you enter an incorrect password on the next attempt, this device\u2019s data will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct lock pattern before the user is removed. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_pattern_attempt_before_wipe_user">If you enter an incorrect pattern on the next attempt, this user will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct PIN before the user is removed. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_pin_attempt_before_wipe_user">If you enter an incorrect PIN on the next attempt, this user will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct password before the user is removed. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_password_attempt_before_wipe_user">If you enter an incorrect password on the next attempt, this user will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct pattern before the work profile is removed. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_pattern_attempt_before_wipe_profile">If you enter an incorrect pattern on the next attempt, your work profile and its data will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct PIN before the work profile is removed. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_pin_attempt_before_wipe_profile">If you enter an incorrect PIN on the next attempt, your work profile and its data will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct password before the work profile is removed. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_password_attempt_before_wipe_profile">If you enter an incorrect password on the next attempt, your work profile and its data will be deleted.</string> + <!-- Content of a dialog shown when the user has failed to provide the device lock too many times and the device is wiped. [CHAR LIMIT=NONE] --> <string name="biometric_dialog_failed_attempts_now_wiping_device">Too many incorrect attempts. This device\u2019s data will be deleted.</string> <!-- Content of a dialog shown when the user has failed to provide the user lock too many times and the user is removed. [CHAR LIMIT=NONE] --> @@ -2636,14 +2658,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 +2704,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] --> @@ -2694,7 +2715,7 @@ <string name="controls_structure_tooltip">Swipe to see more</string> <!-- Message to tell the user to wait while systemui attempts to load a set of - recommended controls [CHAR_LIMIT=30] --> + recommended controls [CHAR_LIMIT=60] --> <string name="controls_seeding_in_progress">Loading recommendations</string> <!-- Close the controls associated with a specific media session [CHAR_LIMIT=NONE] --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 1233d4dc73e7..9f1e63e0aa27 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -305,6 +305,9 @@ <item name="android:windowExitAnimation">@null</item> </style> + <style name="Animation.ShutdownUi" parent="@android:style/Animation.Toast"> + </style> + <!-- Standard animations for hiding and showing the status bar. --> <style name="Animation.StatusBar"> </style> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index 49e3e5724988..3bda3c8df699 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -20,9 +20,7 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -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; @@ -38,7 +36,6 @@ import android.app.ActivityTaskManager; import android.app.AppGlobals; import android.app.IAssistDataReceiver; import android.app.WindowConfiguration; -import android.app.WindowConfiguration.ActivityType; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -113,15 +110,18 @@ public class ActivityManagerWrapper { * @return the top running task (can be {@code null}). */ public ActivityManager.RunningTaskInfo getRunningTask() { - return getRunningTask(ACTIVITY_TYPE_RECENTS /* ignoreActivityType */); + return getRunningTask(false /* filterVisibleRecents */); } - public ActivityManager.RunningTaskInfo getRunningTask(@ActivityType int ignoreActivityType) { + /** + * @return the top running task filtering only for tasks that can be visible in the recent tasks + * list (can be {@code null}). + */ + public ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleRecents) { // Note: The set of running tasks from the system is ordered by recency try { List<ActivityManager.RunningTaskInfo> tasks = - ActivityTaskManager.getService().getFilteredTasks(1, ignoreActivityType, - WINDOWING_MODE_PINNED /* ignoreWindowingMode */); + ActivityTaskManager.getService().getFilteredTasks(1, filterOnlyVisibleRecents); if (tasks.isEmpty()) { return null; } 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..7cbc840afed4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -127,6 +127,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final boolean DEBUG = KeyguardConstants.DEBUG; private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES; private static final boolean DEBUG_FACE = true; + private static final boolean DEBUG_SPEW = false; private static final int LOW_BATTERY_THRESHOLD = 20; private static final String ACTION_FACE_UNLOCK_STARTED @@ -324,7 +325,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } }; - private class BiometricAuthenticated { + @VisibleForTesting + static class BiometricAuthenticated { private final boolean mAuthenticated; private final boolean mIsStrongBiometric; @@ -338,11 +340,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private SparseBooleanArray mUserHasTrust = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsUsuallyManaged = new SparseBooleanArray(); - private SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>(); - private SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>(); private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray(); private Map<Integer, Intent> mSecondaryLockscreenRequirement = new HashMap<Integer, Intent>(); + @VisibleForTesting + SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>(); + @VisibleForTesting + SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>(); + private static int sCurrentUser; private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState; @@ -966,17 +971,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); @@ -1847,11 +1855,33 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. - return (mBouncer || mAuthInterruptActive || awakeKeyguard || shouldListenForFaceAssistant()) + final boolean shouldListen = + (mBouncer || mAuthInterruptActive || awakeKeyguard + || shouldListenForFaceAssistant()) && !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer && !mKeyguardGoingAway && mFaceSettingEnabledForUser.get(user) && !mLockIconPressed && strongAuthAllowsScanning && mIsPrimaryUser && !mSecureCameraLaunched; + + // Too chatty, but very useful when debugging issues. + if (DEBUG_SPEW) { + Log.v(TAG, "shouldListenForFace(" + user + ")=" + shouldListen + "... " + + ", mBouncer: " + mBouncer + + ", mAuthInterruptActive: " + mAuthInterruptActive + + ", awakeKeyguard: " + awakeKeyguard + + ", shouldListenForFaceAssistant: " + shouldListenForFaceAssistant() + + ", mSwitchingUser: " + mSwitchingUser + + ", isFaceDisabled(" + user + "): " + isFaceDisabled(user) + + ", becauseCannotSkipBouncer: " + becauseCannotSkipBouncer + + ", mKeyguardGoingAway: " + mKeyguardGoingAway + + ", mFaceSettingEnabledForUser(" + user + "): " + + mFaceSettingEnabledForUser.get(user) + + ", mLockIconPressed: " + mLockIconPressed + + ", strongAuthAllowsScanning: " + strongAuthAllowsScanning + + ", isPrimaryUser: " + mIsPrimaryUser + + ", mSecureCameraLaunched: " + mSecureCameraLaunched); + } + return shouldListen; } /** @@ -2046,8 +2076,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab /** * Handle {@link #MSG_USER_SWITCHING} */ - private void handleUserSwitching(int userId, IRemoteCallback reply) { + @VisibleForTesting + void handleUserSwitching(int userId, IRemoteCallback reply) { Assert.isMainThread(); + clearBiometricRecognized(); mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId)); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); 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/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 5f004a631e46..281b566b6ffb 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -33,6 +33,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.Dimension; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -122,7 +123,8 @@ public class ScreenDecorations extends SystemUI implements Tunable { protected int mRoundedDefaultBottom; @VisibleForTesting protected View[] mOverlays; - private DisplayCutoutView[] mCutoutViews = new DisplayCutoutView[BOUNDS_POSITION_LENGTH]; + @Nullable + private DisplayCutoutView[] mCutoutViews; private float mDensity; private WindowManager mWindowManager; private int mRotation; @@ -135,18 +137,32 @@ public class ScreenDecorations extends SystemUI implements Tunable { new CameraAvailabilityListener.CameraTransitionCallback() { @Override public void onApplyCameraProtection(@NonNull Path protectionPath, @NonNull Rect bounds) { + if (mCutoutViews == null) { + Log.w(TAG, "DisplayCutoutView do not initialized"); + return; + } // Show the extra protection around the front facing camera if necessary for (DisplayCutoutView dcv : mCutoutViews) { - dcv.setProtection(protectionPath, bounds); - dcv.setShowProtection(true); + // Check Null since not all mCutoutViews[pos] be inflated at the meanwhile + if (dcv != null) { + dcv.setProtection(protectionPath, bounds); + dcv.setShowProtection(true); + } } } @Override public void onHideCameraProtection() { + if (mCutoutViews == null) { + Log.w(TAG, "DisplayCutoutView do not initialized"); + return; + } // Go back to the regular anti-aliasing for (DisplayCutoutView dcv : mCutoutViews) { - dcv.setShowProtection(false); + // Check Null since not all mCutoutViews[pos] be inflated at the meanwhile + if (dcv != null) { + dcv.setShowProtection(false); + } } } }; 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/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java index 13f3c0fce5c2..b006bc1351a3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java @@ -347,21 +347,35 @@ public abstract class AuthCredentialView extends LinearLayout { showError(message); } - // Only show popup dialog before wipe. + // Only show dialog if <=1 attempts are left before wiping. final int remainingAttempts = maxAttempts - numAttempts; - if (remainingAttempts <= 0) { - showNowWipingMessage(); - mContainerView.animateAway(AuthDialogCallback.DISMISSED_ERROR); + if (remainingAttempts == 1) { + showLastAttemptBeforeWipeDialog(); + } else if (remainingAttempts <= 0) { + showNowWipingDialog(); } return true; } - private void showNowWipingMessage() { + private void showLastAttemptBeforeWipeDialog() { + final AlertDialog alertDialog = new AlertDialog.Builder(mContext) + .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title) + .setMessage( + getLastAttemptBeforeWipeMessageRes(getUserTypeForWipe(), mCredentialType)) + .setPositiveButton(android.R.string.ok, null) + .create(); + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); + alertDialog.show(); + } + + private void showNowWipingDialog() { final AlertDialog alertDialog = new AlertDialog.Builder(mContext) .setMessage(getNowWipingMessageRes(getUserTypeForWipe())) .setPositiveButton(R.string.biometric_dialog_now_wiping_dialog_dismiss, null) + .setOnDismissListener( + dialog -> mContainerView.animateAway(AuthDialogCallback.DISMISSED_ERROR)) .create(); - alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); alertDialog.show(); } @@ -377,6 +391,59 @@ public abstract class AuthCredentialView extends LinearLayout { } } + private static @StringRes int getLastAttemptBeforeWipeMessageRes( + @UserType int userType, @Utils.CredentialType int credentialType) { + switch (userType) { + case USER_TYPE_PRIMARY: + return getLastAttemptBeforeWipeDeviceMessageRes(credentialType); + case USER_TYPE_MANAGED_PROFILE: + return getLastAttemptBeforeWipeProfileMessageRes(credentialType); + case USER_TYPE_SECONDARY: + return getLastAttemptBeforeWipeUserMessageRes(credentialType); + default: + throw new IllegalArgumentException("Unrecognized user type:" + userType); + } + } + + private static @StringRes int getLastAttemptBeforeWipeDeviceMessageRes( + @Utils.CredentialType int credentialType) { + switch (credentialType) { + case Utils.CREDENTIAL_PIN: + return R.string.biometric_dialog_last_pin_attempt_before_wipe_device; + case Utils.CREDENTIAL_PATTERN: + return R.string.biometric_dialog_last_pattern_attempt_before_wipe_device; + case Utils.CREDENTIAL_PASSWORD: + default: + return R.string.biometric_dialog_last_password_attempt_before_wipe_device; + } + } + + private static @StringRes int getLastAttemptBeforeWipeProfileMessageRes( + @Utils.CredentialType int credentialType) { + switch (credentialType) { + case Utils.CREDENTIAL_PIN: + return R.string.biometric_dialog_last_pin_attempt_before_wipe_profile; + case Utils.CREDENTIAL_PATTERN: + return R.string.biometric_dialog_last_pattern_attempt_before_wipe_profile; + case Utils.CREDENTIAL_PASSWORD: + default: + return R.string.biometric_dialog_last_password_attempt_before_wipe_profile; + } + } + + private static @StringRes int getLastAttemptBeforeWipeUserMessageRes( + @Utils.CredentialType int credentialType) { + switch (credentialType) { + case Utils.CREDENTIAL_PIN: + return R.string.biometric_dialog_last_pin_attempt_before_wipe_user; + case Utils.CREDENTIAL_PATTERN: + return R.string.biometric_dialog_last_pattern_attempt_before_wipe_user; + case Utils.CREDENTIAL_PASSWORD: + default: + return R.string.biometric_dialog_last_password_attempt_before_wipe_user; + } + } + private static @StringRes int getNowWipingMessageRes(@UserType int userType) { switch (userType) { case USER_TYPE_PRIMARY: diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java index a1cb7f61ad04..0b59ebcd57e9 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java @@ -28,6 +28,8 @@ import com.android.launcher3.icons.DotRenderer; import com.android.systemui.Interpolators; import com.android.systemui.R; +import java.util.EnumSet; + /** * View that displays an adaptive icon with an app-badge and a dot. * @@ -42,12 +44,27 @@ public class BadgedImageView extends ImageView { /** Same as value in Launcher3 IconShape */ public static final int DEFAULT_PATH_SIZE = 100; - static final int DOT_STATE_DEFAULT = 0; - static final int DOT_STATE_SUPPRESSED_FOR_FLYOUT = 1; - static final int DOT_STATE_ANIMATING = 2; + /** + * Flags that suppress the visibility of the 'new' dot, for one reason or another. If any of + * these flags are set, the dot will not be shown even if {@link Bubble#showDot()} returns true. + */ + enum SuppressionFlag { + // Suppressed because the flyout is visible - it will morph into the dot via animation. + FLYOUT_VISIBLE, + // Suppressed because this bubble is behind others in the collapsed stack. + BEHIND_STACK, + } - // Flyout gets shown before the dot - private int mCurrentDotState = DOT_STATE_SUPPRESSED_FOR_FLYOUT; + /** + * Start by suppressing the dot because the flyout is visible - most bubbles are added with a + * flyout, so this is a reasonable default. + */ + private final EnumSet<SuppressionFlag> mDotSuppressionFlags = + EnumSet.of(SuppressionFlag.FLYOUT_VISIBLE); + + private float mDotScale = 0f; + private float mAnimatingToDotScale = 0f; + private boolean mDotIsAnimating = false; private BubbleViewProvider mBubble; @@ -57,8 +74,6 @@ public class BadgedImageView extends ImageView { private boolean mOnLeft; private int mDotColor; - private float mDotScale = 0f; - private boolean mDotDrawn; private Rect mTempBounds = new Rect(); @@ -88,23 +103,21 @@ public class BadgedImageView extends ImageView { /** * Updates the view with provided info. */ - public void update(BubbleViewProvider bubble) { + public void setRenderedBubble(BubbleViewProvider bubble) { mBubble = bubble; setImageBitmap(bubble.getBadgedImage()); - setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT); mDotColor = bubble.getDotColor(); drawDot(bubble.getDotPath()); - animateDot(); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); - if (isDotHidden()) { - mDotDrawn = false; + + if (!shouldDrawDot()) { return; } - mDotDrawn = mDotScale > 0.1f; + getDrawingRect(mTempBounds); mDrawParams.color = mDotColor; @@ -115,23 +128,33 @@ public class BadgedImageView extends ImageView { mDotRenderer.draw(canvas, mDrawParams); } - /** - * Sets the dot state, does not animate changes. - */ - void setDotState(int state) { - mCurrentDotState = state; - if (state == DOT_STATE_SUPPRESSED_FOR_FLYOUT || state == DOT_STATE_DEFAULT) { - mDotScale = mBubble.showDot() ? 1f : 0f; - invalidate(); + /** Adds a dot suppression flag, updating dot visibility if needed. */ + void addDotSuppressionFlag(SuppressionFlag flag) { + if (mDotSuppressionFlags.add(flag)) { + // Update dot visibility, and animate out if we're now behind the stack. + updateDotVisibility(flag == SuppressionFlag.BEHIND_STACK /* animate */); } } - /** - * Whether the dot should be hidden based on current dot state. - */ - private boolean isDotHidden() { - return (mCurrentDotState == DOT_STATE_DEFAULT && !mBubble.showDot()) - || mCurrentDotState == DOT_STATE_SUPPRESSED_FOR_FLYOUT; + /** Removes a dot suppression flag, updating dot visibility if needed. */ + void removeDotSuppressionFlag(SuppressionFlag flag) { + if (mDotSuppressionFlags.remove(flag)) { + // Update dot visibility, animating if we're no longer behind the stack. + updateDotVisibility(flag == SuppressionFlag.BEHIND_STACK); + } + } + + /** Updates the visibility of the dot, animating if requested. */ + void updateDotVisibility(boolean animate) { + final float targetScale = shouldDrawDot() ? 1f : 0f; + + if (animate) { + animateDotScale(targetScale, null /* after */); + } else { + mDotScale = targetScale; + mAnimatingToDotScale = targetScale; + invalidate(); + } } /** @@ -194,11 +217,11 @@ public class BadgedImageView extends ImageView { } /** Sets the position of the 'new' dot, animating it out and back in if requested. */ - void setDotPosition(boolean onLeft, boolean animate) { - if (animate && onLeft != getDotOnLeft() && !isDotHidden()) { - animateDot(false /* showDot */, () -> { + void setDotPositionOnLeft(boolean onLeft, boolean animate) { + if (animate && onLeft != getDotOnLeft() && shouldDrawDot()) { + animateDotScale(0f /* showDot */, () -> { setDotOnLeft(onLeft); - animateDot(true /* showDot */, null); + animateDotScale(1.0f, null /* after */); }); } else { setDotOnLeft(onLeft); @@ -209,28 +232,34 @@ public class BadgedImageView extends ImageView { return getDotOnLeft(); } - /** Changes the dot's visibility to match the bubble view's state. */ - void animateDot() { - if (mCurrentDotState == DOT_STATE_DEFAULT) { - animateDot(mBubble.showDot(), null); - } + /** Whether to draw the dot in onDraw(). */ + private boolean shouldDrawDot() { + // Always render the dot if it's animating, since it could be animating out. Otherwise, show + // it if the bubble wants to show it, and we aren't suppressing it. + return mDotIsAnimating || (mBubble.showDot() && mDotSuppressionFlags.isEmpty()); } /** - * Animates the dot to show or hide. + * Animates the dot to the given scale, running the optional callback when the animation ends. */ - private void animateDot(boolean showDot, Runnable after) { - if (mDotDrawn == showDot) { - // State is consistent, do nothing. + private void animateDotScale(float toScale, @Nullable Runnable after) { + mDotIsAnimating = true; + + // Don't restart the animation if we're already animating to the given value. + if (mAnimatingToDotScale == toScale || !shouldDrawDot()) { + mDotIsAnimating = false; return; } - setDotState(DOT_STATE_ANIMATING); + mAnimatingToDotScale = toScale; + + final boolean showDot = toScale > 0f; // Do NOT wait until after animation ends to setShowDot // to avoid overriding more recent showDot states. clearAnimation(); - animate().setDuration(200) + animate() + .setDuration(200) .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .setUpdateListener((valueAnimator) -> { float fraction = valueAnimator.getAnimatedFraction(); @@ -238,7 +267,7 @@ public class BadgedImageView extends ImageView { setDotScale(fraction); }).withEndAction(() -> { setDotScale(showDot ? 1f : 0f); - setDotState(DOT_STATE_DEFAULT); + mDotIsAnimating = false; if (after != null) { after.run(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 726a7dd111d7..afa3164cbd38 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -247,7 +247,7 @@ class Bubble implements BubbleViewProvider { mExpandedView.update(/* bubble */ this); } if (mIconView != null) { - mIconView.update(/* bubble */ this); + mIconView.setRenderedBubble(/* bubble */ this); } } @@ -306,7 +306,7 @@ class Bubble implements BubbleViewProvider { void markAsAccessedAt(long lastAccessedMillis) { mLastAccessed = lastAccessedMillis; setSuppressNotification(true); - setShowDot(false /* show */, true /* animate */); + setShowDot(false /* show */); } /** @@ -346,12 +346,11 @@ class Bubble implements BubbleViewProvider { /** * Sets whether the bubble for this notification should show a dot indicating updated content. */ - void setShowDot(boolean showDot, boolean animate) { + void setShowDot(boolean showDot) { mShowBubbleUpdateDot = showDot; - if (animate && mIconView != null) { - mIconView.animateDot(); - } else if (mIconView != null) { - mIconView.invalidate(); + + if (mIconView != null) { + mIconView.updateDotVisibility(true /* animate */); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index c9ce8a10cca7..9d885fd3c207 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; @@ -327,19 +331,20 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @Override public void onZenChanged(int zen) { for (Bubble b : mBubbleData.getBubbles()) { - b.setShowDot(b.showInShade(), true /* animate */); + b.setShowDot(b.showInShade()); } } @Override public void onConfigChanged(ZenModeConfig config) { for (Bubble b : mBubbleData.getBubbles()) { - b.setShowDot(b.showInShade(), true /* animate */); + b.setShowDot(b.showInShade()); } } }); 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); } @@ -1094,7 +1101,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } else if (interceptBubbleDismissal) { Bubble bubble = mBubbleData.getBubbleWithKey(entry.getKey()); bubble.setSuppressNotification(true); - bubble.setShowDot(false /* show */, true /* animate */); + bubble.setShowDot(false /* show */); } else { return false; } @@ -1134,7 +1141,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi Bubble bubbleChild = mBubbleData.getBubbleWithKey(child.getKey()); mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry()); bubbleChild.setSuppressNotification(true); - bubbleChild.setShowDot(false /* show */, true /* animate */); + bubbleChild.setShowDot(false /* show */); } else { // non-bubbled children can be removed for (NotifCallback cb : mCallbacks) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index fcbd9121fcda..1c69594469c1 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; } } @@ -286,7 +288,7 @@ public class BubbleData { boolean isBubbleExpandedAndSelected = mExpanded && mSelectedBubble == bubble; boolean suppress = isBubbleExpandedAndSelected || !showInShade || !bubble.showInShade(); bubble.setSuppressNotification(suppress); - bubble.setShowDot(!isBubbleExpandedAndSelected /* show */, true /* animate */); + bubble.setShowDot(!isBubbleExpandedAndSelected /* show */); dispatchPendingChanges(); } @@ -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/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java index 4fb2d0881ede..13669a68defa 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java @@ -112,7 +112,7 @@ public class BubbleOverflow implements BubbleViewProvider { mPath.transform(matrix); mOverflowBtn.setVisibility(GONE); - mOverflowBtn.update(this); + mOverflowBtn.setRenderedBubble(this); } ImageView getBtn() { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java index 7636c6712e41..2231d11b7bc2 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.setRenderedBubble(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..4b036812dbed 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -22,8 +22,6 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION; import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION; -import static com.android.systemui.bubbles.BadgedImageView.DOT_STATE_DEFAULT; -import static com.android.systemui.bubbles.BadgedImageView.DOT_STATE_SUPPRESSED_FOR_FLYOUT; import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW; import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; @@ -80,6 +78,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; @@ -224,7 +224,7 @@ public class BubbleStackView extends FrameLayout { private boolean mIsExpanded; /** Whether the stack is currently on the left side of the screen, or animating there. */ - private boolean mStackOnLeftOrWillBe = false; + private boolean mStackOnLeftOrWillBe = true; /** Whether a touch gesture, such as a stack/bubble drag or flyout drag, is in progress. */ private boolean mIsGestureInProgress = false; @@ -241,6 +241,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 +438,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 +447,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); @@ -930,9 +934,13 @@ public class BubbleStackView extends FrameLayout { mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide(); } + if (bubble.getIconView() == null) { + return; + } + // Set the dot position to the opposite of the side the stack is resting on, since the stack // resting slightly off-screen would result in the dot also being off-screen. - bubble.getIconView().setDotPosition( + bubble.getIconView().setDotPositionOnLeft( !mStackOnLeftOrWillBe /* onLeft */, false /* animate */); mBubbleContainer.addView(bubble.getIconView(), 0, @@ -940,7 +948,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 +958,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 +1063,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); @@ -1686,7 +1700,7 @@ public class BubbleStackView extends FrameLayout { || mBubbleToExpandAfterFlyoutCollapse != null || bubbleView == null) { if (bubbleView != null) { - bubbleView.setDotState(DOT_STATE_DEFAULT); + bubbleView.removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); } // Skip the message if none exists, we're expanded or animating expansion, or we're // about to expand a bubble from the previous tapped flyout, or if bubble view is null. @@ -1705,12 +1719,16 @@ public class BubbleStackView extends FrameLayout { mBubbleData.setExpanded(true); mBubbleToExpandAfterFlyoutCollapse = null; } - bubbleView.setDotState(DOT_STATE_DEFAULT); + + // Stop suppressing the dot now that the flyout has morphed into the dot. + bubbleView.removeDotSuppressionFlag( + BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); }; mFlyout.setVisibility(INVISIBLE); - // Don't show the dot when we're animating the flyout - bubbleView.setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT); + // Suppress the dot when we are animating the flyout. + bubbleView.addDotSuppressionFlag( + BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); // Start flyout expansion. Post in case layout isn't complete and getWidth returns 0. post(() -> { @@ -1731,6 +1749,11 @@ public class BubbleStackView extends FrameLayout { }; mFlyout.postDelayed(mAnimateInFlyout, 200); }; + + if (bubble.getIconView() == null) { + return; + } + mFlyout.setupFlyoutStartingAsDot(flyoutMessage, mStackAnimationController.getStackPosition(), getWidth(), mStackAnimationController.isStackOnLeftSide(), @@ -1865,9 +1888,19 @@ public class BubbleStackView extends FrameLayout { for (int i = 0; i < bubbleCount; i++) { BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i); bv.setZ((mMaxBubbles * mBubbleElevation) - i); + // If the dot is on the left, and so is the stack, we need to change the dot position. if (bv.getDotPositionOnLeft() == mStackOnLeftOrWillBe) { - bv.setDotPosition(!mStackOnLeftOrWillBe, animate); + bv.setDotPositionOnLeft(!mStackOnLeftOrWillBe, animate); + } + + if (!mIsExpanded && i > 0) { + // If we're collapsed and this bubble is behind other bubbles, suppress its dot. + bv.addDotSuppressionFlag( + BadgedImageView.SuppressionFlag.BEHIND_STACK); + } else { + bv.removeDotSuppressionFlag( + BadgedImageView.SuppressionFlag.BEHIND_STACK); } } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index 7ee162e03dbc..00de8b4a51b8 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -287,7 +287,7 @@ public class StackAnimationController extends /** Whether the stack is on the left side of the screen. */ public boolean isStackOnLeftSide() { if (mLayout == null || !isStackPositionSet()) { - return false; + return true; // Default to left, which is where it starts by default. } float stackCenter = mStackPosition.x + mBubbleBitmapSize / 2; 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/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index 6c49c82acdc0..fdb0e4c95bed 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -65,7 +65,7 @@ class ControlsControllerImpl @Inject constructor ( companion object { private const val TAG = "ControlsControllerImpl" - internal const val CONTROLS_AVAILABLE = "systemui.controls_available" + internal const val CONTROLS_AVAILABLE = Settings.Secure.CONTROLS_ENABLED internal val URI = Settings.Secure.getUriFor(CONTROLS_AVAILABLE) private const val USER_CHANGE_RETRY_DELAY = 500L // ms private const val DEFAULT_ENABLED = 1 @@ -252,10 +252,17 @@ class ControlsControllerImpl @Inject constructor ( it.controlId in favoritesForComponentKeys ) } + val removedControls = mutableListOf<ControlStatus>() + Favorites.getStructuresForComponent(componentName).forEach { st -> + st.controls.forEach { + if (it.controlId in removed) { + val r = createRemovedStatus(componentName, it, st.structure) + removedControls.add(r) + } + } + } val loadData = createLoadDataObject( - Favorites.getControlsForComponent(componentName) - .filter { it.controlId in removed } - .map { createRemovedStatus(componentName, it) } + + removedControls + controlsWithFavorite, favoritesForComponentKeys ) @@ -266,17 +273,15 @@ class ControlsControllerImpl @Inject constructor ( override fun error(message: String) { loadCanceller = null executor.execute { - val loadData = Favorites.getControlsForComponent(componentName) - .let { controls -> - val keys = controls.map { it.controlId } - createLoadDataObject( - controls.map { - createRemovedStatus(componentName, it, false) - }, - keys, - true - ) - } + val controls = Favorites.getStructuresForComponent(componentName) + .flatMap { st -> + st.controls.map { + createRemovedStatus(componentName, it, st.structure, + false) + } + } + val keys = controls.map { it.control.controlId } + val loadData = createLoadDataObject(controls, keys, true) dataCallback.accept(loadData) } } @@ -372,6 +377,7 @@ class ControlsControllerImpl @Inject constructor ( private fun createRemovedStatus( componentName: ComponentName, controlInfo: ControlInfo, + structure: CharSequence, setRemoved: Boolean = true ): ControlStatus { val intent = Intent(Intent.ACTION_MAIN).apply { @@ -384,6 +390,8 @@ class ControlsControllerImpl @Inject constructor ( 0) val control = Control.StatelessBuilder(controlInfo.controlId, pendingIntent) .setTitle(controlInfo.controlTitle) + .setSubtitle(controlInfo.controlSubtitle) + .setStructure(structure) .setDeviceType(controlInfo.deviceType) .build() return ControlStatus(control, componentName, true, setRemoved) @@ -431,13 +439,14 @@ class ControlsControllerImpl @Inject constructor ( Log.d(TAG, "Controls not available") return } - executor.execute { - val changed = Favorites.updateControls( - componentName, - listOf(control) - ) - if (changed) { - persistenceWrapper.storeFavorites(Favorites.getAllStructures()) + + // Assume that non STATUS_OK responses may contain incomplete or invalid information about + // the control, and do not attempt to update it + if (control.getStatus() == Control.STATUS_OK) { + executor.execute { + if (Favorites.updateControls(componentName, listOf(control))) { + persistenceWrapper.storeFavorites(Favorites.getAllStructures()) + } } } uiController.onRefreshState(componentName, listOf(control)) 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/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index 563c2f677801..764fda05354c 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -68,6 +68,8 @@ class ControlAdapter( width = ViewGroup.LayoutParams.MATCH_PARENT } elevation = this@ControlAdapter.elevation + background = parent.context.getDrawable( + R.drawable.control_background_ripple) } ) { id, favorite -> model?.changeFavoriteStatus(id, favorite) @@ -137,10 +139,7 @@ private class ControlHolder(view: View, val favoriteCallback: ModelFavoriteChang private val title: TextView = itemView.requireViewById(R.id.title) private val subtitle: TextView = itemView.requireViewById(R.id.subtitle) private val removed: TextView = itemView.requireViewById(R.id.status) - private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite) - private val favoriteFrame: ViewGroup = itemView - .requireViewById<ViewGroup>(R.id.favorite_container) - .apply { + private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply { visibility = View.VISIBLE } @@ -155,7 +154,7 @@ private class ControlHolder(view: View, val favoriteCallback: ModelFavoriteChang favorite.setOnClickListener { favoriteCallback(data.control.controlId, favorite.isChecked) } - favoriteFrame.setOnClickListener { favorite.performClick() } + itemView.setOnClickListener { favorite.performClick() } applyRenderInfo(renderInfo) } 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/ChallengeDialogs.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt index 15c2a0afe819..a7a41033bb5d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt @@ -34,24 +34,29 @@ import com.android.systemui.R /** * Creates all dialogs for challengeValues that can occur from a call to - * {@link ControlsProviderService#performControlAction}. The types of challenge - * responses are listed in {@link ControlAction.ResponseResult}. + * [ControlsProviderService#performControlAction]. The types of challenge responses are listed in + * [ControlAction.ResponseResult]. */ object ChallengeDialogs { - fun createPinDialog(cvh: ControlViewHolder): Dialog? { + private const val WINDOW_TYPE = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY + private const val STYLE = android.R.style.Theme_DeviceDefault_Dialog_Alert + + /** + * AlertDialogs to handle [ControlAction#RESPONSE_CHALLENGE_PIN] and + * [ControlAction#RESPONSE_CHALLENGE_PIN] responses, decided by the useAlphaNumeric + * parameter. + */ + fun createPinDialog(cvh: ControlViewHolder, useAlphaNumeric: Boolean): Dialog? { val lastAction = cvh.lastAction if (lastAction == null) { Log.e(ControlsUiController.TAG, "PIN Dialog attempted but no last action is set. Will not show") return null } - val builder = AlertDialog.Builder( - cvh.context, - android.R.style.Theme_DeviceDefault_Dialog_Alert - ).apply { + val builder = AlertDialog.Builder(cvh.context, STYLE).apply { val res = cvh.context.resources - setTitle(res.getString(R.string.controls_pin_verify, *arrayOf(cvh.title.getText()))) + setTitle(res.getString(R.string.controls_pin_verify, cvh.title.getText())) setView(R.layout.controls_dialog_pin) setPositiveButton( android.R.string.ok, @@ -71,25 +76,64 @@ object ChallengeDialogs { } return builder.create().apply { getWindow().apply { - setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY) + setType(WINDOW_TYPE) setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE) } setOnShowListener(DialogInterface.OnShowListener { _ -> val editText = requireViewById<EditText>(R.id.controls_pin_input) - requireViewById<CheckBox>(R.id.controls_pin_use_alpha).setOnClickListener { v -> - if ((v as CheckBox).isChecked) { - editText.setInputType( - InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD) - } else { - editText.setInputType( - InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD) - } + val useAlphaCheckBox = requireViewById<CheckBox>(R.id.controls_pin_use_alpha) + useAlphaCheckBox.setChecked(useAlphaNumeric) + setInputType(editText, useAlphaCheckBox.isChecked()) + requireViewById<CheckBox>(R.id.controls_pin_use_alpha).setOnClickListener { _ -> + setInputType(editText, useAlphaCheckBox.isChecked()) } editText.requestFocus() }) } } + /** + * AlertDialogs to handle [ControlAction#RESPONSE_CHALLENGE_ACK] response type. + */ + fun createConfirmationDialog(cvh: ControlViewHolder): Dialog? { + val lastAction = cvh.lastAction + if (lastAction == null) { + Log.e(ControlsUiController.TAG, + "Confirmation Dialog attempted but no last action is set. Will not show") + return null + } + val builder = AlertDialog.Builder(cvh.context, STYLE).apply { + val res = cvh.context.resources + setMessage(res.getString( + R.string.controls_confirmation_message, cvh.title.getText())) + setPositiveButton( + android.R.string.ok, + DialogInterface.OnClickListener { dialog, _ -> + cvh.action(addChallengeValue(lastAction, "true")) + dialog.dismiss() + }) + setNegativeButton( + android.R.string.cancel, + DialogInterface.OnClickListener { dialog, _ -> dialog.cancel() } + ) + } + return builder.create().apply { + getWindow().apply { + setType(WINDOW_TYPE) + } + } + } + + private fun setInputType(editText: EditText, useTextInput: Boolean) { + if (useTextInput) { + editText.setInputType( + InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD) + } else { + editText.setInputType( + InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD) + } + } + private fun addChallengeValue(action: ControlAction, challengeValue: String): ControlAction { val id = action.getTemplateId() return when (action) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index 9f5dd02b4514..7d3a86091869 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -21,6 +21,7 @@ import android.graphics.drawable.ClipDrawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.service.controls.Control +import android.service.controls.DeviceTypes import android.service.controls.actions.ControlAction import android.service.controls.templates.ControlTemplate import android.service.controls.templates.StatelessTemplate @@ -156,7 +157,11 @@ class ControlViewHolder( statusExtra.setTextColor(fg) icon.setImageDrawable(ri.icon) - icon.setImageTintList(fg) + + // do not color app icons + if (deviceType != DeviceTypes.TYPE_ROUTINE) { + icon.setImageTintList(fg) + } (clipLayer.getDrawable() as GradientDrawable).apply { setColor(context.getResources().getColor(bg, context.getTheme())) 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..05a0c45c2e15 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 @@ -439,7 +442,15 @@ class ControlsUiControllerImpl @Inject constructor ( controlViewsById.get(key)?.let { cvh -> when (response) { ControlAction.RESPONSE_CHALLENGE_PIN -> { - activeDialog = ChallengeDialogs.createPinDialog(cvh) + activeDialog = ChallengeDialogs.createPinDialog(cvh, false) + activeDialog?.show() + } + ControlAction.RESPONSE_CHALLENGE_PASSPHRASE -> { + activeDialog = ChallengeDialogs.createPinDialog(cvh, true) + activeDialog?.show() + } + ControlAction.RESPONSE_CHALLENGE_ACK -> { + activeDialog = ChallengeDialogs.createConfirmationDialog(cvh) activeDialog?.show() } else -> cvh.actionResponse(response) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt index 27e46497b20a..810ea65c7873 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt @@ -64,7 +64,7 @@ data class RenderInfo( val iconState = deviceIconMap.getValue(iconKey) val resourceId = iconState[enabled] - var icon: Drawable? = null + var icon: Drawable? if (resourceId == APP_ICON_ID) { icon = appIconMap.get(componentName) if (icon == null) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt index c495c58fff2a..f79c8b2393d0 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt @@ -64,12 +64,13 @@ class ToggleRangeBehavior : Behavior { val gestureListener = ToggleRangeGestureListener(cvh.layout) val gestureDetector = GestureDetector(context, gestureListener) - cvh.layout.setOnTouchListener { _: View, e: MotionEvent -> + cvh.layout.setOnTouchListener { v: View, e: MotionEvent -> if (gestureDetector.onTouchEvent(e)) { return@setOnTouchListener true } if (e.getAction() == MotionEvent.ACTION_UP && gestureListener.isDragging) { + v.getParent().requestDisallowInterceptTouchEvent(false) gestureListener.isDragging = false endUpdateRange() return@setOnTouchListener true @@ -254,6 +255,7 @@ class ToggleRangeBehavior : Behavior { yDiff: Float ): Boolean { if (!isDragging) { + v.getParent().requestDisallowInterceptTouchEvent(true) this@ToggleRangeBehavior.beginUpdateRange() isDragging = true } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java index e50f1fb2a3ee..915359374bfe 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -92,7 +92,7 @@ public class DozeScreenState implements DozeMachine.Part { final boolean pulseEnding = oldState == DOZE_PULSE_DONE && newState.isAlwaysOn(); final boolean turningOn = (oldState == DOZE_AOD_PAUSED || oldState == DOZE) && newState.isAlwaysOn(); - final boolean turningOff = (newState.isAlwaysOn() && newState == DOZE) + final boolean turningOff = (oldState.isAlwaysOn() && newState == DOZE) || (oldState == DOZE_AOD_PAUSING && newState == DOZE_AOD_PAUSED); final boolean justInitialized = oldState == DozeMachine.State.INITIALIZED; if (messagePending || justInitialized || pulseEnding || turningOn) { 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/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 12955a153360..ce29859f12ca 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -49,8 +49,6 @@ import dagger.Lazy; public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks { - private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f; - private final Context mContext; private final Lazy<GlobalActionsDialog> mGlobalActionsDialogLazy; private final KeyguardStateController mKeyguardStateController; @@ -124,7 +122,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); window.setBackgroundDrawable(background); - window.setWindowAnimations(R.style.Animation_Toast); + window.setWindowAnimations(com.android.systemui.R.style.Animation_ShutdownUi); d.setContentView(R.layout.shutdown_dialog); d.setCancelable(false); @@ -153,7 +151,9 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(), mBlurUtils.blurRadiusOfRatio(1)); } else { - background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255)); + float backgroundAlpha = mContext.getResources().getFloat( + com.android.systemui.R.dimen.shutdown_scrim_behind_alpha); + background.setAlpha((int) (backgroundAlpha * 255)); } d.show(); 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 e64a9190ae91..6ce5e7cf506c 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -42,6 +42,7 @@ import android.window.ITaskOrganizer; import android.window.IWindowContainer; import android.view.SurfaceControl; import android.window.WindowContainerTransaction; +import android.window.WindowOrganizer; import com.android.internal.os.SomeArgs; import com.android.systemui.R; @@ -76,7 +77,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { private final Handler mMainHandler; private final Handler mUpdateHandler; - private final ITaskOrganizerController mTaskOrganizerController; private final PipBoundsHandler mPipBoundsHandler; private final PipAnimationController mPipAnimationController; private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); @@ -188,7 +188,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper) { mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); - mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController(); mPipBoundsHandler = boundsHandler; mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); @@ -218,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), @@ -251,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()) { @@ -265,10 +264,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } @Override - public void transactionReady(int id, SurfaceControl.Transaction t) { - } - - @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { final PictureInPictureParams newParams = info.pictureInPictureParams; if (!shouldUpdateDestinationBounds(newParams)) { @@ -351,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); } @@ -450,7 +446,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { wct.setBounds(mToken, taskBounds); } wct.setBoundsChangeTransaction(mToken, tx); - mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */); + WindowOrganizer.applyTransaction(wct); } catch (RemoteException e) { Log.e(TAG, "Failed to apply container transaction", e); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index a0dff3f98173..e89ce2e3a56f 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -18,6 +18,7 @@ package com.android.systemui.pip.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.window.WindowOrganizer.TaskOrganizer; import android.app.ActivityManager; import android.app.ActivityTaskManager; @@ -35,6 +36,7 @@ import android.util.Pair; import android.view.DisplayInfo; import android.view.IPinnedStackController; import android.window.WindowContainerTransaction; +import android.window.WindowOrganizer; import com.android.systemui.Dependency; import com.android.systemui.UiOffloadThread; @@ -248,8 +250,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mPipBoundsHandler.onDisplayInfoChanged(displayInfo); try { - ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer( - mPipTaskOrganizer, WINDOWING_MODE_PINNED); + TaskOrganizer.registerOrganizer(mPipTaskOrganizer, WINDOWING_MODE_PINNED); ActivityManager.StackInfo stackInfo = activityTaskManager.getStackInfo( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (stackInfo != 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/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 050acd5a8728..2dcf1f89c3ec 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -19,6 +19,7 @@ package com.android.systemui.pip.tv; import static android.app.ActivityTaskManager.INVALID_STACK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.window.WindowOrganizer.TaskOrganizer; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.StackInfo; @@ -44,6 +45,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.view.DisplayInfo; +import android.window.WindowOrganizer; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -293,8 +295,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio try { WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener); - ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer( - mPipTaskOrganizer, WINDOWING_MODE_PINNED); + TaskOrganizer.registerOrganizer(mPipTaskOrganizer, WINDOWING_MODE_PINNED); } catch (RemoteException e) { Log.e(TAG, "Failed to register pinned stack listener", e); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 0403a0505b00..cd737217b84a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -142,6 +142,9 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { @Override public void computeScroll() { if (!mScroller.isFinished() && mScroller.computeScrollOffset()) { + if (!isFakeDragging()) { + beginFakeDrag(); + } fakeDragBy(getScrollX() - mScroller.getCurrX()); // Keep on drawing until the animation has finished. postInvalidateOnAnimation(); 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/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java index aa6444973a6f..3b3d9dde3b7e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java @@ -17,7 +17,6 @@ package com.android.systemui.recents; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.systemui.Prefs.Key.DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT; @@ -27,8 +26,7 @@ import static com.android.systemui.Prefs.Key.HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDIN import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_COUNT; import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_FROM_HOME_COUNT; import static com.android.systemui.shared.system.LauncherEventUtil.DISMISS; -import static com.android.systemui.shared.system.LauncherEventUtil - .RECENTS_QUICK_SCRUB_ONBOARDING_TIP; +import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_QUICK_SCRUB_ONBOARDING_TIP; import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP; import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE; @@ -139,7 +137,7 @@ public class RecentsOnboarding { private void onAppLaunch() { ActivityManager.RunningTaskInfo info = ActivityManagerWrapper.getInstance() - .getRunningTask(ACTIVITY_TYPE_UNDEFINED /* ignoreActivityType */); + .getRunningTask(); if (info == null) { return; } 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/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index 3d5281247f63..01498f933383 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -20,11 +20,11 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; +import static android.window.WindowOrganizer.TaskOrganizer; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; -import android.app.ActivityTaskManager; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -38,6 +38,7 @@ import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; import android.window.WindowContainerTransaction; +import android.window.WindowOrganizer; import androidx.annotation.Nullable; @@ -177,8 +178,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, private boolean getSecondaryHasFocus(int displayId) { try { - IWindowContainer imeSplit = ActivityTaskManager.getTaskOrganizerController() - .getImeTarget(displayId); + IWindowContainer imeSplit = TaskOrganizer.getImeTarget(displayId); return imeSplit != null && (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder()); } catch (RemoteException e) { @@ -267,8 +267,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); } try { - ActivityTaskManager.getTaskOrganizerController() - .applyContainerTransaction(wct, null /* organizer */); + WindowOrganizer.applyTransaction(wct); } catch (RemoteException e) { } @@ -474,13 +473,12 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, mImeController.addPositionProcessor(mImePositionProcessor); mDisplayController.addDisplayChangingController(mRotationController); try { - mSplits.init(ActivityTaskManager.getTaskOrganizerController(), mSurfaceSession); + mSplits.init(mSurfaceSession); // Set starting tile bounds based on middle target final WindowContainerTransaction tct = new WindowContainerTransaction(); int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position; mSplitLayout.resizeSplits(midPos, tct); - ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(tct, - null /* organizer */); + WindowOrganizer.applyTransaction(tct); } catch (Exception e) { Slog.e(TAG, "Failed to register docked stack listener", e); } @@ -499,8 +497,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, final WindowContainerTransaction tct = new WindowContainerTransaction(); mSplitLayout.resizeSplits(midPos, tct); try { - ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(tct, - null /* organizer */); + WindowOrganizer.applyTransaction(tct); } catch (RemoteException e) { } } else if (mRotateSplitLayout != null diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 131f4e1c9d59..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 @@ -339,7 +343,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, insets = state.calculateInsets(state.getDisplayFrame(), null /* ignoringVisibilityState */, insets.isRound(), insets.shouldAlwaysConsumeSystemBars(), insets.getDisplayCutout(), - null /* legacyContentInsets */, null /* legacyStableInsets */, 0 /* legacySystemUiFlags */, SOFT_INPUT_ADJUST_NOTHING, null /* typeSideMap */); } @@ -355,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(); @@ -1086,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 f82936a4492b..6cb7f4ff7204 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java @@ -22,16 +22,19 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.view.Display.DEFAULT_DISPLAY; +import static android.window.WindowOrganizer.TaskOrganizer; import android.app.ActivityManager.RunningTaskInfo; -import android.window.ITaskOrganizerController; 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,19 +46,20 @@ 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) { mDivider = divider; } - void init(ITaskOrganizerController organizerController, SurfaceSession session) - throws RemoteException { - organizerController.registerTaskOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - organizerController.registerTaskOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - mPrimary = organizerController.createRootTask(Display.DEFAULT_DISPLAY, + void init(SurfaceSession session) throws RemoteException { + TaskOrganizer.registerOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + TaskOrganizer.registerOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + mPrimary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY, WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - mSecondary = organizerController.createRootTask(Display.DEFAULT_DISPLAY, + mSecondary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY, WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); mPrimarySurface = mPrimary.token.getLeash(); mSecondarySurface = mSecondary.token.getLeash(); @@ -83,15 +87,11 @@ class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub { } @Override - public void taskAppeared(RunningTaskInfo taskInfo) { - } - - @Override - public void taskVanished(RunningTaskInfo taskInfo) { + public void onTaskAppeared(RunningTaskInfo taskInfo) { } @Override - public void transactionReady(int id, SurfaceControl.Transaction t) { + 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 0e391ec7580a..6ed7afe152df 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.Display.DEFAULT_DISPLAY; +import static android.window.WindowOrganizer.TaskOrganizer; import android.annotation.NonNull; import android.app.ActivityManager; @@ -28,9 +29,10 @@ 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; @@ -111,8 +113,7 @@ public class WindowManagerProxy { WindowContainerTransaction t = new WindowContainerTransaction(); splitLayout.resizeSplits(position, t); try { - ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(t, - null /* organizer */); + WindowOrganizer.applyTransaction(t); } catch (RemoteException e) { } } @@ -122,10 +123,8 @@ public class WindowManagerProxy { boolean resizable = false; try { List<ActivityManager.RunningTaskInfo> rootTasks = parent == null - ? ActivityTaskManager.getTaskOrganizerController().getRootTasks( - Display.DEFAULT_DISPLAY, HOME_AND_RECENTS) - : ActivityTaskManager.getTaskOrganizerController().getChildTasks(parent, - HOME_AND_RECENTS); + ? TaskOrganizer.getRootTasks(Display.DEFAULT_DISPLAY, HOME_AND_RECENTS) + : TaskOrganizer.getChildTasks(parent, HOME_AND_RECENTS); for (int i = 0, n = rootTasks.size(); i < n; ++i) { final ActivityManager.RunningTaskInfo ti = rootTasks.get(i); out.add(ti.token); @@ -158,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; } @@ -174,26 +174,27 @@ public class WindowManagerProxy { // Set launchtile first so that any stack created after // getAllStackInfos and before reparent (even if unlikely) are placed // correctly. - ActivityTaskManager.getTaskOrganizerController().setLaunchRoot( - DEFAULT_DISPLAY, tiles.mSecondary.token); + TaskOrganizer.setLaunchRoot(DEFAULT_DISPLAY, tiles.mSecondary.token); List<ActivityManager.RunningTaskInfo> rootTasks = - ActivityTaskManager.getTaskOrganizerController().getRootTasks(DEFAULT_DISPLAY, - null /* activityTypes */); + TaskOrganizer.getRootTasks(DEFAULT_DISPLAY, null /* activityTypes */); WindowContainerTransaction wct = new WindowContainerTransaction(); 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); - ActivityTaskManager.getTaskOrganizerController() - .applyContainerTransaction(wct, null /* organizer */); + WindowOrganizer.applyTransaction(wct); return isHomeResizable; } catch (RemoteException e) { Log.w(TAG, "Error moving fullscreen tasks to secondary split: " + e); @@ -216,22 +217,19 @@ public class WindowManagerProxy { try { // Set launch root first so that any task created after getChildContainers and // before reparent (pretty unlikely) are put into fullscreen. - ActivityTaskManager.getTaskOrganizerController().setLaunchRoot(Display.DEFAULT_DISPLAY, - null); + 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 = - ActivityTaskManager.getTaskOrganizerController().getChildTasks( - tiles.mPrimary.token, null /* activityTypes */); + TaskOrganizer.getChildTasks(tiles.mPrimary.token, null /* activityTypes */); List<ActivityManager.RunningTaskInfo> secondaryChildren = - ActivityTaskManager.getTaskOrganizerController().getChildTasks( - tiles.mSecondary.token, null /* activityTypes */); + TaskOrganizer.getChildTasks(tiles.mSecondary.token, null /* activityTypes */); // In some cases (eg. non-resizable is launched), system-server will leave split-screen. // as a result, the above will not capture any tasks; yet, we need to clean-up the // home task bounds. List<ActivityManager.RunningTaskInfo> freeHomeAndRecents = - ActivityTaskManager.getTaskOrganizerController().getRootTasks( - Display.DEFAULT_DISPLAY, HOME_AND_RECENTS); + TaskOrganizer.getRootTasks(Display.DEFAULT_DISPLAY, HOME_AND_RECENTS); if (primaryChildren.isEmpty() && secondaryChildren.isEmpty() && freeHomeAndRecents.isEmpty()) { return; @@ -281,8 +279,7 @@ public class WindowManagerProxy { } // Reset focusable to true wct.setFocusable(tiles.mPrimary.token, true /* focusable */); - ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct, - null /* organizer */); + WindowOrganizer.applyTransaction(wct); } catch (RemoteException e) { Log.w(TAG, "Failed to remove stack: " + e); } @@ -290,8 +287,7 @@ public class WindowManagerProxy { static void applyContainerTransaction(WindowContainerTransaction wct) { try { - ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct, - null /* organizer */); + WindowOrganizer.applyTransaction(wct); } catch (RemoteException e) { Log.w(TAG, "Error setting focusability: " + e); } 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/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index b906442b6ba0..bb0fa2d2c7cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -340,7 +340,7 @@ public class KeyguardIndicationController implements StateListener, mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked); mTextView.setTextColor(mInitialTextColorState); } else if (!TextUtils.isEmpty(mTransientIndication)) { - if (powerIndication != null) { + if (powerIndication != null && !mTransientIndication.equals(powerIndication)) { String indication = mContext.getResources().getString( R.string.keyguard_indication_trust_unlocked_plugged_in, mTransientIndication, powerIndication); 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/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java index 4882a235c5d8..82f7c71c48cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java @@ -117,11 +117,6 @@ public class ImageTransformState extends TransformState { } @Override - protected boolean transformScale(TransformState otherState) { - return sameAs(otherState); - } - - @Override public void recycle() { super.recycle(); if (getClass() == ImageTransformState.class) { 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/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java index 82fb49144181..27109d2acfa2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java @@ -248,7 +248,7 @@ public class TransformState { } protected boolean transformScale(TransformState otherState) { - return false; + return sameAs(otherState); } /** 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/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt index 91a2e7c815d4..593de23c58de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt @@ -53,6 +53,9 @@ class NotificationConversationTemplateViewWrapper constructor( private lateinit var conversationTitle: View private lateinit var importanceRing: View private lateinit var appName: View + private var facePileBottomBg: View? = null + private var facePileBottom: View? = null + private var facePileTop: View? = null private fun resolveViews() { messagingLinearLayout = conversationLayout.messagingLinearLayout @@ -67,6 +70,10 @@ class NotificationConversationTemplateViewWrapper constructor( importanceRing = requireViewById(com.android.internal.R.id.conversation_icon_badge_ring) appName = requireViewById(com.android.internal.R.id.app_name_text) conversationTitle = requireViewById(com.android.internal.R.id.conversation_text) + facePileTop = findViewById(com.android.internal.R.id.conversation_face_pile_top) + facePileBottom = findViewById(com.android.internal.R.id.conversation_face_pile_bottom) + facePileBottomBg = + findViewById(com.android.internal.R.id.conversation_face_pile_bottom_background) } } @@ -118,7 +125,10 @@ class NotificationConversationTemplateViewWrapper constructor( conversationIcon, conversationBadgeBg, expandButton, - importanceRing + importanceRing, + facePileTop, + facePileBottom, + facePileBottomBg ) } @@ -140,9 +150,9 @@ class NotificationConversationTemplateViewWrapper constructor( else super.getMinLayoutHeight() - private fun addTransformedViews(vararg vs: View) = - vs.forEach(mTransformationHelper::addTransformedView) + private fun addTransformedViews(vararg vs: View?) = + vs.forEach { view -> view?.let(mTransformationHelper::addTransformedView) } - private fun addViewsTransformingToSimilar(vararg vs: View) = - vs.forEach(mTransformationHelper::addViewTransformingToSimilar) + private fun addViewsTransformingToSimilar(vararg vs: View?) = + vs.forEach { view -> view?.let(mTransformationHelper::addViewTransformingToSimilar) } } 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/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index adca10ff7677..ecfe1168b7f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -110,10 +110,14 @@ public class EdgeBackGestureHandler implements DisplayListener, private final float mTouchSlop; // Duration after which we consider the event as longpress. private final int mLongPressTimeout; + // The back gesture type + private int mBackType; private final PointF mDownPoint = new PointF(); + private final PointF mEndPoint = new PointF(); private boolean mThresholdCrossed = false; private boolean mAllowGesture = false; + private boolean mLogGesture = false; private boolean mInRejectedExclusion = false; private boolean mIsOnLeftEdge; @@ -141,24 +145,16 @@ public class EdgeBackGestureHandler implements DisplayListener, mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x, (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge); - int backtype = (mInRejectedExclusion - ? SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED : - SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED); - SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype, - (int) mDownPoint.y, mIsOnLeftEdge - ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT : - SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT); + logGesture(mInRejectedExclusion + ? SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED + : SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED); } @Override public void cancelBack() { + logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE); mOverviewProxyService.notifyBackAction(false, (int) mDownPoint.x, (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge); - int backtype = SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE; - SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype, - (int) mDownPoint.y, mIsOnLeftEdge - ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT : - SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT); } }; @@ -331,39 +327,55 @@ public class EdgeBackGestureHandler implements DisplayListener, } private boolean isWithinTouchRegion(int x, int y) { - // Disallow if too far from the edge - if (x > mEdgeWidthLeft + mLeftInset - && x < (mDisplaySize.x - mEdgeWidthRight - mRightInset)) { + // Disallow if we are in the bottom gesture area + if (y >= (mDisplaySize.y - mBottomGestureHeight)) { return false; } - // Disallow if we are in the bottom gesture area - if (y >= (mDisplaySize.y - mBottomGestureHeight)) { + // If the point is way too far (twice the margin), it is + // not interesting to us for logging purposes, nor we + // should process it. Simply return false and keep + // mLogGesture = false. + if (x > 2 * (mEdgeWidthLeft + mLeftInset) + && x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) { return false; } + // Denotes whether we should proceed with the gesture. + // Even if it is false, we may want to log it assuming + // it is not invalid due to exclusion. + boolean withinRange = x <= mEdgeWidthLeft + mLeftInset + || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset); + // Always allow if the user is in a transient sticky immersive state if (mIsNavBarShownTransiently) { - return true; + mLogGesture = true; + return withinRange; } - boolean isInExcludedRegion = mExcludeRegion.contains(x, y); - if (isInExcludedRegion) { - mOverviewProxyService.notifyBackAction(false /* completed */, -1, -1, - false /* isButton */, !mIsOnLeftEdge); - SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, - SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_EXCLUDED, y, - mIsOnLeftEdge ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT : - SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT); - } else { - mInRejectedExclusion = mUnrestrictedExcludeRegion.contains(x, y); + if (mExcludeRegion.contains(x, y)) { + if (withinRange) { + // Log as exclusion only if it is in acceptable range in the first place. + mOverviewProxyService.notifyBackAction( + false /* completed */, -1, -1, false /* isButton */, !mIsOnLeftEdge); + // We don't have the end point for logging purposes. + mEndPoint.x = -1; + mEndPoint.y = -1; + mLogGesture = true; + logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_EXCLUDED); + } + return false; } - return !isInExcludedRegion; + + mInRejectedExclusion = mUnrestrictedExcludeRegion.contains(x, y); + mLogGesture = true; + return withinRange; } private void cancelGesture(MotionEvent ev) { // Send action cancel to reset all the touch events mAllowGesture = false; + mLogGesture = false; mInRejectedExclusion = false; MotionEvent cancelEv = MotionEvent.obtain(ev); cancelEv.setAction(MotionEvent.ACTION_CANCEL); @@ -371,51 +383,86 @@ public class EdgeBackGestureHandler implements DisplayListener, cancelEv.recycle(); } + private void logGesture(int backType) { + if (!mLogGesture) { + return; + } + mLogGesture = false; + SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backType, + (int) mDownPoint.y, mIsOnLeftEdge + ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT + : SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT, + (int) mDownPoint.x, (int) mDownPoint.y, + (int) mEndPoint.x, (int) mEndPoint.y, + mEdgeWidthLeft + mLeftInset, + mDisplaySize.x - (mEdgeWidthRight + mRightInset)); + } + private void onMotionEvent(MotionEvent ev) { int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) { // Verify if this is in within the touch region and we aren't in immersive mode, and // either the bouncer is showing or the notification panel is hidden mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset; + mLogGesture = false; mInRejectedExclusion = false; mAllowGesture = !QuickStepContract.isBackGestureDisabled(mSysUiFlags) && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()); if (mAllowGesture) { mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge); mEdgeBackPlugin.onMotionEvent(ev); - + } + if (mLogGesture) { mDownPoint.set(ev.getX(), ev.getY()); + mEndPoint.set(-1, -1); mThresholdCrossed = false; } - - } else if (mAllowGesture) { + } else if (mAllowGesture || mLogGesture) { if (!mThresholdCrossed) { + mEndPoint.x = (int) ev.getX(); + mEndPoint.y = (int) ev.getY(); if (action == MotionEvent.ACTION_POINTER_DOWN) { - // We do not support multi touch for back gesture - cancelGesture(ev); + if (mAllowGesture) { + logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH); + // We do not support multi touch for back gesture + cancelGesture(ev); + } + mLogGesture = false; return; } else if (action == MotionEvent.ACTION_MOVE) { if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) { - cancelGesture(ev); + if (mAllowGesture) { + logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_LONG_PRESS); + cancelGesture(ev); + } + mLogGesture = false; return; } float dx = Math.abs(ev.getX() - mDownPoint.x); float dy = Math.abs(ev.getY() - mDownPoint.y); if (dy > dx && dy > mTouchSlop) { - cancelGesture(ev); + if (mAllowGesture) { + logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_VERTICAL_MOVE); + cancelGesture(ev); + } + mLogGesture = false; return; - } else if (dx > dy && dx > mTouchSlop) { - mThresholdCrossed = true; - // Capture inputs - mInputMonitor.pilferPointers(); + if (mAllowGesture) { + mThresholdCrossed = true; + // Capture inputs + mInputMonitor.pilferPointers(); + } else { + logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_FAR_FROM_EDGE); + } } } - } - // forward touch - mEdgeBackPlugin.onMotionEvent(ev); + if (mAllowGesture) { + // forward touch + mEdgeBackPlugin.onMotionEvent(ev); + } } Dependency.get(ProtoTracer.class).update(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 3074e33c46a8..ceefff1d14e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -203,6 +203,7 @@ public class KeyguardBouncer { Log.wtf(TAG, "onFullyShown when view was null"); } else { mKeyguardView.onResume(); + mRoot.announceForAccessibility(mKeyguardView.getAccessibilityTitleForCurrentMode()); } } @@ -438,7 +439,6 @@ public class KeyguardBouncer { mStatusBarHeight = mRoot.getResources().getDimensionPixelOffset( com.android.systemui.R.dimen.status_bar_height); mRoot.setVisibility(View.INVISIBLE); - mRoot.setAccessibilityPaneTitle(mKeyguardView.getAccessibilityTitleForCurrentMode()); final WindowInsets rootInsets = mRoot.getRootWindowInsets(); if (rootInsets != null) { 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/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index 496bf68e67a5..bf5900ff24bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -67,9 +67,9 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC private final Handler mBgHandler; protected final Context mContext; - private int mLevel; - private boolean mPluggedIn; - private boolean mCharging; + protected int mLevel; + protected boolean mPluggedIn; + protected boolean mCharging; private boolean mCharged; private boolean mPowerSave; private boolean mAodPowerSave; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index 7c963869ed47..c2fc18fe21a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -37,10 +37,10 @@ import java.util.Objects; public class WifiSignalController extends SignalController<WifiSignalController.WifiState, SignalController.IconGroup> { - private final boolean mHasMobileData; + private final boolean mHasMobileDataFeature; private final WifiStatusTracker mWifiTracker; - public WifiSignalController(Context context, boolean hasMobileData, + public WifiSignalController(Context context, boolean hasMobileDataFeature, CallbackHandler callbackHandler, NetworkControllerImpl networkController, WifiManager wifiManager) { super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI, @@ -52,7 +52,7 @@ public class WifiSignalController extends mWifiTracker = new WifiStatusTracker(mContext, wifiManager, networkScoreManager, connectivityManager, this::handleStatusUpdated); mWifiTracker.setListening(true); - mHasMobileData = hasMobileData; + mHasMobileDataFeature = hasMobileDataFeature; if (wifiManager != null) { wifiManager.registerTrafficStateCallback(context.getMainExecutor(), new WifiTrafficStateCallback()); @@ -85,9 +85,10 @@ public class WifiSignalController extends // only show wifi in the cluster if connected or if wifi-only boolean visibleWhenEnabled = mContext.getResources().getBoolean( R.bool.config_showWifiIndicatorWhenEnabled); - boolean wifiVisible = mCurrentState.enabled - && ((mCurrentState.connected && mCurrentState.inetCondition == 1) - || !mHasMobileData || visibleWhenEnabled); + boolean wifiVisible = mCurrentState.enabled && ( + (mCurrentState.connected && mCurrentState.inetCondition == 1) + || !mHasMobileDataFeature || mWifiTracker.isDefaultNetwork + || visibleWhenEnabled); String wifiDesc = mCurrentState.connected ? mCurrentState.ssid : null; boolean ssidPresent = wifiVisible && mCurrentState.ssid != null; String contentDescription = getTextIfExists(getContentDescription()).toString(); 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/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 9d9ba1bc91b9..eecde7218d28 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -52,6 +52,7 @@ import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; import android.os.Handler; +import android.os.IRemoteCallback; import android.os.UserHandle; import android.os.UserManager; import android.telephony.ServiceState; @@ -63,6 +64,7 @@ import android.testing.TestableContext; import android.testing.TestableLooper; import com.android.internal.telephony.TelephonyIntents; +import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; @@ -506,6 +508,24 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test + public void testBiometricsCleared_whenUserSwitches() throws Exception { + final IRemoteCallback reply = new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle data) {} // do nothing + }; + final BiometricAuthenticated dummyAuthentication = + new BiometricAuthenticated(true /* authenticated */, true /* strong */); + mKeyguardUpdateMonitor.mUserFaceAuthenticated.put(0 /* user */, dummyAuthentication); + mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.put(0 /* user */, dummyAuthentication); + assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1); + assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1); + + mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, reply); + assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0); + assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0); + } + + @Test public void testGetUserCanSkipBouncer_whenTrust() { int user = KeyguardUpdateMonitor.getCurrentUser(); mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */); 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/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt index d5a654dc2b6f..93aee33cc1c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt @@ -161,7 +161,7 @@ class ControlsControllerImplTest : SysuiTestCase() { verify(listingController).addCallback(capture(listingCallbackCaptor)) } - private fun builderFromInfo( + private fun statelessBuilderFromInfo( controlInfo: ControlInfo, structure: CharSequence = "" ): Control.StatelessBuilder { @@ -170,6 +170,15 @@ class ControlsControllerImplTest : SysuiTestCase() { .setSubtitle(controlInfo.controlSubtitle).setStructure(structure) } + private fun statefulBuilderFromInfo( + controlInfo: ControlInfo, + structure: CharSequence = "" + ): Control.StatefulBuilder { + return Control.StatefulBuilder(controlInfo.controlId, pendingIntent) + .setDeviceType(controlInfo.deviceType).setTitle(controlInfo.controlTitle) + .setSubtitle(controlInfo.controlSubtitle).setStructure(structure) + } + @Test fun testStartOnUser() { assertEquals(user, controller.currentUserId) @@ -236,7 +245,7 @@ class ControlsControllerImplTest : SysuiTestCase() { @Test fun testLoadForComponent_noFavorites() { var loaded = false - val control = builderFromInfo(TEST_CONTROL_INFO).build() + val control = statelessBuilderFromInfo(TEST_CONTROL_INFO).build() controller.loadForComponent(TEST_COMPONENT, Consumer { data -> val controls = data.allControls @@ -263,8 +272,8 @@ class ControlsControllerImplTest : SysuiTestCase() { @Test fun testLoadForComponent_favorites() { var loaded = false - val control = builderFromInfo(TEST_CONTROL_INFO).build() - val control2 = builderFromInfo(TEST_CONTROL_INFO_2).build() + val control = statelessBuilderFromInfo(TEST_CONTROL_INFO).build() + val control2 = statelessBuilderFromInfo(TEST_CONTROL_INFO_2).build() controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO_2) delayableExecutor.runAllReady() @@ -307,6 +316,7 @@ class ControlsControllerImplTest : SysuiTestCase() { assertEquals(1, controls.size) val controlStatus = controls[0] assertEquals(TEST_CONTROL_ID, controlStatus.control.controlId) + assertEquals(TEST_STRUCTURE_INFO.structure, controlStatus.control.structure) assertTrue(controlStatus.favorite) assertTrue(controlStatus.removed) @@ -337,6 +347,7 @@ class ControlsControllerImplTest : SysuiTestCase() { assertEquals(1, controls.size) val controlStatus = controls[0] assertEquals(TEST_CONTROL_ID, controlStatus.control.controlId) + assertEquals(TEST_STRUCTURE_INFO.structure, controlStatus.control.structure) assertTrue(controlStatus.favorite) assertFalse(controlStatus.removed) @@ -443,7 +454,7 @@ class ControlsControllerImplTest : SysuiTestCase() { delayableExecutor.runAllReady() val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2) - val control = builderFromInfo(newControlInfo).build() + val control = statelessBuilderFromInfo(newControlInfo).build() controller.loadForComponent(TEST_COMPONENT, Consumer {}) @@ -459,11 +470,11 @@ class ControlsControllerImplTest : SysuiTestCase() { } @Test - fun testFavoriteInformationModifiedOnRefresh() { + fun testFavoriteInformationModifiedOnRefreshWithOkStatus() { controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2) - val control = builderFromInfo(newControlInfo).build() + val control = statefulBuilderFromInfo(newControlInfo).setStatus(Control.STATUS_OK).build() controller.refreshStatus(TEST_COMPONENT, control) @@ -475,6 +486,23 @@ class ControlsControllerImplTest : SysuiTestCase() { } @Test + fun testFavoriteInformationNotModifiedOnRefreshWithNonOkStatus() { + controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) + + val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2) + val control = statefulBuilderFromInfo(newControlInfo).setStatus(Control.STATUS_ERROR) + .build() + + controller.refreshStatus(TEST_COMPONENT, control) + + delayableExecutor.runAllReady() + + val favorites = controller.getFavorites().flatMap { it.controls } + assertEquals(1, favorites.size) + assertEquals(TEST_CONTROL_INFO, favorites[0]) + } + + @Test fun testSwitchUsers() { controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) delayableExecutor.runAllReady() @@ -760,7 +788,8 @@ class ControlsControllerImplTest : SysuiTestCase() { @Test fun testSeedFavoritesForComponent() { var succeeded = false - val control = builderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure).build() + val control = statelessBuilderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure) + .build() controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted -> succeeded = accepted @@ -801,7 +830,8 @@ class ControlsControllerImplTest : SysuiTestCase() { fun testSeedFavoritesForComponent_inProgressCallback() { var succeeded = false var seeded = false - val control = builderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure).build() + val control = statelessBuilderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure) + .build() controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted -> succeeded = accepted 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/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java index e8a3c0ec2e98..41d7fd64fe7a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java @@ -217,4 +217,20 @@ public class DozeScreenStateTest extends SysuiTestCase { assertEquals(Display.STATE_OFF, mServiceFake.screenState); } + @Test + public void test_animatesOff() { + ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); + doAnswer(invocation -> null).when(mDozeHost).prepareForGentleSleep(captor.capture()); + mHandlerFake.setMode(QUEUEING); + + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + mScreen.transitionTo(DOZE_AOD, DOZE); + + mHandlerFake.dispatchQueuedMessages(); + verify(mDozeHost).prepareForGentleSleep(eq(captor.getValue())); + captor.getValue().run(); + assertEquals(Display.STATE_OFF, mServiceFake.screenState); + } + }
\ No newline at end of file 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/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index 35971bd4037c..e052ae2653f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -26,6 +26,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; @@ -56,11 +57,13 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -91,13 +94,14 @@ public class KeyguardBouncerTest extends SysuiTestCase { private Handler mHandler; @Mock private KeyguardSecurityModel mKeyguardSecurityModel; - + @Rule + public MockitoRule mRule = MockitoJUnit.rule(); + private ViewGroup mRootView; private KeyguardBouncer mBouncer; @Before public void setup() { allowTestableLooperAsMainThread(); - MockitoAnnotations.initMocks(this); mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor); mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel); mDependency.injectMockDependency(KeyguardStateController.class); @@ -115,6 +119,8 @@ public class KeyguardBouncerTest extends SysuiTestCase { protected void inflateView() { super.inflateView(); mKeyguardView = mKeyguardHostView; + mRoot = spy(mRoot); + mRootView = mRoot; } }; } @@ -217,6 +223,7 @@ public class KeyguardBouncerTest extends SysuiTestCase { mBouncer.setExpansion(0); verify(mKeyguardHostView).onResume(); + verify(mRootView).announceForAccessibility(any()); } @Test 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/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml index c71d0d7bc543..9328611f5d3f 100644 --- a/packages/Tethering/AndroidManifest.xml +++ b/packages/Tethering/AndroidManifest.xml @@ -27,7 +27,7 @@ added to the privileged permissions whitelist for that package. --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.BLUETOOTH" /> - <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> + <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.MANAGE_USB" /> 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..4b2c9215f7b7 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -303,31 +303,26 @@ 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); + mNetdCallback = new NetdCallback(); // Load tethering configuration. updateConfiguration(); - // NetdCallback should be registered after updateConfiguration() to ensure - // TetheringConfiguration is created. - mNetdCallback = new NetdCallback(); + } + + /** + * Start to register callbacks. + * Call this function when tethering is ready to handle callback events. + */ + public void startStateMachineUpdaters() { try { mNetd.registerUnsolicitedEventListener(mNetdCallback); } catch (RemoteException e) { mLog.e("Unable to register netd UnsolicitedEventListener"); } - - startStateMachineUpdaters(mHandler); - startTrackDefaultNetwork(); - - final WifiManager wifiManager = getWifiManager(); - if (wifiManager != null) { - wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback()); - } - } - - private void startStateMachineUpdaters(Handler handler) { mCarrierConfigChange.startListening(); mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); @@ -340,7 +335,14 @@ public class Tethering { filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED); filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED); - mContext.registerReceiver(mStateReceiver, filter, null, handler); + mContext.registerReceiver(mStateReceiver, filter, null, mHandler); + + final WifiManager wifiManager = getWifiManager(); + if (wifiManager != null) { + wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback()); + } + + startTrackDefaultNetwork(); } private class TetheringThreadExecutor implements Executor { @@ -369,9 +371,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 +434,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 +636,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 +1000,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 +1026,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 +1574,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 +2157,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 +2177,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/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java index c5329d8d3316..c30be25dbd22 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java @@ -80,6 +80,7 @@ public class TetheringService extends Service { mContext = mDeps.getContext(); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mTethering = makeTethering(mDeps); + mTethering.startStateMachineUpdaters(); } /** 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..a59c6fd9e193 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); @@ -485,6 +484,7 @@ public class TetheringTest { mServiceContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_TETHER_STATE_CHANGED)); mTethering = makeTethering(); + mTethering.startStateMachineUpdaters(); verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any()); verify(mNetd).registerUnsolicitedEventListener(any()); final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor = @@ -1073,13 +1073,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 +1089,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 +1401,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/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java index 6112da5cd13b..fe9f60fe2859 100644 --- a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java +++ b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java @@ -37,7 +37,6 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; -import android.util.Size; import android.view.Display; import android.view.View; import android.widget.Toast; @@ -358,8 +357,8 @@ public class WallpaperCropActivity extends Activity { // Get the crop boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; - Size windowSize = getWindowManager().getCurrentWindowMetrics().getSize(); - boolean isPortrait = windowSize.getWidth() < windowSize.getHeight(); + Rect windowBounds = getWindowManager().getCurrentWindowMetrics().getBounds(); + boolean isPortrait = windowBounds.width() < windowBounds.height(); Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(), getDisplay()); diff --git a/services/Android.bp b/services/Android.bp index 490481c7e5a2..52c5993a2862 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -142,6 +142,11 @@ droidstubs { baseline_file: "api/lint-baseline.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system-server/api", + dest: "android.txt", + }, } java_library { 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/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index 61c6ef5cfda9..bc38fbf50000 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -274,23 +274,38 @@ public class TouchExplorer extends BaseEventStreamTransformation public void onAccessibilityEvent(AccessibilityEvent event) { final int eventType = event.getEventType(); + if (eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { + sendsPendingA11yEventsIfNeed(); + } + super.onAccessibilityEvent(event); + } + + /* + * Sends pending {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END} or {@{@link + * AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}} after receiving last hover exit + * event. + */ + private void sendsPendingA11yEventsIfNeed() { + // The last hover exit A11y event should be sent by view after receiving hover exit motion + // event. In some view hierarchy, the ViewGroup transforms hover move motion event to hover + // exit motion event and than dispatch to itself. It causes unexpected A11y exit events. + if (mSendHoverExitDelayed.isPending()) { + return; + } // The event for gesture end should be strictly after the // last hover exit event. - if (mSendTouchExplorationEndDelayed.isPending() - && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { - mSendTouchExplorationEndDelayed.cancel(); + if (mSendTouchExplorationEndDelayed.isPending()) { + mSendTouchExplorationEndDelayed.cancel(); mDispatcher.sendAccessibilityEvent( AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END); } // The event for touch interaction end should be strictly after the // last hover exit and the touch exploration gesture end events. - if (mSendTouchInteractionEndDelayed.isPending() - && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { + if (mSendTouchInteractionEndDelayed.isPending()) { mSendTouchInteractionEndDelayed.cancel(); mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); } - super.onAccessibilityEvent(event); } @Override 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 e98ac75053a7..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, @@ -321,6 +322,7 @@ public final class InlineSuggestionFactory { public void onContent(SurfaceControlViewHost.SurfacePackage surface) throws RemoteException { callback.onContent(surface); + surface.release(); } @Override diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 32bca35009a8..9486b0d39bc2 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -17,7 +17,7 @@ package com.android.server.contentcapture; import static android.service.contentcapture.ContentCaptureService.setClientState; -import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID; +import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID; import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED; import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID; import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_ERROR; diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java index aa63e40747ee..06ab426c6698 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java @@ -16,9 +16,9 @@ package com.android.server.contentcapture; import static android.service.contentcapture.ContentCaptureService.setClientState; +import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID; import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE; import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_TRUE; -import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID; import static android.view.contentcapture.ContentCaptureSession.STATE_ACTIVE; import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED; import static android.view.contentcapture.ContentCaptureSession.STATE_SERVICE_RESURRECTED; 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/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index f1f5005b23db..8dd4fa6d8fd1 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -1101,6 +1101,9 @@ public final class BatteryService extends SystemService { * Synchronize on BatteryService. */ public void updateLightsLocked() { + if (mBatteryLight == null) { + return; + } final int level = mHealthInfo.batteryLevel; final int status = mHealthInfo.batteryStatus; if (level < mLowBatteryWarningLevel) { 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 84f144eda713..237a961fd338 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; @@ -234,7 +236,6 @@ import java.util.SortedSet; import java.util.StringJoiner; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Function; /** * @hide @@ -2703,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; } @@ -5779,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(); @@ -5989,12 +6007,12 @@ public class ConnectivityService extends IConnectivityManager.Stub * @return true if routes changed between oldLp and newLp */ private boolean updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) { - Function<RouteInfo, IpPrefix> getDestination = (r) -> r.getDestination(); // compare the route diff to determine which routes have been updated - CompareOrUpdateResult<IpPrefix, RouteInfo> routeDiff = new CompareOrUpdateResult<>( - oldLp != null ? oldLp.getAllRoutes() : null, - newLp != null ? newLp.getAllRoutes() : null, - getDestination); + final CompareOrUpdateResult<RouteInfo.RouteKey, RouteInfo> routeDiff = + new CompareOrUpdateResult<>( + oldLp != null ? oldLp.getAllRoutes() : null, + newLp != null ? newLp.getAllRoutes() : null, + (r) -> r.getRouteKey()); // add routes before removing old in case it helps with continuous connectivity diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 1bb3c3ac0cda..0ddfa1c16a0a 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -886,6 +886,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { @Override public void setIPv6AddrGenMode(String iface, int mode) throws ServiceSpecificException { + NetworkStack.checkNetworkStackPermission(mContext); try { mNetdService.setIPv6AddrGenMode(iface, mode); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 41a104c5d4dc..d9e7c3851906 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -1062,7 +1062,12 @@ public class PackageWatchdog { public void updatePackagesLocked(List<MonitoredPackage> packages) { for (int pIndex = 0; pIndex < packages.size(); pIndex++) { MonitoredPackage p = packages.get(pIndex); - this.packages.put(p.getName(), p); + MonitoredPackage existingPackage = this.packages.get(p.getName()); + if (existingPackage != null) { + existingPackage.updateHealthCheckDuration(p.mDurationMs); + } else { + this.packages.put(p.getName(), p); + } } } @@ -1331,6 +1336,12 @@ public class PackageWatchdog { return updateHealthCheckStateLocked(); } + /** Explicitly update the monitoring duration of the package. */ + @GuardedBy("mLock") + public void updateHealthCheckDuration(long newDurationMs) { + mDurationMs = newDurationMs; + } + /** * Marks the health check as passed and transitions to {@link HealthCheckState.PASSED} * if not yet {@link HealthCheckState.FAILED}. 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..4d8c86c87e9e 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; @@ -4430,9 +4459,8 @@ class StorageManagerService extends IStorageManager.Stub String.format("/storage/emulated/%d/Android/data/%s/", userId, pkg); - int appUid = - UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid()); // Create package obb and data dir if it doesn't exist. + int appUid = UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid()); File file = new File(packageObbDir); if (!file.exists()) { vold.setupAppDir(packageObbDir, appUid); 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/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index dd9cc641f2dd..ac4a42ca7024 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -605,15 +605,16 @@ public class VibratorService extends IVibratorService.Stub } @Override // Binder call - public boolean[] areEffectsSupported(int[] effectIds) { - // Return null to indicate that the HAL doesn't actually tell us what effects are - // supported. + public int[] areEffectsSupported(int[] effectIds) { + int[] supported = new int[effectIds.length]; if (mSupportedEffects == null) { - return null; - } - boolean[] supported = new boolean[effectIds.length]; - for (int i = 0; i < effectIds.length; i++) { - supported[i] = mSupportedEffects.contains(effectIds[i]); + Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN); + } else { + for (int i = 0; i < effectIds.length; i++) { + supported[i] = mSupportedEffects.contains(effectIds[i]) + ? Vibrator.VIBRATION_EFFECT_SUPPORT_YES + : Vibrator.VIBRATION_EFFECT_SUPPORT_NO; + } } return supported; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 4cfcd2b218d1..38405e1ccc03 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -185,8 +185,6 @@ import android.app.PendingIntent; import android.app.ProcessMemoryState; import android.app.ProfilerInfo; import android.app.WaitResult; -import android.app.WindowConfiguration.ActivityType; -import android.app.WindowConfiguration.WindowingMode; import android.app.backup.IBackupManager; import android.app.usage.UsageEvents; import android.app.usage.UsageEvents.Event; @@ -6513,13 +6511,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public List<RunningTaskInfo> getFilteredTasks(int maxNum, @ActivityType int ignoreActivityType, - @WindowingMode int ignoreWindowingMode) { - return mActivityTaskManager.getFilteredTasks( - maxNum, ignoreActivityType, ignoreWindowingMode); - } - - @Override public void cancelTaskWindowTransition(int taskId) { mActivityTaskManager.cancelTaskWindowTransition(taskId); } @@ -8780,6 +8771,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/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 4d08bd2e0ed7..bee0e055cf3f 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -98,6 +98,7 @@ import android.provider.DeviceConfig; import android.system.Os; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.EventLog; import android.util.LongSparseArray; import android.util.Pair; @@ -138,6 +139,7 @@ import java.util.BitSet; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** * Activity manager code dealing with processes. @@ -2155,15 +2157,6 @@ public final class ProcessList { result.put(packageName, Pair.create(volumeUuid, inode)); } } - if (mAppDataIsolationWhitelistedApps != null) { - for (String packageName : mAppDataIsolationWhitelistedApps) { - String volumeUuid = pmInt.getPackage(packageName).getVolumeUuid(); - long inode = pmInt.getCeDataInode(packageName, userId); - if (inode != 0) { - result.put(packageName, Pair.create(volumeUuid, inode)); - } - } - } return result; } @@ -2184,34 +2177,42 @@ public final class ProcessList { app.setHasForegroundActivities(true); } - StorageManagerInternal storageManagerInternal = LocalServices.getService( - StorageManagerInternal.class); final Map<String, Pair<String, Long>> pkgDataInfoMap; + final Map<String, Pair<String, Long>> whitelistedAppDataInfoMap; boolean bindMountAppStorageDirs = false; - - if (shouldIsolateAppData(app)) { - // Get all packages belongs to the same shared uid. sharedPackages is empty array - // if it doesn't have shared uid. - final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked(); - final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage( - app.info.packageName, app.userId); - pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, sharedPackages.length == 0 - ? new String[]{app.info.packageName} : sharedPackages, uid); - - int userId = UserHandle.getUserId(uid); - if (mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid) && - !storageManagerInternal.isExternalStorageService(uid)) { - bindMountAppStorageDirs = true; - if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(), - app.processName)) { - // Cannot prepare Android/app and Android/obb directory, - // so we won't mount it in zygote. - app.bindMountPending = true; - bindMountAppStorageDirs = false; - } + boolean bindMountAppsData = shouldIsolateAppData(app); + + // Get all packages belongs to the same shared uid. sharedPackages is empty array + // if it doesn't have shared uid. + final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked(); + final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage( + app.info.packageName, app.userId); + final String[] targetPackagesList = sharedPackages.length == 0 + ? new String[]{app.info.packageName} : sharedPackages; + pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, targetPackagesList, uid); + + // Remove all packages in pkgDataInfoMap from mAppDataIsolationWhitelistedApps, so + // it won't be mounted twice. + final Set<String> whitelistedApps = new ArraySet<>(mAppDataIsolationWhitelistedApps); + for (String pkg : targetPackagesList) { + whitelistedApps.remove(pkg); + } + whitelistedAppDataInfoMap = getPackageAppDataInfoMap(pmInt, + whitelistedApps.toArray(new String[0]), uid); + + int userId = UserHandle.getUserId(uid); + StorageManagerInternal storageManagerInternal = LocalServices.getService( + StorageManagerInternal.class); + if (mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid) && + !storageManagerInternal.isExternalStorageService(uid)) { + bindMountAppStorageDirs = true; + if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(), + app.processName)) { + // Cannot prepare Android/app and Android/obb directory, + // so we won't mount it in zygote. + app.bindMountPending = true; + bindMountAppStorageDirs = false; } - } else { - pkgDataInfoMap = null; } final Process.ProcessStartResult startResult; @@ -2229,7 +2230,8 @@ public final class ProcessList { app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, null, app.info.packageName, /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp, - app.mDisabledCompatChanges, pkgDataInfoMap, bindMountAppStorageDirs, + app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap, + bindMountAppsData, bindMountAppStorageDirs, new String[]{PROC_START_SEQ_IDENT + app.startSeq}); } else { startResult = Process.start(entryPoint, @@ -2237,7 +2239,7 @@ public final class ProcessList { app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags, isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap, - bindMountAppStorageDirs, + whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs, new String[]{PROC_START_SEQ_IDENT + app.startSeq}); } checkSlow(startTime, "startProcess: returned from zygote!"); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index f3d42ad1c793..94675ab7d795 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -693,7 +693,7 @@ public class AudioService extends IAudioService.Stub AudioSystem.DEFAULT_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = defaultCallVolume; } else { AudioSystem.DEFAULT_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = - (maxCallVolume * 3) / 4; + (MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] * 3) / 4; } int maxMusicVolume = SystemProperties.getInt("ro.config.media_vol_steps", -1); @@ -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: @@ -2325,6 +2324,13 @@ public class AudioService extends IAudioService.Stub // For legacy reason, propagate to all streams associated to this volume group for (final int groupedStream : vgs.getLegacyStreamTypes()) { + try { + ensureValidStreamType(groupedStream); + } catch (IllegalArgumentException e) { + Log.d(TAG, "volume group " + volumeGroup + " has internal streams (" + groupedStream + + "), do not change associated stream volume"); + continue; + } setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage, Binder.getCallingUid()); } @@ -2590,8 +2596,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 +2785,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 +2831,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 +3041,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 +4575,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); @@ -4882,10 +4888,6 @@ public class AudioService extends IAudioService.Stub public void applyAllVolumes() { synchronized (VolumeGroupState.class) { - if (mLegacyStreamType != AudioSystem.STREAM_DEFAULT) { - // No-op to avoid regression with stream based volume management - return; - } // apply device specific volumes first int index; for (int i = 0; i < mIndexMap.size(); i++) { @@ -5165,7 +5167,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 +5190,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 +5391,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 +8237,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/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 05a757bde179..8eb771046e6d 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -646,7 +646,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { + "id=" + physicalDisplayId + ", state=" + Display.stateToString(state) + ")"); } - mBacklight.setVrMode(isVrEnabled); + if (mBacklight != null) { + mBacklight.setVrMode(isVrEnabled); + } } private void setDisplayState(int state) { @@ -708,7 +710,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { BrightnessSynchronizer.brightnessFloatToInt(getContext(), brightness)); } - mBacklight.setBrightness(brightness); + if (mBacklight != null) { + mBacklight.setBrightness(brightness); + } Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenBrightness", BrightnessSynchronizer.brightnessFloatToInt( 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/lights/LightsManager.java b/services/core/java/com/android/server/lights/LightsManager.java index 521913a0c439..706c74137755 100644 --- a/services/core/java/com/android/server/lights/LightsManager.java +++ b/services/core/java/com/android/server/lights/LightsManager.java @@ -16,6 +16,7 @@ package com.android.server.lights; +import android.annotation.Nullable; import android.hardware.light.V2_0.Type; public abstract class LightsManager { @@ -30,7 +31,8 @@ public abstract class LightsManager { public static final int LIGHT_ID_COUNT = Type.COUNT; /** - * Returns the logical light with the given type. + * Returns the logical light with the given type, if it exists, or null. */ + @Nullable public abstract LogicalLight getLight(int id); } diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java index a42dec8b575f..3c6e8d29cae0 100644 --- a/services/core/java/com/android/server/lights/LightsService.java +++ b/services/core/java/com/android/server/lights/LightsService.java @@ -52,8 +52,8 @@ public class LightsService extends SystemService { static final String TAG = "LightsService"; static final boolean DEBUG = false; - private LightImpl[] mLights = null; - private SparseArray<LightImpl> mLightsById = null; + private final LightImpl[] mLightsByType = new LightImpl[LightsManager.LIGHT_ID_COUNT]; + private final SparseArray<LightImpl> mLightsById = new SparseArray<>(); private ILights mVintfLights = null; @@ -96,8 +96,8 @@ public class LightsService extends SystemService { synchronized (LightsService.this) { final List<Light> lights = new ArrayList<Light>(); for (int i = 0; i < mLightsById.size(); i++) { - HwLight hwLight = mLightsById.valueAt(i).getHwLight(); - if (!isSystemLight(hwLight)) { + if (!mLightsById.valueAt(i).isSystemLight()) { + HwLight hwLight = mLightsById.valueAt(i).mHwLight; lights.add(new Light(hwLight.id, hwLight.ordinal, hwLight.type)); } } @@ -138,7 +138,7 @@ public class LightsService extends SystemService { synchronized (LightsService.this) { final LightImpl light = mLightsById.get(lightId); - if (light == null || isSystemLight(light.getHwLight())) { + if (light == null || light.isSystemLight()) { throw new IllegalArgumentException("Invalid light: " + lightId); } return new LightState(light.getColor()); @@ -184,9 +184,8 @@ public class LightsService extends SystemService { private void checkRequestIsValid(int[] lightIds) { for (int i = 0; i < lightIds.length; i++) { final LightImpl light = mLightsById.get(lightIds[i]); - final HwLight hwLight = light.getHwLight(); - Preconditions.checkState(light != null && !isSystemLight(hwLight), - "invalid lightId " + hwLight.id); + Preconditions.checkState(light != null && !light.isSystemLight(), + "Invalid lightId " + lightIds[i]); } } @@ -205,9 +204,8 @@ public class LightsService extends SystemService { } for (int i = 0; i < mLightsById.size(); i++) { LightImpl light = mLightsById.valueAt(i); - HwLight hwLight = light.getHwLight(); - if (!isSystemLight(hwLight)) { - LightState state = states.get(hwLight.id); + if (!light.isSystemLight()) { + LightState state = states.get(light.mHwLight.id); if (state != null) { light.setColor(state.getColor()); } else { @@ -385,26 +383,22 @@ public class LightsService extends SystemService { int brightnessMode) { Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLightState(" + mHwLight.id + ", 0x" + Integer.toHexString(color) + ")"); - if (mVintfLights != null) { - HwLightState lightState = new HwLightState(); - lightState.color = color; - lightState.flashMode = (byte) mode; - lightState.flashOnMs = onMS; - lightState.flashOffMs = offMS; - lightState.brightnessMode = (byte) brightnessMode; - try { + try { + if (mVintfLights != null) { + HwLightState lightState = new HwLightState(); + lightState.color = color; + lightState.flashMode = (byte) mode; + lightState.flashOnMs = onMS; + lightState.flashOffMs = offMS; + lightState.brightnessMode = (byte) brightnessMode; mVintfLights.setLightState(mHwLight.id, lightState); - } catch (RemoteException | UnsupportedOperationException ex) { - Slog.e(TAG, "Failed issuing setLightState", ex); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_POWER); - } - } else { - try { + } else { setLight_native(mHwLight.id, color, mode, onMS, offMS, brightnessMode); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_POWER); } + } catch (RemoteException | UnsupportedOperationException ex) { + Slog.e(TAG, "Failed issuing setLightState", ex); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); } } @@ -412,8 +406,14 @@ public class LightsService extends SystemService { return mVrModeEnabled && mUseLowPersistenceForVR; } - private HwLight getHwLight() { - return mHwLight; + /** + * Returns whether a light is system-use-only or should be accessible to + * applications using the {@link android.hardware.lights.LightsManager} API. + */ + private boolean isSystemLight() { + // LIGHT_ID_COUNT comes from the 2.0 HIDL HAL and only contains system lights. + // Newly-added lights are made available via the public LightsManager API. + return (0 <= mHwLight.type && mHwLight.type < LightsManager.LIGHT_ID_COUNT); } private int getColor() { @@ -451,36 +451,37 @@ public class LightsService extends SystemService { } private void populateAvailableLights(Context context) { - mLights = new LightImpl[LightsManager.LIGHT_ID_COUNT]; - mLightsById = new SparseArray<>(); - if (mVintfLights != null) { - try { - for (HwLight availableLight : mVintfLights.getLights()) { - LightImpl light = new LightImpl(context, availableLight); - int type = (int) availableLight.type; - if (0 <= type && type < mLights.length && mLights[type] == null) { - mLights[type] = light; - } - mLightsById.put(availableLight.id, light); - } - } catch (RemoteException ex) { - Slog.e(TAG, "Unable to get lights for initialization", ex); + populateAvailableLightsFromAidl(context); + } else { + populateAvailableLightsFromHidl(context); + } + + for (int i = mLightsById.size() - 1; i >= 0; i--) { + final int type = mLightsById.keyAt(i); + if (0 <= type && type < mLightsByType.length) { + mLightsByType[type] = mLightsById.valueAt(i); } } + } - // In the case where only the old HAL is available, all lights will be initialized here - for (int i = 0; i < mLights.length; i++) { - if (mLights[i] == null) { - // The ordinal can be anything if there is only 1 light of each type. Set it to 1. - HwLight light = new HwLight(); - light.id = (byte) i; - light.ordinal = 1; - light.type = (byte) i; - - mLights[i] = new LightImpl(context, light); - mLightsById.put(i, mLights[i]); + private void populateAvailableLightsFromAidl(Context context) { + try { + for (HwLight hwLight : mVintfLights.getLights()) { + mLightsById.put(hwLight.id, new LightImpl(context, hwLight)); } + } catch (RemoteException ex) { + Slog.e(TAG, "Unable to get lights from HAL", ex); + } + } + + private void populateAvailableLightsFromHidl(Context context) { + for (int i = 0; i < mLightsByType.length; i++) { + HwLight hwLight = new HwLight(); + hwLight.id = (byte) i; + hwLight.ordinal = 1; + hwLight.type = (byte) i; + mLightsById.put(hwLight.id, new LightImpl(context, hwLight)); } } @@ -505,25 +506,14 @@ public class LightsService extends SystemService { private final LightsManager mService = new LightsManager() { @Override public LogicalLight getLight(int lightType) { - if (mLights != null && 0 <= lightType && lightType < mLights.length) { - return mLights[lightType]; + if (mLightsByType != null && 0 <= lightType && lightType < mLightsByType.length) { + return mLightsByType[lightType]; } else { return null; } } }; - /** - * Returns whether a light is system-use-only or should be accessible to - * applications using the {@link android.hardware.lights.LightsManager} API. - */ - private static boolean isSystemLight(HwLight light) { - // LIGHT_ID_COUNT comes from the 2.0 HIDL HAL and only contains system - // lights. Newly added lights will be made available via the - // LightsManager API. - return 0 <= light.type && light.type < LightsManager.LIGHT_ID_COUNT; - } - static native void setLight_native(int light, int color, int mode, int onMS, int offMS, int brightnessMode); } diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java index 11f068533a6d..f6652892c3de 100644 --- a/services/core/java/com/android/server/location/RemoteListenerHelper.java +++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java @@ -273,7 +273,7 @@ public abstract class RemoteListenerHelper<TRequest, TListener extends IInterfac } @Nullable - protected TRequest getRequest() { + public TRequest getRequest() { return mRequest; } } diff --git a/services/core/java/com/android/server/location/ExponentialBackOff.java b/services/core/java/com/android/server/location/gnss/ExponentialBackOff.java index 8c77b2176b74..05a534fa36e9 100644 --- a/services/core/java/com/android/server/location/ExponentialBackOff.java +++ b/services/core/java/com/android/server/location/gnss/ExponentialBackOff.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; /** * A simple implementation of exponential backoff. diff --git a/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java index bc50ebc2c5c3..d839095542c7 100644 --- a/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location; +package com.android.server.location.gnss; import android.content.Context; import android.location.GnssAntennaInfo; @@ -23,6 +23,8 @@ import android.os.Handler; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.location.CallerIdentity; +import com.android.server.location.RemoteListenerHelper; import java.util.List; diff --git a/services/core/java/com/android/server/location/GnssBatchingProvider.java b/services/core/java/com/android/server/location/gnss/GnssBatchingProvider.java index f3918ee9e8ff..f583a3ed3136 100644 --- a/services/core/java/com/android/server/location/GnssBatchingProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssBatchingProvider.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; import android.util.Log; diff --git a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java b/services/core/java/com/android/server/location/gnss/GnssCapabilitiesProvider.java index 5c8507f7fde0..71b5b33c242b 100644 --- a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssCapabilitiesProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location; +package com.android.server.location.gnss; import android.location.GnssCapabilities; import android.util.Log; diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java index a3523f23ddcf..14ab79e7ecde 100644 --- a/services/core/java/com/android/server/location/GnssConfiguration.java +++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location; +package com.android.server.location.gnss; import android.content.Context; import android.os.PersistableBundle; diff --git a/services/core/java/com/android/server/location/GnssGeofenceProvider.java b/services/core/java/com/android/server/location/gnss/GnssGeofenceProvider.java index a84b0b1c4335..53883b91c36d 100644 --- a/services/core/java/com/android/server/location/GnssGeofenceProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssGeofenceProvider.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; import android.location.IGpsGeofenceHardware; import android.util.Log; diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index 58e332ab6d1d..ad3c8a61182f 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location; +package com.android.server.location.gnss; import android.app.AlarmManager; import android.app.AppOpsManager; @@ -78,8 +78,9 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.server.DeviceIdleInternal; import com.android.server.FgThread; import com.android.server.LocalServices; -import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback; -import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback; +import com.android.server.location.AbstractLocationProvider; +import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback; +import com.android.server.location.gnss.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -653,9 +654,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 +675,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/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java index b57c261931f8..9e64e3aeae59 100644 --- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java +++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java @@ -55,14 +55,6 @@ import com.android.server.LocationManagerServiceUtils.LinkedListener; import com.android.server.LocationManagerServiceUtils.LinkedListenerBase; import com.android.server.location.AppForegroundHelper; import com.android.server.location.CallerIdentity; -import com.android.server.location.GnssAntennaInfoProvider; -import com.android.server.location.GnssBatchingProvider; -import com.android.server.location.GnssCapabilitiesProvider; -import com.android.server.location.GnssLocationProvider; -import com.android.server.location.GnssMeasurementCorrectionsProvider; -import com.android.server.location.GnssMeasurementsProvider; -import com.android.server.location.GnssNavigationMessageProvider; -import com.android.server.location.GnssStatusListenerHelper; import com.android.server.location.LocationUsageLogger; import com.android.server.location.RemoteListenerHelper; import com.android.server.location.SettingsHelper; diff --git a/services/core/java/com/android/server/location/GnssMeasurementCorrectionsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementCorrectionsProvider.java index 82528caa0b4e..ac165d1a206b 100644 --- a/services/core/java/com/android/server/location/GnssMeasurementCorrectionsProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementCorrectionsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location; +package com.android.server.location.gnss; import android.location.GnssMeasurementCorrections; import android.os.Handler; diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java index 6ba5f079264c..76c3ad02e7e9 100644 --- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location; +package com.android.server.location.gnss; import android.content.Context; import android.location.GnssMeasurementsEvent; @@ -26,6 +26,8 @@ import android.provider.Settings; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.location.CallerIdentity; +import com.android.server.location.RemoteListenerHelper; /** * An base implementation for GPS measurements provider. It abstracts out the responsibility of diff --git a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java index fb901e86f494..722be3df2691 100644 --- a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location; +package com.android.server.location.gnss; import android.content.Context; import android.location.GnssNavigationMessage; @@ -24,6 +24,8 @@ import android.os.RemoteException; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.location.CallerIdentity; +import com.android.server.location.RemoteListenerHelper; /** * An base implementation for GPS navigation messages provider. diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java index 93227bd78a81..3fb713bc01a5 100644 --- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java +++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location; +package com.android.server.location.gnss; import android.content.Context; import android.database.Cursor; @@ -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/location/GnssPositionMode.java b/services/core/java/com/android/server/location/gnss/GnssPositionMode.java index 36838fc3647f..045118afbda0 100644 --- a/services/core/java/com/android/server/location/GnssPositionMode.java +++ b/services/core/java/com/android/server/location/gnss/GnssPositionMode.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; import java.util.Arrays; diff --git a/services/core/java/com/android/server/location/GnssSatelliteBlacklistHelper.java b/services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java index eb99a851115f..dccef9b9a9c4 100644 --- a/services/core/java/com/android/server/location/GnssSatelliteBlacklistHelper.java +++ b/services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; import android.content.ContentResolver; import android.content.Context; diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/gnss/GnssStatusListenerHelper.java index 1d16c03fd6f7..d2ecdeebd9d0 100644 --- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java +++ b/services/core/java/com/android/server/location/gnss/GnssStatusListenerHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -11,16 +11,19 @@ * 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 + * limitations under the License. */ -package com.android.server.location; +package com.android.server.location.gnss; import android.content.Context; import android.location.IGnssStatusListener; import android.os.Handler; import android.util.Log; +import com.android.server.location.CallerIdentity; +import com.android.server.location.RemoteListenerHelper; + /** * Implementation of a handler for {@link IGnssStatusListener}. */ diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java index 2b5fc7989d8b..06fa0ea7791d 100644 --- a/services/core/java/com/android/server/location/GnssVisibilityControl.java +++ b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location; +package com.android.server.location.gnss; import android.annotation.Nullable; import android.annotation.SuppressLint; diff --git a/services/core/java/com/android/server/location/GpsPsdsDownloader.java b/services/core/java/com/android/server/location/gnss/GpsPsdsDownloader.java index 6fcb7d1af2ce..273f9cb13a1b 100644 --- a/services/core/java/com/android/server/location/GpsPsdsDownloader.java +++ b/services/core/java/com/android/server/location/gnss/GpsPsdsDownloader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location; +package com.android.server.location.gnss; import android.net.TrafficStats; import android.text.TextUtils; diff --git a/services/core/java/com/android/server/location/NtpTimeHelper.java b/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java index d2296ea27913..2bbb61fbe018 100644 --- a/services/core/java/com/android/server/location/NtpTimeHelper.java +++ b/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; import android.content.Context; import android.net.ConnectivityManager; @@ -12,6 +28,7 @@ import android.util.NtpTrustedTime; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.location.gnss.ExponentialBackOff; import java.util.Date; diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 4b925ef7c245..6aae62ea3f18 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -155,13 +155,18 @@ class MediaRouter2ServiceImpl { final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); - final boolean trusted = mContext.checkCallingOrSelfPermission( + final boolean hasConfigureWifiDisplayPermission = mContext.checkCallingOrSelfPermission( android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) == PackageManager.PERMISSION_GRANTED; + final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_AUDIO_ROUTING) + == PackageManager.PERMISSION_GRANTED; + final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - registerRouter2Locked(router, uid, pid, packageName, userId, trusted); + registerRouter2Locked(router, uid, pid, packageName, userId, + hasConfigureWifiDisplayPermission, hasModifyAudioRoutingPermission); } } finally { Binder.restoreCallingIdentity(token); @@ -341,8 +346,6 @@ class MediaRouter2ServiceImpl { throw new IllegalArgumentException("packageName must not be empty"); } - final boolean trusted = true; - final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); @@ -350,7 +353,7 @@ class MediaRouter2ServiceImpl { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - registerManagerLocked(manager, uid, pid, packageName, userId, trusted); + registerManagerLocked(manager, uid, pid, packageName, userId); } } finally { Binder.restoreCallingIdentity(token); @@ -532,7 +535,8 @@ class MediaRouter2ServiceImpl { //////////////////////////////////////////////////////////////// private void registerRouter2Locked(@NonNull IMediaRouter2 router, int uid, int pid, - @NonNull String packageName, int userId, boolean trusted) { + @NonNull String packageName, int userId, boolean hasConfigureWifiDisplayPermission, + boolean hasModifyAudioRoutingPermission) { final IBinder binder = router.asBinder(); if (mAllRouterRecords.get(binder) != null) { Slog.w(TAG, "Same router already exists. packageName=" + packageName); @@ -540,8 +544,8 @@ class MediaRouter2ServiceImpl { } UserRecord userRecord = getOrCreateUserRecordLocked(userId); - RouterRecord routerRecord = new RouterRecord( - userRecord, router, uid, pid, packageName, trusted); + RouterRecord routerRecord = new RouterRecord(userRecord, router, uid, pid, packageName, + hasConfigureWifiDisplayPermission, hasModifyAudioRoutingPermission); try { binder.linkToDeath(routerRecord, 0); } catch (RemoteException ex) { @@ -704,14 +708,14 @@ class MediaRouter2ServiceImpl { } List<RoutingSessionInfo> sessionInfos = new ArrayList<>(); - for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mMediaProviders) { + for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mRouteProviders) { sessionInfos.addAll(provider.getSessionInfos()); } return sessionInfos; } private void registerManagerLocked(@NonNull IMediaRouter2Manager manager, - int uid, int pid, @NonNull String packageName, int userId, boolean trusted) { + int uid, int pid, @NonNull String packageName, int userId) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); @@ -721,7 +725,7 @@ class MediaRouter2ServiceImpl { } UserRecord userRecord = getOrCreateUserRecordLocked(userId); - managerRecord = new ManagerRecord(userRecord, manager, uid, pid, packageName, trusted); + managerRecord = new ManagerRecord(userRecord, manager, uid, pid, packageName); try { binder.linkToDeath(managerRecord, 0); } catch (RemoteException ex) { @@ -778,7 +782,7 @@ class MediaRouter2ServiceImpl { @NonNull IMediaRouter2Manager manager, @NonNull String packageName, @NonNull MediaRoute2Info route) { ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder()); - if (managerRecord == null || !managerRecord.mTrusted) { + if (managerRecord == null) { return; } @@ -976,14 +980,16 @@ class MediaRouter2ServiceImpl { public final IMediaRouter2 mRouter; public final int mUid; public final int mPid; - public final boolean mTrusted; + public final boolean mHasConfigureWifiDisplayPermission; + public final boolean mHasModifyAudioRoutingPermission; public final int mRouterId; public RouteDiscoveryPreference mDiscoveryPreference; public MediaRoute2Info mSelectedRoute; - RouterRecord(UserRecord userRecord, IMediaRouter2 router, - int uid, int pid, String packageName, boolean trusted) { + RouterRecord(UserRecord userRecord, IMediaRouter2 router, int uid, int pid, + String packageName, boolean hasConfigureWifiDisplayPermission, + boolean hasModifyAudioRoutingPermission) { mUserRecord = userRecord; mPackageName = packageName; mSelectRouteSequenceNumbers = new ArrayList<>(); @@ -991,7 +997,8 @@ class MediaRouter2ServiceImpl { mRouter = router; mUid = uid; mPid = pid; - mTrusted = trusted; + mHasConfigureWifiDisplayPermission = hasConfigureWifiDisplayPermission; + mHasModifyAudioRoutingPermission = hasModifyAudioRoutingPermission; mRouterId = mNextRouterOrManagerId.getAndIncrement(); } @@ -1011,17 +1018,15 @@ class MediaRouter2ServiceImpl { public final int mUid; public final int mPid; public final String mPackageName; - public final boolean mTrusted; public final int mManagerId; ManagerRecord(UserRecord userRecord, IMediaRouter2Manager manager, - int uid, int pid, String packageName, boolean trusted) { + int uid, int pid, String packageName) { mUserRecord = userRecord; mManager = manager; mUid = uid; mPid = pid; mPackageName = packageName; - mTrusted = trusted; mManagerId = mNextRouterOrManagerId.getAndIncrement(); } @@ -1036,9 +1041,6 @@ class MediaRouter2ServiceImpl { public void dump(PrintWriter pw, String prefix) { pw.println(prefix + this); - - final String indent = prefix + " "; - pw.println(indent + "mTrusted=" + mTrusted); } @Override @@ -1057,7 +1059,7 @@ class MediaRouter2ServiceImpl { //TODO: Make this thread-safe. private final SystemMediaRoute2Provider mSystemProvider; - private final ArrayList<MediaRoute2Provider> mMediaProviders = + private final ArrayList<MediaRoute2Provider> mRouteProviders = new ArrayList<>(); private final List<MediaRoute2ProviderInfo> mLastProviderInfos = new ArrayList<>(); @@ -1072,7 +1074,7 @@ class MediaRouter2ServiceImpl { mServiceRef = new WeakReference<>(service); mUserRecord = userRecord; mSystemProvider = new SystemMediaRoute2Provider(service.mContext, this); - mMediaProviders.add(mSystemProvider); + mRouteProviders.add(mSystemProvider); mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this, this, mUserRecord.mUserId); } @@ -1095,13 +1097,13 @@ class MediaRouter2ServiceImpl { @Override public void onAddProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) { proxy.setCallback(this); - mMediaProviders.add(proxy); + mRouteProviders.add(proxy); proxy.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference); } @Override public void onRemoveProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) { - mMediaProviders.remove(proxy); + mRouteProviders.remove(proxy); } @Override @@ -1146,10 +1148,10 @@ class MediaRouter2ServiceImpl { //TODO: notify session info updates private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) { - int providerIndex = getProviderInfoIndex(provider.getUniqueId()); + int providerInfoIndex = getLastProviderInfoIndex(provider.getUniqueId()); MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo(); MediaRoute2ProviderInfo prevInfo = - (providerIndex < 0) ? null : mLastProviderInfos.get(providerIndex); + (providerInfoIndex < 0) ? null : mLastProviderInfos.get(providerInfoIndex); if (Objects.equals(prevInfo, providerInfo)) return; @@ -1169,7 +1171,7 @@ class MediaRouter2ServiceImpl { this, getRouters(), new ArrayList<>(removedRoutes))); } } else { - mLastProviderInfos.set(providerIndex, providerInfo); + mLastProviderInfos.set(providerInfoIndex, providerInfo); List<MediaRoute2Info> addedRoutes = new ArrayList<>(); List<MediaRoute2Info> removedRoutes = new ArrayList<>(); List<MediaRoute2Info> changedRoutes = new ArrayList<>(); @@ -1217,7 +1219,7 @@ class MediaRouter2ServiceImpl { } } - private int getProviderInfoIndex(@NonNull String providerId) { + private int getLastProviderInfoIndex(@NonNull String providerId) { for (int i = 0; i < mLastProviderInfos.size(); i++) { MediaRoute2ProviderInfo providerInfo = mLastProviderInfos.get(i); if (TextUtils.equals(providerInfo.getUniqueId(), providerId)) { @@ -1793,13 +1795,13 @@ class MediaRouter2ServiceImpl { new RouteDiscoveryPreference.Builder(discoveryPreferences) .build(); } - for (MediaRoute2Provider provider : mMediaProviders) { + for (MediaRoute2Provider provider : mRouteProviders) { provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference); } } private MediaRoute2Provider findProvider(@Nullable String providerId) { - for (MediaRoute2Provider provider : mMediaProviders) { + for (MediaRoute2Provider provider : mRouteProviders) { if (TextUtils.equals(provider.getUniqueId(), providerId)) { return provider; } diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index c7d14e0afe7b..c69787d602e2 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -54,6 +54,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE"; + static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE"; static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION"; private final AudioManager mAudioManager; @@ -67,14 +68,17 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SystemMediaRoute2Provider.class.getName()); private String mSelectedRouteId; + // For apps without MODIFYING_AUDIO_ROUTING permission. + // This should be the currently selected route. MediaRoute2Info mDefaultRoute; + MediaRoute2Info mDeviceRoute; final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() { @Override public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) { mHandler.post(() -> { - updateDefaultRoute(newRoutes); + updateDeviceRoute(newRoutes); notifyProviderState(); }); } @@ -97,7 +101,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); } catch (RemoteException e) { } - updateDefaultRoute(newAudioRoutes); + updateDeviceRoute(newAudioRoutes); mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> { publishProviderState(); @@ -109,7 +113,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } }); updateSessionInfosIfNeeded(); - mContext.registerReceiver(new VolumeChangeReceiver(), new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION)); @@ -150,7 +153,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { @Override public void transferToRoute(long requestId, String sessionId, String routeId) { - if (TextUtils.equals(routeId, mDefaultRoute.getId())) { + if (TextUtils.equals(routeId, mDeviceRoute.getId())) { mBtRouteProvider.transferTo(null); } else { mBtRouteProvider.transferTo(routeId); @@ -170,7 +173,11 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { // Do nothing since we don't support grouping volume yet. } - private void updateDefaultRoute(AudioRoutesInfo newRoutes) { + public MediaRoute2Info getDefaultRoute() { + return mDefaultRoute; + } + + private void updateDeviceRoute(AudioRoutesInfo newRoutes) { int name = R.string.default_audio_route_name; if (newRoutes != null) { mCurAudioRoutesInfo.mainType = newRoutes.mainType; @@ -185,8 +192,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { name = com.android.internal.R.string.default_audio_route_name_usb; } } - mDefaultRoute = new MediaRoute2Info.Builder( - DEFAULT_ROUTE_ID, mContext.getResources().getText(name).toString()) + mDeviceRoute = new MediaRoute2Info.Builder( + DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) .setVolumeHandling(mAudioManager.isVolumeFixed() ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) @@ -203,7 +210,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { private void updateProviderState() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); - builder.addRoute(mDefaultRoute); + builder.addRoute(mDeviceRoute); if (mBtRouteProvider != null) { for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) { builder.addRoute(route); @@ -228,11 +235,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { MediaRoute2Info selectedRoute = mBtRouteProvider.getSelectedRoute(); if (selectedRoute == null) { - selectedRoute = mDefaultRoute; + selectedRoute = mDeviceRoute; } else { - builder.addTransferableRoute(mDefaultRoute.getId()); + builder.addTransferableRoute(mDeviceRoute.getId()); } mSelectedRouteId = selectedRoute.getId(); + mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute).build(); builder.addSelectedRoute(mSelectedRouteId); for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) { @@ -282,8 +290,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0); if (newVolume != oldVolume) { - if (TextUtils.equals(mDefaultRoute.getId(), mSelectedRouteId)) { - mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute) + if (TextUtils.equals(mDeviceRoute.getId(), mSelectedRouteId)) { + mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) .setVolume(newVolume) .build(); } else { 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..87061e1d488c 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -107,6 +107,7 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -139,6 +140,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 +245,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 +389,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; @@ -1580,7 +1580,9 @@ public class NotificationManagerService extends SystemService { } } else if (action.equals(Intent.ACTION_USER_PRESENT)) { // turn off LED when user passes through lock screen - mNotificationLight.turnOff(); + if (mNotificationLight != null) { + mNotificationLight.turnOff(); + } } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); mUserProfiles.updateCache(context); @@ -2752,24 +2754,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 +2804,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 +2825,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; @@ -2848,20 +2843,18 @@ public class NotificationManagerService extends SystemService { record = mToastQueue.get(index); record.update(duration); } else { - // Limit the number of toasts that any given package except the android - // package can enqueue. Prevents DOS attacks and deals with leaks. - if (!isSystemToast) { - int count = 0; - final int N = mToastQueue.size(); - for (int i = 0; i < N; i++) { - final ToastRecord r = mToastQueue.get(i); - if (r.pkg.equals(pkg)) { - count++; - if (count >= MAX_PACKAGE_NOTIFICATIONS) { - Slog.e(TAG, "Package has already posted " + count - + " toasts. Not showing more. Package=" + pkg); - return; - } + // Limit the number of toasts that any given package can enqueue. + // Prevents DOS attacks and deals with leaks. + int count = 0; + final int N = mToastQueue.size(); + for (int i = 0; i < N; i++) { + final ToastRecord r = mToastQueue.get(i); + if (r.pkg.equals(pkg)) { + count++; + if (count >= MAX_PACKAGE_NOTIFICATIONS) { + Slog.e(TAG, "Package has already posted " + count + + " toasts. Not showing more. Package=" + pkg); + return; } } } @@ -3454,7 +3447,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 +3470,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 +3632,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 +3658,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 +3741,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 +3768,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 +3785,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 +3794,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"); @@ -4175,7 +4194,7 @@ public class NotificationManagerService extends SystemService { @Override public int getInterruptionFilterFromListener(INotificationListener token) throws RemoteException { - synchronized (mNotificationLight) { + synchronized (mNotificationLock) { return mInterruptionFilter; } } @@ -5648,7 +5667,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)) { @@ -6754,7 +6774,7 @@ public class NotificationManagerService extends SystemService { if (canShowLightsLocked(record, aboveThreshold)) { mLights.add(key); updateLightsLocked(); - if (mUseAttentionLight) { + if (mUseAttentionLight && mAttentionLight != null) { mAttentionLight.pulse(); } blink = true; @@ -7955,6 +7975,10 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") void updateLightsLocked() { + if (mNotificationLight == null) { + return; + } + // handle notification lights NotificationRecord ledNotification = null; while (ledNotification == null && !mLights.isEmpty()) { 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/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index c37ea8ba68bd..c97f33b5103f 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -16,22 +16,22 @@ package com.android.server.pm; +import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; + import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; import android.apex.ApexSessionParams; import android.apex.IApexService; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.parsing.PackageInfoWithoutStateUtils; import android.os.Environment; import android.os.RemoteException; import android.os.ServiceManager; @@ -44,8 +44,9 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.os.BackgroundThread; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.Preconditions; +import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.utils.TimingsTraceAndSlog; @@ -61,7 +62,9 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; /** @@ -72,7 +75,7 @@ public abstract class ApexManager { private static final String TAG = "ApexManager"; - static final int MATCH_ACTIVE_PACKAGE = 1 << 0; + public static final int MATCH_ACTIVE_PACKAGE = 1 << 0; static final int MATCH_FACTORY_PACKAGE = 1 << 1; private static final Singleton<ApexManager> sApexManagerSingleton = @@ -139,7 +142,14 @@ public abstract class ApexManager { */ public abstract List<ActiveApexInfo> getActiveApexInfos(); - abstract void systemReady(Context context); + /** + * Called by package manager service to scan apex package files when device boots up. + * + * @param packageParser The package parser which supports caches. + * @param executorService An executor to support parallel package parsing. + */ + abstract void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, + @NonNull ExecutorService executorService); /** * Retrieves information about an APEX package. @@ -154,7 +164,7 @@ public abstract class ApexManager { * is not found. */ @Nullable - abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags); + public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags); /** * Retrieves information about all active APEX packages. @@ -189,6 +199,27 @@ public abstract class ApexManager { abstract boolean isApexPackage(String packageName); /** + * Whether the APEX package is pre-installed or not. + * + * @param packageInfo the package to check + * @return {@code true} if this package is pre-installed, {@code false} otherwise. + */ + public static boolean isFactory(@NonNull PackageInfo packageInfo) { + return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + + /** + * Returns the active apex package's name that contains the (apk) package. + * + * @param containedPackage The (apk) package that might be in a apex + * @return the apex package's name of {@code null} if the {@code containedPackage} is not inside + * any apex. + */ + @Nullable + public abstract String getActiveApexPackageNameContainingPackage( + @NonNull AndroidPackage containedPackage); + + /** * Retrieves information about an apexd staged session i.e. the internal state used by apexd to * track the different states of a session. * @@ -342,7 +373,7 @@ public abstract class ApexManager { * difference between {@code packageName} and {@code apexModuleName}. */ @GuardedBy("mLock") - private Map<String, List<String>> mApksInApex = new ArrayMap<>(); + private ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>(); @GuardedBy("mLock") private List<PackageInfo> mAllPackagesCache; @@ -357,7 +388,7 @@ public abstract class ApexManager { * the apk container to {@code apexModuleName} of the apex-payload inside. */ @GuardedBy("mLock") - private Map<String, String> mPackageNameToApexModuleName; + private ArrayMap<String, String> mPackageNameToApexModuleName; ApexManagerImpl(IApexService apexService) { mApexService = apexService; @@ -373,16 +404,6 @@ public abstract class ApexManager { return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0; } - /** - * Whether the APEX package is pre-installed or not. - * - * @param packageInfo the package to check - * @return {@code true} if this package is pre-installed, {@code false} otherwise. - */ - private static boolean isFactory(PackageInfo packageInfo) { - return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - } - @Override public List<ActiveApexInfo> getActiveApexInfos() { final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", @@ -411,104 +432,94 @@ public abstract class ApexManager { } @Override - void systemReady(Context context) { - context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - // Post populateAllPackagesCacheIfNeeded to a background thread, since it's - // expensive to run it in broadcast handler thread. - BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded()); - context.unregisterReceiver(this); - } - }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); - } - - private void populatePackageNameToApexModuleNameIfNeeded() { - synchronized (mLock) { - if (mPackageNameToApexModuleName != null) { - return; - } - try { - mPackageNameToApexModuleName = new ArrayMap<>(); - final ApexInfo[] allPkgs = mApexService.getAllPackages(); - for (int i = 0; i < allPkgs.length; i++) { - ApexInfo ai = allPkgs[i]; - PackageParser.PackageLite pkgLite; - try { - File apexFile = new File(ai.modulePath); - pkgLite = PackageParser.parsePackageLite(apexFile, 0); - } catch (PackageParser.PackageParserException pe) { - throw new IllegalStateException("Unable to parse: " - + ai.modulePath, pe); - } - mPackageNameToApexModuleName.put(pkgLite.packageName, ai.moduleName); - } - } catch (RemoteException re) { - Slog.e(TAG, "Unable to retrieve packages from apexservice: ", re); - throw new RuntimeException(re); + void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, + @NonNull ExecutorService executorService) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanApexPackagesTraced"); + try { + synchronized (mLock) { + scanApexPackagesInternalLocked(packageParser, executorService); } + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } - private void populateAllPackagesCacheIfNeeded() { - synchronized (mLock) { - if (mAllPackagesCache != null) { - return; - } - try { - mAllPackagesCache = new ArrayList<>(); - HashSet<String> activePackagesSet = new HashSet<>(); - HashSet<String> factoryPackagesSet = new HashSet<>(); - final ApexInfo[] allPkgs = mApexService.getAllPackages(); - for (ApexInfo ai : allPkgs) { - // If the device is using flattened APEX, don't report any APEX - // packages since they won't be managed or updated by PackageManager. - if ((new File(ai.modulePath)).isDirectory()) { - break; - } - int flags = PackageManager.GET_META_DATA - | PackageManager.GET_SIGNING_CERTIFICATES - | PackageManager.GET_SIGNATURES; - PackageParser.Package pkg; - try { - File apexFile = new File(ai.modulePath); - PackageParser pp = new PackageParser(); - pkg = pp.parsePackage(apexFile, flags, false); - PackageParser.collectCertificates(pkg, false); - } catch (PackageParser.PackageParserException pe) { - throw new IllegalStateException("Unable to parse: " + ai, pe); - } + @GuardedBy("mLock") + private void scanApexPackagesInternalLocked(PackageParser2 packageParser, + ExecutorService executorService) { + final ApexInfo[] allPkgs; + try { + mAllPackagesCache = new ArrayList<>(); + mPackageNameToApexModuleName = new ArrayMap<>(); + allPkgs = mApexService.getAllPackages(); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); + throw new RuntimeException(re); + } + if (allPkgs.length == 0) { + return; + } + int flags = PackageManager.GET_META_DATA + | PackageManager.GET_SIGNING_CERTIFICATES + | PackageManager.GET_SIGNATURES; + ArrayMap<File, ApexInfo> parsingApexInfo = new ArrayMap<>(); + ParallelPackageParser parallelPackageParser = + new ParallelPackageParser(packageParser, executorService); + + for (ApexInfo ai : allPkgs) { + File apexFile = new File(ai.modulePath); + parallelPackageParser.submit(apexFile, flags); + parsingApexInfo.put(apexFile, ai); + } - final PackageInfo packageInfo = - PackageParser.generatePackageInfo(pkg, ai, flags); - mAllPackagesCache.add(packageInfo); - if (ai.isActive) { - if (activePackagesSet.contains(packageInfo.packageName)) { - throw new IllegalStateException( - "Two active packages have the same name: " - + packageInfo.packageName); - } - activePackagesSet.add(packageInfo.packageName); + HashSet<String> activePackagesSet = new HashSet<>(); + HashSet<String> factoryPackagesSet = new HashSet<>(); + // Process results one by one + for (int i = 0; i < parsingApexInfo.size(); i++) { + ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); + Throwable throwable = parseResult.throwable; + ApexInfo ai = parsingApexInfo.get(parseResult.scanFile); + + if (throwable == null) { + final PackageInfo packageInfo = PackageInfoWithoutStateUtils.generate( + parseResult.parsedPackage, ai, flags); + if (packageInfo == null) { + throw new IllegalStateException("Unable to generate package info: " + + ai.modulePath); + } + mAllPackagesCache.add(packageInfo); + mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName); + if (ai.isActive) { + if (activePackagesSet.contains(packageInfo.packageName)) { + throw new IllegalStateException( + "Two active packages have the same name: " + + packageInfo.packageName); } - if (ai.isFactory) { - if (factoryPackagesSet.contains(packageInfo.packageName)) { - throw new IllegalStateException( - "Two factory packages have the same name: " - + packageInfo.packageName); - } - factoryPackagesSet.add(packageInfo.packageName); + activePackagesSet.add(packageInfo.packageName); + } + if (ai.isFactory) { + if (factoryPackagesSet.contains(packageInfo.packageName)) { + throw new IllegalStateException( + "Two factory packages have the same name: " + + packageInfo.packageName); } + factoryPackagesSet.add(packageInfo.packageName); } - } catch (RemoteException re) { - Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); - throw new RuntimeException(re); + } else if (throwable instanceof PackageParser.PackageParserException) { + throw new IllegalStateException("Unable to parse: " + ai.modulePath, throwable); + } else { + throw new IllegalStateException("Unexpected exception occurred while parsing " + + ai.modulePath, throwable); } } } @Override - @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) { - populateAllPackagesCacheIfNeeded(); + @Nullable + public PackageInfo getPackageInfo(String packageName, + @PackageInfoFlags int flags) { + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0; boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0; for (PackageInfo packageInfo: mAllPackagesCache) { @@ -525,7 +536,8 @@ public abstract class ApexManager { @Override List<PackageInfo> getActivePackages() { - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); return mAllPackagesCache .stream() .filter(item -> isActive(item)) @@ -534,7 +546,8 @@ public abstract class ApexManager { @Override List<PackageInfo> getFactoryPackages() { - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); return mAllPackagesCache .stream() .filter(item -> isFactory(item)) @@ -543,7 +556,8 @@ public abstract class ApexManager { @Override List<PackageInfo> getInactivePackages() { - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); return mAllPackagesCache .stream() .filter(item -> !isActive(item)) @@ -553,7 +567,8 @@ public abstract class ApexManager { @Override boolean isApexPackage(String packageName) { if (!isApexSupported()) return false; - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); for (PackageInfo packageInfo : mAllPackagesCache) { if (packageInfo.packageName.equals(packageName)) { return true; @@ -563,6 +578,36 @@ public abstract class ApexManager { } @Override + @Nullable + public String getActiveApexPackageNameContainingPackage( + @NonNull AndroidPackage containedPackage) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); + + Objects.requireNonNull(containedPackage); + + synchronized (mLock) { + int numApksInApex = mApksInApex.size(); + for (int apkInApexNum = 0; apkInApexNum < numApksInApex; apkInApexNum++) { + if (mApksInApex.valueAt(apkInApexNum).contains( + containedPackage.getPackageName())) { + String apexModuleName = mApksInApex.keyAt(apkInApexNum); + + int numApexPkgs = mPackageNameToApexModuleName.size(); + for (int apexPkgNum = 0; apexPkgNum < numApexPkgs; apexPkgNum++) { + if (mPackageNameToApexModuleName.valueAt(apexPkgNum).equals( + apexModuleName)) { + return mPackageNameToApexModuleName.keyAt(apexPkgNum); + } + } + } + } + } + + return null; + } + + @Override @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) { try { ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId); @@ -684,8 +729,9 @@ public abstract class ApexManager { @Override List<String> getApksInApex(String apexPackageName) { - populatePackageNameToApexModuleNameIfNeeded(); synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); String moduleName = mPackageNameToApexModuleName.get(apexPackageName); if (moduleName == null) { return Collections.emptyList(); @@ -697,17 +743,19 @@ public abstract class ApexManager { @Override @Nullable public String getApexModuleNameForPackageName(String apexPackageName) { - populatePackageNameToApexModuleNameIfNeeded(); synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); return mPackageNameToApexModuleName.get(apexPackageName); } } @Override public long snapshotCeData(int userId, int rollbackId, String apexPackageName) { - populatePackageNameToApexModuleNameIfNeeded(); String apexModuleName; synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); apexModuleName = mPackageNameToApexModuleName.get(apexPackageName); } if (apexModuleName == null) { @@ -724,9 +772,10 @@ public abstract class ApexManager { @Override public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) { - populatePackageNameToApexModuleNameIfNeeded(); String apexModuleName; synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); apexModuleName = mPackageNameToApexModuleName.get(apexPackageName); } if (apexModuleName == null) { @@ -797,15 +846,7 @@ public abstract class ApexManager { void dump(PrintWriter pw, @Nullable String packageName) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); try { - populateAllPackagesCacheIfNeeded(); ipw.println(); - ipw.println("Active APEX packages:"); - dumpFromPackagesCache(getActivePackages(), packageName, ipw); - ipw.println("Inactive APEX packages:"); - dumpFromPackagesCache(getInactivePackages(), packageName, ipw); - ipw.println("Factory APEX packages:"); - dumpFromPackagesCache(getFactoryPackages(), packageName, ipw); - ipw.increaseIndent(); ipw.println("APEX session state:"); ipw.increaseIndent(); final ApexSessionInfo[] sessions = mApexService.getSessions(); @@ -834,6 +875,17 @@ public abstract class ApexManager { ipw.decreaseIndent(); } ipw.decreaseIndent(); + ipw.println(); + if (mAllPackagesCache == null) { + ipw.println("APEX packages have not been scanned"); + return; + } + ipw.println("Active APEX packages:"); + dumpFromPackagesCache(getActivePackages(), packageName, ipw); + ipw.println("Inactive APEX packages:"); + dumpFromPackagesCache(getInactivePackages(), packageName, ipw); + ipw.println("Factory APEX packages:"); + dumpFromPackagesCache(getFactoryPackages(), packageName, ipw); } catch (RemoteException e) { ipw.println("Couldn't communicate with apexd."); } @@ -879,12 +931,13 @@ public abstract class ApexManager { } @Override - void systemReady(Context context) { + void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, + @NonNull ExecutorService executorService) { // No-op } @Override - PackageInfo getPackageInfo(String packageName, int flags) { + public PackageInfo getPackageInfo(String packageName, int flags) { return null; } @@ -909,6 +962,15 @@ public abstract class ApexManager { } @Override + @Nullable + public String getActiveApexPackageNameContainingPackage( + @NonNull AndroidPackage containedPackage) { + Objects.requireNonNull(containedPackage); + + return null; + } + + @Override ApexSessionInfo getStagedSessionInfo(int sessionId) { throw new UnsupportedOperationException(); } diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index 83da38195053..40876754eae8 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -64,6 +64,7 @@ import com.android.server.wm.ActivityTaskManagerInternal; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private static final String TAG = "CrossProfileAppsService"; @@ -249,16 +250,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } private boolean canRequestInteractAcrossProfilesUnchecked(String packageName) { - List<UserHandle> targetUserProfiles = - getTargetUserProfilesUnchecked(packageName, mInjector.getCallingUserId()); - if (targetUserProfiles.isEmpty()) { + final int[] enabledProfileIds = + mInjector.getUserManager().getEnabledProfileIds(mInjector.getCallingUserId()); + if (enabledProfileIds.length < 2) { return false; } - if (!hasRequestedAppOpPermission( - AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) { - return false; - } - return isCrossProfilePackageWhitelisted(packageName); + return hasRequestedAppOpPermission( + AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName); } private boolean hasRequestedAppOpPermission(String permission, String packageName) { @@ -450,7 +448,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { final int uid = mInjector.getPackageManager() .getPackageUidAsUser(packageName, /* flags= */ 0, userId); if (currentModeEquals(newMode, packageName, uid)) { - Slog.w(TAG, "Attempt to set mode to existing value of " + newMode + " for " + Slog.i(TAG, "Attempt to set mode to existing value of " + newMode + " for " + packageName + " on user ID " + userId); return; } @@ -540,6 +538,17 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return isCrossProfilePackageWhitelisted(packageName); } + @Override + public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) { + final int[] profileIds = mInjector.getUserManager().getProfileIds( + mInjector.getCallingUserId(), /* enabledOnly= */ false); + if (profileIds.length < 2) { + return false; + } + return hasRequestedAppOpPermission( + AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName); + } + private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { return mInjector.withCleanCallingIdentity(() -> { final int[] profileIds = @@ -569,6 +578,24 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { setInteractAcrossProfilesAppOp(packageName, AppOpsManager.opToDefaultMode(op)); } + @Override + public void clearInteractAcrossProfilesAppOps() { + final int defaultMode = + AppOpsManager.opToDefaultMode( + AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES)); + findAllPackageNames() + .forEach(packageName -> setInteractAcrossProfilesAppOp(packageName, defaultMode)); + } + + private List<String> findAllPackageNames() { + return mInjector.getPackageManagerInternal() + .getInstalledApplications( + /* flags= */ 0, mInjector.getCallingUserId(), mInjector.getCallingUid()) + .stream() + .map(applicationInfo -> applicationInfo.packageName) + .collect(Collectors.toList()); + } + CrossProfileAppsInternal getLocalService() { return mLocalService; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 2ff3d2a3a938..8ff7ea9d61dd 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -490,6 +490,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements throw new SecurityException("User restriction prevents installing"); } + if (params.dataLoaderParams != null + && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("You need the " + + "com.android.permission.USE_INSTALLER_V2 permission " + + "to use a data loader"); + } + String requestedInstallerPackageName = params.installerPackageName != null ? params.installerPackageName : installerPackageName; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 348a4cb77163..1248ec01e020 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2479,12 +2479,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public DataLoaderParamsParcel getDataLoaderParams() { + mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null; } @Override public void addFile(int location, String name, long lengthBytes, byte[] metadata, byte[] signature) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); if (!isDataLoaderInstallation()) { throw new IllegalStateException( "Cannot add files to non-data loader installation session."); @@ -2517,6 +2519,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void removeFile(int location, String name) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); if (!isDataLoaderInstallation()) { throw new IllegalStateException( "Cannot add files to non-data loader installation session."); @@ -3242,8 +3245,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { new ComponentName( readStringAttribute(in, ATTR_DATALOADER_PACKAGE_NAME), readStringAttribute(in, ATTR_DATALOADER_CLASS_NAME)), - readStringAttribute(in, ATTR_DATALOADER_ARGUMENTS), - null); + readStringAttribute(in, ATTR_DATALOADER_ARGUMENTS)); } final File appIconFile = buildAppIconFile(sessionId, sessionsDir); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b79f75a2d335..34363c8df8f6 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. * @@ -2894,6 +2900,9 @@ public class PackageManagerService extends IPackageManager.Stub mMetrics, mCacheDir, mPackageParserCallback); ExecutorService executorService = ParallelPackageParser.makeExecutorService(); + // Prepare apex package info before scanning APKs, these information are needed when + // scanning apk in apex. + mApexManager.scanApexPackagesTraced(packageParser, executorService); // Collect vendor/product/system_ext overlay packages. (Do this before scanning // any apps.) // For security and version matching reason, only consider overlay packages if they @@ -5064,15 +5073,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 +6174,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 +6207,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 +6529,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 +6742,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 +6829,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 +7615,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 +7802,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 +7893,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 +7925,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 +8063,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 +16478,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 +17588,7 @@ public class PackageManagerService extends IPackageManager.Stub } catch (RemoteException e) { Log.i(TAG, "Observer no longer exists."); } //end catch + notifyPackageChangeObserversOnDelete(packageName, versionCode); }); } @@ -20631,7 +20699,6 @@ public class PackageManagerService extends IPackageManager.Stub storage.registerListener(mStorageListener); mInstallerService.systemReady(); - mApexManager.systemReady(mContext); mPackageDexOptimizer.systemReady(); mInjector.getStorageManagerInternal().addExternalStoragePolicy( @@ -22980,8 +23047,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 +24489,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..765ecb9710cb 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; @@ -40,6 +42,7 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED; import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED; +import static com.android.server.pm.ApexManager.MATCH_ACTIVE_PACKAGE; import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING; import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS; @@ -128,6 +131,7 @@ import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.Watchdog; +import com.android.server.pm.ApexManager; import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; import com.android.server.pm.SharedUserSetting; @@ -3238,31 +3242,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; } @@ -3321,8 +3319,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivileged() && !platformPackage && platformPermission) { if (!hasPrivappWhitelistEntry(perm, pkg)) { - // Only report violations for apps on system image - if (!mSystemReady && !pkgSetting.getPkgState().isUpdatedSystemApp()) { + ApexManager apexMgr = ApexManager.getInstance(); + String apexContainingPkg = apexMgr.getActiveApexPackageNameContainingPackage(pkg); + + // Only enforce whitelist this on boot + if (!mSystemReady + // Updated system apps do not need to be whitelisted + && !pkgSetting.getPkgState().isUpdatedSystemApp() + // Apps that are in updated apexs' do not need to be whitelisted + && (apexContainingPkg == null || apexMgr.isFactory( + apexMgr.getPackageInfo(apexContainingPkg, MATCH_ACTIVE_PACKAGE)))) { // it's only a reportable violation if the permission isn't explicitly denied ArraySet<String> deniedPermissions = null; if (pkg.isVendor()) { 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/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 1e1256547f2c..c9736401f680 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -49,7 +49,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; -import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; @@ -2091,7 +2090,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Window manager does the checking for this. outAppOp[0] = OP_TOAST_WINDOW; return ADD_OKAY; - case TYPE_DREAM: case TYPE_INPUT_METHOD: case TYPE_WALLPAPER: case TYPE_PRESENTATION: @@ -2230,7 +2228,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { case TYPE_STATUS_BAR: case TYPE_NAVIGATION_BAR: case TYPE_WALLPAPER: - case TYPE_DREAM: return false; default: // Hide only windows below the keyguard host window. 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/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index efe2af3352c2..7eb3f01798c5 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -31,7 +31,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_DRAG; -import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; @@ -806,9 +805,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { return canAddInternalSystemWindow ? 13 : 10; case TYPE_APPLICATION_OVERLAY: return 12; - case TYPE_DREAM: - // used for Dreams (screensavers with TYPE_DREAM windows) - return 14; case TYPE_INPUT_METHOD: // on-screen keyboards and other such input method user interfaces go here. return 15; diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index b1c40cc07ba4..86ff926af7d4 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -241,7 +241,7 @@ public final class PowerManagerService extends SystemService private final Context mContext; private final ServiceThread mHandlerThread; - private final PowerManagerHandler mHandler; + private final Handler mHandler; private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; private final BatterySaverController mBatterySaverController; private final BatterySaverPolicy mBatterySaverPolicy; @@ -252,6 +252,7 @@ public final class PowerManagerService extends SystemService private final LocalService mLocalService; private final NativeWrapper mNativeWrapper; private final SystemPropertiesWrapper mSystemProperties; + private final Clock mClock; private final Injector mInjector; private LightsManager mLightsManager; @@ -597,7 +598,7 @@ public final class PowerManagerService extends SystemService @Override public void onForegroundProfileSwitch(@UserIdInt int newProfileId) throws RemoteException { - final long now = SystemClock.uptimeMillis(); + final long now = mClock.uptimeMillis(); synchronized (mLock) { mForegroundProfile = newProfileId; maybeUpdateForegroundProfileLastActivityLocked(now); @@ -625,11 +626,11 @@ public final class PowerManagerService extends SystemService // Whether profile has been locked last time it timed out. boolean mLockingNotified; - public ProfilePowerState(@UserIdInt int userId, long screenOffTimeout) { + public ProfilePowerState(@UserIdInt int userId, long screenOffTimeout, long now) { mUserId = userId; mScreenOffTimeout = screenOffTimeout; // Not accurate but at least won't cause immediate locking of the profile. - mLastUserActivityTime = SystemClock.uptimeMillis(); + mLastUserActivityTime = now; } } @@ -756,6 +757,15 @@ public final class PowerManagerService extends SystemService } } + /** Functional interface for providing time. */ + @VisibleForTesting + interface Clock { + /** + * Returns current time in milliseconds since boot, not counting time spent in deep sleep. + */ + long uptimeMillis(); + } + @VisibleForTesting static class Injector { Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, @@ -822,6 +832,17 @@ public final class PowerManagerService extends SystemService }; } + Clock createClock() { + return SystemClock::uptimeMillis; + } + + /** + * Handler for asynchronous operations performed by the power manager. + */ + Handler createHandler(Looper looper, Handler.Callback callback) { + return new Handler(looper, callback, true /*async*/); + } + void invalidateIsInteractiveCaches() { PowerManager.invalidateIsInteractiveCaches(); } @@ -853,12 +874,14 @@ public final class PowerManagerService extends SystemService mLocalService = new LocalService(); mNativeWrapper = injector.createNativeWrapper(); mSystemProperties = injector.createSystemPropertiesWrapper(); + mClock = injector.createClock(); mInjector = injector; mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/); mHandlerThread.start(); - mHandler = new PowerManagerHandler(mHandlerThread.getLooper()); + mHandler = injector.createHandler(mHandlerThread.getLooper(), + new PowerManagerHandlerCallback()); mConstants = new Constants(mHandler); mAmbientDisplayConfiguration = mInjector.createAmbientDisplayConfiguration(context); mAmbientDisplaySuppressionController = @@ -1002,7 +1025,7 @@ public final class PowerManagerService extends SystemService incrementBootCount(); } else if (phase == PHASE_BOOT_COMPLETED) { - final long now = SystemClock.uptimeMillis(); + final long now = mClock.uptimeMillis(); mBootCompleted = true; mDirty |= DIRTY_BOOT_COMPLETED; @@ -1011,7 +1034,7 @@ public final class PowerManagerService extends SystemService now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); if (sQuiescent) { - goToSleepNoUpdateLocked(SystemClock.uptimeMillis(), + goToSleepNoUpdateLocked(mClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_QUIESCENT, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); } @@ -1354,7 +1377,7 @@ public final class PowerManagerService extends SystemService opPackageName = wakeLock.mPackageName; opUid = wakeLock.mOwnerUid; } - wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), + wakeUpNoUpdateLocked(mClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag, opUid, opPackageName, opUid); } @@ -1420,7 +1443,7 @@ public final class PowerManagerService extends SystemService private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) { if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0 && isScreenLock(wakeLock)) { - userActivityNoUpdateLocked(SystemClock.uptimeMillis(), + userActivityNoUpdateLocked(mClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER, PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS, wakeLock.mOwnerUid); @@ -1484,7 +1507,7 @@ public final class PowerManagerService extends SystemService } private void restartNofifyLongTimerLocked(WakeLock wakeLock) { - wakeLock.mAcquireTime = SystemClock.uptimeMillis(); + wakeLock.mAcquireTime = mClock.uptimeMillis(); if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) == PowerManager.PARTIAL_WAKE_LOCK && mNotifyLongScheduled == 0) { enqueueNotifyLongMsgLocked(wakeLock.mAcquireTime + MIN_LONG_WAKE_CHECK_INTERVAL); @@ -1569,7 +1592,7 @@ public final class PowerManagerService extends SystemService private void onUserAttention() { synchronized (mLock) { - if (userActivityNoUpdateLocked(SystemClock.uptimeMillis(), + if (userActivityNoUpdateLocked(mClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_ATTENTION, 0 /* flags */, Process.SYSTEM_UID)) { updatePowerStateLocked(); @@ -1844,7 +1867,7 @@ public final class PowerManagerService extends SystemService * had the system not been told the user was inactive. */ private void logSleepTimeoutRecapturedLocked() { - final long now = SystemClock.uptimeMillis(); + final long now = mClock.uptimeMillis(); final long savedWakeTimeMs = mOverriddenTimeout - now; if (savedWakeTimeMs >= 0) { EventLogTags.writePowerSoftSleepRequested(savedWakeTimeMs); @@ -1867,7 +1890,7 @@ public final class PowerManagerService extends SystemService } if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) { Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0); - final int latencyMs = (int) (SystemClock.uptimeMillis() - mLastWakeTime); + final int latencyMs = (int) (mClock.uptimeMillis() - mLastWakeTime); if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) { Slog.w(TAG, "Screen on took " + latencyMs + " ms"); } @@ -1903,7 +1926,7 @@ public final class PowerManagerService extends SystemService // Phase 1: Update wakefulness. // Loop because the wake lock and user activity computations are influenced // by changes in wakefulness. - final long now = SystemClock.uptimeMillis(); + final long now = mClock.uptimeMillis(); int dirtyPhase2 = 0; for (;;) { int dirtyPhase1 = mDirty; @@ -1996,7 +2019,7 @@ public final class PowerManagerService extends SystemService // and it shuts off right away. // Some devices also wake the device when plugged or unplugged because // they don't have a charging LED. - final long now = SystemClock.uptimeMillis(); + final long now = mClock.uptimeMillis(); if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType, dockedOnWirelessCharger)) { wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN, @@ -2204,7 +2227,7 @@ public final class PowerManagerService extends SystemService void checkForLongWakeLocks() { synchronized (mLock) { - final long now = SystemClock.uptimeMillis(); + final long now = mClock.uptimeMillis(); mNotifyLongDispatched = now; final long when = now - MIN_LONG_WAKE_CHECK_INTERVAL; long nextCheckTime = Long.MAX_VALUE; @@ -2533,7 +2556,7 @@ public final class PowerManagerService extends SystemService if (DEBUG_SPEW) { Slog.d(TAG, "updateWakefulnessLocked: Bed time..."); } - final long time = SystemClock.uptimeMillis(); + final long time = mClock.uptimeMillis(); if (isAttentiveTimeoutExpired(time)) { changed = goToSleepNoUpdateLocked(time, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); @@ -2568,7 +2591,7 @@ public final class PowerManagerService extends SystemService return false; } - long now = SystemClock.uptimeMillis(); + long now = mClock.uptimeMillis(); if (isAttentiveTimeoutExpired(now)) { return !isBeingKeptFromInattentiveSleepLocked(); } else { @@ -2699,7 +2722,7 @@ public final class PowerManagerService extends SystemService } // Determine whether the dream should continue. - long now = SystemClock.uptimeMillis(); + long now = mClock.uptimeMillis(); if (wakefulness == WAKEFULNESS_DREAMING) { if (isDreaming && canDreamLocked()) { if (mDreamsBatteryLevelDrainCutoffConfig >= 0 @@ -2881,7 +2904,7 @@ public final class PowerManagerService extends SystemService private void updateScreenBrightnessBoostLocked(int dirty) { if ((dirty & DIRTY_SCREEN_BRIGHTNESS_BOOST) != 0) { if (mScreenBrightnessBoostInProgress) { - final long now = SystemClock.uptimeMillis(); + final long now = mClock.uptimeMillis(); mHandler.removeMessages(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT); if (mLastScreenBrightnessBoostTime > mLastSleepTime) { final long boostTimeout = mLastScreenBrightnessBoostTime + @@ -2968,7 +2991,7 @@ public final class PowerManagerService extends SystemService synchronized (mLock) { mProximityPositive = false; mDirty |= DIRTY_PROXIMITY_POSITIVE; - userActivityNoUpdateLocked(SystemClock.uptimeMillis(), + userActivityNoUpdateLocked(mClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); updatePowerStateLocked(); } @@ -3289,7 +3312,8 @@ public final class PowerManagerService extends SystemService if (profile != null) { profile.mScreenOffTimeout = timeMs; } else { - mProfilePowerState.put(userId, new ProfilePowerState(userId, timeMs)); + mProfilePowerState.put(userId, new ProfilePowerState(userId, timeMs, + mClock.uptimeMillis())); // We need to recalculate wake locks for the new profile state. mDirty |= DIRTY_WAKE_LOCKS; } @@ -3507,7 +3531,9 @@ public final class PowerManagerService extends SystemService } // Control light outside of lock. - light.setFlashing(color, LogicalLight.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); + if (light != null) { + light.setFlashing(color, LogicalLight.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); + } } private void setDozeAfterScreenOffInternal(boolean on) { @@ -3646,14 +3672,14 @@ public final class PowerManagerService extends SystemService @VisibleForTesting boolean wasDeviceIdleForInternal(long ms) { synchronized (mLock) { - return mLastUserActivityTime + ms < SystemClock.uptimeMillis(); + return mLastUserActivityTime + ms < mClock.uptimeMillis(); } } @VisibleForTesting void onUserActivity() { synchronized (mLock) { - mLastUserActivityTime = SystemClock.uptimeMillis(); + mLastUserActivityTime = mClock.uptimeMillis(); } } @@ -3662,7 +3688,7 @@ public final class PowerManagerService extends SystemService synchronized (mLock) { mForceSuspendActive = true; // Place the system in an non-interactive state - goToSleepInternal(SystemClock.uptimeMillis(), + goToSleepInternal(mClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid); @@ -3777,21 +3803,21 @@ public final class PowerManagerService extends SystemService if (mNotifyLongScheduled == 0) { pw.print("(none)"); } else { - TimeUtils.formatDuration(mNotifyLongScheduled, SystemClock.uptimeMillis(), pw); + TimeUtils.formatDuration(mNotifyLongScheduled, mClock.uptimeMillis(), pw); } pw.println(); pw.print(" mNotifyLongDispatched="); if (mNotifyLongDispatched == 0) { pw.print("(none)"); } else { - TimeUtils.formatDuration(mNotifyLongDispatched, SystemClock.uptimeMillis(), pw); + TimeUtils.formatDuration(mNotifyLongDispatched, mClock.uptimeMillis(), pw); } pw.println(); pw.print(" mNotifyLongNextCheck="); if (mNotifyLongNextCheck == 0) { pw.print("(none)"); } else { - TimeUtils.formatDuration(mNotifyLongNextCheck, SystemClock.uptimeMillis(), pw); + TimeUtils.formatDuration(mNotifyLongNextCheck, mClock.uptimeMillis(), pw); } pw.println(); pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)); @@ -4389,15 +4415,11 @@ public final class PowerManagerService extends SystemService }; /** - * Handler for asynchronous operations performed by the power manager. + * Callback for asynchronous operations performed by the power manager. */ - private final class PowerManagerHandler extends Handler { - public PowerManagerHandler(Looper looper) { - super(looper, null, true /*async*/); - } - + private final class PowerManagerHandlerCallback implements Handler.Callback { @Override - public void handleMessage(Message msg) { + public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_USER_ACTIVITY_TIMEOUT: handleUserActivityTimeout(); @@ -4415,6 +4437,8 @@ public final class PowerManagerService extends SystemService handleAttentiveTimeout(); break; } + + return true; } } @@ -4505,7 +4529,7 @@ public final class PowerManagerService extends SystemService } if (mNotifiedAcquired) { sb.append(" ACQ="); - TimeUtils.formatDuration(mAcquireTime-SystemClock.uptimeMillis(), sb); + TimeUtils.formatDuration(mAcquireTime-mClock.uptimeMillis(), sb); } if (mNotifiedLong) { sb.append(" LONG"); @@ -4817,7 +4841,7 @@ public final class PowerManagerService extends SystemService @Override // Binder call public void userActivity(long eventTime, int event, int flags) { - final long now = SystemClock.uptimeMillis(); + final long now = mClock.uptimeMillis(); if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER) != PackageManager.PERMISSION_GRANTED && mContext.checkCallingOrSelfPermission( @@ -4855,7 +4879,7 @@ public final class PowerManagerService extends SystemService @Override // Binder call public void wakeUp(long eventTime, @WakeReason int reason, String details, String opPackageName) { - if (eventTime > SystemClock.uptimeMillis()) { + if (eventTime > mClock.uptimeMillis()) { throw new IllegalArgumentException("event time must not be in the future"); } @@ -4873,7 +4897,7 @@ public final class PowerManagerService extends SystemService @Override // Binder call public void goToSleep(long eventTime, int reason, int flags) { - if (eventTime > SystemClock.uptimeMillis()) { + if (eventTime > mClock.uptimeMillis()) { throw new IllegalArgumentException("event time must not be in the future"); } @@ -4891,7 +4915,7 @@ public final class PowerManagerService extends SystemService @Override // Binder call public void nap(long eventTime) { - if (eventTime > SystemClock.uptimeMillis()) { + if (eventTime > mClock.uptimeMillis()) { throw new IllegalArgumentException("event time must not be in the future"); } @@ -5286,7 +5310,7 @@ public final class PowerManagerService extends SystemService @Override // Binder call public void boostScreenBrightness(long eventTime) { - if (eventTime > SystemClock.uptimeMillis()) { + if (eventTime > mClock.uptimeMillis()) { throw new IllegalArgumentException("event time must not be in the future"); } 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/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index e133c0fa7435..5e1be51bfed6 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -18,6 +18,7 @@ package com.android.server.stats.pull; import static android.app.AppOpsManager.OP_FLAG_SELF; import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY; +import static android.app.usage.NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN; import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; import static android.os.Debug.getIonHeapsSizeKb; @@ -34,6 +35,9 @@ import static com.android.server.stats.pull.ProcfsMemoryUtil.getProcessCmdlines; import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs; import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs; +import static java.util.concurrent.TimeUnit.MICROSECONDS; + +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; @@ -61,12 +65,13 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.health.V2_0.IHealth; import android.net.ConnectivityManager; import android.net.INetworkStatsService; +import android.net.INetworkStatsSession; import android.net.Network; import android.net.NetworkRequest; import android.net.NetworkStats; +import android.net.NetworkTemplate; import android.net.wifi.WifiManager; import android.os.BatteryStats; -import android.os.BatteryStatsInternal; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -200,7 +205,8 @@ public class StatsPullAtomService extends SystemService { private final Object mNetworkStatsLock = new Object(); @GuardedBy("mNetworkStatsLock") - private INetworkStatsService mNetworkStatsService; + @Nullable + private INetworkStatsSession mNetworkStatsSession; private final Object mThermalLock = new Object(); @GuardedBy("mThermalLock") @@ -289,13 +295,13 @@ public class StatsPullAtomService extends SystemService { try { switch (atomTag) { case FrameworkStatsLog.WIFI_BYTES_TRANSFER: - return pullWifiBytesTransfer(atomTag, data); + return pullWifiBytesTransfer(atomTag, data, false); case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: - return pullWifiBytesTransferBackground(atomTag, data); + return pullWifiBytesTransfer(atomTag, data, true); case FrameworkStatsLog.MOBILE_BYTES_TRANSFER: - return pullMobileBytesTransfer(atomTag, data); + return pullMobileBytesTransfer(atomTag, data, false); case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: - return pullMobileBytesTransferBackground(atomTag, data); + return pullMobileBytesTransfer(atomTag, data, true); case FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER: return pullBluetoothBytesTransfer(atomTag, data); case FrameworkStatsLog.KERNEL_WAKELOCK: @@ -574,26 +580,35 @@ public class StatsPullAtomService extends SystemService { registerBatteryCycleCount(); } - private INetworkStatsService getINetworkStatsService() { + /** + * Return the {@code INetworkStatsSession} object that holds the necessary properties needed + * for the subsequent queries to {@link com.android.server.net.NetworkStatsService}. Or + * null if the service or binder cannot be obtained. + */ + @Nullable + private INetworkStatsSession getNetworkStatsSession() { synchronized (mNetworkStatsLock) { - if (mNetworkStatsService == null) { - mNetworkStatsService = INetworkStatsService.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); - if (mNetworkStatsService != null) { - try { - mNetworkStatsService.asBinder().linkToDeath(() -> { - synchronized (mNetworkStatsLock) { - mNetworkStatsService = null; - } - }, /* flags */ 0); - } catch (RemoteException e) { - Slog.e(TAG, "linkToDeath with NetworkStatsService failed", e); - mNetworkStatsService = null; - } - } + if (mNetworkStatsSession != null) return mNetworkStatsSession; + + final INetworkStatsService networkStatsService = + INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + if (networkStatsService == null) return null; + try { + networkStatsService.asBinder().linkToDeath(() -> { + synchronized (mNetworkStatsLock) { + mNetworkStatsSession = null; + } + }, /* flags */ 0); + mNetworkStatsSession = networkStatsService.openSessionForUsageStats( + FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN, mContext.getOpPackageName()); + } catch (RemoteException e) { + Slog.e(TAG, "Cannot get NetworkStats session", e); + mNetworkStatsSession = null; } - return mNetworkStatsService; + + return mNetworkStatsSession; } } @@ -698,42 +713,38 @@ public class StatsPullAtomService extends SystemService { ); } - int pullWifiBytesTransfer(int atomTag, List<StatsEvent> pulledData) { - INetworkStatsService networkStatsService = getINetworkStatsService(); - if (networkStatsService == null) { - Slog.e(TAG, "NetworkStats Service is not available!"); - return StatsManager.PULL_SKIP; + private int pullWifiBytesTransfer( + int atomTag, @NonNull List<StatsEvent> pulledData, boolean withFgbg) { + final NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard(); + final NetworkStats stats = getUidNetworkStatsSinceBoot(template, withFgbg); + if (stats != null) { + addNetworkStats(atomTag, pulledData, stats, withFgbg); + return StatsManager.PULL_SUCCESS; } - long token = Binder.clearCallingIdentity(); - try { - // TODO(b/148402814): Consider caching the following call to get BatteryStatsInternal. - BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); - String[] ifaces = bs.getWifiIfaces(); - if (ifaces.length == 0) { - return StatsManager.PULL_SKIP; - } - // Combine all the metrics per Uid into one record. - NetworkStats stats = networkStatsService.getDetailedUidStats(ifaces).groupedByUid(); - addNetworkStats(atomTag, pulledData, stats, false); - } catch (RemoteException e) { - Slog.e(TAG, "Pulling netstats for wifi bytes has error", e); - return StatsManager.PULL_SKIP; - } finally { - Binder.restoreCallingIdentity(token); + return StatsManager.PULL_SKIP; + } + + private int pullMobileBytesTransfer( + int atomTag, @NonNull List<StatsEvent> pulledData, boolean withFgbg) { + final NetworkTemplate template = NetworkTemplate.buildTemplateMobileWildcard(); + final NetworkStats stats = getUidNetworkStatsSinceBoot(template, withFgbg); + if (stats != null) { + addNetworkStats(atomTag, pulledData, stats, withFgbg); + return StatsManager.PULL_SUCCESS; } - return StatsManager.PULL_SUCCESS; + return StatsManager.PULL_SKIP; } - private void addNetworkStats( - int tag, List<StatsEvent> ret, NetworkStats stats, boolean withFGBG) { + private void addNetworkStats(int atomTag, @NonNull List<StatsEvent> ret, + @NonNull NetworkStats stats, boolean withFgbg) { int size = stats.size(); - NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling + final NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling for (int j = 0; j < size; j++) { stats.getValues(j, entry); StatsEvent.Builder e = StatsEvent.newBuilder(); - e.setAtomId(tag); + e.setAtomId(atomTag); e.writeInt(entry.uid); - if (withFGBG) { + if (withFgbg) { e.writeInt(entry.set); } e.writeLong(entry.rxBytes); @@ -744,11 +755,27 @@ public class StatsPullAtomService extends SystemService { } } + @Nullable private NetworkStats getUidNetworkStatsSinceBoot( + @NonNull NetworkTemplate template, boolean withFgbg) { + + final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime(); + final long currentTimeInMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro()); + try { + final NetworkStats stats = getNetworkStatsSession().getSummaryForAllUid(template, + currentTimeInMillis - elapsedMillisSinceBoot, currentTimeInMillis, false); + return withFgbg ? rollupNetworkStatsByFgbg(stats) : stats.groupedByUid(); + } catch (RemoteException | NullPointerException e) { + Slog.e(TAG, "Pulling netstats for " + template + + " fgbg= " + withFgbg + " bytes has error", e); + } + return null; + } + /** * Allows rollups per UID but keeping the set (foreground/background) slicing. * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java */ - private NetworkStats rollupNetworkStatsByFGBG(NetworkStats stats) { + @NonNull private NetworkStats rollupNetworkStatsByFgbg(@NonNull NetworkStats stats) { final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1); final NetworkStats.Entry entry = new NetworkStats.Entry(); @@ -758,7 +785,7 @@ public class StatsPullAtomService extends SystemService { entry.roaming = NetworkStats.ROAMING_ALL; int size = stats.size(); - NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values + final NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values for (int i = 0; i < size; i++) { stats.getValues(i, recycle); @@ -790,31 +817,6 @@ public class StatsPullAtomService extends SystemService { ); } - int pullWifiBytesTransferBackground(int atomTag, List<StatsEvent> pulledData) { - INetworkStatsService networkStatsService = getINetworkStatsService(); - if (networkStatsService == null) { - Slog.e(TAG, "NetworkStats Service is not available!"); - return StatsManager.PULL_SKIP; - } - long token = Binder.clearCallingIdentity(); - try { - BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); - String[] ifaces = bs.getWifiIfaces(); - if (ifaces.length == 0) { - return StatsManager.PULL_SKIP; - } - NetworkStats stats = rollupNetworkStatsByFGBG( - networkStatsService.getDetailedUidStats(ifaces)); - addNetworkStats(atomTag, pulledData, stats, true); - } catch (RemoteException e) { - Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e); - return StatsManager.PULL_SKIP; - } finally { - Binder.restoreCallingIdentity(token); - } - return StatsManager.PULL_SUCCESS; - } - private void registerMobileBytesTransfer() { int tagId = FrameworkStatsLog.MOBILE_BYTES_TRANSFER; PullAtomMetadata metadata = new PullAtomMetadata.Builder() @@ -828,31 +830,6 @@ public class StatsPullAtomService extends SystemService { ); } - int pullMobileBytesTransfer(int atomTag, List<StatsEvent> pulledData) { - INetworkStatsService networkStatsService = getINetworkStatsService(); - if (networkStatsService == null) { - Slog.e(TAG, "NetworkStats Service is not available!"); - return StatsManager.PULL_SKIP; - } - long token = Binder.clearCallingIdentity(); - try { - BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); - String[] ifaces = bs.getMobileIfaces(); - if (ifaces.length == 0) { - return StatsManager.PULL_SKIP; - } - // Combine all the metrics per Uid into one record. - NetworkStats stats = networkStatsService.getDetailedUidStats(ifaces).groupedByUid(); - addNetworkStats(atomTag, pulledData, stats, false); - } catch (RemoteException e) { - Slog.e(TAG, "Pulling netstats for mobile bytes has error", e); - return StatsManager.PULL_SKIP; - } finally { - Binder.restoreCallingIdentity(token); - } - return StatsManager.PULL_SUCCESS; - } - private void registerMobileBytesTransferBackground() { int tagId = FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG; PullAtomMetadata metadata = new PullAtomMetadata.Builder() @@ -866,31 +843,6 @@ public class StatsPullAtomService extends SystemService { ); } - int pullMobileBytesTransferBackground(int atomTag, List<StatsEvent> pulledData) { - INetworkStatsService networkStatsService = getINetworkStatsService(); - if (networkStatsService == null) { - Slog.e(TAG, "NetworkStats Service is not available!"); - return StatsManager.PULL_SKIP; - } - long token = Binder.clearCallingIdentity(); - try { - BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); - String[] ifaces = bs.getMobileIfaces(); - if (ifaces.length == 0) { - return StatsManager.PULL_SKIP; - } - NetworkStats stats = rollupNetworkStatsByFGBG( - networkStatsService.getDetailedUidStats(ifaces)); - addNetworkStats(atomTag, pulledData, stats, true); - } catch (RemoteException e) { - Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e); - return StatsManager.PULL_SKIP; - } finally { - Binder.restoreCallingIdentity(token); - } - return StatsManager.PULL_SUCCESS; - } - private void registerBluetoothBytesTransfer() { int tagId = FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER; PullAtomMetadata metadata = new PullAtomMetadata.Builder() diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java index b051bab71f12..8ff2a1b6364b 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java @@ -37,9 +37,9 @@ class TimeZoneDetectorShellCommand extends ShellCommand { } switch (cmd) { - case "suggestTelephonyTimeZone": + case "suggest_telephony_time_zone": return runSuggestTelephonyTimeZone(); - case "suggestManualTimeZone": + case "suggest_manual_time_zone": return runSuggestManualTimeZone(); default: { return handleDefaultCommands(cmd); @@ -105,9 +105,9 @@ class TimeZoneDetectorShellCommand extends ShellCommand { pw.println("Time Zone Detector (time_zone_detector) commands:"); pw.println(" help"); pw.println(" Print this help text."); - pw.println(" suggestTelephonyTimeZone"); + pw.println(" suggest_telephony_time_zone"); pw.println(" --suggestion <telephony suggestion opts>"); - pw.println(" suggestManualTimeZone"); + pw.println(" suggest_manual_time_zone"); pw.println(" --suggestion <manual suggestion opts>"); pw.println(); ManualTimeZoneSuggestion.printCommandLineOpts(pw); diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 28c5a2857aa6..6dded00321b5 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -657,12 +657,12 @@ public class TunerResourceManagerService extends SystemService { } private void enforceTunerAccessPermission(String apiName) { - getContext().enforceCallingPermission("android.Manifest.permission.ACCESS_TV_TUNER", + getContext().enforceCallingPermission("android.permission.ACCESS_TV_TUNER", TAG + ": " + apiName); } private void enforceDescramblerAccessPermission(String apiName) { - getContext().enforceCallingPermission("android.Manifest.permission.ACCESS_TV_DESCRAMBLER", + getContext().enforceCallingPermission("android.permission.ACCESS_TV_DESCRAMBLER", TAG + ": " + apiName); } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index a298b8915110..ddf166eb0bd5 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -16,6 +16,7 @@ package com.android.server.wallpaper; +import static android.app.WallpaperManager.COMMAND_REAPPLY; import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_SYSTEM; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO; @@ -1070,7 +1071,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub /** * Collect needed info for a display. */ - private final class DisplayConnector { + @VisibleForTesting + final class DisplayConnector { final int mDisplayId; final Binder mToken = new Binder(); IWallpaperEngine mEngine; @@ -1213,7 +1215,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - private boolean isUsableDisplay(Display display) { + @VisibleForTesting + boolean isUsableDisplay(Display display) { if (display == null || !display.hasAccess(mClientUid)) { return false; } @@ -2438,9 +2441,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub * the caller here writes new bitmap data. */ if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) { - if (DEBUG) { - Slog.i(TAG, "Migrating system->lock to preserve"); - } + Slog.i(TAG, "Migrating current wallpaper to be lock-only before" + + "updating system wallpaper"); migrateSystemToLockWallpaperLocked(userId); } @@ -2508,6 +2510,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile, MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE); if (!SELinux.restorecon(wallpaper.wallpaperFile)) { + Slog.w(TAG, "restorecon failed for wallpaper file: " + + wallpaper.wallpaperFile.getPath()); return null; } wallpaper.name = name; @@ -2517,10 +2521,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } // Nullify field to require new computation wallpaper.primaryColors = null; - if (DEBUG) { - Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId - + " name=" + name + " file=" + wallpaper.wallpaperFile.getName()); - } + Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId + + " name=" + name + " file=" + wallpaper.wallpaperFile.getName()); return fd; } catch (FileNotFoundException e) { Slog.w(TAG, "Error setting wallpaper", e); @@ -2553,7 +2555,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub WallpaperData wallpaper; synchronized (mLock) { - if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name); + Slog.v(TAG, "setWallpaperComponent name=" + name); wallpaper = mWallpaperMap.get(userId); if (wallpaper == null) { throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); @@ -2568,6 +2570,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (mLockWallpaperMap.get(userId) == null) { // We're using the static imagery and there is no lock-specific image in place, // therefore it's a shared system+lock image that we need to migrate. + Slog.i(TAG, "Migrating current wallpaper to be lock-only before" + + "updating system wallpaper"); migrateSystemToLockWallpaperLocked(userId); } } @@ -2583,6 +2587,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) { if (!same) { wallpaper.primaryColors = null; + } else { + if (wallpaper.connection != null) { + wallpaper.connection.forEachDisplayConnector(displayConnector -> { + try { + if (displayConnector.mEngine != null) { + displayConnector.mEngine.dispatchWallpaperCommand( + COMMAND_REAPPLY, 0, 0, 0, null); + } + } catch (RemoteException e) { + Slog.w(TAG, "Error sending apply message to wallpaper", e); + } + }); + } } wallpaper.wallpaperId = makeWallpaperIdLocked(); notifyCallbacksLocked(wallpaper); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 6d53786ddf41..29f7d523e9a0 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2085,7 +2085,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** Returns true if this activity is opaque and fills the entire space of this task. */ boolean occludesParent() { - return mOccludesParent; + return !finishing && mOccludesParent; } boolean setOccludesParent(boolean occludesParent) { @@ -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)); } /** @@ -5198,7 +5201,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } finishLaunchTickingLocked(); if (task != null) { - task.hasBeenVisible = true; + task.setHasBeenVisible(true); } } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index e8bfe8ef63ac..90898597c265 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() @@ -2358,8 +2342,7 @@ class ActivityStack extends Task { if (!newTask && isOrhasTask) { // Starting activity cannot be occluding activity, otherwise starting window could be // remove immediately without transferring to starting activity. - final ActivityRecord occludingActivity = getActivity( - (ar) -> !ar.finishing && ar.occludesParent(), true, r); + final ActivityRecord occludingActivity = getOccludingActivityAbove(r); if (occludingActivity != null) { // Here it is! Now, if this is not yet visible (occluded by another task) to the // user, then just add it without starting; it will get started when the user @@ -2588,6 +2571,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(); @@ -3078,6 +3068,14 @@ class ActivityStack extends Task { task.setOverrideDisplayedBounds(bounds == null || bounds.isEmpty() ? null : bounds); } + /** + * Returns the top-most activity that occludes the given one, or @{code null} if none. + */ + @Nullable + private ActivityRecord getOccludingActivityAbove(ActivityRecord activity) { + return getActivity((ar) -> ar.occludesParent(), true /* traverseTopToBottom */, activity); + } + boolean willActivityBeVisible(IBinder token) { final ActivityRecord r = ActivityRecord.forTokenLocked(token); if (r == null) { @@ -3085,9 +3083,7 @@ class ActivityStack extends Task { } // See if there is an occluding activity on-top of this one. - final ActivityRecord occludingActivity = getActivity((ar) -> - ar.occludesParent() && !ar.finishing, - r, false /*includeBoundary*/, true /*traverseTopToBottom*/); + final ActivityRecord occludingActivity = getOccludingActivityAbove(r); if (occludingActivity != null) return false; if (r.finishing) Slog.e(TAG, "willActivityBeVisible: Returning false," @@ -3542,6 +3538,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 +3572,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 +3609,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 +3619,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 +3642,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 +3754,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 +3784,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 +3828,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 184439587a02..bf92542cdd63 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -31,12 +31,11 @@ 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_UNDEFINED; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; 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 +121,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 +144,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,8 +229,9 @@ import android.util.proto.ProtoOutputStream; import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; -import android.window.WindowContainerTransaction; import android.view.WindowManager; +import android.window.IWindowOrganizerController; +import android.window.WindowContainerTransaction; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -663,10 +663,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private FontScaleSettingObserver mFontScaleSettingObserver; - /** - * Stores the registration and state of TaskOrganizers in use. - */ - TaskOrganizerController mTaskOrganizerController = new TaskOrganizerController(this); + WindowOrganizerController mWindowOrganizerController; + TaskOrganizerController mTaskOrganizerController; private int mDeviceOwnerUid = Process.INVALID_UID; @@ -724,6 +722,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mLifecycleManager = new ClientLifecycleManager(); mInternal = new LocalService(); GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED); + mWindowOrganizerController = new WindowOrganizerController(this); + mTaskOrganizerController = mWindowOrganizerController.mTaskOrganizerController; } public void onSystemReady() { @@ -1273,9 +1273,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; @@ -2348,16 +2352,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); @@ -2608,13 +2614,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) { - return getFilteredTasks(maxNum, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED); + return getFilteredTasks(maxNum, false /* filterForVisibleRecents */); } + /** + * @param filterOnlyVisibleRecents whether to filter the tasks based on whether they would ever + * be visible in the recent task list in systemui + */ @Override public List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, - @WindowConfiguration.ActivityType int ignoreActivityType, - @WindowConfiguration.WindowingMode int ignoreWindowingMode) { + boolean filterOnlyVisibleRecents) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final boolean crossUser = isCrossUserAllowed(callingPid, callingUid); @@ -2630,8 +2639,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum); final boolean allowed = isGetTasksAllowed("getTasks", callingPid, callingUid); - mRootWindowContainer.getRunningTasks(maxNum, list, ignoreActivityType, - ignoreWindowingMode, callingUid, allowed, crossUser, callingProfileIds); + mRootWindowContainer.getRunningTasks(maxNum, list, filterOnlyVisibleRecents, callingUid, + allowed, crossUser, callingProfileIds); } return list; @@ -2754,25 +2763,23 @@ 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); - mTaskOrganizerController.applyContainerTransaction(wct, null); + wct.reparent(task.getStack().mRemoteToken, primarySplitTask.mRemoteToken, toTop); + mWindowOrganizerController.applyTransaction(wct); } /** @@ -3238,7 +3245,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 @@ -4277,19 +4285,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; } @@ -4311,7 +4309,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } wct.setBounds(secondary.mRemoteToken, otherRect); - mTaskOrganizerController.applyContainerTransaction(wct, null /* organizer */); + mWindowOrganizerController.applyTransaction(wct); } } finally { Binder.restoreCallingIdentity(ident); @@ -4332,10 +4330,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public ITaskOrganizerController getTaskOrganizerController() { + public IWindowOrganizerController getWindowOrganizerController() { mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, - "getTaskOrganizerController()"); - return mTaskOrganizerController; + "getWindowOrganizerController()"); + return mWindowOrganizerController; } /** @@ -7476,10 +7474,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/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 6a47c9e217f8..654ccc80f8a8 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -618,6 +618,7 @@ public class AppTransitionController { Animation anim = mService.mPolicy.createKeyguardWallpaperExit( (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0); if (anim != null) { + anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked()); mDisplayContent.mWallpaperController.startWallpaperAnimation(anim); } } 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..31e0b4a07075 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +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; @@ -67,7 +68,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFI import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; -import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; @@ -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"). @@ -754,11 +766,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mTmpInitial) { w.resetContentChanged(); } - if (w.mAttrs.type == TYPE_DREAM) { - // Don't layout windows behind a dream, so that if it does stuff like hide - // the status bar we won't get a bad transition when it goes away. - mTmpWindow = w; - } w.mLayoutNeeded = false; w.prelayout(); final boolean firstLayout = !w.isLaidOut(); @@ -812,10 +819,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo + " mContainingFrame=" + w.getContainingFrame() + " mDisplayFrame=" + w.getDisplayFrameLw()); } - } else if (w.mAttrs.type == TYPE_DREAM) { - // Don't layout windows behind a dream, so that if it does stuff like hide the - // status bar we won't get a bad transition when it goes away. - mTmpWindow = mTmpWindow2; } }; @@ -899,17 +902,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Take care of the window being ready to display. final boolean committed = winAnimator.commitFinishDrawingLocked(); if (isDefaultDisplay && committed) { - if (w.mAttrs.type == TYPE_DREAM) { - // HACK: When a dream is shown, it may at that point hide the lock screen. - // So we need to redo the layout to let the phone window manager make this - // happen. - pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT; - if (DEBUG_LAYOUT_REPEATS) { - surfacePlacer.debugLayoutRepeats( - "dream and commitFinishDrawingLocked true", - pendingLayoutChanges); - } - } if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "First draw done in potential wallpaper target " + w); @@ -1305,8 +1297,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 +1487,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 +1516,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 +1563,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 +2118,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 +2591,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 +2614,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); @@ -3010,6 +3001,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (recentsStack != null) { pw.println(prefix + "recentsStack=" + recentsStack.getName()); } + final ActivityStack dreamStack = + getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_DREAM); + if (dreamStack != null) { + pw.println(prefix + "dreamStack=" + dreamStack.getName()); + } pw.println(); mPinnedStackControllerLocked.dump(prefix, pw); @@ -3198,7 +3194,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mode == UPDATE_FOCUS_PLACING_SURFACES) { performLayout(true /*initial*/, updateInputWindows); } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) { - mWmService.mRoot.performSurfacePlacement(false); + mWmService.mRoot.performSurfacePlacement(); } } @@ -3835,7 +3831,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } // TODO: Super crazy long method that should be broken down... - void applySurfaceChangesTransaction(boolean recoveringMemory) { + void applySurfaceChangesTransaction() { final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked; mTmpUpdateAllDrawn.clear(); @@ -4013,6 +4009,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 +4293,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 +4326,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 +4366,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 +4377,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 +4416,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 +4428,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo protected void removeChild(ActivityStack stack) { super.removeChild(stack); mDisplayContent.onStackRemoved(stack); + mAtmService.updateSleepIfNeededLocked(); removeStackReferenceIfNeeded(stack); } @@ -4494,6 +4483,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 +4637,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 +4783,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 +4963,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 +5634,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 +5652,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mPreferredTopFocusableStack = null; } releaseSelfIfNeeded(); - mAtmService.updateSleepIfNeededLocked(); onStackOrderChanged(stack); } @@ -5702,6 +5689,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 +5706,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 +5749,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 +5814,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 +5824,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 +5837,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 +5878,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 +5920,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 +6073,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 +6145,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 +6167,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 +6186,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 +6316,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 +6341,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 +6477,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 +6567,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 +6671,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 +6708,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 +6729,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 +6784,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..367151cf0f79 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; @@ -79,7 +78,6 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; -import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; @@ -115,7 +113,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 +175,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 +329,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 +387,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 +406,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 +425,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) @@ -861,7 +849,6 @@ public class DisplayPolicy { | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; break; - case TYPE_DREAM: case TYPE_WALLPAPER: // Dreams and wallpapers don't have an app window token and can thus not be // letterboxed. Hence always let them extend under the cutout. @@ -1236,13 +1223,6 @@ public class DisplayPolicy { if (DEBUG_ANIM) Slog.i(TAG, "**** STARTING EXIT"); return R.anim.app_starting_exit; } - } else if (win.getAttrs().type == TYPE_DREAM && mDreamingLockscreen - && transit == TRANSIT_ENTER) { - // Special case: we are animating in a dream, while the keyguard - // is shown. We don't want an animation on the dream, because - // we need it shown immediately with the keyguard animating away - // to reveal it. - return ANIMATION_NONE; } return ANIMATION_STYLEABLE; @@ -1503,14 +1483,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 +1511,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 +1533,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()) { @@ -2541,7 +2519,7 @@ public class DisplayPolicy { if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) { mForceStatusBar = true; } - if (attrs.type == TYPE_DREAM) { + if (win.isDreamWindow()) { // If the lockscreen was showing when the dream started then wait // for the dream to draw before hiding the lockscreen. if (!mDreamingLockscreen @@ -2638,15 +2616,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 +3129,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 +3808,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 776a9863dc6a..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; } @@ -531,8 +528,7 @@ public class DisplayRotation { try { mDisplayContent.sendNewConfiguration(); if (t != null) { - mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, - null /* organizer */); + mService.mAtmService.mWindowOrganizerController.applyTransaction(t); } } finally { mService.mAtmService.continueWindowLayout(); diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index e2c7d52fa673..88cdd1781aee 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -301,7 +301,7 @@ final class InputMonitor { * we may have some issues with modal-windows, but I guess we can * cross that bridge when we come to implementing full-screen TaskOrg */ - if (child.getTask() != null && child.getTask().isControlledByTaskOrganizer()) { + if (child.getTask() != null && child.getTask().isOrganized()) { inputWindowHandle.replaceTouchableRegionWithCrop(null /* Use this surfaces crop */); } diff --git a/services/core/java/com/android/server/wm/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..12be9df55fad 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -21,9 +21,11 @@ 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; +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.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -1298,6 +1300,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: @@ -1307,6 +1310,7 @@ class RecentTasks { == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) { return false; } + break; } // Ignore certain windowing modes @@ -1314,23 +1318,21 @@ class RecentTasks { case WINDOWING_MODE_PINNED: return false; case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().getTopMostTask()); + if (DEBUG_RECENTS_TRIM_TASKS) { + Slog.d(TAG, "\ttop=" + task.getStack().getTopMostTask()); + } final ActivityStack stack = task.getStack(); if (stack != null && stack.getTopMostTask() == task) { // Only the non-top task of the primary split screen mode is visible return false; } - } - - // Tasks managed by/associated with an ActivityView should be excluded from recents. - // singleTaskInstance is set on the VirtualDisplay managed by ActivityView - // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance - final ActivityStack stack = task.getStack(); - if (stack != null) { - DisplayContent display = stack.getDisplay(); - if (display != null && display.isSingleTaskInstance()) { - return false; - } + break; + case WINDOWING_MODE_MULTI_WINDOW: + // Ignore tasks that are always on top + if (task.isAlwaysOnTop()) { + return false; + } + break; } // If we're in lock task mode, ignore the root task diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index adafdec40d37..057592c0c2fc 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -388,17 +388,24 @@ 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); throw e; } finally { mService.continueWindowLayout(); + // Make sure the surfaces are updated with the latest state. Sometimes the + // surface placement may be skipped if display configuration is changed (i.e. + // {@link DisplayContent#mWaitingForConfig} is true). + if (mWindowManager.mRoot.isLayoutNeeded()) { + mWindowManager.mRoot.performSurfacePlacement(); + } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } }); 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..bc5295a6fdb8 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; @@ -35,7 +36,6 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE; -import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY; @@ -795,10 +795,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return leakedSurface || killedApps; } - void performSurfacePlacement(boolean recoveringMemory) { + void performSurfacePlacement() { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement"); try { - performSurfacePlacementNoTrace(recoveringMemory); + performSurfacePlacementNoTrace(); } finally { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -806,7 +806,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // "Something has changed! Let's make it correct now." // TODO: Super crazy long method that should be broken down... - void performSurfacePlacementNoTrace(boolean recoveringMemory) { + void performSurfacePlacementNoTrace() { if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by " + Debug.getCallers(3)); @@ -841,7 +841,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges"); mWmService.openSurfaceTransaction(); try { - applySurfaceChangesTransaction(recoveringMemory); + applySurfaceChangesTransaction(); } catch (RuntimeException e) { Slog.wtf(TAG, "Unhandled exception in Window Manager", e); } finally { @@ -1041,7 +1041,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - private void applySurfaceChangesTransaction(boolean recoveringMemory) { + private void applySurfaceChangesTransaction() { mHoldScreenWindow = null; mObscuringWindow = null; @@ -1064,7 +1064,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final int count = mChildren.size(); for (int j = 0; j < count; ++j) { final DisplayContent dc = mChildren.get(j); - dc.applySurfaceChangesTransaction(recoveringMemory); + dc.applySurfaceChangesTransaction(); } // Give the display manager a chance to adjust properties like display rotation if it needs @@ -1137,7 +1137,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // While a dream or keyguard is showing, obscure ordinary application content on // secondary displays (by forcibly enabling mirroring unless there is other content // we want to show) but still allow opaque keyguard dialogs to be shown. - if (type == TYPE_DREAM || mWmService.mPolicy.isKeyguardShowing()) { + if (w.isDreamWindow() || mWmService.mPolicy.isKeyguardShowing()) { mObscureApplicationContentOnSecondaryDisplays = true; } displayHasContent = true; @@ -1970,8 +1970,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 +2109,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 +2796,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 +2826,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 +2851,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 +2873,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 +2927,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 @@ -3381,11 +3392,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> @VisibleForTesting void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list, - @WindowConfiguration.ActivityType int ignoreActivityType, - @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid, - boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) { - mStackSupervisor.getRunningTasks().getTasks(maxNum, list, ignoreActivityType, - ignoreWindowingMode, this, callingUid, allowed, crossUser, profileIds); + boolean filterOnlyVisibleRecents, int callingUid, boolean allowed, boolean crossUser, + ArraySet<Integer> profileIds) { + mStackSupervisor.getRunningTasks().getTasks(maxNum, list, filterOnlyVisibleRecents, this, + callingUid, allowed, crossUser, profileIds); } void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) { diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 02077fbf453e..3509ba72d058 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -16,12 +16,10 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import android.app.ActivityManager.RunningTaskInfo; -import android.app.WindowConfiguration.ActivityType; -import android.app.WindowConfiguration.WindowingMode; import android.os.UserHandle; import android.util.ArraySet; @@ -49,13 +47,13 @@ class RunningTasks { private boolean mCrossUser; private ArraySet<Integer> mProfileIds; private boolean mAllowed; - private int mIgnoreActivityType; - private int mIgnoreWindowingMode; + private boolean mFilterOnlyVisibleRecents; private ActivityStack mTopDisplayFocusStack; + private RecentTasks mRecentTasks; - void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType, - @WindowingMode int ignoreWindowingMode, RootWindowContainer root, - int callingUid, boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) { + void getTasks(int maxNum, List<RunningTaskInfo> list, boolean filterOnlyVisibleRecents, + RootWindowContainer root, int callingUid, boolean allowed, boolean crossUser, + ArraySet<Integer> profileIds) { // Return early if there are no tasks to fetch if (maxNum <= 0) { return; @@ -68,9 +66,9 @@ class RunningTasks { mCrossUser = crossUser; mProfileIds = profileIds; mAllowed = allowed; - mIgnoreActivityType = ignoreActivityType; - mIgnoreWindowingMode = ignoreWindowingMode; + mFilterOnlyVisibleRecents = filterOnlyVisibleRecents; mTopDisplayFocusStack = root.getTopDisplayFocusedStack(); + mRecentTasks = root.mService.getRecentTasks(); final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this, PooledLambda.__(Task.class)); @@ -107,14 +105,12 @@ class RunningTasks { return; } } - if (mIgnoreActivityType != ACTIVITY_TYPE_UNDEFINED - && task.getActivityType() == mIgnoreActivityType) { - // Skip ignored activity type - return; - } - if (mIgnoreWindowingMode != WINDOWING_MODE_UNDEFINED - && task.getWindowingMode() == mIgnoreWindowingMode) { - // Skip ignored windowing mode + if (mFilterOnlyVisibleRecents + && task.getActivityType() != ACTIVITY_TYPE_HOME + && task.getActivityType() != ACTIVITY_TYPE_RECENTS + && !mRecentTasks.isVisibleRecentTask(task)) { + // Skip if this task wouldn't be visibile (ever) from recents, with an exception for the + // home & recent tasks return; } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index eca7d2e19288..91b4ec95f8fd 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; @@ -256,7 +258,7 @@ class Task extends WindowContainer<WindowContainer> { boolean autoRemoveRecents; // If true, we should automatically remove the task from // recents when activity finishes boolean askedCompatMode;// Have asked the user about compat mode for this task. - boolean hasBeenVisible; // Set if any activities in the task have been visible to the user. + private boolean mHasBeenVisible; // Set if any activities in the task have been visible String stringName; // caching of toString() result. boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity @@ -481,6 +483,10 @@ class Task extends WindowContainer<WindowContainer> { */ ITaskOrganizer mTaskOrganizer; private int mLastTaskOrganizerWindowingMode = -1; + /** + * Prevent duplicate calls to onTaskAppeared. + */ + boolean mTaskAppearedSent; /** * Last Picture-in-Picture params applicable to the task. Updated when the app @@ -489,6 +495,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 +526,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 +551,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 +595,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 +611,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 +1370,7 @@ class Task extends WindowContainer<WindowContainer> { if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) { return applicationType; } - return getChildAt(0).getActivityType(); + return getTopChild().getActivityType(); } @Override @@ -1378,6 +1383,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 +1429,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 +1481,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); } @@ -1504,7 +1521,7 @@ class Task extends WindowContainer<WindowContainer> { // We will automatically remove the task either if it has explicitly asked for // this, or it is empty and has never contained an activity that got shown to // the user. - return autoRemoveRecents || (!hasChild() && !hasBeenVisible); + return autoRemoveRecents || (!hasChild() && !getHasBeenVisible()); } /** @@ -1878,7 +1895,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) { @@ -2012,7 +2034,7 @@ class Task extends WindowContainer<WindowContainer> { } private void saveLaunchingStateIfNeeded(DisplayContent display) { - if (!hasBeenVisible) { + if (!getHasBeenVisible()) { // Not ever visible to user. return; } @@ -2312,18 +2334,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 +2446,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 +2632,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 +3146,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 +3251,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 +3430,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 +3466,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; @@ -3518,7 +3562,7 @@ class Task extends WindowContainer<WindowContainer> { pw.print(prefix); pw.print("mRootProcess="); pw.println(mRootProcess); } pw.print(prefix); pw.print("taskId=" + mTaskId); pw.println(" stackId=" + getRootTaskId()); - pw.print(prefix + "hasBeenVisible=" + hasBeenVisible); + pw.print(prefix + "mHasBeenVisible=" + getHasBeenVisible()); pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode)); pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture); pw.print(" isResizeable=" + isResizeable()); @@ -3717,8 +3761,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 +3785,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,36 +4060,73 @@ 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); } + void setHasBeenVisible(boolean hasBeenVisible) { + mHasBeenVisible = hasBeenVisible; + if (hasBeenVisible) { + sendTaskAppeared(); + if (!isRootTask()) { + getRootTask().setHasBeenVisible(true); + } + } + } + + boolean getHasBeenVisible() { + return mHasBeenVisible; + } + + /** In the case that these three conditions are true, we want to send the Task to + * the organizer: + * 1. We have a SurfaceControl + * 2. An organizer has been set + * 3. We have finished drawing + * Any time any of these conditions are updated, the updating code should call + * sendTaskAppeared. + */ + private boolean taskAppearedReady() { + return mSurfaceControl != null && mTaskOrganizer != null && getHasBeenVisible(); + } + private void sendTaskAppeared() { - if (mSurfaceControl != null && mTaskOrganizer != null) { + if (taskAppearedReady() && !mTaskAppearedSent) { + mTaskAppearedSent = true; mAtmService.mTaskOrganizerController.onTaskAppeared(mTaskOrganizer, this); } } private void sendTaskVanished() { - if (mTaskOrganizer != null) { + if (mTaskOrganizer != null && mTaskAppearedSent) { + mTaskAppearedSent = false; mAtmService.mTaskOrganizerController.onTaskVanished(mTaskOrganizer, this); } } @@ -4060,11 +4143,14 @@ class Task extends WindowContainer<WindowContainer> { return true; } - // Called on Binder death. - void taskOrganizerDied() { + void taskOrganizerUnregistered() { mTaskOrganizer = null; + mTaskAppearedSent = false; mLastTaskOrganizerWindowingMode = -1; onTaskOrganizerChanged(); + if (mCreatedByOrganizer) { + removeImmediately(); + } } /** @@ -4117,27 +4203,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 9bf4d6355071..4382e9d578ad 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -17,65 +17,45 @@ 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; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; -import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; -import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; -import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; -import static com.android.server.wm.WindowContainer.POSITION_TOP; +import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS; +import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS; import android.annotation.Nullable; import android.app.ActivityManager.RunningTaskInfo; -import android.app.WindowConfiguration; +import android.content.Intent; import android.content.pm.ActivityInfo; -import android.content.res.Configuration; -import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.util.ArraySet; import android.util.Slog; -import android.view.SurfaceControl; -import android.window.ITaskOrganizerController; +import android.util.SparseArray; import android.window.ITaskOrganizer; +import android.window.ITaskOrganizerController; import android.window.IWindowContainer; -import android.window.WindowContainerTransaction; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.function.pooled.PooledConsumer; -import com.android.internal.util.function.pooled.PooledLambda; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; +import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.WeakHashMap; /** * Stores the TaskOrganizers associated with a given windowing mode and * their associated state. */ -class TaskOrganizerController extends ITaskOrganizerController.Stub - implements BLASTSyncEngine.TransactionReadyListener { +class TaskOrganizerController extends ITaskOrganizerController.Stub { private static final String TAG = "TaskOrganizerController"; - - /** Flag indicating that an applied transaction may have effected lifecycle */ - private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1; - private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1; + private static final LinkedList<TaskOrganizerState> EMPTY_LIST = new LinkedList<>(); /** - * Masks specifying which configurations task-organizers can control. Incoming transactions - * will be filtered to only include these. - */ - private static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION - | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE; - private static final int CONTROLLABLE_WINDOW_CONFIGS = WindowConfiguration.WINDOW_CONFIG_BOUNDS - | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; - /** * Masks specifying which configurations are important to report back to an organizer when * changed. */ @@ -96,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 { @@ -130,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); } @@ -144,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); } @@ -152,42 +119,29 @@ 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(); - - final HashMap<Integer, ITaskOrganizer> mTaskOrganizersByPendingSyncId = 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<>(); - private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine(); - final ActivityTaskManagerService mService; RunningTaskInfo mTmpTaskInfo; @@ -223,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) { @@ -248,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; } @@ -284,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 { @@ -302,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 { @@ -387,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); @@ -400,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 { @@ -409,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); @@ -440,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; } @@ -480,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; @@ -498,253 +454,4 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub Binder.restoreCallingIdentity(ident); } } - - private int sanitizeAndApplyChange(WindowContainer container, - WindowContainerTransaction.Change change) { - if (!(container instanceof Task)) { - throw new RuntimeException("Invalid token in task transaction"); - } - final Task task = (Task) container; - // The "client"-facing API should prevent bad changes; however, just in case, sanitize - // masks here. - final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS; - final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS; - int effects = 0; - if (configMask != 0) { - Configuration c = new Configuration(container.getRequestedOverrideConfiguration()); - c.setTo(change.getConfiguration(), configMask, windowMask); - container.onRequestedOverrideConfigurationChanged(c); - // TODO(b/145675353): remove the following once we could apply new bounds to the - // pinned stack together with its children. - resizePinnedStackIfNeeded(container, configMask, windowMask, c); - effects |= TRANSACT_EFFECTS_CLIENT_CONFIG; - } - if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) { - if (container.setFocusable(change.getFocusable())) { - effects |= TRANSACT_EFFECTS_LIFECYCLE; - } - } - if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) { - if (task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, change.getHidden())) { - effects |= TRANSACT_EFFECTS_LIFECYCLE; - } - } - return effects; - } - - private int sanitizeAndApplyHierarchyOp(WindowContainer container, - WindowContainerTransaction.HierarchyOp hop) { - if (!(container instanceof Task)) { - throw new IllegalArgumentException("Invalid container in hierarchy op"); - } - if (container.getDisplayContent() == null) { - Slog.w(TAG, "Container is no longer attached: " + container); - return 0; - } - 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); - } - } - if (hop.getToTop()) { - as.getDisplay().positionStackAtTop(as, false /* includingParents */); - } else { - as.getDisplay().positionStackAtBottom(as); - } - } else if (container instanceof Task) { - 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 (hop.getToTop()) { - as.getDisplay().positionStackAtTop(as, false /* includingParents */); - } else { - as.getDisplay().positionStackAtBottom(as); - } - } else { - container.getParent().positionChildAt( - hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, - container, false /* includingParents */); - } - } - return TRANSACT_EFFECTS_LIFECYCLE; - } - - private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask, - int windowMask, Configuration config) { - if ((container instanceof ActivityStack) - && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0) - && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) { - final ActivityStack stack = (ActivityStack) container; - if (stack.inPinnedWindowingMode()) { - stack.resize(config.windowConfiguration.getBounds(), - null /* configBounds */, PRESERVE_WINDOWS, true /* deferResume */); - } - } - } - - private int applyWindowContainerChange(WindowContainer wc, - WindowContainerTransaction.Change c) { - int effects = sanitizeAndApplyChange(wc, c); - - final Task tr = wc.asTask(); - - final SurfaceControl.Transaction t = c.getBoundsChangeTransaction(); - if (t != null) { - tr.setMainWindowSizeChangeTransaction(t); - } - - Rect enterPipBounds = c.getEnterPipBounds(); - if (enterPipBounds != null) { - mService.mStackSupervisor.updatePictureInPictureMode(tr, - enterPipBounds, true); - } - - final int windowingMode = c.getWindowingMode(); - if (windowingMode > -1) { - tr.setWindowingMode(windowingMode); - } - final int childWindowingMode = c.getActivityWindowingMode(); - if (childWindowingMode > -1) { - tr.setActivityWindowingMode(childWindowingMode); - } - - return effects; - } - - @Override - public int applyContainerTransaction(WindowContainerTransaction t, ITaskOrganizer organizer) { - enforceStackPermission("applyContainerTransaction()"); - int syncId = -1; - if (t == null) { - throw new IllegalArgumentException( - "Null transaction passed to applyContainerTransaction"); - } - long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - int effects = 0; - - /** - * If organizer is non-null we are looking to synchronize this transaction - * by collecting all the results in to a SurfaceFlinger transaction and - * then delivering that to the given organizers transaction ready callback. - * See {@link BLASTSyncEngine} for the details of the operation. But at - * a high level we create a sync operation with a given ID and an associated - * organizer. Then we notify each WindowContainer in this WindowContainer - * transaction that it is participating in a sync operation with that - * ID. Once everything is notified we tell the BLASTSyncEngine - * "setSyncReady" which means that we have added everything - * to the set. At any point after this, all the WindowContainers - * will eventually finish applying their changes and notify the - * BLASTSyncEngine which will deliver the Transaction to the organizer. - */ - if (organizer != null) { - syncId = startSyncWithOrganizer(organizer); - } - mService.deferWindowLayout(); - try { - ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>(); - Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries = - t.getChanges().entrySet().iterator(); - while (entries.hasNext()) { - final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = - entries.next(); - final WindowContainer wc = WindowContainer.fromBinder(entry.getKey()); - int containerEffect = applyWindowContainerChange(wc, entry.getValue()); - effects |= containerEffect; - - // Lifecycle changes will trigger ensureConfig for everything. - if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0 - && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { - haveConfigChanges.add(wc); - } - if (syncId >= 0) { - mBLASTSyncEngine.addToSyncSet(syncId, wc); - } - } - // Hierarchy changes - final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); - for (int i = 0, n = hops.size(); i < n; ++i) { - final WindowContainerTransaction.HierarchyOp hop = hops.get(i); - final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - effects |= sanitizeAndApplyHierarchyOp(wc, hop); - } - if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) { - // Already calls ensureActivityConfig - mService.mRootWindowContainer.ensureActivitiesVisible( - null, 0, PRESERVE_WINDOWS); - } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { - final PooledConsumer f = PooledLambda.obtainConsumer( - ActivityRecord::ensureActivityConfiguration, - PooledLambda.__(ActivityRecord.class), 0, - 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); - } - } - } finally { - f.recycle(); - } - } - } finally { - mService.continueWindowLayout(); - if (syncId >= 0) { - setSyncReady(syncId); - } - } - } - } finally { - Binder.restoreCallingIdentity(ident); - } - return syncId; - } - - @Override - public void transactionReady(int id, SurfaceControl.Transaction sc) { - final ITaskOrganizer organizer = mTaskOrganizersByPendingSyncId.get(id); - if (organizer == null) { - Slog.e(TAG, "Got transaction complete for unexpected ID"); - } - try { - organizer.transactionReady(id, sc); - } catch (RemoteException e) { - } - - mTaskOrganizersByPendingSyncId.remove(id); - } - - int startSyncWithOrganizer(ITaskOrganizer organizer) { - int id = mBLASTSyncEngine.startSyncSet(this); - mTaskOrganizersByPendingSyncId.put(id, organizer); - return id; - } - - void setSyncReady(int id) { - mBLASTSyncEngine.setReady(id); - } } 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/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 57d0a335a688..29a2e18f46a8 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -142,11 +142,13 @@ class WallpaperController { mFindResults.setUseTopWallpaperAsTarget(true); } - final boolean keyguardGoingAwayWithWallpaper = (w.mActivityRecord != null - && w.mActivityRecord.isAnimating(TRANSITION | PARENTS) - && AppTransition.isKeyguardGoingAwayTransit(w.mActivityRecord.getTransit()) - && (w.mActivityRecord.getTransitFlags() - & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0); + final WindowContainer animatingContainer = w.mActivityRecord != null + ? w.mActivityRecord.getAnimatingContainer() : null; + final boolean keyguardGoingAwayWithWallpaper = (animatingContainer != null + && animatingContainer.isAnimating(TRANSITION | PARENTS) + && AppTransition.isKeyguardGoingAwayTransit(animatingContainer.mTransit) + && (animatingContainer.mTransitFlags + & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0); boolean needsShowWhenLockedWallpaper = false; if ((w.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 @@ -166,8 +168,6 @@ class WallpaperController { final RecentsAnimationController recentsAnimationController = mService.getRecentsAnimationController(); - final WindowContainer animatingContainer = - w.mActivityRecord != null ? w.mActivityRecord.getAnimatingContainer() : null; final boolean animationWallpaper = animatingContainer != null && animatingContainer.getAnimation() != null && animatingContainer.getAnimation().getShowWallpaper(); 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..b1f22f8623d6 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -58,7 +58,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CO import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; -import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; @@ -1482,12 +1481,6 @@ public class WindowManagerService extends IWindowManager.Stub + "%s. Aborting.", attrs.token); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } - } else if (rootType == TYPE_DREAM) { - if (token.windowType != TYPE_DREAM) { - ProtoLog.w(WM_ERROR, "Attempted to add Dream window with bad token " - + "%s. Aborting.", attrs.token); - return WindowManagerGlobal.ADD_BAD_APP_TOKEN; - } } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) { if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) { ProtoLog.w(WM_ERROR, @@ -1723,11 +1716,6 @@ public class WindowManagerService extends IWindowManager.Stub + "%s. Aborting.", tokenForLog); return false; } - if (rootType == TYPE_DREAM) { - ProtoLog.w(WM_ERROR, "Attempted to add Dream window with unknown token " - + "%s. Aborting.", tokenForLog); - return false; - } if (rootType == TYPE_QS_DIALOG) { ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with unknown token " + "%s. Aborting.", tokenForLog); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java new file mode 100644 index 000000000000..5f21e1799958 --- /dev/null +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -0,0 +1,360 @@ +/* + * 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 static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; +import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; +import static com.android.server.wm.WindowContainer.POSITION_TOP; + +import android.app.WindowConfiguration; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Binder; +import android.os.IBinder; +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; +import android.window.WindowContainerTransaction; + +import com.android.internal.util.function.pooled.PooledConsumer; +import com.android.internal.util.function.pooled.PooledLambda; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Server side implementation for the interface for organizing windows + * @see android.window.WindowOrganizer + */ +class WindowOrganizerController extends IWindowOrganizerController.Stub + implements BLASTSyncEngine.TransactionReadyListener { + + private static final String TAG = "WindowOrganizerController"; + + /** Flag indicating that an applied transaction may have effected lifecycle */ + private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1; + private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1; + + /** + * Masks specifying which configurations task-organizers can control. Incoming transactions + * will be filtered to only include these. + */ + static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION + | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE; + static final int CONTROLLABLE_WINDOW_CONFIGS = WindowConfiguration.WINDOW_CONFIG_BOUNDS + | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; + + private final ActivityTaskManagerService mService; + private final WindowManagerGlobalLock mGlobalLock; + + private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine(); + private final HashMap<Integer, IWindowContainerTransactionCallback> + 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 + public void applyTransaction(WindowContainerTransaction t) { + applySyncTransaction(t, null /*callback*/); + } + + @Override + public int applySyncTransaction(WindowContainerTransaction t, + IWindowContainerTransactionCallback callback) { + enforceStackPermission("applySyncTransaction()"); + int syncId = -1; + if (t == null) { + throw new IllegalArgumentException( + "Null transaction passed to applySyncTransaction"); + } + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + int effects = 0; + + /** + * If callback is non-null we are looking to synchronize this transaction by + * collecting all the results in to a SurfaceFlinger transaction and then delivering + * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the + * details of the operation. But at a high level we create a sync operation with a + * given ID and an associated callback. Then we notify each WindowContainer in this + * WindowContainer transaction that it is participating in a sync operation with + * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady" + * which means that we have added everything to the set. At any point after this, + * all the WindowContainers will eventually finish applying their changes and notify + * the BLASTSyncEngine which will deliver the Transaction to the callback. + */ + if (callback != null) { + syncId = startSyncWithOrganizer(callback); + } + mService.deferWindowLayout(); + try { + ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>(); + Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries = + t.getChanges().entrySet().iterator(); + while (entries.hasNext()) { + final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = + entries.next(); + final WindowContainer wc = WindowContainer.fromBinder(entry.getKey()); + int containerEffect = applyWindowContainerChange(wc, entry.getValue()); + effects |= containerEffect; + + // Lifecycle changes will trigger ensureConfig for everything. + if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0 + && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { + haveConfigChanges.add(wc); + } + if (syncId >= 0) { + mBLASTSyncEngine.addToSyncSet(syncId, wc); + } + } + // Hierarchy changes + final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); + for (int i = 0, n = hops.size(); i < n; ++i) { + final WindowContainerTransaction.HierarchyOp hop = hops.get(i); + final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); + effects |= sanitizeAndApplyHierarchyOp(wc, hop); + } + if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) { + // Already calls ensureActivityConfig + mService.mRootWindowContainer.ensureActivitiesVisible( + null, 0, PRESERVE_WINDOWS); + } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { + final PooledConsumer f = PooledLambda.obtainConsumer( + ActivityRecord::ensureActivityConfiguration, + PooledLambda.__(ActivityRecord.class), 0, + false /* preserveWindow */); + try { + for (int i = haveConfigChanges.size() - 1; i >= 0; --i) { + haveConfigChanges.valueAt(i).forAllActivities(f); + } + } finally { + f.recycle(); + } + } + } finally { + mService.continueWindowLayout(); + if (syncId >= 0) { + setSyncReady(syncId); + } + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + return syncId; + } + + private int sanitizeAndApplyChange(WindowContainer container, + WindowContainerTransaction.Change change) { + if (!(container instanceof Task)) { + throw new RuntimeException("Invalid token in task transaction"); + } + final Task task = (Task) container; + // The "client"-facing API should prevent bad changes; however, just in case, sanitize + // masks here. + final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS; + final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS; + int effects = 0; + if (configMask != 0) { + Configuration c = new Configuration(container.getRequestedOverrideConfiguration()); + c.setTo(change.getConfiguration(), configMask, windowMask); + container.onRequestedOverrideConfigurationChanged(c); + // TODO(b/145675353): remove the following once we could apply new bounds to the + // pinned stack together with its children. + resizePinnedStackIfNeeded(container, configMask, windowMask, c); + effects |= TRANSACT_EFFECTS_CLIENT_CONFIG; + } + if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) { + if (container.setFocusable(change.getFocusable())) { + effects |= TRANSACT_EFFECTS_LIFECYCLE; + } + } + if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) { + if (task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, change.getHidden())) { + effects |= TRANSACT_EFFECTS_LIFECYCLE; + } + } + return effects; + } + + private int sanitizeAndApplyHierarchyOp(WindowContainer container, + WindowContainerTransaction.HierarchyOp hop) { + final Task task = container.asTask(); + if (task == null) { + throw new IllegalArgumentException("Invalid container in hierarchy op"); + } + 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()) { + 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"); + } + } else { + final ActivityStack rootTask = + (ActivityStack) (newParent != null ? newParent : task.getRootTask()); + if (hop.getToTop()) { + as.getDisplay().positionStackAtTop(rootTask, false /* includingParents */); + } else { + as.getDisplay().positionStackAtBottom(rootTask); + } + } + } else { + throw new RuntimeException("Reparenting leaf Tasks is not supported now."); + } + } else { + // Ugh, of course ActivityStack has its own special reorder logic... + if (task.isRootTask()) { + if (hop.getToTop()) { + dc.positionStackAtTop(as, false /* includingParents */); + } else { + dc.positionStackAtBottom(as); + } + } else { + task.getParent().positionChildAt( + hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, + task, false /* includingParents */); + } + } + return TRANSACT_EFFECTS_LIFECYCLE; + } + + private int applyWindowContainerChange(WindowContainer wc, + WindowContainerTransaction.Change c) { + int effects = sanitizeAndApplyChange(wc, c); + + final Task tr = wc.asTask(); + + final SurfaceControl.Transaction t = c.getBoundsChangeTransaction(); + if (t != null) { + tr.setMainWindowSizeChangeTransaction(t); + } + + Rect enterPipBounds = c.getEnterPipBounds(); + if (enterPipBounds != null) { + mService.mStackSupervisor.updatePictureInPictureMode(tr, + enterPipBounds, true); + } + + final int windowingMode = c.getWindowingMode(); + if (windowingMode > -1) { + tr.setWindowingMode(windowingMode); + } + final int childWindowingMode = c.getActivityWindowingMode(); + if (childWindowingMode > -1) { + tr.setActivityWindowingMode(childWindowingMode); + } + + return effects; + } + + private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask, + int windowMask, Configuration config) { + if ((container instanceof ActivityStack) + && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0) + && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) { + final ActivityStack stack = (ActivityStack) container; + if (stack.inPinnedWindowingMode()) { + stack.resize(config.windowConfiguration.getBounds(), + null /* configBounds */, PRESERVE_WINDOWS, true /* deferResume */); + } + } + } + + @Override + public ITaskOrganizerController getTaskOrganizerController() { + enforceStackPermission("getTaskOrganizerController()"); + return mTaskOrganizerController; + } + + @Override + public IDisplayAreaOrganizerController getDisplayAreaOrganizerController() { + enforceStackPermission("getDisplayAreaOrganizerController()"); + return mDisplayAreaOrganizerController; + } + + int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) { + int id = mBLASTSyncEngine.startSyncSet(this); + mTransactionCallbacksByPendingSyncId.put(id, callback); + return id; + } + + void setSyncReady(int id) { + mBLASTSyncEngine.setReady(id); + } + + @Override + public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { + final IWindowContainerTransactionCallback callback = + mTransactionCallbacksByPendingSyncId.get(mSyncId); + + try { + callback.transactionReady(mSyncId, mergedTransaction); + } catch (RemoteException e) { + } + + mTransactionCallbacksByPendingSyncId.remove(mSyncId); + } + + private void enforceStackPermission(String func) { + mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func); + } +} diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 161152ba0d74..b4487409c74e 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.OP_NONE; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.isSplitScreenWindowingMode; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.os.PowerManager.DRAW_WAKE_LOCK; @@ -131,6 +132,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 +140,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 +609,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. */ @@ -1689,6 +1695,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return !isParentWindowHidden() || isAnimating(TRANSITION | PARENTS); } + boolean isDreamWindow() { + return mActivityRecord != null + && mActivityRecord.getActivityType() == ACTIVITY_TYPE_DREAM; + } + /** * Whether this window's drawn state might affect the drawn states of the app token. * @@ -2270,9 +2281,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 +3349,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/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 0cfdebc6792d..6b9fbcbf459f 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -157,9 +157,7 @@ class WindowSurfacePlacer { mInLayout = true; - boolean recoveringMemory = false; if (!mService.mForceRemoves.isEmpty()) { - recoveringMemory = true; // Wait a little bit for things to settle down, and off we go. while (!mService.mForceRemoves.isEmpty()) { final WindowState ws = mService.mForceRemoves.remove(0); @@ -177,7 +175,7 @@ class WindowSurfacePlacer { } try { - mService.mRoot.performSurfacePlacement(recoveringMemory); + mService.mRoot.performSurfacePlacement(); mInLayout = false; 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_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 822f3835cc07..e4061b4fc34c 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -2068,8 +2068,8 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, class_gnssClock = (jclass) env->NewGlobalRef(gnssClockClass); method_gnssClockCtor = env->GetMethodID(class_gnssClock, "<init>", "()V"); - jclass gnssConfiguration_halInterfaceVersionClass = - env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion"); + jclass gnssConfiguration_halInterfaceVersionClass = env->FindClass( + "com/android/server/location/gnss/GnssConfiguration$HalInterfaceVersion"); class_gnssConfiguration_halInterfaceVersion = (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass); method_halInterfaceVersionCtor = @@ -3738,39 +3738,29 @@ static const JNINativeMethod sNetworkConnectivityMethods[] = { }; static const JNINativeMethod sConfigurationMethods[] = { - /* name, signature, funcPtr */ - {"native_get_gnss_configuration_version", - "()Lcom/android/server/location/GnssConfiguration$HalInterfaceVersion;", - reinterpret_cast<void *>( - android_location_GnssConfiguration_get_gnss_configuration_version)}, - {"native_set_supl_es", - "(I)Z", - reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_es)}, - {"native_set_supl_version", - "(I)Z", - reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_version)}, - {"native_set_supl_mode", - "(I)Z", - reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_mode)}, - {"native_set_lpp_profile", - "(I)Z", - reinterpret_cast<void *>(android_location_GnssConfiguration_set_lpp_profile)}, - {"native_set_gnss_pos_protocol_select", - "(I)Z", - reinterpret_cast<void *>( - android_location_GnssConfiguration_set_gnss_pos_protocol_select)}, - {"native_set_gps_lock", - "(I)Z", - reinterpret_cast<void *>(android_location_GnssConfiguration_set_gps_lock)}, - {"native_set_emergency_supl_pdn", - "(I)Z", - reinterpret_cast<void *>(android_location_GnssConfiguration_set_emergency_supl_pdn)}, - {"native_set_satellite_blacklist", - "([I[I)Z", - reinterpret_cast<void *>(android_location_GnssConfiguration_set_satellite_blacklist)}, - {"native_set_es_extension_sec", - "(I)Z", - reinterpret_cast<void *>(android_location_GnssConfiguration_set_es_extension_sec)}, + /* name, signature, funcPtr */ + {"native_get_gnss_configuration_version", + "()Lcom/android/server/location/gnss/GnssConfiguration$HalInterfaceVersion;", + reinterpret_cast<void*>( + android_location_GnssConfiguration_get_gnss_configuration_version)}, + {"native_set_supl_es", "(I)Z", + reinterpret_cast<void*>(android_location_GnssConfiguration_set_supl_es)}, + {"native_set_supl_version", "(I)Z", + reinterpret_cast<void*>(android_location_GnssConfiguration_set_supl_version)}, + {"native_set_supl_mode", "(I)Z", + reinterpret_cast<void*>(android_location_GnssConfiguration_set_supl_mode)}, + {"native_set_lpp_profile", "(I)Z", + reinterpret_cast<void*>(android_location_GnssConfiguration_set_lpp_profile)}, + {"native_set_gnss_pos_protocol_select", "(I)Z", + reinterpret_cast<void*>(android_location_GnssConfiguration_set_gnss_pos_protocol_select)}, + {"native_set_gps_lock", "(I)Z", + reinterpret_cast<void*>(android_location_GnssConfiguration_set_gps_lock)}, + {"native_set_emergency_supl_pdn", "(I)Z", + reinterpret_cast<void*>(android_location_GnssConfiguration_set_emergency_supl_pdn)}, + {"native_set_satellite_blacklist", "([I[I)Z", + reinterpret_cast<void*>(android_location_GnssConfiguration_set_satellite_blacklist)}, + {"native_set_es_extension_sec", "(I)Z", + reinterpret_cast<void*>(android_location_GnssConfiguration_set_es_extension_sec)}, }; static const JNINativeMethod sVisibilityControlMethods[] = { @@ -3782,53 +3772,27 @@ static const JNINativeMethod sVisibilityControlMethods[] = { }; int register_android_server_location_GnssLocationProvider(JNIEnv* env) { - jniRegisterNativeMethods(env, "com/android/server/location/GnssAntennaInfoProvider", + jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssAntennaInfoProvider", sAntennaInfoMethods, NELEM(sAntennaInfoMethods)); - jniRegisterNativeMethods( - env, - "com/android/server/location/GnssBatchingProvider", - sMethodsBatching, - NELEM(sMethodsBatching)); - jniRegisterNativeMethods( - env, - "com/android/server/location/GnssGeofenceProvider", - sGeofenceMethods, - NELEM(sGeofenceMethods)); - jniRegisterNativeMethods( - env, - "com/android/server/location/GnssMeasurementsProvider", - sMeasurementMethods, - NELEM(sMeasurementMethods)); - jniRegisterNativeMethods( - env, - "com/android/server/location/GnssMeasurementCorrectionsProvider", - sMeasurementCorrectionsMethods, - NELEM(sMeasurementCorrectionsMethods)); - jniRegisterNativeMethods( - env, - "com/android/server/location/GnssNavigationMessageProvider", - sNavigationMessageMethods, - NELEM(sNavigationMessageMethods)); - jniRegisterNativeMethods( - env, - "com/android/server/location/GnssNetworkConnectivityHandler", - sNetworkConnectivityMethods, - NELEM(sNetworkConnectivityMethods)); - jniRegisterNativeMethods( - env, - "com/android/server/location/GnssConfiguration", - sConfigurationMethods, - NELEM(sConfigurationMethods)); - jniRegisterNativeMethods( - env, - "com/android/server/location/GnssVisibilityControl", - sVisibilityControlMethods, - NELEM(sVisibilityControlMethods)); - return jniRegisterNativeMethods( - env, - "com/android/server/location/GnssLocationProvider", - sMethods, - NELEM(sMethods)); + jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssBatchingProvider", + sMethodsBatching, NELEM(sMethodsBatching)); + jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssGeofenceProvider", + sGeofenceMethods, NELEM(sGeofenceMethods)); + jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssMeasurementsProvider", + sMeasurementMethods, NELEM(sMeasurementMethods)); + jniRegisterNativeMethods(env, + "com/android/server/location/gnss/GnssMeasurementCorrectionsProvider", + sMeasurementCorrectionsMethods, NELEM(sMeasurementCorrectionsMethods)); + jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssNavigationMessageProvider", + sNavigationMessageMethods, NELEM(sNavigationMessageMethods)); + jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssNetworkConnectivityHandler", + sNetworkConnectivityMethods, NELEM(sNetworkConnectivityMethods)); + jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssConfiguration", + sConfigurationMethods, NELEM(sConfigurationMethods)); + jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssVisibilityControl", + sVisibilityControlMethods, NELEM(sVisibilityControlMethods)); + return jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssLocationProvider", + sMethods, NELEM(sMethods)); } } /* namespace android */ diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 740c5cb3e75a..b0eb14852251 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); @@ -7137,7 +7171,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ActiveAdmin admin; synchronized (getLockObject()) { if (who == null) { - if ((frpManagementAgentUid != mInjector.binderGetCallingUid())) { + if ((frpManagementAgentUid != mInjector.binderGetCallingUid()) + && (mContext.checkCallingPermission(permission.MASTER_CLEAR) + != PackageManager.PERMISSION_GRANTED)) { throw new SecurityException( "Must be called by the FRP management agent on device"); } @@ -9148,8 +9184,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return true; } - Log.w(LOG_TAG, String.format("Package %s (uid=%d, pid=%d) cannot access Device IDs", - packageName, uid, pid)); return false; } @@ -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) { @@ -15705,9 +15740,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + final int suspendedState = suspended + ? PERSONAL_APPS_SUSPENDED_EXPLICITLY + : PERSONAL_APPS_NOT_SUSPENDED; mInjector.binderWithCleanCallingIdentity( - () -> applyPersonalAppsSuspension( - callingUserId, PERSONAL_APPS_SUSPENDED_EXPLICITLY)); + () -> applyPersonalAppsSuspension(callingUserId, suspendedState)); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_PERSONAL_APPS_SUSPENDED) @@ -15793,7 +15830,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 +15850,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/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index ed85b931c08e..ae27c7aabdda 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -286,7 +286,6 @@ void IncrementalService::onDump(int fd) { dprintf(fd, "\t\t\tpackageName: %s\n", params.packageName.c_str()); dprintf(fd, "\t\t\tclassName: %s\n", params.className.c_str()); dprintf(fd, "\t\t\targuments: %s\n", params.arguments.c_str()); - dprintf(fd, "\t\t\tdynamicArgs: %d\n", int(params.dynamicArgs.size())); } dprintf(fd, "\t\tstorages (%d):\n", int(mnt.storages.size())); for (auto&& [storageId, storage] : mnt.storages) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b019e9dd03ba..c631eebe4a01 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -144,7 +144,6 @@ import com.android.server.power.ThermalManagerService; import com.android.server.recoverysystem.RecoverySystemService; import com.android.server.restrictions.RestrictionsManagerService; import com.android.server.role.RoleManagerService; -import com.android.server.rollback.RollbackManagerService; import com.android.server.security.FileIntegrityService; import com.android.server.security.KeyAttestationApplicationIdProviderService; import com.android.server.security.KeyChainSystemService; @@ -294,6 +293,8 @@ public final class SystemServer { "com.android.server.DeviceIdleController"; private static final String BLOB_STORE_MANAGER_SERVICE_CLASS = "com.android.server.blob.BlobStoreManagerService"; + private static final String ROLLBACK_MANAGER_SERVICE_CLASS = + "com.android.server.rollback.RollbackManagerService"; private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; @@ -964,7 +965,7 @@ public final class SystemServer { // Manages apk rollbacks. t.traceBegin("StartRollbackManagerService"); - mSystemServiceManager.startService(RollbackManagerService.class); + mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS); t.traceEnd(); // Service to capture bugreports. diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index ae8d5743668a..136ee91dd685 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -26,6 +26,7 @@ import android.app.NotificationManager; import android.app.Person; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; +import android.app.usage.UsageEvents; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -70,6 +71,7 @@ import com.android.server.notification.NotificationManagerInternal; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -237,6 +239,27 @@ public class DataManager { eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType)); } + /** + * Queries events for moving app to foreground between {@code startTime} and {@code endTime}. + */ + @NonNull + public List<UsageEvents.Event> queryAppMovingToForegroundEvents(@UserIdInt int callingUserId, + long startTime, long endTime) { + return UsageStatsQueryHelper.queryAppMovingToForegroundEvents(callingUserId, startTime, + endTime); + } + + /** + * Queries launch counts of apps within {@code packageNameFilter} between {@code startTime} + * and {@code endTime}. + */ + @NonNull + public Map<String, Integer> queryAppLaunchCount(@UserIdInt int callingUserId, long startTime, + long endTime, Set<String> packageNameFilter) { + return UsageStatsQueryHelper.queryAppLaunchCount(callingUserId, startTime, endTime, + packageNameFilter); + } + /** Prunes the data for the specified user. */ public void pruneDataForUser(@UserIdInt int userId, @NonNull CancellationSignal signal) { UserData userData = getUnlockedUserData(userId); @@ -382,7 +405,13 @@ public class DataManager { } } - private int mimeTypeToShareEventType(String mimeType) { + /** + * Converts {@code mimeType} to {@link Event.EventType}. + */ + public int mimeTypeToShareEventType(String mimeType) { + if (mimeType == null) { + return Event.TYPE_SHARE_OTHER; + } if (mimeType.startsWith("text/")) { return Event.TYPE_SHARE_TEXT; } else if (mimeType.startsWith("image/")) { diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java index 72f1abb70e34..6e6fea93c803 100644 --- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java +++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java @@ -19,6 +19,8 @@ package com.android.server.people.data; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.usage.UsageEvents; +import android.app.usage.UsageStats; +import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.content.ComponentName; import android.content.LocusId; @@ -27,7 +29,10 @@ import android.util.ArrayMap; import com.android.server.LocalServices; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; /** A helper class that queries {@link UsageStatsManagerInternal}. */ @@ -46,7 +51,7 @@ class UsageStatsQueryHelper { */ UsageStatsQueryHelper(@UserIdInt int userId, Function<String, PackageData> packageDataGetter) { - mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class); + mUsageStatsManagerInternal = getUsageStatsManagerInternal(); mUserId = userId; mPackageDataGetter = packageDataGetter; } @@ -106,6 +111,53 @@ class UsageStatsQueryHelper { return mLastEventTimestamp; } + /** + * Queries {@link UsageStatsManagerInternal} events for moving app to foreground between + * {@code startTime} and {@code endTime}. + * + * @return a list containing events moving app to foreground. + */ + static List<UsageEvents.Event> queryAppMovingToForegroundEvents(@UserIdInt int userId, + long startTime, long endTime) { + List<UsageEvents.Event> res = new ArrayList<>(); + UsageEvents usageEvents = getUsageStatsManagerInternal().queryEventsForUser(userId, + startTime, endTime, + UsageEvents.HIDE_SHORTCUT_EVENTS | UsageEvents.HIDE_LOCUS_EVENTS); + if (usageEvents == null) { + return res; + } + while (usageEvents.hasNextEvent()) { + UsageEvents.Event e = new UsageEvents.Event(); + usageEvents.getNextEvent(e); + if (e.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED) { + res.add(e); + } + } + return res; + } + + /** + * Queries {@link UsageStatsManagerInternal} for launch count of apps within {@code + * packageNameFilter} between {@code startTime} and {@code endTime}.obfuscateInstantApps + * + * @return a map which keys are package names and values are app launch counts. + */ + static Map<String, Integer> queryAppLaunchCount(@UserIdInt int userId, long startTime, + long endTime, Set<String> packageNameFilter) { + List<UsageStats> stats = getUsageStatsManagerInternal().queryUsageStatsForUser(userId, + UsageStatsManager.INTERVAL_BEST, startTime, endTime, + /* obfuscateInstantApps= */ false); + Map<String, Integer> aggregatedStats = new ArrayMap<>(); + for (UsageStats stat : stats) { + String packageName = stat.getPackageName(); + if (packageNameFilter.contains(packageName)) { + aggregatedStats.put(packageName, + aggregatedStats.getOrDefault(packageName, 0) + stat.getAppLaunchCount()); + } + } + return aggregatedStats; + } + private void onInAppConversationEnded(@NonNull PackageData packageData, @NonNull UsageEvents.Event endEvent) { ComponentName activityName = @@ -138,4 +190,8 @@ class UsageStatsQueryHelper { EventStore.CATEGORY_LOCUS_ID_BASED, locusId.getId()); eventHistory.addEvent(event); } + + private static UsageStatsManagerInternal getUsageStatsManagerInternal() { + return LocalServices.getService(UsageStatsManagerInternal.class); + } } diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java index 8e5d75be12b7..d09d0b379769 100644 --- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java +++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java @@ -27,13 +27,11 @@ import android.app.prediction.AppTargetId; import android.content.IntentFilter; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager.ShareShortcutInfo; -import android.util.Range; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ChooserActivity; import com.android.server.people.data.ConversationInfo; import com.android.server.people.data.DataManager; -import com.android.server.people.data.Event; import com.android.server.people.data.EventHistory; import com.android.server.people.data.PackageData; @@ -42,6 +40,9 @@ import java.util.Collections; import java.util.List; import java.util.function.Consumer; +/** + * Predictor that predicts the {@link AppTarget} the user is most likely to open on share sheet. + */ class ShareTargetPredictor extends AppTargetPredictor { private final IntentFilter mIntentFilter; @@ -66,7 +67,9 @@ class ShareTargetPredictor extends AppTargetPredictor { @Override void predictTargets() { List<ShareTarget> shareTargets = getDirectShareTargets(); - rankTargets(shareTargets); + SharesheetModelScorer.computeScore(shareTargets, getShareEventType(mIntentFilter), + System.currentTimeMillis()); + Collections.sort(shareTargets, (t1, t2) -> -Float.compare(t1.getScore(), t2.getScore())); List<AppTarget> res = new ArrayList<>(); for (int i = 0; i < Math.min(getPredictionContext().getPredictedTargetCount(), shareTargets.size()); i++) { @@ -80,36 +83,16 @@ class ShareTargetPredictor extends AppTargetPredictor { @Override void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) { List<ShareTarget> shareTargets = getAppShareTargets(targets); - rankTargets(shareTargets); + SharesheetModelScorer.computeScoreForAppShare(shareTargets, + getShareEventType(mIntentFilter), getPredictionContext().getPredictedTargetCount(), + System.currentTimeMillis(), getDataManager(), + mCallingUserId); + Collections.sort(shareTargets, (t1, t2) -> -Float.compare(t1.getScore(), t2.getScore())); List<AppTarget> appTargetList = new ArrayList<>(); shareTargets.forEach(t -> appTargetList.add(t.getAppTarget())); callback.accept(appTargetList); } - private void rankTargets(List<ShareTarget> shareTargets) { - // Rank targets based on recency of sharing history only for the moment. - // TODO: Take more factors into ranking, e.g. frequency, mime type, foreground app. - Collections.sort(shareTargets, (t1, t2) -> { - if (t1.getEventHistory() == null) { - return 1; - } - if (t2.getEventHistory() == null) { - return -1; - } - Range<Long> timeSlot1 = t1.getEventHistory().getEventIndex( - Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot(); - Range<Long> timeSlot2 = t2.getEventHistory().getEventIndex( - Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot(); - if (timeSlot1 == null) { - return 1; - } else if (timeSlot2 == null) { - return -1; - } else { - return -Long.compare(timeSlot1.getUpper(), timeSlot2.getUpper()); - } - }); - } - private List<ShareTarget> getDirectShareTargets() { List<ShareTarget> shareTargets = new ArrayList<>(); List<ShareShortcutInfo> shareShortcuts = @@ -153,6 +136,11 @@ class ShareTargetPredictor extends AppTargetPredictor { return shareTargets; } + private int getShareEventType(IntentFilter intentFilter) { + String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null; + return getDataManager().mimeTypeToShareEventType(mimeType); + } + @VisibleForTesting static class ShareTarget { @@ -162,13 +150,16 @@ class ShareTargetPredictor extends AppTargetPredictor { private final EventHistory mEventHistory; @Nullable private final ConversationInfo mConversationInfo; + private float mScore; - private ShareTarget(@NonNull AppTarget appTarget, + @VisibleForTesting + ShareTarget(@NonNull AppTarget appTarget, @Nullable EventHistory eventHistory, @Nullable ConversationInfo conversationInfo) { mAppTarget = appTarget; mEventHistory = eventHistory; mConversationInfo = conversationInfo; + mScore = 0f; } @NonNull @@ -188,5 +179,15 @@ class ShareTargetPredictor extends AppTargetPredictor { ConversationInfo getConversationInfo() { return mConversationInfo; } + + @VisibleForTesting + float getScore() { + return mScore; + } + + @VisibleForTesting + void setScore(float score) { + mScore = score; + } } } diff --git a/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java b/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java new file mode 100644 index 000000000000..0ac5724210da --- /dev/null +++ b/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java @@ -0,0 +1,406 @@ +/* + * 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.people.prediction; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.usage.UsageEvents; +import android.util.ArrayMap; +import android.util.Pair; +import android.util.Range; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ChooserActivity; +import com.android.server.people.data.DataManager; +import com.android.server.people.data.Event; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.concurrent.TimeUnit; + +/** Ranking scorer for Sharesheet targets. */ +class SharesheetModelScorer { + + private static final String TAG = "SharesheetModelScorer"; + private static final boolean DEBUG = false; + private static final Integer RECENCY_SCORE_COUNT = 6; + private static final float RECENCY_INITIAL_BASE_SCORE = 0.4F; + private static final float RECENCY_SCORE_INITIAL_DECAY = 0.05F; + private static final float RECENCY_SCORE_SUBSEQUENT_DECAY = 0.02F; + private static final long ONE_MONTH_WINDOW = TimeUnit.DAYS.toMillis(30); + private static final long FOREGROUND_APP_PROMO_TIME_WINDOW = TimeUnit.MINUTES.toMillis(10); + private static final float FREQUENTLY_USED_APP_SCORE_DECAY = 0.9F; + @VisibleForTesting + static final float FOREGROUND_APP_WEIGHT = 0F; + @VisibleForTesting + static final String CHOOSER_ACTIVITY = ChooserActivity.class.getSimpleName(); + + // Keep constructor private to avoid class being instantiated. + private SharesheetModelScorer() { + } + + /** + * Computes each target's recency, frequency and frequency of the same {@code shareEventType} + * based on past sharing history. Update {@link ShareTargetPredictor.ShareTargetScore}. + */ + static void computeScore(List<ShareTargetPredictor.ShareTarget> shareTargets, + int shareEventType, long now) { + if (shareTargets.isEmpty()) { + return; + } + float totalFreqScore = 0f; + int freqScoreCount = 0; + float totalMimeFreqScore = 0f; + int mimeFreqScoreCount = 0; + // Top of this heap has lowest rank. + PriorityQueue<Pair<ShareTargetRankingScore, Range<Long>>> recencyMinHeap = + new PriorityQueue<>(RECENCY_SCORE_COUNT, + Comparator.comparingLong(p -> p.second.getUpper())); + List<ShareTargetRankingScore> scoreList = new ArrayList<>(shareTargets.size()); + for (ShareTargetPredictor.ShareTarget target : shareTargets) { + ShareTargetRankingScore shareTargetScore = new ShareTargetRankingScore(); + scoreList.add(shareTargetScore); + if (target.getEventHistory() == null) { + continue; + } + // Counts frequency + List<Range<Long>> timeSlots = target.getEventHistory().getEventIndex( + Event.SHARE_EVENT_TYPES).getActiveTimeSlots(); + if (!timeSlots.isEmpty()) { + for (Range<Long> timeSlot : timeSlots) { + shareTargetScore.incrementFrequencyScore( + getFreqDecayedOnElapsedTime(now - timeSlot.getLower())); + } + totalFreqScore += shareTargetScore.getFrequencyScore(); + freqScoreCount++; + } + // Counts frequency for sharing same mime type + List<Range<Long>> timeSlotsOfSameType = target.getEventHistory().getEventIndex( + shareEventType).getActiveTimeSlots(); + if (!timeSlotsOfSameType.isEmpty()) { + for (Range<Long> timeSlot : timeSlotsOfSameType) { + shareTargetScore.incrementMimeFrequencyScore( + getFreqDecayedOnElapsedTime(now - timeSlot.getLower())); + } + totalMimeFreqScore += shareTargetScore.getMimeFrequencyScore(); + mimeFreqScoreCount++; + } + // Records most recent targets + Range<Long> mostRecentTimeSlot = target.getEventHistory().getEventIndex( + Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot(); + if (mostRecentTimeSlot == null) { + continue; + } + if (recencyMinHeap.size() < RECENCY_SCORE_COUNT + || mostRecentTimeSlot.getUpper() > recencyMinHeap.peek().second.getUpper()) { + if (recencyMinHeap.size() == RECENCY_SCORE_COUNT) { + recencyMinHeap.poll(); + } + recencyMinHeap.offer(new Pair(shareTargetScore, mostRecentTimeSlot)); + } + } + // Calculates recency score + while (!recencyMinHeap.isEmpty()) { + float recencyScore = RECENCY_INITIAL_BASE_SCORE; + if (recencyMinHeap.size() > 1) { + recencyScore = RECENCY_INITIAL_BASE_SCORE - RECENCY_SCORE_INITIAL_DECAY + - RECENCY_SCORE_SUBSEQUENT_DECAY * (recencyMinHeap.size() - 2); + } + recencyMinHeap.poll().first.setRecencyScore(recencyScore); + } + + Float avgFreq = freqScoreCount != 0 ? totalFreqScore / freqScoreCount : 0f; + Float avgMimeFreq = mimeFreqScoreCount != 0 ? totalMimeFreqScore / mimeFreqScoreCount : 0f; + for (int i = 0; i < scoreList.size(); i++) { + ShareTargetPredictor.ShareTarget target = shareTargets.get(i); + ShareTargetRankingScore targetScore = scoreList.get(i); + // Normalizes freq and mimeFreq score + targetScore.setFrequencyScore(normalizeFreqScore( + avgFreq.equals(0f) ? 0f : targetScore.getFrequencyScore() / avgFreq)); + targetScore.setMimeFrequencyScore(normalizeMimeFreqScore(avgMimeFreq.equals(0f) ? 0f + : targetScore.getMimeFrequencyScore() / avgMimeFreq)); + // Calculates total score + targetScore.setTotalScore( + probOR(probOR(targetScore.getRecencyScore(), targetScore.getFrequencyScore()), + targetScore.getMimeFrequencyScore())); + target.setScore(targetScore.getTotalScore()); + + if (DEBUG) { + Slog.d(TAG, String.format( + "SharesheetModel: packageName: %s, className: %s, shortcutId: %s, " + + "recency:%.2f, freq_all:%.2f, freq_mime:%.2f, total:%.2f", + target.getAppTarget().getPackageName(), + target.getAppTarget().getClassName(), + target.getAppTarget().getShortcutInfo() != null + ? target.getAppTarget().getShortcutInfo().getId() : null, + targetScore.getRecencyScore(), + targetScore.getFrequencyScore(), + targetScore.getMimeFrequencyScore(), + targetScore.getTotalScore())); + } + } + } + + /** + * Computes ranking score for app sharing. Update {@link ShareTargetPredictor.ShareTargetScore}. + */ + static void computeScoreForAppShare(List<ShareTargetPredictor.ShareTarget> shareTargets, + int shareEventType, int targetsLimit, long now, @NonNull DataManager dataManager, + @UserIdInt int callingUserId) { + computeScore(shareTargets, shareEventType, now); + postProcess(shareTargets, targetsLimit, dataManager, callingUserId); + } + + private static void postProcess(List<ShareTargetPredictor.ShareTarget> shareTargets, + int targetsLimit, @NonNull DataManager dataManager, @UserIdInt int callingUserId) { + // Populates a map which key is package name and value is list of shareTargets descended + // on total score. + Map<String, List<ShareTargetPredictor.ShareTarget>> shareTargetMap = new ArrayMap<>(); + for (ShareTargetPredictor.ShareTarget shareTarget : shareTargets) { + String packageName = shareTarget.getAppTarget().getPackageName(); + shareTargetMap.computeIfAbsent(packageName, key -> new ArrayList<>()); + List<ShareTargetPredictor.ShareTarget> targetsList = shareTargetMap.get(packageName); + int index = 0; + while (index < targetsList.size()) { + if (shareTarget.getScore() > targetsList.get(index).getScore()) { + break; + } + index++; + } + targetsList.add(index, shareTarget); + } + promoteForegroundApp(shareTargetMap, dataManager, callingUserId); + promoteFrequentlyUsedApps(shareTargetMap, targetsLimit, dataManager, callingUserId); + } + + /** + * Promotes frequently used sharing apps, if recommended apps based on sharing history have not + * reached the limit (e.g. user did not share any content in last couple weeks) + */ + private static void promoteFrequentlyUsedApps( + Map<String, List<ShareTargetPredictor.ShareTarget>> shareTargetMap, int targetsLimit, + @NonNull DataManager dataManager, @UserIdInt int callingUserId) { + int validPredictionNum = 0; + float minValidScore = 1f; + for (List<ShareTargetPredictor.ShareTarget> targets : shareTargetMap.values()) { + for (ShareTargetPredictor.ShareTarget target : targets) { + if (target.getScore() > 0f) { + validPredictionNum++; + minValidScore = Math.min(target.getScore(), minValidScore); + } + } + } + // Skips if recommended apps based on sharing history have already reached the limit. + if (validPredictionNum >= targetsLimit) { + return; + } + long now = System.currentTimeMillis(); + Map<String, Integer> appLaunchCountsMap = dataManager.queryAppLaunchCount( + callingUserId, now - ONE_MONTH_WINDOW, now, shareTargetMap.keySet()); + List<Pair<String, Integer>> appLaunchCounts = new ArrayList<>(); + for (Map.Entry<String, Integer> entry : appLaunchCountsMap.entrySet()) { + if (entry.getValue() > 0) { + appLaunchCounts.add(new Pair(entry.getKey(), entry.getValue())); + } + } + Collections.sort(appLaunchCounts, (p1, p2) -> -Integer.compare(p1.second, p2.second)); + for (Pair<String, Integer> entry : appLaunchCounts) { + if (!shareTargetMap.containsKey(entry.first)) { + continue; + } + ShareTargetPredictor.ShareTarget target = shareTargetMap.get(entry.first).get(0); + if (target.getScore() > 0f) { + continue; + } + minValidScore *= FREQUENTLY_USED_APP_SCORE_DECAY; + target.setScore(minValidScore); + if (DEBUG) { + Slog.d(TAG, String.format( + "SharesheetModel: promoteFrequentUsedApps packageName: %s, className: %s," + + " total:%.2f", + target.getAppTarget().getPackageName(), + target.getAppTarget().getClassName(), + target.getScore())); + } + validPredictionNum++; + if (validPredictionNum == targetsLimit) { + return; + } + } + } + + /** + * Promotes the foreground app just prior to source sharing app. Share often occurs between + * two apps the user is switching. + */ + private static void promoteForegroundApp( + Map<String, List<ShareTargetPredictor.ShareTarget>> shareTargetMap, + @NonNull DataManager dataManager, @UserIdInt int callingUserId) { + String sharingForegroundApp = findSharingForegroundApp(shareTargetMap, dataManager, + callingUserId); + if (sharingForegroundApp != null) { + ShareTargetPredictor.ShareTarget target = shareTargetMap.get(sharingForegroundApp).get( + 0); + target.setScore(probOR(target.getScore(), FOREGROUND_APP_WEIGHT)); + if (DEBUG) { + Slog.d(TAG, String.format( + "SharesheetModel: promoteForegroundApp packageName: %s, className: %s, " + + "total:%.2f", + target.getAppTarget().getPackageName(), + target.getAppTarget().getClassName(), + target.getScore())); + } + } + } + + /** + * Find the foreground app just prior to source sharing app from usageStatsManager. Returns null + * if it is not available. + */ + @Nullable + private static String findSharingForegroundApp( + Map<String, List<ShareTargetPredictor.ShareTarget>> shareTargetMap, + @NonNull DataManager dataManager, @UserIdInt int callingUserId) { + String sharingForegroundApp = null; + long now = System.currentTimeMillis(); + List<UsageEvents.Event> events = dataManager.queryAppMovingToForegroundEvents( + callingUserId, now - FOREGROUND_APP_PROMO_TIME_WINDOW, now); + String sourceApp = null; + for (int i = events.size() - 1; i >= 0; i--) { + String className = events.get(i).getClassName(); + String packageName = events.get(i).getPackageName(); + if (packageName == null || (className != null && className.contains(CHOOSER_ACTIVITY)) + || packageName.contains(CHOOSER_ACTIVITY)) { + continue; + } + if (sourceApp == null) { + sourceApp = packageName; + } else if (!packageName.equals(sourceApp) && shareTargetMap.containsKey(packageName)) { + sharingForegroundApp = packageName; + break; + } + } + return sharingForegroundApp; + } + + /** + * Probabilistic OR (also known as the algebraic sum). If a <= 1 and b <= 1, the result will be + * <= 1.0. + */ + private static float probOR(float a, float b) { + return 1f - (1f - a) * (1f - b); + } + + /** Counts frequency of share targets. Decays frequency for old shares. */ + private static float getFreqDecayedOnElapsedTime(long elapsedTimeMillis) { + Duration duration = Duration.ofMillis(elapsedTimeMillis); + if (duration.compareTo(Duration.ofDays(1)) <= 0) { + return 1.0f; + } else if (duration.compareTo(Duration.ofDays(3)) <= 0) { + return 0.9f; + } else if (duration.compareTo(Duration.ofDays(7)) <= 0) { + return 0.8f; + } else if (duration.compareTo(Duration.ofDays(14)) <= 0) { + return 0.7f; + } else { + return 0.6f; + } + } + + /** Normalizes frequency score. */ + private static float normalizeFreqScore(double freqRatio) { + if (freqRatio >= 2.5) { + return 0.2f; + } else if (freqRatio >= 1.5) { + return 0.15f; + } else if (freqRatio >= 1.0) { + return 0.1f; + } else if (freqRatio >= 0.75) { + return 0.05f; + } else { + return 0f; + } + } + + /** Normalizes mimetype-specific frequency score. */ + private static float normalizeMimeFreqScore(double freqRatio) { + if (freqRatio >= 2.0) { + return 0.2f; + } else if (freqRatio >= 1.2) { + return 0.15f; + } else if (freqRatio > 0.0) { + return 0.1f; + } else { + return 0f; + } + } + + private static class ShareTargetRankingScore { + + private float mRecencyScore = 0f; + private float mFrequencyScore = 0f; + private float mMimeFrequencyScore = 0f; + private float mTotalScore = 0f; + + float getTotalScore() { + return mTotalScore; + } + + void setTotalScore(float totalScore) { + mTotalScore = totalScore; + } + + float getRecencyScore() { + return mRecencyScore; + } + + void setRecencyScore(float recencyScore) { + mRecencyScore = recencyScore; + } + + float getFrequencyScore() { + return mFrequencyScore; + } + + void setFrequencyScore(float frequencyScore) { + mFrequencyScore = frequencyScore; + } + + void incrementFrequencyScore(float incremental) { + mFrequencyScore += incremental; + } + + float getMimeFrequencyScore() { + return mMimeFrequencyScore; + } + + void setMimeFrequencyScore(float mimeFrequencyScore) { + mMimeFrequencyScore = mimeFrequencyScore; + } + + void incrementMimeFrequencyScore(float incremental) { + mMimeFrequencyScore += incremental; + } + } +} diff --git a/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssAntennaInfoProviderTest.java index 76f7ad646bb1..d8acd6ea6948 100644 --- a/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssAntennaInfoProviderTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.location; +package com.android.server.location.gnss; import static com.google.common.truth.Truth.assertThat; @@ -41,7 +41,8 @@ import org.robolectric.RuntimeEnvironment; @Presubmit public class GnssAntennaInfoProviderTest { @Mock - private GnssAntennaInfoProvider.GnssAntennaInfoProviderNative mMockNative; + private GnssAntennaInfoProvider.GnssAntennaInfoProviderNative + mMockNative; private GnssAntennaInfoProvider mTestProvider; /** Setup. */ diff --git a/services/robotests/src/com/android/server/location/GnssBatchingProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssBatchingProviderTest.java index d58c3f73b8e8..25d6aa4dae29 100644 --- a/services/robotests/src/com/android/server/location/GnssBatchingProviderTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssBatchingProviderTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; import static com.google.common.truth.Truth.assertThat; @@ -11,7 +27,7 @@ import static org.mockito.Mockito.when; import android.platform.test.annotations.Presubmit; -import com.android.server.location.GnssBatchingProvider.GnssBatchingProviderNative; +import com.android.server.location.gnss.GnssBatchingProvider.GnssBatchingProviderNative; import org.junit.Before; import org.junit.Test; diff --git a/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssGeofenceProviderTest.java index 30c73368da15..4a533aac01db 100644 --- a/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssGeofenceProviderTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; import static org.mockito.ArgumentMatchers.anyDouble; import static org.mockito.ArgumentMatchers.anyInt; diff --git a/services/robotests/src/com/android/server/location/GnssMeasurementsProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java index b349b67dab0c..e7d9ef810e51 100644 --- a/services/robotests/src/com/android/server/location/GnssMeasurementsProviderTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; import static com.google.common.truth.Truth.assertThat; @@ -26,7 +42,8 @@ import org.robolectric.RuntimeEnvironment; @Presubmit public class GnssMeasurementsProviderTest { @Mock - private GnssMeasurementsProvider.GnssMeasurementProviderNative mMockNative; + private GnssMeasurementsProvider.GnssMeasurementProviderNative + mMockNative; private GnssMeasurementsProvider mTestProvider; @Before diff --git a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssNavigationMessageProviderTest.java index aa2a96e6fad4..c21db73fb56c 100644 --- a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssNavigationMessageProviderTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; import static com.google.common.truth.Truth.assertThat; @@ -25,7 +41,8 @@ import org.robolectric.RuntimeEnvironment; @Presubmit public class GnssNavigationMessageProviderTest { @Mock - private GnssNavigationMessageProvider.GnssNavigationMessageProviderNative mMockNative; + private GnssNavigationMessageProvider.GnssNavigationMessageProviderNative + mMockNative; private GnssNavigationMessageProvider mTestProvider; @Before diff --git a/services/robotests/src/com/android/server/location/GnssPositionModeTest.java b/services/robotests/src/com/android/server/location/gnss/GnssPositionModeTest.java index f37f50e76ae5..7117ff95b401 100644 --- a/services/robotests/src/com/android/server/location/GnssPositionModeTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssPositionModeTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; import static com.google.common.truth.Truth.assertThat; diff --git a/services/robotests/src/com/android/server/location/GnssSatelliteBlacklistHelperTest.java b/services/robotests/src/com/android/server/location/gnss/GnssSatelliteBlacklistHelperTest.java index ba4a753e4813..7c73a2f92f71 100644 --- a/services/robotests/src/com/android/server/location/GnssSatelliteBlacklistHelperTest.java +++ b/services/robotests/src/com/android/server/location/gnss/GnssSatelliteBlacklistHelperTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; import static com.google.common.truth.Truth.assertThat; @@ -35,7 +51,8 @@ public class GnssSatelliteBlacklistHelperTest { private ContentResolver mContentResolver; @Mock - private GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback mCallback; + private GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback + mCallback; @Before public void setUp() { diff --git a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java b/services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java index 9c5d4ad6ceeb..9b59aaddc4d4 100644 --- a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java +++ b/services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java @@ -1,4 +1,20 @@ -package com.android.server.location; +/* + * 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.location.gnss; import static com.google.common.truth.Truth.assertThat; @@ -10,7 +26,7 @@ import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.NtpTrustedTime; -import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback; +import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback; import org.junit.Before; import org.junit.Test; diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java index fa0febd7f20f..138f9829c088 100644 --- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java @@ -17,6 +17,7 @@ package com.android.server.pm; import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES; import static android.content.Intent.FLAG_RECEIVER_FOREGROUND; import static android.content.Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND; @@ -104,6 +105,7 @@ public class CrossProfileAppsServiceImplRoboTest { private final CrossProfileAppsServiceImpl mCrossProfileAppsServiceImpl = new CrossProfileAppsServiceImpl(mContext, mInjector); private final Map<UserHandle, Set<Intent>> mSentUserBroadcasts = new HashMap<>(); + private final Map<Integer, List<ApplicationInfo>> installedApplications = new HashMap<>(); @Mock private PackageManagerInternal mPackageManagerInternal; @Mock private IPackageManager mIPackageManager; @@ -112,12 +114,18 @@ public class CrossProfileAppsServiceImplRoboTest { @Before public void initializeMocks() throws Exception { MockitoAnnotations.initMocks(this); + initializeInstalledApplicationsMock(); mockCrossProfileAppInstalledAndEnabledOnEachProfile(); mockCrossProfileAppRequestsInteractAcrossProfiles(); mockCrossProfileAppRegistersBroadcastReceiver(); mockCrossProfileAppWhitelisted(); } + private void initializeInstalledApplicationsMock() { + when(mPackageManagerInternal.getInstalledApplications(anyInt(), anyInt(), eq(CALLING_UID))) + .thenAnswer(invocation -> installedApplications.get(invocation.getArgument(1))); + } + private void mockCrossProfileAppInstalledAndEnabledOnEachProfile() { // They are enabled by default, so we simply have to ensure that a package info with an // application info is returned. @@ -138,11 +146,14 @@ public class CrossProfileAppsServiceImplRoboTest { when(mPackageManagerInternal.getPackage(uid)) .thenReturn(((ParsedPackage) PackageImpl.forTesting(CROSS_PROFILE_APP_PACKAGE_NAME) .hideAsParsed()).hideAsFinal()); + installedApplications.putIfAbsent(userId, new ArrayList<>()); + installedApplications.get(userId).add(packageInfo.applicationInfo); } private PackageInfo buildTestPackageInfo() { PackageInfo packageInfo = new PackageInfo(); packageInfo.applicationInfo = new ApplicationInfo(); + packageInfo.applicationInfo.packageName = CROSS_PROFILE_APP_PACKAGE_NAME; return packageInfo; } @@ -419,6 +430,45 @@ public class CrossProfileAppsServiceImplRoboTest { .isTrue(); } + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsTrue() { + mockUninstallCrossProfileAppFromWorkProfile(); + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isTrue(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse() + throws Exception { + mockCrossProfileAppDoesNotRequestInteractAcrossProfiles(); + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsTrue() { + mockCrossProfileAppNotWhitelisted(); + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isTrue(); + } + + @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_returnsTrue() { + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isTrue(); + } + + @Test + public void clearInteractAcrossProfilesAppOps() { + explicitlySetInteractAcrossProfilesAppOp(MODE_ALLOWED); + mCrossProfileAppsServiceImpl.clearInteractAcrossProfilesAppOps(); + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_DEFAULT); + } + private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) { explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode); } diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java index bb149cf327b8..09af4421406d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -496,7 +496,7 @@ public class AlarmManagerServiceTest { // This one should get deferred on set setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota, getNewMockPendingIntent()); - final long expectedNextTrigger = firstTrigger + 1 + mAppStandbyWindow; + final long expectedNextTrigger = firstTrigger + mAppStandbyWindow; assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed()); } @@ -516,7 +516,7 @@ public class AlarmManagerServiceTest { mNowElapsedTest = mTestTimer.getElapsed(); mTestTimer.expire(); } - final long expectedNextTrigger = firstTrigger + 1 + mAppStandbyWindow; + final long expectedNextTrigger = firstTrigger + mAppStandbyWindow; assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed()); } @@ -676,7 +676,7 @@ public class AlarmManagerServiceTest { final int rareQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_RARE); // The last alarm should now be deferred. final long expectedNextTrigger = (firstTrigger + workingQuota - 1 - rareQuota) - + mAppStandbyWindow + 1; + + mAppStandbyWindow; assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed()); } @@ -695,7 +695,7 @@ public class AlarmManagerServiceTest { } } // The last alarm should be deferred due to exceeding the quota - final long deferredTrigger = firstTrigger + 1 + mAppStandbyWindow; + final long deferredTrigger = firstTrigger + mAppStandbyWindow; assertEquals(deferredTrigger, mTestTimer.getElapsed()); // Upgrading the bucket now @@ -730,7 +730,7 @@ public class AlarmManagerServiceTest { mTestTimer.expire(); } // Any subsequent alarms in queue should all be deferred - assertEquals(firstTrigger + mAppStandbyWindow + 1, mTestTimer.getElapsed()); + assertEquals(firstTrigger + mAppStandbyWindow, mTestTimer.getElapsed()); // Paroling now assertAndHandleParoleChanged(true); @@ -744,7 +744,7 @@ public class AlarmManagerServiceTest { assertAndHandleParoleChanged(false); // Subsequent alarms should again get deferred - final long expectedNextTrigger = (firstTrigger + 5) + 1 + mAppStandbyWindow; + final long expectedNextTrigger = (firstTrigger + 5) + mAppStandbyWindow; assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed()); } diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java index 307092d24d84..5a42c4bc7f3a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java @@ -16,7 +16,9 @@ package com.android.server.wallpaper; +import static android.app.WallpaperManager.COMMAND_REAPPLY; import static android.app.WallpaperManager.FLAG_SYSTEM; +import static android.view.Display.DEFAULT_DISPLAY; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -34,6 +36,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -49,6 +52,7 @@ import android.hardware.display.DisplayManager; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.service.wallpaper.IWallpaperConnection; +import android.service.wallpaper.IWallpaperEngine; import android.service.wallpaper.WallpaperService; import android.testing.TestableContext; import android.util.Log; @@ -110,6 +114,7 @@ public class WallpaperManagerServiceTests { public final TemporaryFolder mFolder = new TemporaryFolder(); private final SparseArray<File> mTempDirs = new SparseArray<>(); private WallpaperManagerService mService; + private static IWallpaperConnection.Stub sWallpaperService; @BeforeClass public static void setUpClass() { @@ -134,7 +139,7 @@ public class WallpaperManagerServiceTests { doNothing().when(sContext).sendBroadcastAsUser(any(), any()); //Wallpaper components - final IWallpaperConnection.Stub wallpaperService = mock(IWallpaperConnection.Stub.class); + sWallpaperService = mock(IWallpaperConnection.Stub.class); sImageWallpaperComponentName = ComponentName.unflattenFromString( sContext.getResources().getString(R.string.image_wallpaper_component)); // Mock default wallpaper as image wallpaper if there is no pre-defined default wallpaper. @@ -145,10 +150,10 @@ public class WallpaperManagerServiceTests { doReturn(sImageWallpaperComponentName).when(() -> WallpaperManager.getDefaultWallpaperComponent(any())); } else { - sContext.addMockService(sDefaultWallpaperComponent, wallpaperService); + sContext.addMockService(sDefaultWallpaperComponent, sWallpaperService); } - sContext.addMockService(sImageWallpaperComponentName, wallpaperService); + sContext.addMockService(sImageWallpaperComponentName, sWallpaperService); } @AfterClass @@ -264,6 +269,30 @@ public class WallpaperManagerServiceTests { } /** + * Tests that when setWallpaperComponent is called with the currently set component, a command + * is issued to the wallpaper. + */ + @Test + public void testSetCurrentComponent() throws Exception { + final int testUserId = UserHandle.USER_SYSTEM; + mService.switchUser(testUserId, null); + verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent); + verifyCurrentSystemData(testUserId); + + spyOn(mService.mLastWallpaper.connection); + doReturn(true).when(mService.mLastWallpaper.connection).isUsableDisplay(any()); + mService.mLastWallpaper.connection.attachEngine(mock(IWallpaperEngine.class), + DEFAULT_DISPLAY); + + WallpaperManagerService.WallpaperConnection.DisplayConnector connector = + mService.mLastWallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY); + mService.setWallpaperComponent(sDefaultWallpaperComponent); + + verify(connector.mEngine).dispatchWallpaperCommand( + eq(COMMAND_REAPPLY), anyInt(), anyInt(), anyInt(), any()); + } + + /** * Tests internal data should be correct and no crash after switch user continuously. */ @Test diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 449e75cd11a0..e58e91179931 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -91,7 +91,15 @@ android_test { enabled: false, }, - data: [":JobTestApp"], + data: [ + ":JobTestApp", + ], + + java_resources: [ + ":PackageParserTestApp1", + ":PackageParserTestApp2", + ":PackageParserTestApp3", + ], resource_zips: [":FrameworksServicesTests_apks_as_resources"], } 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/MountServiceTests.java b/services/tests/servicestests/src/com/android/server/MountServiceTests.java deleted file mode 100644 index b1b31744c88b..000000000000 --- a/services/tests/servicestests/src/com/android/server/MountServiceTests.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2010 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; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.Resources.NotFoundException; -import android.os.FileUtils; -import android.os.storage.OnObbStateChangeListener; -import android.os.storage.StorageManager; -import android.test.AndroidTestCase; -import android.test.ComparisonFailure; -import android.test.suitebuilder.annotation.LargeTest; -import android.util.Log; - -import com.android.frameworks.servicestests.R; - -import java.io.File; -import java.io.InputStream; - -public class MountServiceTests extends AndroidTestCase { - private static final String TAG = "MountServiceTests"; - - private static final long MAX_WAIT_TIME = 25*1000; - private static final long WAIT_TIME_INCR = 5*1000; - - private static final String OBB_MOUNT_PREFIX = "/mnt/obb/"; - - private static void assertStartsWith(String message, String prefix, String actual) { - if (!actual.startsWith(prefix)) { - throw new ComparisonFailure(message, prefix, actual); - } - } - - private static class ObbObserver extends OnObbStateChangeListener { - private String path; - - public int state = -1; - boolean done = false; - - @Override - public void onObbStateChange(String path, int state) { - Log.d(TAG, "Received message. path=" + path + ", state=" + state); - synchronized (this) { - this.path = path; - this.state = state; - done = true; - notifyAll(); - } - } - - public String getPath() { - assertTrue("Expected ObbObserver to have received a state change.", done); - return path; - } - - public int getState() { - assertTrue("Expected ObbObserver to have received a state change.", done); - return state; - } - - public void reset() { - this.path = null; - this.state = -1; - done = false; - } - - public boolean isDone() { - return done; - } - - public boolean waitForCompletion() { - long waitTime = 0; - synchronized (this) { - while (!isDone() && waitTime < MAX_WAIT_TIME) { - try { - wait(WAIT_TIME_INCR); - waitTime += WAIT_TIME_INCR; - } catch (InterruptedException e) { - Log.i(TAG, "Interrupted during sleep", e); - } - } - } - - return isDone(); - } - } - - private File getFilePath(String name) { - final File filesDir = mContext.getFilesDir(); - final File outFile = new File(filesDir, name); - return outFile; - } - - private void copyRawToFile(int rawResId, File outFile) { - Resources res = mContext.getResources(); - InputStream is = null; - try { - is = res.openRawResource(rawResId); - } catch (NotFoundException e) { - fail("Failed to load resource with id: " + rawResId); - } - FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG - | FileUtils.S_IRWXO, -1, -1); - assertTrue(FileUtils.copyToFile(is, outFile)); - FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG - | FileUtils.S_IRWXO, -1, -1); - } - - private StorageManager getStorageManager() { - return (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE); - } - - private void mountObb(StorageManager sm, final int resource, final File file, - int expectedState) { - copyRawToFile(resource, file); - - final ObbObserver observer = new ObbObserver(); - assertTrue("mountObb call on " + file.getPath() + " should succeed", - sm.mountObb(file.getPath(), null, observer)); - - assertTrue("Mount should have completed", - observer.waitForCompletion()); - - if (expectedState == OnObbStateChangeListener.MOUNTED) { - assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath())); - } - - assertEquals("Actual file and resolved file should be the same", - file.getPath(), observer.getPath()); - - assertEquals(expectedState, observer.getState()); - } - - private ObbObserver mountObbWithoutWait(final StorageManager sm, final int resource, - final File file) { - copyRawToFile(resource, file); - - final ObbObserver observer = new ObbObserver(); - assertTrue("mountObb call on " + file.getPath() + " should succeed", sm.mountObb(file - .getPath(), null, observer)); - - return observer; - } - - private void waitForObbActionCompletion(final StorageManager sm, final File file, - final ObbObserver observer, int expectedState, boolean checkPath) { - assertTrue("Mount should have completed", observer.waitForCompletion()); - - assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath())); - - if (checkPath) { - assertEquals("Actual file and resolved file should be the same", file.getPath(), - observer.getPath()); - } - - assertEquals(expectedState, observer.getState()); - } - - private String checkMountedPath(final StorageManager sm, final File file) { - final String mountPath = sm.getMountedObbPath(file.getPath()); - assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX, - OBB_MOUNT_PREFIX, - mountPath); - return mountPath; - } - - private void unmountObb(final StorageManager sm, final File file, int expectedState) { - final ObbObserver observer = new ObbObserver(); - - assertTrue("unmountObb call on test1.obb should succeed", - sm.unmountObb(file.getPath(), false, observer)); - - assertTrue("Unmount should have completed", - observer.waitForCompletion()); - - assertEquals(expectedState, observer.getState()); - - if (expectedState == OnObbStateChangeListener.UNMOUNTED) { - assertFalse("OBB should not be mounted", sm.isObbMounted(file.getPath())); - } - } - - @LargeTest - public void testMountAndUnmountObbNormal() { - StorageManager sm = getStorageManager(); - - final File outFile = getFilePath("test1.obb"); - - mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED); - - mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); - - final String mountPath = checkMountedPath(sm, outFile); - final File mountDir = new File(mountPath); - - assertTrue("OBB mounted path should be a directory", - mountDir.isDirectory()); - - unmountObb(sm, outFile, OnObbStateChangeListener.UNMOUNTED); - } - - @LargeTest - public void testAttemptMountNonObb() { - StorageManager sm = getStorageManager(); - - final File outFile = getFilePath("test1_nosig.obb"); - - try { - mountObb(sm, R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL); - fail("mountObb should've failed with an exception"); - } catch (IllegalArgumentException e) { - // Expected - } - - assertFalse("OBB should not be mounted", - sm.isObbMounted(outFile.getPath())); - - assertNull("OBB's mounted path should be null", - sm.getMountedObbPath(outFile.getPath())); - } - - @LargeTest - public void testAttemptMountObbWrongPackage() { - StorageManager sm = getStorageManager(); - - final File outFile = getFilePath("test1_wrongpackage.obb"); - - mountObb(sm, R.raw.test1_wrongpackage, outFile, - OnObbStateChangeListener.ERROR_PERMISSION_DENIED); - - assertFalse("OBB should not be mounted", - sm.isObbMounted(outFile.getPath())); - - assertNull("OBB's mounted path should be null", - sm.getMountedObbPath(outFile.getPath())); - } - - @LargeTest - public void testMountAndUnmountTwoObbs() { - StorageManager sm = getStorageManager(); - - final File file1 = getFilePath("test1.obb"); - final File file2 = getFilePath("test2.obb"); - - ObbObserver oo1 = mountObbWithoutWait(sm, R.raw.test1, file1); - ObbObserver oo2 = mountObbWithoutWait(sm, R.raw.test1, file2); - - Log.d(TAG, "Waiting for OBB #1 to complete mount"); - waitForObbActionCompletion(sm, file1, oo1, OnObbStateChangeListener.MOUNTED, false); - Log.d(TAG, "Waiting for OBB #2 to complete mount"); - waitForObbActionCompletion(sm, file2, oo2, OnObbStateChangeListener.MOUNTED, false); - - final String mountPath1 = checkMountedPath(sm, file1); - final File mountDir1 = new File(mountPath1); - assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory()); - - final String mountPath2 = checkMountedPath(sm, file2); - final File mountDir2 = new File(mountPath2); - assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory()); - - unmountObb(sm, file1, OnObbStateChangeListener.UNMOUNTED); - unmountObb(sm, file2, OnObbStateChangeListener.UNMOUNTED); - } -} 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/location/gnss/GnssManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java index a99c982753c5..be2a5c529181 100644 --- a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java @@ -61,17 +61,9 @@ import android.os.RemoteException; import com.android.server.LocalServices; import com.android.server.location.AppForegroundHelper; -import com.android.server.location.GnssAntennaInfoProvider; -import com.android.server.location.GnssAntennaInfoProvider.GnssAntennaInfoProviderNative; -import com.android.server.location.GnssBatchingProvider; -import com.android.server.location.GnssCapabilitiesProvider; -import com.android.server.location.GnssLocationProvider; -import com.android.server.location.GnssMeasurementCorrectionsProvider; -import com.android.server.location.GnssMeasurementsProvider; -import com.android.server.location.GnssMeasurementsProvider.GnssMeasurementProviderNative; -import com.android.server.location.GnssNavigationMessageProvider; -import com.android.server.location.GnssNavigationMessageProvider.GnssNavigationMessageProviderNative; -import com.android.server.location.GnssStatusListenerHelper; +import com.android.server.location.gnss.GnssAntennaInfoProvider.GnssAntennaInfoProviderNative; +import com.android.server.location.gnss.GnssMeasurementsProvider.GnssMeasurementProviderNative; +import com.android.server.location.gnss.GnssNavigationMessageProvider.GnssNavigationMessageProviderNative; import com.android.server.location.LocationUsageLogger; import com.android.server.location.SettingsHelper; diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java index 7934d33f907d..03d9ad51e6c5 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java @@ -21,15 +21,16 @@ import static com.android.server.people.data.TestUtils.timestamp; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.usage.UsageEvents; +import android.app.usage.UsageStats; import android.app.usage.UsageStatsManagerInternal; import android.content.Context; import android.content.LocusId; @@ -50,6 +51,7 @@ import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import java.util.function.Predicate; @@ -58,7 +60,8 @@ import java.util.function.Predicate; public final class UsageStatsQueryHelperTest { private static final int USER_ID_PRIMARY = 0; - private static final String PKG_NAME = "pkg"; + private static final String PKG_NAME_1 = "pkg_1"; + private static final String PKG_NAME_2 = "pkg_2"; private static final String ACTIVITY_NAME = "TestActivity"; private static final String SHORTCUT_ID = "abc"; private static final LocusId LOCUS_ID_1 = new LocusId("locus_1"); @@ -80,7 +83,7 @@ public final class UsageStatsQueryHelperTest { File testDir = new File(ctx.getCacheDir(), "testdir"); ScheduledExecutorService scheduledExecutorService = new MockScheduledExecutorService(); - mPackageData = new TestPackageData(PKG_NAME, USER_ID_PRIMARY, pkg -> false, pkg -> false, + mPackageData = new TestPackageData(PKG_NAME_1, USER_ID_PRIMARY, pkg -> false, pkg -> false, scheduledExecutorService, testDir); mPackageData.mConversationStore.mConversationInfo = new ConversationInfo.Builder() .setShortcutId(SHORTCUT_ID) @@ -173,10 +176,72 @@ public final class UsageStatsQueryHelperTest { assertEquals(createInAppConversationEvent(130_000L, 30), events.get(2)); } + @Test + public void testQueryAppMovingToForegroundEvents() { + addUsageEvents( + createShortcutInvocationEvent(100_000L), + createActivityResumedEvent(110_000L), + createActivityStoppedEvent(120_000L), + createActivityResumedEvent(130_000L)); + + List<UsageEvents.Event> events = mHelper.queryAppMovingToForegroundEvents(USER_ID_PRIMARY, + 90_000L, + 200_000L); + + assertEquals(2, events.size()); + assertEquals(UsageEvents.Event.ACTIVITY_RESUMED, events.get(0).getEventType()); + assertEquals(110_000L, events.get(0).getTimeStamp()); + assertEquals(UsageEvents.Event.ACTIVITY_RESUMED, events.get(1).getEventType()); + assertEquals(130_000L, events.get(1).getTimeStamp()); + } + + @Test + public void testQueryAppLaunchCount() { + + UsageStats packageStats1 = createUsageStats(PKG_NAME_1, 2); + UsageStats packageStats2 = createUsageStats(PKG_NAME_1, 3); + UsageStats packageStats3 = createUsageStats(PKG_NAME_2, 1); + when(mUsageStatsManagerInternal.queryUsageStatsForUser(anyInt(), anyInt(), anyLong(), + anyLong(), anyBoolean())).thenReturn( + List.of(packageStats1, packageStats2, packageStats3)); + + Map<String, Integer> appLaunchCounts = mHelper.queryAppLaunchCount(USER_ID_PRIMARY, 90_000L, + 200_000L, Set.of(PKG_NAME_1, PKG_NAME_2)); + + assertEquals(2, appLaunchCounts.size()); + assertEquals(5, (long) appLaunchCounts.get(PKG_NAME_1)); + assertEquals(1, (long) appLaunchCounts.get(PKG_NAME_2)); + } + + @Test + public void testQueryAppLaunchCount_packageNameFiltered() { + + UsageStats packageStats1 = createUsageStats(PKG_NAME_1, 2); + UsageStats packageStats2 = createUsageStats(PKG_NAME_1, 3); + UsageStats packageStats3 = createUsageStats(PKG_NAME_2, 1); + when(mUsageStatsManagerInternal.queryUsageStatsForUser(anyInt(), anyInt(), anyLong(), + anyLong(), anyBoolean())).thenReturn( + List.of(packageStats1, packageStats2, packageStats3)); + + Map<String, Integer> appLaunchCounts = mHelper.queryAppLaunchCount(USER_ID_PRIMARY, 90_000L, + 200_000L, + Set.of(PKG_NAME_1)); + + assertEquals(1, appLaunchCounts.size()); + assertEquals(5, (long) appLaunchCounts.get(PKG_NAME_1)); + } + private void addUsageEvents(UsageEvents.Event... events) { UsageEvents usageEvents = new UsageEvents(Arrays.asList(events), new String[]{}); when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(), - eq(UsageEvents.SHOW_ALL_EVENT_DATA))).thenReturn(usageEvents); + anyInt())).thenReturn(usageEvents); + } + + private static UsageStats createUsageStats(String packageName, int launchCount) { + UsageStats packageStats = new UsageStats(); + packageStats.mPackageName = packageName; + packageStats.mAppLaunchCount = launchCount; + return packageStats; } private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { @@ -203,9 +268,15 @@ public final class UsageStatsQueryHelperTest { return e; } + private static UsageEvents.Event createActivityResumedEvent(long timestamp) { + UsageEvents.Event e = createUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, timestamp); + e.mClass = ACTIVITY_NAME; + return e; + } + private static UsageEvents.Event createUsageEvent(int eventType, long timestamp) { UsageEvents.Event e = new UsageEvents.Event(eventType, timestamp); - e.mPackage = PKG_NAME; + e.mPackage = PKG_NAME_1; return e; } diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java index c6cd34732acf..1480627b9b9f 100644 --- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java +++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java @@ -127,6 +127,9 @@ public final class ShareTargetPredictorTest { when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory1.getEventIndex(anyInt())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anyInt())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anyInt())).thenReturn(mEventIndex3); when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L)); when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L)); when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L)); @@ -183,6 +186,12 @@ public final class ShareTargetPredictorTest { when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); when(mEventHistory6.getEventIndex(anySet())).thenReturn(mEventIndex6); + when(mEventHistory1.getEventIndex(anyInt())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anyInt())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anyInt())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anyInt())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anyInt())).thenReturn(mEventIndex5); + when(mEventHistory6.getEventIndex(anyInt())).thenReturn(mEventIndex6); when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L)); when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L)); when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L)); @@ -220,19 +229,19 @@ public final class ShareTargetPredictorTest { @Test public void testSortTargets() { AppTarget appTarget1 = new AppTarget.Builder( - new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)) + new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)) .setClassName(CLASS_1) .build(); AppTarget appTarget2 = new AppTarget.Builder( - new AppTargetId("cls2#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)) + new AppTargetId("cls2#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)) .setClassName(CLASS_2) .build(); AppTarget appTarget3 = new AppTarget.Builder( - new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) + new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) .setClassName(CLASS_1) .build(); AppTarget appTarget4 = new AppTarget.Builder( - new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) + new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) .setClassName(CLASS_2) .build(); AppTarget appTarget5 = new AppTarget.Builder( @@ -251,6 +260,10 @@ public final class ShareTargetPredictorTest { when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory1.getEventIndex(anyInt())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anyInt())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anyInt())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anyInt())).thenReturn(mEventIndex4); when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L)); when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L)); when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L)); @@ -265,14 +278,14 @@ public final class ShareTargetPredictorTest { appTarget4, appTarget3, appTarget2, appTarget1, appTarget5); } - private ShareShortcutInfo buildShareShortcut( + private static ShareShortcutInfo buildShareShortcut( String packageName, String className, String shortcutId) { ShortcutInfo shortcutInfo = buildShortcut(packageName, shortcutId); ComponentName componentName = new ComponentName(packageName, className); return new ShareShortcutInfo(shortcutInfo, componentName); } - private ShortcutInfo buildShortcut(String packageName, String shortcutId) { + private static ShortcutInfo buildShortcut(String packageName, String shortcutId) { Context mockContext = mock(Context.class); when(mockContext.getPackageName()).thenReturn(packageName); when(mockContext.getUserId()).thenReturn(USER_ID); diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java new file mode 100644 index 000000000000..9d96d6b7d861 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java @@ -0,0 +1,406 @@ +/* + * 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.people.prediction; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.prediction.AppTarget; +import android.app.prediction.AppTargetId; +import android.app.usage.UsageEvents; +import android.os.UserHandle; +import android.util.Range; + +import com.android.server.people.data.DataManager; +import com.android.server.people.data.Event; +import com.android.server.people.data.EventHistory; +import com.android.server.people.data.EventIndex; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.time.Duration; +import java.util.List; +import java.util.Map; + +@RunWith(JUnit4.class) +public final class SharesheetModelScorerTest { + + private static final int USER_ID = 0; + private static final String PACKAGE_1 = "pkg1"; + private static final String PACKAGE_2 = "pkg2"; + private static final String PACKAGE_3 = "pkg3"; + private static final String CLASS_1 = "cls1"; + private static final String CLASS_2 = "cls2"; + private static final double DELTA = 1e-6; + private static final long NOW = System.currentTimeMillis(); + private static final Range<Long> WITHIN_ONE_DAY = new Range( + NOW - Duration.ofHours(23).toMillis(), + NOW - Duration.ofHours(22).toMillis()); + private static final Range<Long> TWO_DAYS_AGO = new Range( + NOW - Duration.ofHours(50).toMillis(), + NOW - Duration.ofHours(49).toMillis()); + private static final Range<Long> FIVE_DAYS_AGO = new Range( + NOW - Duration.ofDays(6).toMillis(), + NOW - Duration.ofDays(5).toMillis()); + private static final Range<Long> EIGHT_DAYS_AGO = new Range( + NOW - Duration.ofDays(9).toMillis(), + NOW - Duration.ofDays(8).toMillis()); + private static final Range<Long> TWELVE_DAYS_AGO = new Range( + NOW - Duration.ofDays(13).toMillis(), + NOW - Duration.ofDays(12).toMillis()); + private static final Range<Long> TWENTY_DAYS_AGO = new Range( + NOW - Duration.ofDays(21).toMillis(), + NOW - Duration.ofDays(20).toMillis()); + private static final Range<Long> FOUR_WEEKS_AGO = new Range( + NOW - Duration.ofDays(29).toMillis(), + NOW - Duration.ofDays(28).toMillis()); + + @Mock + private DataManager mDataManager; + @Mock + private EventHistory mEventHistory1; + @Mock + private EventHistory mEventHistory2; + @Mock + private EventHistory mEventHistory3; + @Mock + private EventHistory mEventHistory4; + @Mock + private EventHistory mEventHistory5; + @Mock + private EventIndex mEventIndex1; + @Mock + private EventIndex mEventIndex2; + @Mock + private EventIndex mEventIndex3; + @Mock + private EventIndex mEventIndex4; + @Mock + private EventIndex mEventIndex5; + @Mock + private EventIndex mEventIndex6; + @Mock + private EventIndex mEventIndex7; + @Mock + private EventIndex mEventIndex8; + @Mock + private EventIndex mEventIndex9; + @Mock + private EventIndex mEventIndex10; + + private ShareTargetPredictor.ShareTarget mShareTarget1; + private ShareTargetPredictor.ShareTarget mShareTarget2; + private ShareTargetPredictor.ShareTarget mShareTarget3; + private ShareTargetPredictor.ShareTarget mShareTarget4; + private ShareTargetPredictor.ShareTarget mShareTarget5; + private ShareTargetPredictor.ShareTarget mShareTarget6; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mShareTarget1 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)) + .setClassName(CLASS_1).build(), + mEventHistory1, null); + mShareTarget2 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder(new AppTargetId("cls2#pkg1"), PACKAGE_1, + UserHandle.of(USER_ID)).setClassName(CLASS_2).build(), + mEventHistory2, null); + mShareTarget3 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) + .setClassName(CLASS_1).build(), + mEventHistory3, null); + mShareTarget4 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) + .setClassName(CLASS_2).build(), + mEventHistory4, null); + mShareTarget5 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls1#pkg3"), PACKAGE_3, UserHandle.of(USER_ID)) + .setClassName(CLASS_1).build(), + mEventHistory5, null); + mShareTarget6 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls2#pkg3"), PACKAGE_3, UserHandle.of(USER_ID)) + .setClassName(CLASS_2).build(), + null, null); + } + + @Test + public void testComputeScore() { + // Frequency and recency + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + + when(mEventIndex1.getActiveTimeSlots()).thenReturn( + List.of(WITHIN_ONE_DAY, TWO_DAYS_AGO, FIVE_DAYS_AGO)); + when(mEventIndex2.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO)); + when(mEventIndex3.getActiveTimeSlots()).thenReturn(List.of(FIVE_DAYS_AGO, TWENTY_DAYS_AGO)); + when(mEventIndex4.getActiveTimeSlots()).thenReturn( + List.of(EIGHT_DAYS_AGO, TWELVE_DAYS_AGO, FOUR_WEEKS_AGO)); + when(mEventIndex5.getActiveTimeSlots()).thenReturn(List.of()); + + when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(WITHIN_ONE_DAY); + when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(TWO_DAYS_AGO); + when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(FIVE_DAYS_AGO); + when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(EIGHT_DAYS_AGO); + when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(null); + + // Frequency of the same mime type + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + + when(mEventIndex6.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO)); + when(mEventIndex7.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO)); + when(mEventIndex8.getActiveTimeSlots()).thenReturn(List.of()); + when(mEventIndex9.getActiveTimeSlots()).thenReturn(List.of(EIGHT_DAYS_AGO)); + when(mEventIndex10.getActiveTimeSlots()).thenReturn(List.of()); + + SharesheetModelScorer.computeScore( + List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5, + mShareTarget6), + Event.TYPE_SHARE_TEXT, + NOW); + + // Verification + assertEquals(0.514f, mShareTarget1.getScore(), DELTA); + assertEquals(0.475125f, mShareTarget2.getScore(), DELTA); + assertEquals(0.33f, mShareTarget3.getScore(), DELTA); + assertEquals(0.4411f, mShareTarget4.getScore(), DELTA); + assertEquals(0f, mShareTarget5.getScore(), DELTA); + assertEquals(0f, mShareTarget6.getScore(), DELTA); + } + + @Test + public void testComputeScoreForAppShare() { + // Frequency and recency + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + + when(mEventIndex1.getActiveTimeSlots()).thenReturn( + List.of(WITHIN_ONE_DAY, TWO_DAYS_AGO, FIVE_DAYS_AGO)); + when(mEventIndex2.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO)); + when(mEventIndex3.getActiveTimeSlots()).thenReturn(List.of(FIVE_DAYS_AGO, TWENTY_DAYS_AGO)); + when(mEventIndex4.getActiveTimeSlots()).thenReturn( + List.of(EIGHT_DAYS_AGO, TWELVE_DAYS_AGO, FOUR_WEEKS_AGO)); + when(mEventIndex5.getActiveTimeSlots()).thenReturn(List.of()); + + when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(WITHIN_ONE_DAY); + when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(TWO_DAYS_AGO); + when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(FIVE_DAYS_AGO); + when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(EIGHT_DAYS_AGO); + when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(null); + + // Frequency of the same mime type + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + + when(mEventIndex6.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO)); + when(mEventIndex7.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO)); + when(mEventIndex8.getActiveTimeSlots()).thenReturn(List.of()); + when(mEventIndex9.getActiveTimeSlots()).thenReturn(List.of(EIGHT_DAYS_AGO)); + when(mEventIndex10.getActiveTimeSlots()).thenReturn(List.of()); + + SharesheetModelScorer.computeScoreForAppShare( + List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5, + mShareTarget6), + Event.TYPE_SHARE_TEXT, 20, NOW, mDataManager, USER_ID); + + // Verification + assertEquals(0.514f, mShareTarget1.getScore(), DELTA); + assertEquals(0.475125f, mShareTarget2.getScore(), DELTA); + assertEquals(0.33f, mShareTarget3.getScore(), DELTA); + assertEquals(0.4411f, mShareTarget4.getScore(), DELTA); + assertEquals(0f, mShareTarget5.getScore(), DELTA); + assertEquals(0f, mShareTarget6.getScore(), DELTA); + } + + @Test + public void testComputeScoreForAppShare_promoteFrequentlyUsedApps() { + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + when(mDataManager.queryAppLaunchCount(anyInt(), anyLong(), anyLong(), anySet())) + .thenReturn( + Map.of(PACKAGE_1, 1, + PACKAGE_2, 2, + PACKAGE_3, 3)); + + SharesheetModelScorer.computeScoreForAppShare( + List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5, + mShareTarget6), + Event.TYPE_SHARE_TEXT, 20, NOW, mDataManager, USER_ID); + + verify(mDataManager, times(1)).queryAppLaunchCount(anyInt(), anyLong(), anyLong(), + anySet()); + assertEquals(0.9f, mShareTarget5.getScore(), DELTA); + assertEquals(0.81f, mShareTarget3.getScore(), DELTA); + assertEquals(0.729f, mShareTarget1.getScore(), DELTA); + assertEquals(0f, mShareTarget2.getScore(), DELTA); + assertEquals(0f, mShareTarget4.getScore(), DELTA); + assertEquals(0f, mShareTarget6.getScore(), DELTA); + } + + @Test + public void testComputeScoreForAppShare_skipPromoteFrequentlyUsedAppsWhenReachesLimit() { + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(WITHIN_ONE_DAY); + when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(TWO_DAYS_AGO); + when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(FIVE_DAYS_AGO); + when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(EIGHT_DAYS_AGO); + when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(null); + when(mDataManager.queryAppLaunchCount(anyInt(), anyLong(), anyLong(), anySet())) + .thenReturn( + Map.of(PACKAGE_1, 1, + PACKAGE_2, 2, + PACKAGE_3, 3)); + + SharesheetModelScorer.computeScoreForAppShare( + List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5, + mShareTarget6), + Event.TYPE_SHARE_TEXT, 4, NOW, mDataManager, USER_ID); + + verify(mDataManager, never()).queryAppLaunchCount(anyInt(), anyLong(), anyLong(), anySet()); + assertEquals(0.4f, mShareTarget1.getScore(), DELTA); + assertEquals(0.35f, mShareTarget2.getScore(), DELTA); + assertEquals(0.33f, mShareTarget3.getScore(), DELTA); + assertEquals(0.31f, mShareTarget4.getScore(), DELTA); + assertEquals(0f, mShareTarget5.getScore(), DELTA); + assertEquals(0f, mShareTarget6.getScore(), DELTA); + } + + @Test + public void testComputeScoreForAppShare_promoteForegroundApp() { + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + when(mDataManager.queryAppMovingToForegroundEvents(anyInt(), anyLong(), + anyLong())).thenReturn( + List.of(createUsageEvent(PACKAGE_2), + createUsageEvent(PACKAGE_3), + createUsageEvent(SharesheetModelScorer.CHOOSER_ACTIVITY), + createUsageEvent(PACKAGE_3), + createUsageEvent(PACKAGE_3)) + ); + + SharesheetModelScorer.computeScoreForAppShare( + List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5, + mShareTarget6), + Event.TYPE_SHARE_TEXT, 20, NOW, mDataManager, USER_ID); + + verify(mDataManager, times(1)).queryAppMovingToForegroundEvents(anyInt(), anyLong(), + anyLong()); + assertEquals(0f, mShareTarget1.getScore(), DELTA); + assertEquals(0f, mShareTarget2.getScore(), DELTA); + assertEquals(SharesheetModelScorer.FOREGROUND_APP_WEIGHT, mShareTarget3.getScore(), DELTA); + assertEquals(0f, mShareTarget4.getScore(), DELTA); + assertEquals(0f, mShareTarget5.getScore(), DELTA); + assertEquals(0f, mShareTarget6.getScore(), DELTA); + } + + @Test + public void testComputeScoreForAppShare_skipPromoteForegroundAppWhenNoValidForegroundApp() { + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + when(mDataManager.queryAppMovingToForegroundEvents(anyInt(), anyLong(), + anyLong())).thenReturn( + List.of(createUsageEvent(PACKAGE_3), + createUsageEvent(PACKAGE_3), + createUsageEvent(SharesheetModelScorer.CHOOSER_ACTIVITY), + createUsageEvent(PACKAGE_3), + createUsageEvent(PACKAGE_3)) + ); + + SharesheetModelScorer.computeScoreForAppShare( + List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5, + mShareTarget6), + Event.TYPE_SHARE_TEXT, 20, NOW, mDataManager, USER_ID); + + verify(mDataManager, times(1)).queryAppMovingToForegroundEvents(anyInt(), anyLong(), + anyLong()); + assertEquals(0f, mShareTarget1.getScore(), DELTA); + assertEquals(0f, mShareTarget2.getScore(), DELTA); + assertEquals(0f, mShareTarget3.getScore(), DELTA); + assertEquals(0f, mShareTarget4.getScore(), DELTA); + assertEquals(0f, mShareTarget5.getScore(), DELTA); + assertEquals(0f, mShareTarget6.getScore(), DELTA); + } + + private static UsageEvents.Event createUsageEvent(String packageName) { + UsageEvents.Event e = new UsageEvents.Event(); + e.mPackage = packageName; + return e; + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index 40ada2aedd59..db1bbab7ed94 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -43,6 +43,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.servicestests.R; +import com.android.server.pm.parsing.PackageParser2; import org.junit.Before; import org.junit.Test; @@ -63,6 +64,7 @@ public class ApexManagerTest { private static final int[] TEST_CHILD_SESSION_ID = {8888, 7777}; private ApexManager mApexManager; private Context mContext; + private PackageParser2 mPackageParser2; private IApexService mApexService = mock(IApexService.class); @@ -70,11 +72,14 @@ public class ApexManagerTest { public void setUp() throws RemoteException { mContext = InstrumentationRegistry.getInstrumentation().getContext(); mApexManager = new ApexManager.ApexManagerImpl(mApexService); + mPackageParser2 = new PackageParser2(null, false, null, null, null); } @Test public void testGetPackageInfo_setFlagsMatchActivePackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); final PackageInfo activePkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_ACTIVE_PACKAGE); @@ -90,6 +95,8 @@ public class ApexManagerTest { @Test public void testGetPackageInfo_setFlagsMatchFactoryPackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); PackageInfo factoryPkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_FACTORY_PACKAGE); @@ -105,6 +112,8 @@ public class ApexManagerTest { @Test public void testGetPackageInfo_setFlagsNone() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getPackageInfo(TEST_APEX_PKG, 0)).isNull(); } @@ -112,6 +121,8 @@ public class ApexManagerTest { @Test public void testGetActivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getActivePackages()).isNotEmpty(); } @@ -119,6 +130,8 @@ public class ApexManagerTest { @Test public void testGetActivePackages_noneActivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getActivePackages()).isEmpty(); } @@ -126,6 +139,8 @@ public class ApexManagerTest { @Test public void testGetFactoryPackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getFactoryPackages()).isNotEmpty(); } @@ -133,6 +148,8 @@ public class ApexManagerTest { @Test public void testGetFactoryPackages_noneFactoryPackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getFactoryPackages()).isEmpty(); } @@ -140,6 +157,8 @@ public class ApexManagerTest { @Test public void testGetInactivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getInactivePackages()).isNotEmpty(); } @@ -147,6 +166,8 @@ public class ApexManagerTest { @Test public void testGetInactivePackages_noneInactivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getInactivePackages()).isEmpty(); } @@ -154,6 +175,8 @@ public class ApexManagerTest { @Test public void testIsApexPackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.isApexPackage(TEST_APEX_PKG)).isTrue(); } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index a19d91976307..d760629552b8 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -17,11 +17,15 @@ package com.android.server.pm; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; + import android.annotation.NonNull; +import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; @@ -30,7 +34,6 @@ import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageParser; import android.content.pm.PackageUserState; -import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.content.pm.parsing.ParsingPackage; @@ -49,6 +52,7 @@ import android.util.ArraySet; import android.util.DisplayMetrics; import androidx.annotation.Nullable; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -69,9 +73,11 @@ import org.junit.runner.RunWith; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -90,6 +96,9 @@ public class PackageParserTest { private File mTmpDir; private static final File FRAMEWORK = new File("/system/framework/framework-res.apk"); + private static final String TEST_APP1_APK = "PackageParserTestApp1.apk"; + private static final String TEST_APP2_APK = "PackageParserTestApp2.apk"; + private static final String TEST_APP3_APK = "PackageParserTestApp3.apk"; @Before public void setUp() throws IOException { @@ -209,6 +218,61 @@ public class PackageParserTest { assertSame(deserialized.getSharedUserId(), deserialized2.getSharedUserId()); } + private static PackageParser2 makeParser() { + return new PackageParser2(null, false, null, null, null); + } + + private File extractFile(String filename) throws Exception { + final Context context = InstrumentationRegistry.getTargetContext(); + final File tmpFile = File.createTempFile(filename, ".apk"); + try (InputStream inputStream = context.getAssets().openNonAsset(filename)) { + Files.copy(inputStream, tmpFile.toPath(), REPLACE_EXISTING); + } + return tmpFile; + } + + /** + * Tests AndroidManifest.xml with no android:isolatedSplits attribute. + */ + @Test + public void testParseIsolatedSplitsDefault() throws Exception { + final File testFile = extractFile(TEST_APP1_APK); + try { + final ParsedPackage pkg = makeParser().parsePackage(testFile, 0, false); + assertFalse("isolatedSplits", pkg.isIsolatedSplitLoading()); + } finally { + testFile.delete(); + } + } + + /** + * Tests AndroidManifest.xml with an android:isolatedSplits attribute set to a constant. + */ + @Test + public void testParseIsolatedSplitsConstant() throws Exception { + final File testFile = extractFile(TEST_APP2_APK); + try { + final ParsedPackage pkg = makeParser().parsePackage(testFile, 0, false); + assertTrue("isolatedSplits", pkg.isIsolatedSplitLoading()); + } finally { + testFile.delete(); + } + } + + /** + * Tests AndroidManifest.xml with an android:isolatedSplits attribute set to a resource. + */ + @Test + public void testParseIsolatedSplitsResource() throws Exception { + final File testFile = extractFile(TEST_APP3_APK); + try { + final ParsedPackage pkg = makeParser().parsePackage(testFile, 0, false); + assertTrue("isolatedSplits", pkg.isIsolatedSplitLoading()); + } finally { + testFile.delete(); + } + } + /** * A trivial subclass of package parser that only caches the package name, and throws away * all other information. diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index bb5147132433..d244e687c8b8 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -61,8 +61,8 @@ import android.os.IBinder; import android.os.Looper; import android.os.PowerManager; import android.os.PowerSaveState; -import android.os.SystemClock; import android.os.UserHandle; +import android.os.test.TestLooper; import android.provider.Settings; import android.service.dreams.DreamManagerInternal; import android.test.mock.MockContentResolver; @@ -84,6 +84,7 @@ import com.android.server.power.batterysaver.BatterySaverController; import com.android.server.power.batterysaver.BatterySaverPolicy; import com.android.server.power.batterysaver.BatterySaverStateMachine; import com.android.server.power.batterysaver.BatterySavingStats; +import com.android.server.testutils.OffsettableClock; import org.junit.After; import org.junit.Before; @@ -132,6 +133,8 @@ public class PowerManagerServiceTest { private BatteryReceiver mBatteryReceiver; private UserSwitchedReceiver mUserSwitchedReceiver; private Resources mResourcesSpy; + private OffsettableClock mClock; + private TestLooper mTestLooper; private class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> { private final IntentFilter mFilter; @@ -189,6 +192,9 @@ public class PowerManagerServiceTest { Settings.Global.putInt(mContextSpy.getContentResolver(), Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); + + mClock = new OffsettableClock.Stopped(); + mTestLooper = new TestLooper(mClock::now); } private PowerManagerService createService() { @@ -250,6 +256,16 @@ public class PowerManagerServiceTest { } @Override + PowerManagerService.Clock createClock() { + return () -> mClock.now(); + } + + @Override + Handler createHandler(Looper looper, Handler.Callback callback) { + return new Handler(mTestLooper.getLooper(), callback); + } + + @Override void invalidateIsInteractiveCaches() { // Avoids an SELinux failure. } @@ -297,21 +313,21 @@ public class PowerManagerServiceTest { } private void forceSleep() { - mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(), + mService.getBinderServiceInstance().goToSleep(mClock.now(), PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); } private void forceDream() { - mService.getBinderServiceInstance().nap(SystemClock.uptimeMillis()); + mService.getBinderServiceInstance().nap(mClock.now()); } private void forceAwake() { - mService.getBinderServiceInstance().wakeUp(SystemClock.uptimeMillis(), + mService.getBinderServiceInstance().wakeUp(mClock.now(), PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name"); } private void forceDozing() { - mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(), + mService.getBinderServiceInstance().goToSleep(mClock.now(), PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0); } @@ -341,6 +357,11 @@ public class PowerManagerServiceTest { .thenReturn(minimumScreenOffTimeoutConfigMillis); } + private void advanceTime(long timeMs) { + mClock.fastForward(timeMs); + mTestLooper.dispatchAll(); + } + @Test public void testUpdatePowerScreenPolicy_UpdateDisplayPowerRequest() { createService(); @@ -403,7 +424,7 @@ public class PowerManagerServiceTest { assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); // Take a nap and verify. - mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(), + mService.getBinderServiceInstance().goToSleep(mClock.now(), PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); } @@ -445,7 +466,7 @@ public class PowerManagerServiceTest { createService(); startSystem(); forceSleep(); - mService.getBinderServiceInstance().wakeUp(SystemClock.uptimeMillis(), + mService.getBinderServiceInstance().wakeUp(mClock.now(), PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name"); assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); } @@ -540,7 +561,7 @@ public class PowerManagerServiceTest { assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); // Take a nap and verify. - mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(), + mService.getBinderServiceInstance().goToSleep(mClock.now(), PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0); assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING); } @@ -550,7 +571,7 @@ public class PowerManagerServiceTest { int interval = 1000; createService(); mService.onUserActivity(); - SystemClock.sleep(interval + 1 /* just a little more */); + advanceTime(interval + 1 /* just a little more */); assertThat(mService.wasDeviceIdleForInternal(interval)).isTrue(); } @@ -678,7 +699,7 @@ public class PowerManagerServiceTest { mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, null /* workSource */, null /* historyTag */); when(mDreamManagerInternalMock.isDreaming()).thenReturn(true); - mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(), + mService.getBinderServiceInstance().goToSleep(mClock.now(), PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0); assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING); assertFalse(isAcquired[0]); @@ -718,16 +739,16 @@ public class PowerManagerServiceTest { createService(); startSystem(); - mService.getBinderServiceInstance().userActivity(SystemClock.uptimeMillis(), + mService.getBinderServiceInstance().userActivity(mClock.now(), PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); verify(mInattentiveSleepWarningControllerMock, never()).show(); - SystemClock.sleep(150); + advanceTime(150); verify(mInattentiveSleepWarningControllerMock, times(1)).show(); verify(mInattentiveSleepWarningControllerMock, never()).dismiss(anyBoolean()); when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true); - mService.getBinderServiceInstance().userActivity(SystemClock.uptimeMillis(), + mService.getBinderServiceInstance().userActivity(mClock.now(), PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); verify(mInattentiveSleepWarningControllerMock, times(1)).dismiss(true); } @@ -740,10 +761,10 @@ public class PowerManagerServiceTest { createService(); startSystem(); - SystemClock.sleep(50); + advanceTime(50); verify(mInattentiveSleepWarningControllerMock, atLeastOnce()).show(); when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true); - SystemClock.sleep(70); + advanceTime(70); assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); forceAwake(); assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); @@ -764,7 +785,7 @@ public class PowerManagerServiceTest { setAttentiveTimeout(5); createService(); startSystem(); - SystemClock.sleep(20); + advanceTime(20); assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); } @@ -772,7 +793,7 @@ public class PowerManagerServiceTest { public void testInattentiveSleep_goesToSleepWithWakeLock() throws Exception { final String pkg = mContextSpy.getOpPackageName(); final Binder token = new Binder(); - final String tag = "sleep_testWithWakeLock"; + final String tag = "testInattentiveSleep_goesToSleepWithWakeLock"; setMinimumScreenOffTimeoutConfig(5); setAttentiveTimeout(30); @@ -783,7 +804,7 @@ public class PowerManagerServiceTest { PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg, null /* workSource */, null /* historyTag */); - SystemClock.sleep(60); + advanceTime(60); assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); } 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/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp new file mode 100644 index 000000000000..c409438e94ae --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp @@ -0,0 +1,53 @@ +// 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. + +android_test_helper_app { + name: "PackageParserTestApp1", + sdk_version: "current", + srcs: ["**/*.java"], + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + manifest: "AndroidManifestApp1.xml", +} + +android_test_helper_app { + name: "PackageParserTestApp2", + sdk_version: "current", + srcs: ["**/*.java"], + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + manifest: "AndroidManifestApp2.xml", +} + +android_test_helper_app { + name: "PackageParserTestApp3", + sdk_version: "current", + srcs: ["**/*.java"], + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + resource_dirs: ["res"], + manifest: "AndroidManifestApp3.xml", +} diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp1.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp1.xml new file mode 100644 index 000000000000..01d335d2cbbd --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp1.xml @@ -0,0 +1,25 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.servicestests.apps.packageparserapp" > + + <application> + <activity android:name=".TestActivity" + android:exported="true" /> + </application> + +</manifest>
\ No newline at end of file diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp2.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp2.xml new file mode 100644 index 000000000000..567946cc70d7 --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp2.xml @@ -0,0 +1,26 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.servicestests.apps.packageparserapp" + android:isolatedSplits="true" > + + <application> + <activity android:name=".TestActivity" + android:exported="true" /> + </application> + +</manifest>
\ No newline at end of file diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp3.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp3.xml new file mode 100644 index 000000000000..77285a805950 --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp3.xml @@ -0,0 +1,26 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.servicestests.apps.packageparserapp" + android:isolatedSplits="@bool/config_isIsolated" > + + <application> + <activity android:name=".TestActivity" + android:exported="true" /> + </application> + +</manifest>
\ No newline at end of file diff --git a/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml b/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml new file mode 100644 index 000000000000..6a4cc653494f --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml @@ -0,0 +1,19 @@ +<?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. +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <bool name="config_isIsolated">true</bool> +</resources>
\ No newline at end of file diff --git a/core/java/android/content/pm/NamedParcelFileDescriptor.aidl b/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/TestActivity.java index 68dd5f54654b..2eacb96fb2aa 100644 --- a/core/java/android/content/pm/NamedParcelFileDescriptor.aidl +++ b/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/TestActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,15 +14,15 @@ * limitations under the License. */ -package android.content.pm; +package com.android.servicestests.apps.packageparserapp; -import android.os.ParcelFileDescriptor; +import android.app.Activity; +import android.os.Bundle; -/** - * A named ParcelFileDescriptor. - * @hide - */ -parcelable NamedParcelFileDescriptor { - @utf8InCpp String name; - ParcelFileDescriptor fd; +public class TestActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + finish(); + } } 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/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 52fc3de89451..12934ee8bb78 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -27,6 +27,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; @@ -56,7 +57,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; import android.app.ActivityManager; import android.app.IApplicationThread; @@ -1157,6 +1157,34 @@ public class ActivityStackTests extends ActivityTestsBase { } @Test + public void testCheckBehindFullscreenActivity() { + final ActivityRecord bottomActivity = + new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build(); + final ActivityRecord topActivity = + new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build(); + doReturn(true).when(mStack).shouldBeVisible(any()); + assertTrue(mStack.checkBehindFullscreenActivity(bottomActivity, + null /* handleBehindFullscreenActivity */)); + assertFalse(mStack.checkBehindFullscreenActivity(topActivity, + null /* handleBehindFullscreenActivity */)); + + doReturn(false).when(topActivity).occludesParent(); + assertFalse(mStack.checkBehindFullscreenActivity(bottomActivity, + null /* handleBehindFullscreenActivity */)); + assertFalse(mStack.checkBehindFullscreenActivity(topActivity, + null /* handleBehindFullscreenActivity */)); + + final ActivityRecord finishingActivity = + new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build(); + finishingActivity.finishing = true; + doCallRealMethod().when(finishingActivity).occludesParent(); + assertFalse(mStack.checkBehindFullscreenActivity(bottomActivity, + null /* handleBehindFullscreenActivity */)); + assertFalse(mStack.checkBehindFullscreenActivity(topActivity, + null /* handleBehindFullscreenActivity */)); + } + + @Test public void testClearUnknownAppVisibilityBehindFullscreenActivity() { final UnknownAppVisibilityController unknownAppVisibilityController = mDefaultDisplay.mDisplayContent.mUnknownAppVisibilityController; 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 873fa0222002..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,19 +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()); - } - @Override - public void taskAppeared(ActivityManager.RunningTaskInfo info) { + mSecondary = WindowContainer.fromBinder(secondary.asBinder()).asTask(); } @Override - public void taskVanished(ActivityManager.RunningTaskInfo info) { + public void onTaskAppeared(ActivityManager.RunningTaskInfo info) { } @Override - public void transactionReady(int id, SurfaceControl.Transaction t) { + public void onTaskVanished(ActivityManager.RunningTaskInfo info) { } @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { @@ -1045,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/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index c71d8194f889..08e492a7b0ff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -28,7 +28,6 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -38,7 +37,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import android.graphics.Rect; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; @@ -70,7 +68,7 @@ public class AppTransitionTests extends WindowTestsBase { @Before public void setUp() throws Exception { - doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean()); + doNothing().when(mWm.mRoot).performSurfacePlacement(); mDc = mWm.getDefaultDisplayContentLocked(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index 6cc57f4dbf8c..cf3cfecbf65e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -174,7 +174,7 @@ public class AppWindowTokenTests extends WindowTestsBase { null /* freezeThisOneIfNeeded */, false /* forceUpdate */); // In this test, DC will not get config update. Set the waiting flag to false. mDisplayContent.mWaitingForConfig = false; - mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */); + mWm.mRoot.performSurfacePlacement(); assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation()); assertTrue(appWindow.mResizeReported); appWindow.removeImmediately(); 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/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index 8f3ff52b8018..ae467c0c811d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -120,7 +120,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { .build(); mTestTask.mUserId = TEST_USER_ID; mTestTask.mLastNonFullscreenBounds = TEST_BOUNDS; - mTestTask.hasBeenVisible = true; + mTestTask.setHasBeenVisible(true); mTaskWithDifferentComponent = new TaskBuilder(mSupervisor) .setComponent(ALTERNATIVE_COMPONENT).build(); @@ -346,7 +346,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { .build(); anotherTaskOfTheSameUser.setWindowingMode(WINDOWING_MODE_FREEFORM); anotherTaskOfTheSameUser.setBounds(200, 300, 400, 500); - anotherTaskOfTheSameUser.hasBeenVisible = true; + anotherTaskOfTheSameUser.setHasBeenVisible(true); mTarget.saveTask(anotherTaskOfTheSameUser); stack = mTestDisplay.createStack(TEST_WINDOWING_MODE, @@ -358,7 +358,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { .build(); anotherTaskOfDifferentUser.setWindowingMode(WINDOWING_MODE_FREEFORM); anotherTaskOfDifferentUser.setBounds(300, 400, 500, 600); - anotherTaskOfDifferentUser.hasBeenVisible = true; + anotherTaskOfDifferentUser.setHasBeenVisible(true); mTarget.saveTask(anotherTaskOfDifferentUser); mTarget.onCleanupUser(TEST_USER_ID); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 66566bc5dff5..da3ee3990137 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; @@ -28,6 +29,7 @@ import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.TYPE_VIRTUAL; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -54,6 +56,7 @@ import static java.lang.Integer.MAX_VALUE; import android.app.ActivityManager.RecentTaskInfo; import android.app.ActivityManager.RunningTaskInfo; +import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.WindowConfiguration; import android.content.ComponentName; @@ -681,24 +684,19 @@ public class RecentTasksTest extends ActivityTestsBase { * Tests that tasks on singleTaskDisplay are not visible and not trimmed/removed. */ @Test - public void testVisibleTasks_singleTaskDisplay() { + public void testVisibleTasks_alwaysOnTop() { mRecentTasks.setOnlyTestVisibleRange(); mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */); - final DisplayContent singleTaskDisplay = - addNewDisplayContentAt(DisplayContent.POSITION_TOP); - singleTaskDisplay.setDisplayToSingleTaskInstance(); - ActivityStack singleTaskStack = singleTaskDisplay.createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); + final Task alwaysOnTopTask = display.createStack(WINDOWING_MODE_MULTI_WINDOW, + ACTIVITY_TYPE_STANDARD, true /* onTop */); + alwaysOnTopTask.setAlwaysOnTop(true); - Task excludedTask1 = createTaskBuilder(".ExcludedTask1") - .setStack(singleTaskStack) - .build(); + assertFalse("Always on top tasks should not be visible recents", + mRecentTasks.isVisibleRecentTask(alwaysOnTopTask)); - assertFalse("Tasks on singleTaskDisplay should not be visible recents", - mRecentTasks.isVisibleRecentTask(excludedTask1)); - - mRecentTasks.add(excludedTask1); + mRecentTasks.add(alwaysOnTopTask); // Add N+1 visible tasks. mRecentTasks.add(mTasks.get(0)); @@ -1366,12 +1364,12 @@ public class RecentTasksTest extends ActivityTestsBase { public boolean mLastAllowed; @Override - void getTasks(int maxNum, List<RunningTaskInfo> list, int ignoreActivityType, - int ignoreWindowingMode, RootWindowContainer root, - int callingUid, boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) { + void getTasks(int maxNum, List<RunningTaskInfo> list, boolean filterOnlyVisibleRecents, + RootWindowContainer root, int callingUid, boolean allowed, boolean crossUser, + ArraySet<Integer> profileIds) { mLastAllowed = allowed; - super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, root, - callingUid, allowed, crossUser, profileIds); + super.getTasks(maxNum, list, filterOnlyVisibleRecents, root, callingUid, allowed, + crossUser, profileIds); } } } 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..da07baca3ce1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -100,7 +100,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean()); + doNothing().when(mWm.mRoot).performSurfacePlacement(); when(mMockRunner.asBinder()).thenReturn(new Binder()); mDefaultDisplay = mWm.mRoot.getDefaultDisplay(); mController = spy(new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks, @@ -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/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java index 0d5565428bf2..d6a67abc9e76 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java @@ -16,9 +16,6 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; - import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; @@ -82,9 +79,8 @@ public class RunningTasksTest extends ActivityTestsBase { // collected from all tasks across all the stacks final int numFetchTasks = 5; ArrayList<RunningTaskInfo> tasks = new ArrayList<>(); - mRunningTasks.getTasks(5, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED, - mRootWindowContainer, -1 /* callingUid */, true /* allowed */, - true /*crossUser */, PROFILE_IDS); + mRunningTasks.getTasks(5, tasks, false /* filterOnlyVisibleRecents */, mRootWindowContainer, + -1 /* callingUid */, true /* allowed */, true /*crossUser */, PROFILE_IDS); assertThat(tasks).hasSize(numFetchTasks); for (int i = 0; i < numFetchTasks; i++) { assertEquals(numTasks - i - 1, tasks.get(i).id); @@ -93,9 +89,9 @@ public class RunningTasksTest extends ActivityTestsBase { // Ensure that requesting more than the total number of tasks only returns the subset // and does not crash tasks.clear(); - mRunningTasks.getTasks(100, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED, - mRootWindowContainer, -1 /* callingUid */, true /* allowed */, - true /* crossUser */, PROFILE_IDS); + mRunningTasks.getTasks(100, tasks, false /* filterOnlyVisibleRecents */, + mRootWindowContainer, -1 /* callingUid */, true /* allowed */, true /* crossUser */, + PROFILE_IDS); assertThat(tasks).hasSize(numTasks); for (int i = 0; i < numTasks; i++) { assertEquals(numTasks - i - 1, tasks.get(i).id); @@ -119,9 +115,9 @@ public class RunningTasksTest extends ActivityTestsBase { final int numFetchTasks = 5; final ArrayList<RunningTaskInfo> tasks = new ArrayList<>(); - mRunningTasks.getTasks(numFetchTasks, tasks, ACTIVITY_TYPE_UNDEFINED, - WINDOWING_MODE_UNDEFINED, mRootWindowContainer, -1 /* callingUid */, - true /* allowed */, true /*crossUser */, PROFILE_IDS); + mRunningTasks.getTasks(numFetchTasks, tasks, false /* filterOnlyVisibleRecents */, + mRootWindowContainer, -1 /* callingUid */, true /* allowed */, true /*crossUser */, + PROFILE_IDS); assertThat(tasks).hasSize(numFetchTasks); for (int i = 0; i < tasks.size(); i++) { final Bundle extras = tasks.get(i).baseIntent.getExtras(); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index edf81ea32ad3..893a14541c48 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -481,7 +481,7 @@ public class SizeCompatTests extends ActivityTestsBase { // The letterbox needs a main window to layout. addWindowToActivity(mActivity); // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}. - mActivity.mRootWindowContainer.performSurfacePlacement(false /* recoveringMemory */); + mActivity.mRootWindowContainer.performSurfacePlacement(); // The letterbox insets should be [350, 0 - 350, 0]. assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0), mActivity.getLetterboxInsets()); 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 1624bbc1d472..4cc84a65fb29 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; @@ -96,130 +96,181 @@ public class TaskOrganizerTests extends WindowTestsBase { return registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); } + Task createTask(ActivityStack stack, boolean fakeDraw) { + final Task task = createTaskInStack(stack, 0); + + if (fakeDraw) { + task.setHasBeenVisible(true); + } + return task; + } + + Task createTask(ActivityStack stack) { + // Fake draw notifications for most of our tests. + return createTask(stack, true); + } + + ActivityStack createStack() { + return createTaskStackOnDisplay(mDisplayContent); + } + @Test public void testAppearVanish() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); + final ITaskOrganizer organizer = registerMockOrganizer(); + + task.setTaskOrganizer(organizer); + verify(organizer).onTaskAppeared(any()); + + task.removeImmediately(); + verify(organizer).onTaskVanished(any()); + } + + @Test + public void testAppearWaitsForVisibility() throws RemoteException { + final ActivityStack stack = createStack(); + final Task task = createTask(stack, false); final ITaskOrganizer organizer = registerMockOrganizer(); task.setTaskOrganizer(organizer); - verify(organizer).taskAppeared(any()); + verify(organizer, never()).onTaskAppeared(any()); + task.setHasBeenVisible(true); + assertTrue(stack.getHasBeenVisible()); + + verify(organizer).onTaskAppeared(any()); + + task.removeImmediately(); + verify(organizer).onTaskVanished(any()); + } + + @Test + public void testNoVanishedIfNoAppear() throws RemoteException { + final ActivityStack stack = createStack(); + final Task task = createTask(stack, false /* hasBeenVisible */); + final ITaskOrganizer organizer = registerMockOrganizer(); + + // In this test we skip making the Task visible, and verify + // that even though a TaskOrganizer is set remove doesn't emit + // a vanish callback, because we never emitted appear. + task.setTaskOrganizer(organizer); + verify(organizer, never()).onTaskAppeared(any()); task.removeImmediately(); - verify(organizer).taskVanished(any()); + verify(organizer, never()).onTaskVanished(any()); } @Test public void testSwapOrganizer() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); 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 public void testSwapWindowingModes() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); 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 public void testClearOrganizer() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); 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 public void testUnregisterOrganizer() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); 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 public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); - final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent); - final Task task2 = createTaskInStack(stack2, 0 /* userId */); - final ActivityStack stack3 = createTaskStackOnDisplay(mDisplayContent); - final Task task3 = createTaskInStack(stack3, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); + final ActivityStack stack2 = createStack(); + final Task task2 = createTask(stack2); + final ActivityStack stack3 = createStack(); + final Task task3 = createTask(stack3); final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); // 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 public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED); - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); - final Task task2 = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); + final Task task2 = createTask(stack); 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 public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); 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 @@ -231,7 +282,7 @@ public class TaskOrganizerTests extends WindowTestsBase { WindowContainerTransaction t = new WindowContainerTransaction(); Rect newBounds = new Rect(10, 10, 100, 100); t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100)); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertEquals(newBounds, task.getBounds()); } @@ -246,7 +297,7 @@ public class TaskOrganizerTests extends WindowTestsBase { assertEquals(stack.mRemoteToken, info.stackToken); Rect newBounds = new Rect(10, 10, 100, 100); t.setBounds(info.stackToken, new Rect(10, 10, 100, 100)); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertEquals(newBounds, stack.getBounds()); } @@ -257,7 +308,7 @@ public class TaskOrganizerTests extends WindowTestsBase { final WindowContainerTransaction t = new WindowContainerTransaction(); t.setWindowingMode(stack.mRemoteToken, WINDOWING_MODE_FULLSCREEN); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertEquals(WINDOWING_MODE_FULLSCREEN, stack.getWindowingMode()); } @@ -270,7 +321,7 @@ public class TaskOrganizerTests extends WindowTestsBase { t.setWindowingMode(stack.mRemoteToken, WINDOWING_MODE_PINNED); t.setActivityWindowingMode(stack.mRemoteToken, WINDOWING_MODE_FULLSCREEN); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode()); assertEquals(WINDOWING_MODE_PINNED, stack.getWindowingMode()); @@ -285,10 +336,10 @@ public class TaskOrganizerTests extends WindowTestsBase { WindowContainerTransaction t = new WindowContainerTransaction(); assertTrue(task.isFocusable()); t.setFocusable(stack.mRemoteToken, false); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertFalse(task.isFocusable()); t.setFocusable(stack.mRemoteToken, true); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertTrue(task.isFocusable()); } @@ -300,10 +351,10 @@ public class TaskOrganizerTests extends WindowTestsBase { WindowContainerTransaction t = new WindowContainerTransaction(); assertTrue(stack.shouldBeVisible(null)); t.setHidden(stack.mRemoteToken, true); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertFalse(stack.shouldBeVisible(null)); t.setHidden(stack.mRemoteToken, false); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertTrue(stack.shouldBeVisible(null)); } @@ -315,19 +366,19 @@ public class TaskOrganizerTests extends WindowTestsBase { final Task task = stack.getTopMostTask(); WindowContainerTransaction t = new WindowContainerTransaction(); t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100)); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); final int origScreenWDp = task.getConfiguration().screenHeightDp; final int origScreenHDp = task.getConfiguration().screenHeightDp; t = new WindowContainerTransaction(); // verify that setting config overrides on parent restricts children. t.setScreenSizeDp(stack.mRemoteToken, origScreenWDp, origScreenHDp); t.setBounds(task.mRemoteToken, new Rect(10, 10, 150, 200)); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp); t = new WindowContainerTransaction(); t.setScreenSizeDp(stack.mRemoteToken, Configuration.SCREEN_WIDTH_DP_UNDEFINED, Configuration.SCREEN_HEIGHT_DP_UNDEFINED); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertNotEquals(origScreenHDp, task.getConfiguration().screenHeightDp); } @@ -348,45 +399,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,13 +464,10 @@ public class TaskOrganizerTests extends WindowTestsBase { final boolean[] called = {false}; ITaskOrganizer listener = new ITaskOrganizer.Stub() { @Override - public void taskAppeared(RunningTaskInfo taskInfo) { } - - @Override - public void taskVanished(RunningTaskInfo container) { } + public void onTaskAppeared(RunningTaskInfo taskInfo) { } @Override - public void transactionReady(int id, SurfaceControl.Transaction t) { } + public void onTaskVanished(RunningTaskInfo container) { } @Override public void onTaskInfoChanged(RunningTaskInfo info) throws RemoteException { @@ -419,8 +484,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); @@ -428,19 +495,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); } @@ -450,13 +522,10 @@ public class TaskOrganizerTests extends WindowTestsBase { final ArrayMap<IBinder, RunningTaskInfo> lastReportedTiles = new ArrayMap<>(); ITaskOrganizer listener = new ITaskOrganizer.Stub() { @Override - public void taskAppeared(RunningTaskInfo taskInfo) { } - - @Override - public void taskVanished(RunningTaskInfo container) { } + public void onTaskAppeared(RunningTaskInfo taskInfo) { } @Override - public void transactionReady(int id, SurfaceControl.Transaction t) { } + public void onTaskVanished(RunningTaskInfo container) { } @Override public void onTaskInfoChanged(RunningTaskInfo info) { @@ -464,9 +533,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); @@ -487,8 +558,7 @@ public class TaskOrganizerTests extends WindowTestsBase { WindowContainerTransaction wct = new WindowContainerTransaction(); wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */); wct.reparent(stack2.mRemoteToken, info2.token, true /* onTop */); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(wct, - null /* organizer */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertFalse(lastReportedTiles.isEmpty()); assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(info1.token.asBinder()).topActivityType); @@ -498,8 +568,7 @@ public class TaskOrganizerTests extends WindowTestsBase { lastReportedTiles.clear(); wct = new WindowContainerTransaction(); wct.reparent(stack2.mRemoteToken, info1.token, false /* onTop */); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(wct, - null /* organizer */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertFalse(lastReportedTiles.isEmpty()); // Standard should still be on top of tile 1, so no change there assertFalse(lastReportedTiles.containsKey(info1.token.asBinder())); @@ -524,29 +593,26 @@ public class TaskOrganizerTests extends WindowTestsBase { lastReportedTiles.clear(); wct = new WindowContainerTransaction(); wct.reorder(stack2.mRemoteToken, true /* onTop */); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(wct, - null /* organizer */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); // Home should now be on top. No change occurs in second tile, so not reported assertEquals(1, lastReportedTiles.size()); assertEquals(ACTIVITY_TYPE_HOME, 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; } @Test public void testTrivialBLASTCallback() throws RemoteException { - final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ActivityStack stackController1 = createStack(); + final Task task = createTask(stackController1); final ITaskOrganizer organizer = registerMockOrganizer(); spyOn(task); @@ -567,8 +633,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testOverlappingBLASTCallback() throws RemoteException { - final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ActivityStack stackController1 = createStack(); + final Task task = createTask(stackController1); final ITaskOrganizer organizer = registerMockOrganizer(); spyOn(task); @@ -596,8 +662,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testBLASTCallbackWithWindow() { - final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ActivityStack stackController1 = createStack(); + final Task task = createTask(stackController1); final ITaskOrganizer organizer = registerMockOrganizer(); final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); makeWindowVisible(w); @@ -620,8 +686,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testBLASTCallbackWithInvisibleWindow() { - final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ActivityStack stackController1 = createStack(); + final Task task = createTask(stackController1); final ITaskOrganizer organizer = registerMockOrganizer(); final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); @@ -642,8 +708,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testBLASTCallbackWithChildWindow() { - final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ActivityStack stackController1 = createStack(); + final Task task = createTask(stackController1); final ITaskOrganizer organizer = registerMockOrganizer(); final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); final WindowState child = createWindow(w, TYPE_APPLICATION, "Other Window"); @@ -676,14 +742,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) { - } - @Override - public void transactionReady(int id, SurfaceControl.Transaction t) { + public void onTaskVanished(RunningTaskInfo info) { } @Override public void onTaskInfoChanged(RunningTaskInfo info) { @@ -696,6 +759,8 @@ public class TaskOrganizerTests extends WindowTestsBase { record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; spyOn(record); doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean()); + + record.getRootTask().setHasBeenVisible(true); return record; } @@ -743,4 +808,23 @@ public class TaskOrganizerTests extends WindowTestsBase { assertEquals(3, ratio.getNumerator()); assertEquals(4, ratio.getDenominator()); } + + @Test + public void testPreventDuplicateAppear() throws RemoteException { + final ActivityStack stack = createStack(); + final Task task = createTask(stack); + final ITaskOrganizer organizer = registerMockOrganizer(); + + task.setTaskOrganizer(organizer); + // setHasBeenVisible was already called once by the set-up code. + task.setHasBeenVisible(true); + verify(organizer, times(1)).onTaskAppeared(any()); + + task.taskOrganizerUnregistered(); + task.setTaskOrganizer(organizer); + verify(organizer, times(2)).onTaskAppeared(any()); + + task.removeImmediately(); + verify(organizer).onTaskVanished(any()); + } } 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..1ad4079c5193 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 { @@ -873,11 +874,11 @@ public class TaskRecordTests extends ActivityTestsBase { spyOn(persister); final Task task = getTestTask(); - task.hasBeenVisible = false; + task.setHasBeenVisible(false); task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN); - task.hasBeenVisible = true; + task.setHasBeenVisible(true); task.onConfigurationChanged(task.getParent().getConfiguration()); verify(persister).saveTask(task, task.getDisplayContent()); @@ -889,7 +890,7 @@ public class TaskRecordTests extends ActivityTestsBase { spyOn(persister); final Task task = getTestTask(); - task.hasBeenVisible = false; + task.setHasBeenVisible(false); task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN); final DisplayContent oldDisplay = task.getDisplayContent(); @@ -899,7 +900,7 @@ public class TaskRecordTests extends ActivityTestsBase { persister.getLaunchParams(task, null, params); assertEquals(WINDOWING_MODE_UNDEFINED, params.mWindowingMode); - task.hasBeenVisible = true; + task.setHasBeenVisible(true); task.removeImmediately(); verify(persister).saveTask(task, oldDisplay); @@ -914,10 +915,10 @@ public class TaskRecordTests extends ActivityTestsBase { spyOn(persister); final Task task = getTestTask(); - task.hasBeenVisible = false; + task.setHasBeenVisible(false); task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN); - task.hasBeenVisible = true; + task.setHasBeenVisible(true); task.onConfigurationChanged(task.getParent().getConfiguration()); verify(persister, never()).saveTask(same(task), any()); @@ -929,16 +930,30 @@ public class TaskRecordTests extends ActivityTestsBase { spyOn(persister); final Task task = getTestTask(); - task.hasBeenVisible = false; + task.setHasBeenVisible(false); task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); task.getStack().setWindowingMode(WINDOWING_MODE_PINNED); - task.hasBeenVisible = true; + task.setHasBeenVisible(true); task.onConfigurationChanged(task.getParent().getConfiguration()); 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..1a38a42873b7 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -361,7 +361,8 @@ public final class TelephonyPermissions { TelephonyCommonStatsLog.write(TelephonyCommonStatsLog.DEVICE_IDENTIFIER_ACCESS_DENIED, callingPackage, message, /* isPreinstalled= */ false, false); } - Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message); + Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message + ":" + + subId); // if the target SDK is pre-Q then check if the calling package would have previously // had access to device identifiers. if (callingPackageInfo != null && ( @@ -442,16 +443,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/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index 558f4cd24471..39a754389254 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -19,9 +19,9 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.hardware.radio.V1_1.EutranBands; import android.hardware.radio.V1_1.GeranBands; import android.hardware.radio.V1_5.AccessNetwork; +import android.hardware.radio.V1_5.EutranBands; import android.hardware.radio.V1_5.UtranBands; import java.lang.annotation.Retention; @@ -212,7 +212,8 @@ public final class AccessNetworkConstants { /** * Frequency bands for EUTRAN. - * http://www.etsi.org/deliver/etsi_ts/136100_136199/136101/14.03.00_60/ts_136101v140p.pdf + * 3GPP TS 36.101, Version 16.4.0, Table 5.5: Operating bands + * https://www.etsi.org/deliver/etsi_ts/136100_136199/136101/15.09.00_60/ts_136101v150900p.pdf */ public static final class EutranBand { public static final int BAND_1 = EutranBands.BAND_1; @@ -259,10 +260,22 @@ public final class AccessNetworkConstants { public static final int BAND_46 = EutranBands.BAND_46; public static final int BAND_47 = EutranBands.BAND_47; public static final int BAND_48 = EutranBands.BAND_48; + public static final int BAND_49 = EutranBands.BAND_49; + public static final int BAND_50 = EutranBands.BAND_50; + public static final int BAND_51 = EutranBands.BAND_51; + public static final int BAND_52 = EutranBands.BAND_52; + public static final int BAND_53 = EutranBands.BAND_53; public static final int BAND_65 = EutranBands.BAND_65; public static final int BAND_66 = EutranBands.BAND_66; public static final int BAND_68 = EutranBands.BAND_68; public static final int BAND_70 = EutranBands.BAND_70; + public static final int BAND_71 = EutranBands.BAND_71; + public static final int BAND_72 = EutranBands.BAND_72; + public static final int BAND_73 = EutranBands.BAND_73; + public static final int BAND_74 = EutranBands.BAND_74; + public static final int BAND_85 = EutranBands.BAND_85; + public static final int BAND_87 = EutranBands.BAND_87; + public static final int BAND_88 = EutranBands.BAND_88; /** @hide */ private EutranBand() {}; @@ -305,9 +318,11 @@ public final class AccessNetworkConstants { /** * Frequency bands for NGRAN + * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810101/15.08.02_60/ts_13810101v150802p.pdf + * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810102/15.08.00_60/ts_13810102v150800p.pdf */ public static final class NgranBands { - /** FR1 bands */ + /** 3GPP TS 38.101-1, Version 16.2.0, Table 5.2-1: FR1 bands */ public static final int BAND_1 = android.hardware.radio.V1_5.NgranBands.BAND_1; public static final int BAND_2 = android.hardware.radio.V1_5.NgranBands.BAND_2; public static final int BAND_3 = android.hardware.radio.V1_5.NgranBands.BAND_3; @@ -346,9 +361,15 @@ public final class AccessNetworkConstants { public static final int BAND_83 = android.hardware.radio.V1_5.NgranBands.BAND_83; public static final int BAND_84 = android.hardware.radio.V1_5.NgranBands.BAND_84; public static final int BAND_86 = android.hardware.radio.V1_5.NgranBands.BAND_86; + public static final int BAND_89 = android.hardware.radio.V1_5.NgranBands.BAND_89; public static final int BAND_90 = android.hardware.radio.V1_5.NgranBands.BAND_90; + public static final int BAND_91 = android.hardware.radio.V1_5.NgranBands.BAND_91; + public static final int BAND_92 = android.hardware.radio.V1_5.NgranBands.BAND_92; + public static final int BAND_93 = android.hardware.radio.V1_5.NgranBands.BAND_93; + public static final int BAND_94 = android.hardware.radio.V1_5.NgranBands.BAND_94; + public static final int BAND_95 = android.hardware.radio.V1_5.NgranBands.BAND_95; - /** FR2 bands */ + /** 3GPP TS 38.101-2, Version 16.2.0, Table 5.2-1: FR2 bands */ public static final int BAND_257 = android.hardware.radio.V1_5.NgranBands.BAND_257; public static final int BAND_258 = android.hardware.radio.V1_5.NgranBands.BAND_258; public static final int BAND_260 = android.hardware.radio.V1_5.NgranBands.BAND_260; @@ -398,7 +419,13 @@ public final class AccessNetworkConstants { BAND_83, BAND_84, BAND_86, + BAND_89, BAND_90, + BAND_91, + BAND_92, + BAND_93, + BAND_94, + BAND_95, BAND_257, BAND_258, BAND_260, @@ -495,7 +522,13 @@ public final class AccessNetworkConstants { case BAND_83: case BAND_84: case BAND_86: + case BAND_89: case BAND_90: + case BAND_91: + case BAND_92: + case BAND_93: + case BAND_94: + case BAND_95: return FREQUENCY_RANGE_GROUP_1; case BAND_257: case BAND_258: diff --git a/telephony/java/android/telephony/AccessNetworkUtils.java b/telephony/java/android/telephony/AccessNetworkUtils.java index 5d2c225f28ec..981ed450004a 100644 --- a/telephony/java/android/telephony/AccessNetworkUtils.java +++ b/telephony/java/android/telephony/AccessNetworkUtils.java @@ -34,12 +34,10 @@ public class AccessNetworkUtils { return DUPLEX_MODE_UNKNOWN; } - if (band >= EutranBand.BAND_68) { + if (band > EutranBand.BAND_88) { return DUPLEX_MODE_UNKNOWN; } else if (band >= EutranBand.BAND_65) { return DUPLEX_MODE_FDD; - } else if (band >= EutranBand.BAND_47) { - return DUPLEX_MODE_UNKNOWN; } else if (band >= EutranBand.BAND_33) { return DUPLEX_MODE_TDD; } else if (band >= EutranBand.BAND_1) { @@ -58,17 +56,53 @@ public class AccessNetworkUtils { * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists */ public static int getOperatingBandForEarfcn(int earfcn) { - if (earfcn > 67535) { + if (earfcn > 70645) { + return INVALID_BAND; + } else if (earfcn >= 70596) { + return EutranBand.BAND_88; + } else if (earfcn >= 70546) { + return EutranBand.BAND_87; + } else if (earfcn >= 70366) { + return EutranBand.BAND_85; + } else if (earfcn > 69465) { + return INVALID_BAND; + } else if (earfcn >= 69036) { + return EutranBand.BAND_74; + } else if (earfcn >= 68986) { + return EutranBand.BAND_73; + } else if (earfcn >= 68936) { + return EutranBand.BAND_72; + } else if (earfcn >= 68586) { + return EutranBand.BAND_71; + } else if (earfcn >= 68336) { + return EutranBand.BAND_70; + } else if (earfcn > 67835) { return INVALID_BAND; + } else if (earfcn >= 67536) { + return EutranBand.BAND_68; } else if (earfcn >= 67366) { return INVALID_BAND; // band 67 only for CarrierAgg } else if (earfcn >= 66436) { return EutranBand.BAND_66; } else if (earfcn >= 65536) { return EutranBand.BAND_65; - } else if (earfcn > 54339) { + } else if (earfcn > 60254) { return INVALID_BAND; - } else if (earfcn >= 46790 /* inferred from the end range of BAND_45 */) { + } else if (earfcn >= 60140) { + return EutranBand.BAND_53; + } else if (earfcn >= 59140) { + return EutranBand.BAND_52; + } else if (earfcn >= 59090) { + return EutranBand.BAND_51; + } else if (earfcn >= 58240) { + return EutranBand.BAND_50; + } else if (earfcn >= 56740) { + return EutranBand.BAND_49; + } else if (earfcn >= 55240) { + return EutranBand.BAND_48; + } else if (earfcn >= 54540) { + return EutranBand.BAND_47; + } else if (earfcn >= 46790) { return EutranBand.BAND_46; } else if (earfcn >= 46590) { return EutranBand.BAND_45; 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/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index c1447465f53f..991375c5bc73 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -2520,7 +2520,6 @@ public final class SmsManager { * @param sentIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is successfully sent, or failed * @throws IllegalArgumentException if contentUri is empty - * @deprecated use {@link MmsManager#sendMultimediaMessage} instead. */ public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent) { @@ -2555,7 +2554,6 @@ public final class SmsManager { * @param downloadedIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is downloaded, or the download is failed * @throws IllegalArgumentException if locationUrl or contentUri is empty - * @deprecated use {@link MmsManager#downloadMultimediaMessage} instead. */ public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent) { 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/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 5d3cc44ac68c..2facd5a16bc8 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -944,6 +944,18 @@ public class SubscriptionManager { if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN"); } + /** + * Callback invoked when {@link SubscriptionManager#addOnSubscriptionsChangedListener( + * Executor, OnSubscriptionsChangedListener)} or + * {@link SubscriptionManager#addOnSubscriptionsChangedListener( + * OnSubscriptionsChangedListener)} fails to complete due to the + * {@link Context#TELEPHONY_REGISTRY_SERVICE} being unavailable. + * @hide + */ + public void onAddListenerFailed() { + Rlog.w(LOG_TAG, "onAddListenerFailed not overridden"); + } + private void log(String s) { Rlog.d(LOG_TAG, s); } @@ -1012,6 +1024,12 @@ public class SubscriptionManager { if (telephonyRegistryManager != null) { telephonyRegistryManager.addOnSubscriptionsChangedListener(listener, executor); + } else { + // If the telephony registry isn't available, we will inform the caller on their + // listener that it failed so they can try to re-register. + loge("addOnSubscriptionsChangedListener: pkgname=" + pkgName + " failed to be added " + + " due to TELEPHONY_REGISTRY_SERVICE being unavailable."); + executor.execute(() -> listener.onAddListenerFailed()); } } diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index 3e1d72c7eeb8..2b1d9e58c4d5 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -74,7 +74,6 @@ public class DctConstants { public static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER; public static final int EVENT_DATA_SETUP_COMPLETE = BASE + 0; public static final int EVENT_RADIO_AVAILABLE = BASE + 1; - public static final int EVENT_RECORDS_LOADED = BASE + 2; public static final int EVENT_TRY_SETUP_DATA = BASE + 3; public static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = BASE + 6; public static final int EVENT_VOICE_CALL_STARTED = BASE + 7; @@ -94,7 +93,6 @@ public class DctConstants { public static final int EVENT_CLEAN_UP_CONNECTION = BASE + 24; public static final int EVENT_RESTART_RADIO = BASE + 26; public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 29; - public static final int EVENT_ICC_CHANGED = BASE + 33; public static final int EVENT_DATA_SETUP_COMPLETE_ERROR = BASE + 35; public static final int CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA = BASE + 36; public static final int CMD_ENABLE_MOBILE_PROVISIONING = BASE + 37; @@ -114,7 +112,7 @@ public class DctConstants { public static final int EVENT_SERVICE_STATE_CHANGED = BASE + 52; public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53; public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54; - public static final int EVENT_UPDATE_CARRIER_CONFIGS = BASE + 55; + public static final int EVENT_CARRIER_CONFIG_CHANGED = BASE + 55; /***** Constants *****/ diff --git a/test-runner/src/android/test/TouchUtils.java b/test-runner/src/android/test/TouchUtils.java index bb4e00ba1ef9..f2f0be73c010 100644 --- a/test-runner/src/android/test/TouchUtils.java +++ b/test-runner/src/android/test/TouchUtils.java @@ -223,7 +223,7 @@ public class TouchUtils { public static void dragViewToBottom(InstrumentationTestCase test, Activity activity, View v, int stepCount) { int screenHeight = - activity.getWindowManager().getCurrentWindowMetrics().getSize().getHeight(); + activity.getWindowManager().getCurrentWindowMetrics().getBounds().height(); int[] xy = new int[2]; v.getLocationOnScreen(xy); diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 2f9a1c8ade0d..0ad30391d1a8 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -46,6 +46,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -72,6 +73,7 @@ public class AppLaunch extends InstrumentationTestCase { private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts"; private static final String KEY_APPS = "apps"; private static final String KEY_IORAP_TRIAL_LAUNCH = "iorap_trial_launch"; + private static final String KEY_IORAP_COMPILER_FILTERS = "iorap_compiler_filters"; private static final String KEY_TRIAL_LAUNCH = "trial_launch"; private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations"; private static final String KEY_LAUNCH_ORDER = "launch_order"; @@ -153,6 +155,7 @@ public class AppLaunch extends InstrumentationTestCase { private BufferedWriter mBufferedWriter = null; private boolean mSimplePerfAppOnly = false; private String[] mCompilerFilters = null; + private List<String> mIorapCompilerFilters = null; private String mLastAppName = ""; private boolean mCycleCleanUp = false; private boolean mTraceAll = false; @@ -509,16 +512,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, @@ -566,6 +621,24 @@ public class AppLaunch extends InstrumentationTestCase { return reason; } + private boolean shouldIncludeIorap(String compilerFilter) { + if (!mIorapTrialLaunch) { + return false; + } + + // No iorap compiler filters specified: treat all compiler filters as ok. + if (mIorapCompilerFilters == null) { + return true; + } + + // iorap compiler filters specified: the compilerFilter must be in the whitelist. + if (mIorapCompilerFilters.indexOf(compilerFilter) != -1) { + return true; + } + + return false; + } + /** * If launch order is "cyclic" then apps will be launched one after the * other for each iteration count. @@ -580,7 +653,7 @@ public class AppLaunch extends InstrumentationTestCase { mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH, /*iorapEnabled*/false)); } } - if (mIorapTrialLaunch) { + if (shouldIncludeIorap(compilerFilter)) { for (int launchCount = 0; launchCount < IORAP_TRIAL_LAUNCH_ITERATIONS; ++launchCount) { for (String app : mNameToResultKey.keySet()) { String reason = makeReasonForIorapTrialLaunch(launchCount); @@ -594,14 +667,16 @@ public class AppLaunch extends InstrumentationTestCase { for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) { for (String app : mNameToResultKey.keySet()) { mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, - String.format(LAUNCH_ITERATION, launchCount), mIorapTrialLaunch)); + String.format(LAUNCH_ITERATION, launchCount), + shouldIncludeIorap(compilerFilter))); } } if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) { for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) { for (String app : mNameToResultKey.keySet()) { mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, - String.format(TRACE_ITERATION, traceCount), mIorapTrialLaunch)); + String.format(TRACE_ITERATION, traceCount), + shouldIncludeIorap(compilerFilter))); } } } @@ -612,7 +687,7 @@ public class AppLaunch extends InstrumentationTestCase { if (mTrialLaunch) { mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH, /*iorapEnabled*/false)); } - if (mIorapTrialLaunch) { + if (shouldIncludeIorap(compilerFilter)) { for (int launchCount = 0; launchCount < IORAP_TRIAL_LAUNCH_ITERATIONS; ++launchCount) { String reason = makeReasonForIorapTrialLaunch(launchCount); mLaunchOrderList.add( @@ -623,12 +698,14 @@ public class AppLaunch extends InstrumentationTestCase { } for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) { mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, - String.format(LAUNCH_ITERATION, launchCount), mIorapTrialLaunch)); + String.format(LAUNCH_ITERATION, launchCount), + shouldIncludeIorap(compilerFilter))); } if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) { for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) { mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, - String.format(TRACE_ITERATION, traceCount), mIorapTrialLaunch)); + String.format(TRACE_ITERATION, traceCount), + shouldIncludeIorap(compilerFilter))); } } } @@ -715,7 +792,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; @@ -770,6 +847,13 @@ public class AppLaunch extends InstrumentationTestCase { mCompilerFilters = new String[1]; } + String iorapCompilerFilterList = args.getString(KEY_IORAP_COMPILER_FILTERS); + if (iorapCompilerFilterList != null) { + // Passing in iorap compiler filters implies an iorap trial launch. + mIorapTrialLaunch = true; + mIorapCompilerFilters = Arrays.asList(iorapCompilerFilterList.split("\\|")); + } + // Pre-populate the results map to avoid null checks. for (String app : mNameToLaunchTime.keySet()) { HashMap<String, List<AppLaunchResult>> map = new HashMap<>(); 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/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java index e255ce234c65..31532a226800 100644 --- a/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java +++ b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java @@ -27,7 +27,6 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; -import android.util.Size; import android.view.Gravity; import android.view.IWindowManager; import android.view.MotionEvent; @@ -90,8 +89,8 @@ public class MirrorSurfaceActivity extends Activity implements View.OnClickListe .getSystemService(WindowManager.class); mIWm = WindowManagerGlobal.getWindowManagerService(); - Size windowSize = mWm.getCurrentWindowMetrics().getSize(); - mWindowBounds.set(0, 0, windowSize.getWidth(), windowSize.getHeight()); + Rect windowBounds = mWm.getCurrentWindowMetrics().getBounds(); + mWindowBounds.set(0, 0, windowBounds.width(), windowBounds.height()); mScaleText = findViewById(R.id.scale); mDisplayFrameText = findViewById(R.id.displayFrame); diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 2957192ecf0f..d011dbbbe5db 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -1123,6 +1123,28 @@ public class PackageWatchdogTest { assertThat(testController.getSyncRequests()).isEqualTo(expectedSyncRequests); } + /** + * Ensure that the failure history of a package is preserved when making duplicate calls to + * observe the package. + */ + @Test + public void testFailureHistoryIsPreserved() { + PackageWatchdog watchdog = createWatchdog(); + TestObserver observer = new TestObserver(OBSERVER_NAME_1); + watchdog.startObservingHealth(observer, List.of(APP_A), SHORT_DURATION); + for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) { + watchdog.onPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); + } + mTestLooper.dispatchAll(); + assertThat(observer.mMitigatedPackages).isEmpty(); + watchdog.startObservingHealth(observer, List.of(APP_A), LONG_DURATION); + watchdog.onPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); + mTestLooper.dispatchAll(); + assertThat(observer.mMitigatedPackages).isEqualTo(List.of(APP_A)); + } + private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() 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 cb75c8ba5873..5afd39ea9de1 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java @@ -17,9 +17,9 @@ package com.android.test.taskembed; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.window.WindowOrganizer.TaskOrganizer; import android.app.ActivityManager; -import android.app.ActivityTaskManager; import android.app.Activity; import android.app.ActivityOptions; import android.content.Context; @@ -27,6 +27,8 @@ import android.content.Intent; import android.graphics.Color; import android.graphics.Rect; import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; import android.view.Gravity; import android.view.MotionEvent; import android.view.SurfaceControl; @@ -34,8 +36,10 @@ import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; import android.window.ITaskOrganizer; +import android.window.IWindowContainerTransactionCallback; import android.window.WindowContainerTransaction; import android.widget.LinearLayout; +import android.window.WindowOrganizer; public class TaskOrganizerMultiWindowTest extends Activity { class SplitLayout extends LinearLayout implements View.OnTouchListener { @@ -112,8 +116,7 @@ public class TaskOrganizerMultiWindowTest extends Activity { final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setBounds(mWc, new Rect(0, 0, width, height)); try { - ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct, - mOrganizer); + WindowOrganizer.applySyncTransaction(wct, mOrganizer.mTransactionCallback); } catch (Exception e) { // Oh well } @@ -127,8 +130,27 @@ public class TaskOrganizerMultiWindowTest extends Activity { class Organizer extends ITaskOrganizer.Stub { private int receivedTransactions = 0; SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction(); + IWindowContainerTransactionCallback mTransactionCallback = + new IWindowContainerTransactionCallback() { + @Override + public void transactionReady(int id, SurfaceControl.Transaction t) + throws RemoteException { + mergedTransaction.merge(t); + receivedTransactions++; + if (receivedTransactions == 2) { + mergedTransaction.apply(); + receivedTransactions = 0; + } + } + + @Override + public IBinder asBinder() { + return null; + } + }; + @Override - public void taskAppeared(ActivityManager.RunningTaskInfo ti) { + public void onTaskAppeared(ActivityManager.RunningTaskInfo ti) { if (!gotFirstTask) { mTaskView1.reparentTask(ti.token); gotFirstTask = true; @@ -136,15 +158,7 @@ public class TaskOrganizerMultiWindowTest extends Activity { mTaskView2.reparentTask(ti.token); } } - public void taskVanished(ActivityManager.RunningTaskInfo ti) { - } - public void transactionReady(int id, SurfaceControl.Transaction t) { - mergedTransaction.merge(t); - receivedTransactions++; - if (receivedTransactions == 2) { - mergedTransaction.apply(); - receivedTransactions = 0; - } + public void onTaskVanished(ActivityManager.RunningTaskInfo ti) { } @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { @@ -158,9 +172,7 @@ public class TaskOrganizerMultiWindowTest extends Activity { super.onCreate(savedInstanceState); try { - ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(mOrganizer, - WINDOWING_MODE_MULTI_WINDOW); - + TaskOrganizer.registerOrganizer(mOrganizer, WINDOWING_MODE_MULTI_WINDOW); } catch (Exception e) { } diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java index 177841d97ff5..520bc255499b 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java @@ -17,19 +17,19 @@ package com.android.test.taskembed; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.window.WindowOrganizer.TaskOrganizer; import android.app.ActivityManager; -import android.app.ActivityTaskManager; import android.app.Service; import android.content.Intent; import android.graphics.Rect; import android.os.IBinder; -import android.view.SurfaceControl; import android.view.ViewGroup; import android.window.ITaskOrganizer; import android.window.WindowContainerTransaction; import android.view.WindowManager; import android.widget.FrameLayout; +import android.window.WindowOrganizer; public class TaskOrganizerPipTest extends Service { static final int PIP_WIDTH = 640; @@ -38,19 +38,17 @@ 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(); wct.scheduleFinishEnterPip(ti.token, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT)); try { - ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct, null); + WindowOrganizer.applyTransaction(wct); } catch (Exception e) { } } - public void taskVanished(ActivityManager.RunningTaskInfo ti) { - } - public void transactionReady(int id, SurfaceControl.Transaction t) { + public void onTaskVanished(ActivityManager.RunningTaskInfo ti) { } public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { } @@ -68,9 +66,7 @@ public class TaskOrganizerPipTest extends Service { super.onCreate(); try { - ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(mOrganizer, - WINDOWING_MODE_PINNED); - + TaskOrganizer.registerOrganizer(mOrganizer, WINDOWING_MODE_PINNED); } catch (Exception e) { } 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/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java index 01bd47b9c608..a826646f69f3 100644 --- a/tests/utils/testutils/java/android/os/test/TestLooper.java +++ b/tests/utils/testutils/java/android/os/test/TestLooper.java @@ -48,6 +48,8 @@ public class TestLooper { private static final Method MESSAGE_MARK_IN_USE_METHOD; private static final String TAG = "TestLooper"; + private final Clock mClock; + private AutoDispatchThread mAutoDispatchThread; static { @@ -69,8 +71,25 @@ public class TestLooper { } } - + /** + * Creates a TestLooper and installs it as the looper for the current thread. + */ public TestLooper() { + this(SystemClock::uptimeMillis); + } + + /** + * Creates a TestLooper with a custom clock and installs it as the looper for the current + * thread. + * + * Messages are dispatched when their {@link Message#when} is before or at {@link + * Clock#uptimeMillis()}. + * Use a custom clock with care. When using an offsettable clock like {@link + * com.android.server.testutils.OffsettableClock} be sure not to double offset messages by + * offsetting the clock and calling {@link #moveTimeForward(long)}. Instead, offset the clock + * and call {@link #dispatchAll()}. + */ + public TestLooper(Clock clock) { try { mLooper = LOOPER_CONSTRUCTOR.newInstance(false); @@ -80,6 +99,8 @@ public class TestLooper { } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { throw new RuntimeException("Reflection error constructing or accessing looper", e); } + + mClock = clock; } public Looper getLooper() { @@ -116,9 +137,13 @@ public class TestLooper { } } + private long currentTime() { + return mClock.uptimeMillis(); + } + private Message messageQueueNext() { try { - long now = SystemClock.uptimeMillis(); + long now = currentTime(); Message prevMsg = null; Message msg = getMessageLinkedList(); @@ -157,7 +182,7 @@ public class TestLooper { public synchronized boolean isIdle() { Message messageList = getMessageLinkedList(); - return messageList != null && SystemClock.uptimeMillis() >= messageList.getWhen(); + return messageList != null && currentTime() >= messageList.getWhen(); } /** @@ -187,6 +212,7 @@ public class TestLooper { /** * Dispatch all messages currently in the queue * Will not fail if there are no messages pending + * * @return the number of messages dispatched */ public synchronized int dispatchAll() { @@ -198,6 +224,10 @@ public class TestLooper { return count; } + public interface Clock { + long uptimeMillis(); + } + /** * Thread used to dispatch messages when the main thread is blocked waiting for a response. */ 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..614786193a18 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -81,7 +81,6 @@ java_library { libs: [ "framework-annotations-lib", "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage - "unsupportedappusage-annotation", // for dalvik.annotation.compat.UnsupportedAppUsage "framework-telephony-stubs", ], srcs: [ @@ -170,24 +169,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/java/android/net/wifi/WifiMigration.java b/wifi/java/android/net/wifi/WifiMigration.java index 87afdc59c2b9..5792d27a94f9 100755 --- a/wifi/java/android/net/wifi/WifiMigration.java +++ b/wifi/java/android/net/wifi/WifiMigration.java @@ -522,7 +522,12 @@ public final class WifiMigration { */ @NonNull public static SettingsMigrationData loadFromSettings(@NonNull Context context) { - return new SettingsMigrationData.Builder() + if (Settings.Global.getInt( + context.getContentResolver(), Settings.Global.WIFI_MIGRATION_COMPLETED, 0) == 1) { + // migration already complete, ignore. + return null; + } + SettingsMigrationData data = new SettingsMigrationData.Builder() .setScanAlwaysAvailable( Settings.Global.getInt(context.getContentResolver(), Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1) @@ -545,5 +550,9 @@ public final class WifiMigration { Settings.Global.getInt(context.getContentResolver(), Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 0) == 1) .build(); + Settings.Global.putInt( + context.getContentResolver(), Settings.Global.WIFI_MIGRATION_COMPLETED, 1); + return data; + } } 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> |