Merge "Fix display of BiometricPrompt wipe warning dialogs" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 3f06ffd..b0e0b35 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1133,16 +1133,6 @@
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 aea0c95..3b30714 100644
--- a/Android.mk
+++ b/Android.mk
@@ -39,11 +39,6 @@
$(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 12f211d..ccd87335 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -115,6 +115,11 @@
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 @@
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 @@
baseline_file: "api/test-lint-baseline.txt",
},
},
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/test/api",
+ dest: "android.txt",
+ },
}
/////////////////////////////////////////////////////////////////////
@@ -214,6 +229,11 @@
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 cd34f98..e8f6e6b 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -84,6 +84,24 @@
},
}
+java_defaults {
+ name: "framework-module-stubs-lib-defaults-publicapi",
+ installable: false,
+ sdk_version: "module_current",
+}
+
+java_defaults {
+ name: "framework-module-stubs-lib-defaults-systemapi",
+ installable: false,
+ sdk_version: "module_current",
+}
+
+java_defaults {
+ name: "framework-module-stubs-lib-defaults-module_libs_api",
+ installable: false,
+ sdk_version: "module_current",
+}
+
// The defaults for module_libs comes in two parts - defaults for API checks
// and defaults for stub generation. This is because we want the API txt
// files to *only* include the module_libs_api, but the stubs to include
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 e5a685f..c8ca44b 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 @@
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 @@
}
}
+ void removeCommitter(@NonNull Committer committer) {
+ synchronized (mMetadataLock) {
+ mCommitters.remove(committer);
+ }
+ }
+
void removeInvalidCommitters(SparseArray<String> packages) {
synchronized (mMetadataLock) {
mCommitters.removeIf(committer ->
@@ -154,7 +160,7 @@
}
}
- 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 65ccb99..6c48511 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 @@
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 @@
final Committer newCommitter = new Committer(session.getOwnerPackageName(),
session.getOwnerUid(), session.getBlobAccessMode());
final Committer existingCommitter = blob.getExistingCommitter(newCommitter);
- blob.addCommitter(newCommitter);
+ blob.addOrReplaceCommitter(newCommitter);
try {
writeBlobsInfoLocked();
session.sendCommitCallbackResult(COMMIT_RESULT_SUCCESS);
} catch (Exception e) {
- blob.addCommitter(existingCommitter);
+ if (existingCommitter == null) {
+ blob.removeCommitter(newCommitter);
+ } else {
+ blob.addOrReplaceCommitter(existingCommitter);
+ }
session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
}
getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
diff --git a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
index 8fbfb1d..d99830dc4 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 bc7a7d3..b76c582 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 e2e1180..484fec3 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 ba7572a..c5dc51c 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 821dd9e..99e82e7 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -132,19 +132,19 @@
java_library {
name: "framework-media-stubs-publicapi",
srcs: [":framework-media-stubs-srcs-publicapi"],
- sdk_version: "current",
+ defaults: ["framework-module-stubs-lib-defaults-publicapi"],
}
java_library {
name: "framework-media-stubs-systemapi",
srcs: [":framework-media-stubs-srcs-systemapi"],
- sdk_version: "system_current",
+ defaults: ["framework-module-stubs-lib-defaults-systemapi"],
}
java_library {
name: "framework-media-stubs-module_libs_api",
srcs: [":framework-media-stubs-srcs-module_libs_api"],
- sdk_version: "system_current",
+ defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
}
java_library {
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 02c55b7..5f86ed6 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.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.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -688,12 +690,83 @@
* Returns an immutable list with the names of the parsers that are suitable for container
* formats with the given {@link MediaFormat}.
*
- * <p>TODO: List which properties are taken into account. E.g. MimeType.
+ * <p>A parser supports a {@link MediaFormat} if the mime type associated with {@link
+ * MediaFormat#KEY_MIME} corresponds to the supported container format.
+ *
+ * @param mediaFormat The {@link MediaFormat} to check support for.
+ * @return The parser names that support the given {@code mediaFormat}, or the list of all
+ * parsers available if no container specific format information is provided.
*/
@NonNull
@ParserName
public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) {
- throw new UnsupportedOperationException();
+ String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
+ mimeType = mimeType == null ? null : Util.toLowerInvariant(mimeType.trim());
+ if (TextUtils.isEmpty(mimeType)) {
+ // No MIME type provided. Return all.
+ return Collections.unmodifiableList(
+ new ArrayList<>(EXTRACTOR_FACTORIES_BY_NAME.keySet()));
+ }
+ ArrayList<String> result = new ArrayList<>();
+ switch (mimeType) {
+ case "video/x-matroska":
+ case "audio/x-matroska":
+ case "video/x-webm":
+ case "audio/x-webm":
+ result.add(PARSER_NAME_MATROSKA);
+ break;
+ case "video/mp4":
+ case "audio/mp4":
+ case "application/mp4":
+ result.add(PARSER_NAME_MP4);
+ result.add(PARSER_NAME_FMP4);
+ break;
+ case "audio/mpeg":
+ result.add(PARSER_NAME_MP3);
+ break;
+ case "audio/aac":
+ result.add(PARSER_NAME_ADTS);
+ break;
+ case "audio/ac3":
+ result.add(PARSER_NAME_AC3);
+ break;
+ case "video/mp2t":
+ case "audio/mp2t":
+ result.add(PARSER_NAME_TS);
+ break;
+ case "video/x-flv":
+ result.add(PARSER_NAME_FLV);
+ break;
+ case "video/ogg":
+ case "audio/ogg":
+ case "application/ogg":
+ result.add(PARSER_NAME_OGG);
+ break;
+ case "video/mp2p":
+ case "video/mp1s":
+ result.add(PARSER_NAME_PS);
+ break;
+ case "audio/vnd.wave":
+ case "audio/wav":
+ case "audio/wave":
+ case "audio/x-wav":
+ result.add(PARSER_NAME_WAV);
+ break;
+ case "audio/amr":
+ result.add(PARSER_NAME_AMR);
+ break;
+ case "audio/ac4":
+ result.add(PARSER_NAME_AC4);
+ break;
+ case "audio/flac":
+ case "audio/x-flac":
+ result.add(PARSER_NAME_FLAC);
+ break;
+ default:
+ // No parsers support the given mime type. Do nothing.
+ break;
+ }
+ return Collections.unmodifiableList(result);
}
// Private fields.
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index 6d96200..3fefeb5 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -84,20 +84,17 @@
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 86f4ab7..707113b 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -86,7 +86,7 @@
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 {
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 {
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 8185bb0..804eb03 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -46,19 +46,11 @@
"//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 @@
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/api/current.txt b/api/current.txt
index 680b224..ff74ce8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -278,7 +278,7 @@
field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb
field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8
field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9
- field public static final int actor = 16844313; // 0x1010619
+ field public static final int actor = 16844312; // 0x1010618
field public static final int addPrintersActivity = 16843750; // 0x10103e6
field public static final int addStatesFromChildren = 16842992; // 0x10100f0
field public static final int adjustViewBounds = 16843038; // 0x101011e
@@ -289,7 +289,6 @@
field public static final int alignmentMode = 16843642; // 0x101037a
field public static final int allContactsName = 16843468; // 0x10102cc
field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601
- field public static final int allowAutoRevokePermissionsExemption = 16844309; // 0x1010615
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowEmbedded = 16843765; // 0x10103f5
@@ -329,6 +328,7 @@
field public static final int autoLink = 16842928; // 0x10100b0
field public static final int autoMirrored = 16843754; // 0x10103ea
field public static final int autoRemoveFromRecents = 16843847; // 0x1010447
+ field public static final int autoRevokePermissions = 16844308; // 0x1010614
field public static final int autoSizeMaxTextSize = 16844102; // 0x1010546
field public static final int autoSizeMinTextSize = 16844088; // 0x1010538
field public static final int autoSizePresetSizes = 16844087; // 0x1010537
@@ -710,7 +710,7 @@
field public static final int gravity = 16842927; // 0x10100af
field public static final int gridViewStyle = 16842865; // 0x1010071
field public static final int groupIndicator = 16843019; // 0x101010b
- field public static final int gwpAsanMode = 16844312; // 0x1010618
+ field public static final int gwpAsanMode = 16844311; // 0x1010617
field public static final int hand_hour = 16843011; // 0x1010103
field public static final int hand_minute = 16843012; // 0x1010104
field public static final int handle = 16843354; // 0x101025a
@@ -955,7 +955,7 @@
field public static final int mediaRouteButtonStyle = 16843693; // 0x10103ad
field public static final int mediaRouteTypes = 16843694; // 0x10103ae
field public static final int menuCategory = 16843230; // 0x10101de
- field public static final int mimeGroup = 16844311; // 0x1010617
+ field public static final int mimeGroup = 16844310; // 0x1010616
field public static final int mimeType = 16842790; // 0x1010026
field public static final int min = 16844089; // 0x1010539
field public static final int minAspectRatio = 16844187; // 0x101059b
@@ -1084,7 +1084,7 @@
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
field public static final int preferenceStyle = 16842894; // 0x101008e
field public static final int presentationTheme = 16843712; // 0x10103c0
- field public static final int preserveLegacyExternalStorage = 16844310; // 0x1010616
+ field public static final int preserveLegacyExternalStorage = 16844309; // 0x1010615
field public static final int previewImage = 16843482; // 0x10102da
field public static final int primaryContentAlpha = 16844114; // 0x1010552
field public static final int priority = 16842780; // 0x101001c
@@ -1141,7 +1141,6 @@
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
@@ -25341,11 +25340,12 @@
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);
@@ -45923,13 +45923,9 @@
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public void onConnectionServiceFocusGained();
method public void onConnectionServiceFocusLost();
- method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
- method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
- method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
diff --git a/api/system-current.txt b/api/system-current.txt
index a1fd0db..7e25382 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6061,7 +6061,7 @@
method public void release();
}
- public class InvalidPacketException extends java.lang.Exception {
+ public final class InvalidPacketException extends java.lang.Exception {
ctor public InvalidPacketException(int);
method public int getError();
field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
@@ -6255,7 +6255,7 @@
field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
}
- public static class NetworkCapabilities.Builder {
+ public static final class NetworkCapabilities.Builder {
ctor public NetworkCapabilities.Builder();
ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 0bd8a19..ba6feed 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -535,6 +535,7 @@
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
@@ -1833,7 +1834,7 @@
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 59544a9..ca1d598 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.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 @@
"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 @@
+ " 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 @@
+ " 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
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 @@
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 void parseBindValue(ContentValues values) {
+ 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 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 @@
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 @@
}
}
- 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 @@
}
}
- 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/statsd/Android.bp b/cmds/statsd/Android.bp
index 4529dff..45f21ae 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -97,6 +97,7 @@
"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 @@
// 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 2631009..2d315d9 100644
--- a/cmds/statsd/benchmark/duration_metric_benchmark.cpp
+++ b/cmds/statsd/benchmark/duration_metric_benchmark.cpp
@@ -137,77 +137,74 @@
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(bucketStartTimeNs + 11,
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(
+ CreateScreenStateChangedEvent(bucketStartTimeNs + 40, android::view::DISPLAY_STATE_ON));
- 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(bucketStartTimeNs + 102,
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450,
+ android::view::DISPLAY_STATE_ON));
- 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(bucketStartTimeNs + 650,
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100,
+ android::view::DISPLAY_STATE_ON));
- 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(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 640,
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 650,
+ android::view::DISPLAY_STATE_ON));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 2));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
+ 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(
- {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
+ events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids1,
+ attributionTags1, "job2"));
+ events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids1,
+ attributionTags1, "job2"));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850));
+ 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(
- {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900));
+ events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 600,
+ attributionUids2, attributionTags1, "job1"));
+ events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900,
+ attributionUids2, attributionTags1, "job1"));
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 10));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 50));
+ 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(attributions1, "ReadEmail",
- bucketStartTimeNs + 200));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 300));
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200, attributionUids3,
+ attributionTags3, "ReadEmail"));
+ events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids3,
+ attributionTags3, "ReadEmail"));
- events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
- bucketStartTimeNs + 400));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids3,
+ attributionTags3, "ReadDoc"));
+ events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids3,
+ attributionTags3, "ReadDoc"));
- events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + 401));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 700));
-
+ 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 @@
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(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(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 501));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + bucketSizeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 501,
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100,
+ android::view::DISPLAY_STATE_ON));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
+ 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"));
- 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));
+ 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"));
- events.push_back(
- CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
- bucketStartTimeNs + bucketSizeNs - 2));
- events.push_back(
- CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
- bucketStartTimeNs + bucketSizeNs + 900));
+ 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"));
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 50));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 110));
+ 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"));
- 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));
+ 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"));
- 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));
+ 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 cfe477d..28bf21a 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 @@
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 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 2a4403e..c7d01cc 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 @@
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 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 8b68743..057e00b 100644
--- a/cmds/statsd/benchmark/log_event_benchmark.cpp
+++ b/cmds/statsd/benchmark/log_event_benchmark.cpp
@@ -39,7 +39,8 @@
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 cca6d52..4bce89f 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 @@
}
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> 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(
- 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> 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> CreateAcquireWakelockEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
- uint64_t timestampNs) {
- return CreateWakelockStateChangedEvent(
- attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs);
+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> 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> 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 9b28d60..6199fa9 100644
--- a/cmds/statsd/benchmark/metric_util.h
+++ b/cmds/statsd/benchmark/metric_util.h
@@ -94,55 +94,31 @@
// 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);
+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> CreateSyncEndEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& name,
- uint64_t timestampNs);
-
-// 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 @@
} // 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 40a24dc..afee79d 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -117,4 +117,6 @@
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 5f2f546..979f950 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -78,7 +78,8 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 {
@@ -8992,3 +9011,37 @@
// 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 6f54ea7..fca48f9 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -80,7 +80,7 @@
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 6d20822..7500ec9 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -230,6 +230,10 @@
// 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 40a313a..e5fe87a 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -830,10 +830,10 @@
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 @@
// 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 @@
(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 @@
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 @@
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 5ebb232..a8ccc62 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -128,6 +128,7 @@
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 0000000..e00fe336
--- /dev/null
+++ b/cmds/statsd/src/statsd_metadata.proto
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.os.statsd.metadata;
+
+message ConfigKey {
+ optional int64 config_id = 1;
+ optional int32 uid = 2;
+}
+
+message Field {
+ optional int32 tag = 1;
+ optional int32 field = 2;
+}
+
+message FieldValue {
+ optional Field field = 1;
+ oneof value {
+ int32 value_int = 2;
+ int64 value_long = 3;
+ float value_float = 4;
+ double value_double = 5;
+ string value_str = 6;
+ bytes value_storage = 7;
+ }
+}
+
+message MetricDimensionKey {
+ repeated FieldValue dimension_key_in_what = 1;
+ repeated FieldValue state_values_key = 2;
+}
+
+message AlertMetadata {
+ optional int64 alert_id = 1;
+ // The earliest time the alert can be fired again in wall clock time.
+ optional int32 last_refractory_ends_sec = 2;
+ optional MetricDimensionKey dimension_key = 3;
+}
+
+// All metadata for a config in statsd
+message StatsMetadata {
+ optional ConfigKey config_key = 1;
+ repeated AlertMetadata alert_metadata = 2;
+}
+
+message StatsMetadataList {
+ repeated StatsMetadata stats_metadata = 1;
+}
\ No newline at end of file
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index a5ff067..0bf24f1 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 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 @@
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 @@
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 @@
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 2de6377..6f4c8e4 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 @@
#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 71adc57..356e40b 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -40,6 +40,7 @@
#ifdef __ANDROID__
const ConfigKey kConfigKey(0, 12345);
+const long kAlertId = 3;
const long timeBaseSec = 1000;
@@ -85,7 +86,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, noReportMetricIds));
+ alertTrackerMap, metricsWithActivation,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -345,6 +353,7 @@
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 @@
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, noReportMetricIds));
+ alertTrackerMap, metricsWithActivation,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -374,6 +384,7 @@
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 @@
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, noReportMetricIds));
+ alertTrackerMap, metricsWithActivation,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, TestMissingPredicate) {
@@ -402,6 +414,7 @@
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 @@
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, noReportMetricIds));
+ alertTrackerMap, metricsWithActivation, noReportMetricIds));
}
TEST(MetricsManagerTest, TestCirclePredicateDependency) {
@@ -430,6 +443,7 @@
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 @@
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, noReportMetricIds));
+ alertTrackerMap, metricsWithActivation,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
@@ -459,6 +474,7 @@
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 @@
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 d6498f4..9e7b7c8 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -253,1416 +253,1420 @@
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 a49c18f..29005a2 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -39,35 +39,28 @@
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 0f39efd5..e58bbb7 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 @@
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 @@
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 @@
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 @@
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 @@
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 609324e..d372ffd 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 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 @@
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 ae6769e..c1d4693 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -1582,9 +1582,9 @@
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 @@
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 @@
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];
+ // 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, baseInfo1.hasBase);
- EXPECT_EQ(5, baseInfo1.base.long_value);
- EXPECT_EQ(false, interval1.hasValue);
- EXPECT_EQ(5, interval1.value.long_value);
+ 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 4c55683..5eef92e 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 @@
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 b1633c6..a0e0095 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 os {
namespace statsd {
+const int32_t timestampNs = 1000;
+
/**
* Mock StateListener class for testing.
* Stores primary key and state pairs.
@@ -56,95 +60,49 @@
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 @@
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 050dbf8..8c8836b 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -428,7 +428,7 @@
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 @@
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 @@
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 @@
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 ead041c..7c01755 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -187,6 +187,8 @@
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> 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> CreateUidProcessStateChangedEvent(
uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state);
+std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const BleScanStateChanged::State state,
+ const bool filtered, const bool firstMatch,
+ const bool opportunistic);
+
+std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid,
+ const string& packageName,
+ const bool usingAlertWindow,
+ const OverlayStateChanged::State state);
+
// Helper function to create an AttributionNodeInternal proto.
AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bd3fee2d..21b56d3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4974,10 +4974,8 @@
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;
@@ -5030,7 +5028,6 @@
synchronized (mResourcesManager) {
mActivities.remove(token);
}
- StrictMode.decrementExpectedActivityCount(activityClass);
return r;
}
@@ -5050,6 +5047,7 @@
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;
@@ -5074,14 +5072,14 @@
}
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;
}
@@ -5093,18 +5091,23 @@
// 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 {
@@ -5334,10 +5337,6 @@
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) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 26db8f3..d4749bd 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -8446,7 +8446,9 @@
/**
* 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 61be01f..0ecc003 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -499,9 +499,11 @@
/**
* 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 9ccfe8d..17fd4ef 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2414,8 +2414,7 @@
: 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 @@
// 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 b8221b4..7fc10ed 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -99,6 +99,7 @@
void unregisterUidObserver(in IUidObserver observer);
boolean isUidActive(int uid, String callingPackage);
int getUidProcessState(int uid, in String callingPackage);
+ boolean isUidActiveOrForeground(int uid, String callingPackage);
// =============== End of transactions used on native side as well ============================
// Special low-level communication with activity manager.
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 948546b..78d3581 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -48,8 +48,6 @@
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 @@
// 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 d650801..10f7835 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -801,6 +801,11 @@
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 811b9c0..0e97e3f 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -29,6 +29,7 @@
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 @@
}
/**
- * 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 5f75603..392c05a 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -695,26 +695,26 @@
}
/**
- * 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 @@
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 @@
}
// Now request an actual Resources object.
- return createResources(activityToken, key, classLoader);
+ return createResources(token, key, classLoader);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 6b40890..37e07de 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -141,6 +141,8 @@
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 @@
ACTIVITY_TYPE_HOME,
ACTIVITY_TYPE_RECENTS,
ACTIVITY_TYPE_ASSISTANT,
+ ACTIVITY_TYPE_DREAM,
})
public @interface ActivityType {}
@@ -746,9 +749,11 @@
* @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 @@
/** @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 @@
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 c4458b3..10309a9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4424,11 +4424,13 @@
* 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.
diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
index 954db04..aa94e81 100644
--- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java
+++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
@@ -43,6 +43,12 @@
* 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 @@
}
}
+ /**
+ * 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 e0674d7..fa62a02 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -379,6 +379,7 @@
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 @@
@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 @@
if (VDBG) {
log("getHiSyncId(" + device + ")");
}
+ verifyDeviceNotNull(device, "getConnectionPolicy");
final IBluetoothHearingAid service = getService();
try {
if (service == null) {
@@ -577,6 +580,13 @@
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 65eb642..31c3232 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -47,6 +47,7 @@
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.ICancellationSignal;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteCallback;
@@ -303,7 +304,23 @@
@Override
public void getTypeAsync(Uri uri, RemoteCallback callback) {
final Bundle result = new Bundle();
- result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
+ try {
+ result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
+ } catch (Exception e) {
+ Parcel parcel = Parcel.obtain();
+ try {
+ try {
+ parcel.writeException(e);
+ } catch (Exception ex) {
+ // getType threw an unparcelable exception. Wrap the message into
+ // a parcelable exception type
+ parcel.writeException(new IllegalStateException(e.getMessage()));
+ }
+ result.putByteArray(ContentResolver.REMOTE_CALLBACK_ERROR, parcel.marshall());
+ } finally {
+ parcel.recycle();
+ }
+ }
callback.sendResult(result);
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 7510ce7..59862ae 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -55,6 +55,7 @@
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 @@
/** @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 @@
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 @@
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 @@
@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 @@
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 95385ee..b1d6c83 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11215,6 +11215,17 @@
&& 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 @@
}
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 @@
}
} 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 7d5fca4..b6706011 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -752,6 +752,30 @@
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 6ba811e..7578ede 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -487,6 +487,34 @@
}
}
+ /**
+ * 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/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index 9b0dae2..e2850f1 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -40,4 +40,5 @@
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/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 39f2858..4c95532 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -192,9 +192,7 @@
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 f2ab60a..079a470 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.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 @@
private boolean hasFragileUserData;
private boolean cantSaveState;
private boolean allowNativeHeapPointerTagging;
- private boolean dontAutoRevokePermissions;
- private boolean allowDontAutoRevokePermissions;
+ private int autoRevokePermissions;
private boolean preserveLegacyExternalStorage;
protected int gwpAsanMode;
@@ -1089,8 +1087,7 @@
dest.writeBoolean(this.hasFragileUserData);
dest.writeBoolean(this.cantSaveState);
dest.writeBoolean(this.allowNativeHeapPointerTagging);
- dest.writeBoolean(this.dontAutoRevokePermissions);
- dest.writeBoolean(this.allowDontAutoRevokePermissions);
+ dest.writeInt(this.autoRevokePermissions);
dest.writeBoolean(this.preserveLegacyExternalStorage);
dest.writeArraySet(this.mimeGroups);
dest.writeInt(this.gwpAsanMode);
@@ -1249,8 +1246,7 @@
this.hasFragileUserData = in.readBoolean();
this.cantSaveState = in.readBoolean();
this.allowNativeHeapPointerTagging = in.readBoolean();
- this.dontAutoRevokePermissions = in.readBoolean();
- this.allowDontAutoRevokePermissions = in.readBoolean();
+ this.autoRevokePermissions = in.readInt();
this.preserveLegacyExternalStorage = in.readBoolean();
this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
this.gwpAsanMode = in.readInt();
@@ -2026,13 +2022,8 @@
}
@Override
- public boolean isDontAutoRevokePermmissions() {
- return dontAutoRevokePermissions;
- }
-
- @Override
- public boolean isAllowDontAutoRevokePermmissions() {
- return allowDontAutoRevokePermissions;
+ public int getAutoRevokePermissions() {
+ return autoRevokePermissions;
}
@Override
@@ -2506,14 +2497,8 @@
}
@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 e700c6a5..687bc23 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -771,11 +771,7 @@
/** @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 789904a..ec77128 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -1829,8 +1829,7 @@
.setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
.setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
.setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
- .setDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_requestAutoRevokePermissionsExemption, sa))
- .setAllowDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_allowAutoRevokePermissionsExemption, sa))
+ .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa))
// targetSdkVersion gated
.setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
.setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
index 8bcaf82..e7219ca 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
@@ -26,7 +26,9 @@
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/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
index c31cb4e..9b3e8c9 100644
--- a/core/java/android/inputmethodservice/InlineSuggestionSession.java
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -166,9 +166,14 @@
}
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 7c34ddc..6daded4 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1249,6 +1249,7 @@
WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
+ mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true);
// IME layout should always be inset by navigation bar, no matter its current visibility,
// unless automotive requests it, since automotive may hide the navigation bar.
diff --git a/core/java/android/net/InvalidPacketException.java b/core/java/android/net/InvalidPacketException.java
index b3b0f11..1873d77 100644
--- a/core/java/android/net/InvalidPacketException.java
+++ b/core/java/android/net/InvalidPacketException.java
@@ -27,7 +27,7 @@
* @hide
*/
@SystemApi
-public class InvalidPacketException extends Exception {
+public final class InvalidPacketException extends Exception {
private final int mError;
// Must match SocketKeepalive#ERROR_INVALID_IP_ADDRESS.
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index fcfcebd..af9414c 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -415,6 +415,20 @@
| (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 @@
}
/**
+ * 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 @@
*/
@SystemApi
@TestApi
- public static class Builder {
+ public static final class Builder {
private final NetworkCapabilities mCaps;
/**
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 785b51d..cdc0019 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -2224,15 +2224,27 @@
* 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 b7b3c4f..5d2c9d1 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -607,6 +607,9 @@
* 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 @@
@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 @@
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/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 5f3f14f..a4c99c0 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -333,6 +333,9 @@
* 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 @@
@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 @@
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 @@
* @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 @@
@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 @@
}
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 @@
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 71f931d..5cc73ca 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -72,16 +72,16 @@
* 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 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 @@
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 @@
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 bbc936d..99bdfd1 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -194,4 +194,5 @@
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 1454aac..aee32ed 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -162,7 +162,12 @@
/** {@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 @@
}
}
+ /**
+ * 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 60f10cd..bbcb9d9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6564,13 +6564,6 @@
* 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";
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index a9addba..b6a8ced 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -49,7 +49,8 @@
private final @NonNull InlinePresentationSpec mInlinePresentationSpec;
/**
- * Indicates whether the UI should be pinned, hence non-scrollable, in the host.
+ * Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
+ * host.
*/
private final boolean mPinned;
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 7d070b1..f8265d6 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -780,15 +780,16 @@
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 @@
}
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 @@
}
}
- 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 @@
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 b54e4d9..45d3465 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -35,6 +35,7 @@
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.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.Set;
/**
@@ -63,6 +65,8 @@
* 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 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 @@
}
private void safelyShutdownHotwordDetector() {
- try {
- synchronized (mLock) {
- if (mHotwordDetector != null) {
- mHotwordDetector.stopRecognition();
- mHotwordDetector.invalidate();
- mHotwordDetector = null;
- }
+ synchronized (mLock) {
+ if (mHotwordDetector == null) {
+ return;
}
- } catch (Exception ex) {
- // Ignore.
+
+ try {
+ mHotwordDetector.stopRecognition();
+ } 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 04be71f..346fe29 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.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 @@
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 @@
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 static class VerifiedSigner {
public final X509Certificate[][] certs;
- public final byte[] verityRootHash;
- public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) {
+ public final byte[] verityRootHash;
+ public final byte[] digest;
+
+ 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 2437af2..4ab541b 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.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 @@
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 8c240d9..d40efce 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -145,7 +145,7 @@
"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 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 c1cee48..ab8f80d3 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -184,27 +184,45 @@
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 4fe8515..2a4b65d 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -421,6 +421,10 @@
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 @@
}
/**
+ * Returns the best digest from the map of available digests.
+ * similarly to compareContentDigestAlgorithm.
+ *
+ * Keep in sync with pickBestDigestForV4 in apksigner's ApkSigningBlockUtils.
+ */
+ static byte[] pickBestDigestForV4(Map<Integer, byte[]> contentDigests) {
+ for (int algo : V4_CONTENT_DIGEST_ALGORITHMS) {
+ if (contentDigests.containsKey(algo)) {
+ return contentDigests.get(algo);
+ }
+ }
+ return null;
+ }
+
+ /**
* Returns new byte buffer whose content is a shared subsequence of this buffer's content
* between the specified start (inclusive) and end (exclusive) positions. As opposed to
* {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 43f80f1..e6bd843 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -17,6 +17,7 @@
package android.view;
import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.toInternalType;
import static android.view.InsetsState.toPublicType;
import static android.view.WindowInsets.Type.all;
import static android.view.WindowInsets.Type.ime;
@@ -26,8 +27,6 @@
import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -38,11 +37,9 @@
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.RemoteException;
-import android.renderscript.Sampler.Value;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
-import android.util.Property;
import android.util.SparseArray;
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetsType;
@@ -467,7 +464,7 @@
if (!localStateChanged && mLastDispachedState.equals(state)) {
return false;
}
- mState.set(state);
+ updateState(state);
mLastDispachedState.set(state, true /* copySources */);
applyLocalVisibilityOverride();
if (localStateChanged) {
@@ -479,6 +476,20 @@
return true;
}
+ private void updateState(InsetsState newState) {
+ mState.setDisplayFrame(newState.getDisplayFrame());
+ for (int i = newState.getSourcesCount() - 1; i >= 0; i--) {
+ InsetsSource source = newState.sourceAt(i);
+ getSourceConsumer(source.getType()).updateSource(source);
+ }
+ for (int i = mState.getSourcesCount() - 1; i >= 0; i--) {
+ InsetsSource source = mState.sourceAt(i);
+ if (newState.peekSource(source.getType()) == null) {
+ mState.removeSource(source.getType());
+ }
+ }
+ }
+
/**
* @see InsetsState#calculateInsets
*/
@@ -858,8 +869,15 @@
control.cancel();
}
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
- if (mRunningAnimations.get(i).runner == control) {
+ RunningAnimation runningAnimation = mRunningAnimations.get(i);
+ if (runningAnimation.runner == control) {
mRunningAnimations.remove(i);
+ ArraySet<Integer> types = toInternalType(control.getTypes());
+ for (int j = types.size() - 1; j >= 0; j--) {
+ if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) {
+ mViewRoot.notifyInsetsChanged();
+ }
+ }
break;
}
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 3325734..f36621c 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 @@
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 @@
// no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
}
+ void updateSource(InsetsSource newSource) {
+ InsetsSource source = mState.peekSource(mType);
+ if (source == null || mController.getAnimationType(mType) == ANIMATION_TYPE_NONE
+ || source.getFrame().equals(newSource.getFrame())) {
+ mState.addSource(newSource);
+ return;
+ }
+
+ // Frame is changing while animating. Keep note of the new frame but keep existing frame
+ // until animaition is finished.
+ newSource = new InsetsSource(newSource);
+ mPendingFrame = new Rect(newSource.getFrame());
+ mPendingVisibleFrame = newSource.getVisibleFrame() != null
+ ? new Rect(newSource.getVisibleFrame())
+ : null;
+ newSource.setFrame(source.getFrame());
+ newSource.setVisibleFrame(source.getVisibleFrame());
+ mState.addSource(newSource);
+ }
+
+ boolean notifyAnimationFinished() {
+ if (mPendingFrame != null) {
+ InsetsSource source = mState.getSource(mType);
+ source.setFrame(mPendingFrame);
+ source.setVisibleFrame(mPendingVisibleFrame);
+ mPendingFrame = null;
+ mPendingVisibleFrame = null;
+ return true;
+ }
+ return false;
+ }
+
/**
* Sets requested visibility from the client, regardless of whether we are able to control it at
* the moment.
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index cd22ad6..fe70ff7 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -192,6 +192,7 @@
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 59fc6e9..c89e0c9 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -176,6 +176,7 @@
boolean mUseAlpha = false;
float mSurfaceAlpha = 1f;
boolean mClipSurfaceToBounds;
+ int mBackgroundColor = Color.BLACK;
@UnsupportedAppUsage
boolean mHaveFrame = false;
@@ -828,6 +829,12 @@
}
}
+ 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 @@
}
updateBackgroundVisibility(mTmpTransaction);
+ updateBackgroundColor(mTmpTransaction);
if (mUseAlpha) {
mTmpTransaction.setAlpha(mSurfaceControl, alpha);
mSurfaceAlpha = alpha;
@@ -1399,10 +1407,8 @@
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 d69357b..4922917 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3475,7 +3475,7 @@
/**
* 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 */
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 39a9ed4..267a5a6 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1242,9 +1242,10 @@
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/widget/Editor.java b/core/java/android/widget/Editor.java
index 4aeea10..62dd192 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -405,6 +405,13 @@
// 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 @@
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 @@
}
}
- 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 @@
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;
}
- return currLine;
+ if (trueLine < prevLine && y <= layout.getLineTop(prevLine) - slop + verticalOffset) {
+ return trueLine;
+ }
+ return prevLine;
}
/**
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 8943da4..4f14539 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -144,9 +144,6 @@
@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 @@
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 @@
*/
@Deprecated
public void setView(View view) {
- mIsCustomToast = true;
mNextView = view;
}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 54ea57a..d64b5f1 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -144,9 +144,6 @@
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 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 @@
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 ae9ce65..c535163 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -387,9 +387,6 @@
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 @@
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/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index a1a434d..dca682e 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2674,7 +2674,7 @@
*/
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 ff03f1a..c758989 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -205,9 +205,15 @@
/** 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 @@
* @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 @@
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 @@
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 @@
* 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 @@
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 @@
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 1a63765..94c1f71 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -227,11 +227,22 @@
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 @@
}
} 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 bc8dfd4..6e880d4 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -258,7 +258,8 @@
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/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index c83cab7..31527e8 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -227,7 +227,7 @@
}
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 17a02b2..34be2a5 100644
--- a/core/jni/android_media_AudioProductStrategies.cpp
+++ b/core/jni/android_media_AudioProductStrategies.cpp
@@ -85,10 +85,23 @@
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 @@
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/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index ea3c0fa..aa2d1b5 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::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 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 @@
* 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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 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 @@
{"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/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index ed2c5b2..ab57e3d 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2665,4 +2665,8 @@
// Open: Settings > Sound > Do Not Disturb > People > Messages
// OS: R
DND_MESSAGES = 1839;
+
+ // Open: Settings > Sound > Do Not Disturb > Apps > <Choose App>
+ // OS: R
+ DND_APPS_BYPASSING = 1840;
}
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index 210feaf..c7e1a21 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" />
diff --git a/core/res/res/values-h480dp/bools.xml b/core/res/res/values-h480dp/bools.xml
index 65e3ae6..7896d9b 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 d6e200a..28eb98b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1827,19 +1827,11 @@
<attr name="gwpAsanMode" />
- <!-- If {@code true} allow requesting that its permissions don't get automatically
- revoked when the app is unused for an extended amount of time.
-
- The default value is {@code false}. -->
- <attr name="requestAutoRevokePermissionsExemption" format="boolean" />
-
- <!-- If {@code true} its permissions shouldn't get automatically
- revoked when the app is unused for an extended amount of time.
-
- This implies {@code requestDontAutoRevokePermissions=true}
-
- The default value is {@code false}. -->
- <attr name="allowAutoRevokePermissionsExemption" format="boolean" />
+ <attr name="autoRevokePermissions">
+ <enum name="allowed" value="0" />
+ <enum name="discouraged" value="1" />
+ <enum name="disallowed" value="2" />
+ </attr>
</declare-styleable>
<!-- An attribution is a logical part of an app and is identified by a tag.
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index c5127dc..fe296c7 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/public.xml b/core/res/res/values/public.xml
index f02d54f..e694e16 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3014,8 +3014,7 @@
<!-- @hide @SystemApi -->
<public name="minExtensionVersion" />
<public name="allowNativeHeapPointerTagging" />
- <public name="requestAutoRevokePermissionsExemption" />
- <public name="allowAutoRevokePermissionsExemption" />
+ <public name="autoRevokePermissions" />
<public name="preserveLegacyExternalStorage" />
<public name="mimeGroup" />
<public name="gwpAsanMode" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0adef75..22caf4c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3929,7 +3929,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 -->
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 2ef0c92..88f9fc2 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -702,7 +702,7 @@
</style>
<style name="Theme.Dream">
- <item name="windowBackground">@null</item>
+ <item name="windowBackground">@color/black</item>
<item name="windowDisablePreview">true</item>
</style>
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 1737bd0..57e5dd8 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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -236,6 +237,16 @@
}
@Test
+ public void testGetType_providerException() {
+ try {
+ mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote/error"));
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ }
+
+ @Test
public void testCanonicalize() {
Uri canonical = mResolver.canonicalize(
Uri.parse("content://android.content.FakeProviderRemote/something"));
diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java
index 1d7ba5d..a320094 100644
--- a/core/tests/coretests/src/android/content/FakeProviderRemote.java
+++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java
@@ -37,6 +37,9 @@
@Override
public String getType(Uri uri) {
+ if (uri.getPath() != null && uri.getPath().contains("error")) {
+ throw new IllegalArgumentException("Expected exception");
+ }
return "fake/remote";
}
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index df5c9d2..4114b28 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 @@
@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 @@
// Create a Resources for the Activity.
Configuration config1 = new Configuration();
config1.densityDpi = 280;
- Resources resources1 = mResourcesManager.createBaseActivityResources(
+ Resources resources1 = mResourcesManager.createBaseTokenResources(
activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config1,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 34a1016..efdb51d 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -31,6 +31,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -638,6 +639,32 @@
});
}
+ @Test
+ public void testFrameUpdateDuringAnimation() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+
+ mController.onControlsChanged(createSingletonControl(ITYPE_IME));
+
+ // Pretend IME is calling
+ mController.show(ime(), true /* fromIme */);
+
+ InsetsState copy = new InsetsState(mController.getState(), true /* copySources */);
+ copy.getSource(ITYPE_IME).setFrame(0, 1, 2, 3);
+ copy.getSource(ITYPE_IME).setVisibleFrame(new Rect(4, 5, 6, 7));
+ mController.onStateChanged(copy);
+ assertNotEquals(new Rect(0, 1, 2, 3),
+ mController.getState().getSource(ITYPE_IME).getFrame());
+ assertNotEquals(new Rect(4, 5, 6, 7),
+ mController.getState().getSource(ITYPE_IME).getVisibleFrame());
+ mController.cancelExistingAnimation();
+ assertEquals(new Rect(0, 1, 2, 3),
+ mController.getState().getSource(ITYPE_IME).getFrame());
+ assertEquals(new Rect(4, 5, 6, 7),
+ mController.getState().getSource(ITYPE_IME).getVisibleFrame());
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
private void waitUntilNextFrame() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
index ba27fac..eae1bbc 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 @@
@Mock
private Context mMockContext;
- private ContentCaptureManager mManager;
-
- @Before
- public void before() {
- mManager = new ContentCaptureManager(mMockContext, /* service= */ null,
- /* options= */ null);
+ @Test
+ public void testConstructor_invalidParametersThrowsException() {
+ assertThrows(NullPointerException.class,
+ () -> new ContentCaptureManager(mMockContext, /* service= */ null, /* options= */
+ null));
}
@Test
- public void testRemoveData_invalid() {
- assertThrows(NullPointerException.class, () -> mManager.removeData(null));
+ public void testRemoveData_invalidParametersThrowsException() {
+ final IContentCaptureManager mockService = mock(IContentCaptureManager.class);
+ final ContentCaptureOptions options = new ContentCaptureOptions(null);
+ final ContentCaptureManager manager =
+ new ContentCaptureManager(mMockContext, mockService, options);
+
+ assertThrows(NullPointerException.class, () -> manager.removeData(null));
}
}
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index f81964c..1b5ce8fd 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -16,6 +16,7 @@
package android.widget;
+import static android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE;
import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.dragOnText;
import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
@@ -37,6 +38,9 @@
import android.app.Instrumentation;
import android.graphics.Rect;
import android.text.Layout;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.AbsoluteSizeSpan;
import android.util.ArraySet;
import android.util.Log;
import android.view.InputDevice;
@@ -489,6 +493,38 @@
}
@Test
+ public void testLineChangeSlop() throws Throwable {
+ TextView tv = mActivity.findViewById(R.id.textview);
+ Spannable s = new SpannableString("a\nb\nc");
+ s.setSpan(new AbsoluteSizeSpan(10), 2, 4, SPAN_INCLUSIVE_EXCLUSIVE);
+ s.setSpan(new AbsoluteSizeSpan(32), 4, 5, SPAN_INCLUSIVE_EXCLUSIVE);
+ mInstrumentation.runOnMainSync(() -> tv.setText(s));
+
+ Layout layout = tv.getLayout();
+ Editor editor = tv.getEditorForTesting();
+ final float verticalOffset = tv.getExtendedPaddingTop();
+ editor.setLineChangeSlopMinMaxForTesting(30, 65);
+ // Hit top part of upper line, jump to upper line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 5 + verticalOffset))
+ .isEqualTo(0);
+ // Hit bottom part of upper line, stay at current line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 40 + verticalOffset))
+ .isEqualTo(1);
+ // Hit current line, stay at current line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 70 + verticalOffset))
+ .isEqualTo(1);
+ // Hit top part of lower line, stay at current line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 85 + verticalOffset))
+ .isEqualTo(1);
+ // Hit bottom part of lower line, jump to lower line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 110 + verticalOffset))
+ .isEqualTo(2);
+ // Hit lower line of lower line, jump to target line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 0, 110 + verticalOffset))
+ .isEqualTo(2);
+ }
+
+ @Test
public void testCursorDrag_snapDistance() throws Throwable {
String text = "line1: This is the 1st line: A\n"
+ "line2: This is the 2nd line: B\n"
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 9af0ed0..4a33da6 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 @@
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 @@
}
@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 @@
}
@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 @@
}
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 5c89da0..98f5824 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -61,7 +61,6 @@
<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/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 383202b..fffdd68 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4579,6 +4579,7 @@
* {@hide}
*/
@UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setWiredDeviceConnectionState(int type, int state, String address, String name) {
final IAudioService service = getService();
try {
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 8b973a1..0a56acc 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -708,6 +708,8 @@
DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET);
}
+ public static final String LEGACY_REMOTE_SUBMIX_ADDRESS = "0";
+
// device states, must match AudioSystem::device_connection_state
@UnsupportedAppUsage
public static final int DEVICE_STATE_UNAVAILABLE = 0;
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index c0461bc..1d70a0d 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -46,6 +46,7 @@
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 @@
* @param block The linear block object
* @param offset The byte offset into the input buffer at which the data starts.
* @param size The number of bytes of valid input data.
- * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
- * may be null for non-encrypted content.
* @return this object
* @throws IllegalStateException if a buffer is already set
*/
public @NonNull QueueRequest setLinearBlock(
@NonNull LinearBlock block,
int offset,
+ int size) {
+ if (!isAccessible()) {
+ throw new IllegalStateException("The request is stale");
+ }
+ if (mLinearBlock != null || mHardwareBuffer != null) {
+ throw new IllegalStateException("Cannot set block twice");
+ }
+ mLinearBlock = block;
+ mOffset = offset;
+ mSize = size;
+ mCryptoInfo = null;
+ return this;
+ }
+
+ /**
+ * Set an encrypted linear block to this queue request. Exactly one buffer must be
+ * set for a queue request before calling {@link #queue}. It is possible
+ * to use the same {@link LinearBlock} object for multiple queue
+ * requests. The behavior is undefined if the range of the buffer
+ * overlaps for multiple requests, or the application writes into the
+ * region being processed by the codec.
+ *
+ * @param block The linear block object
+ * @param offset The byte offset into the input buffer at which the data starts.
+ * @param size The number of bytes of valid input data.
+ * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
+ * @return this object
+ * @throws IllegalStateException if a buffer is already set
+ */
+ public @NonNull QueueRequest setEncryptedLinearBlock(
+ @NonNull LinearBlock block,
+ int offset,
int size,
- @Nullable MediaCodec.CryptoInfo cryptoInfo) {
+ @NonNull MediaCodec.CryptoInfo cryptoInfo) {
+ Objects.requireNonNull(cryptoInfo);
if (!isAccessible()) {
throw new IllegalStateException("The request is stale");
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 29dfd73..9310d38 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::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 @@
return dvrObj;
}
+jobject JTuner::getDemuxCaps() {
+ DemuxCapabilities caps;
+ Result res;
+ mTuner->getDemuxCaps([&](Result r, const DemuxCapabilities& demuxCaps) {
+ caps = demuxCaps;
+ res = r;
+ });
+ if (res != Result::SUCCESS) {
+ return NULL;
+ }
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass clazz = env->FindClass("android/media/tv/tuner/DemuxCapabilities");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIIIIJI[IZ)V");
+
+ jint numDemux = caps.numDemux;
+ jint numRecord = caps.numRecord;
+ jint numPlayback = caps.numPlayback;
+ jint numTsFilter = caps.numTsFilter;
+ jint numSectionFilter = caps.numSectionFilter;
+ jint numAudioFilter = caps.numAudioFilter;
+ jint numVideoFilter = caps.numVideoFilter;
+ jint numPesFilter = caps.numPesFilter;
+ jint numPcrFilter = caps.numPcrFilter;
+ jlong numBytesInSectionFilter = caps.numBytesInSectionFilter;
+ jint filterCaps = static_cast<jint>(caps.filterCaps);
+ jboolean bTimeFilter = caps.bTimeFilter;
+
+ jintArray linkCaps = env->NewIntArray(caps.linkCaps.size());
+ env->SetIntArrayRegion(
+ linkCaps, 0, caps.linkCaps.size(), reinterpret_cast<jint*>(&caps.linkCaps[0]));
+
+ return env->NewObject(clazz, capsInit, numDemux, numRecord, numPlayback, numTsFilter,
+ numSectionFilter, numAudioFilter, numVideoFilter, numPesFilter, numPcrFilter,
+ numBytesInSectionFilter, filterCaps, linkCaps, bTimeFilter);
+}
+
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -2739,8 +2776,9 @@
return tuner->openDvr(DvrType::PLAYBACK, bufferSize);
}
-static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv*, jobject) {
- return NULL;
+static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv* env, jobject thiz) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->getDemuxCaps();
}
static int android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 5d2bba6..18aac28 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -185,6 +185,7 @@
jobject openTimeFilter();
jobject openDescrambler();
jobject openDvr(DvrType type, jlong bufferSize);
+ jobject getDemuxCaps();
protected:
Result openDemux();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index a2fa461..a95677d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -406,6 +406,10 @@
mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
mOk.setEnabled(false);
+
+ if (!mOk.isInTouchMode()) {
+ mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
+ }
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index 2b84196..59735f4 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 @@
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 d17f242..a1fba4a 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 @@
@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 @@
@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 @@
@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 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 c34c365..7ef0801 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 @@
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 b1300a9..922caeb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -66,6 +66,7 @@
private LocalBluetoothManager mLocalBluetoothManager;
private InfoMediaManager mInfoMediaManager;
private String mPackageName;
+ private MediaDevice mOnTransferBluetoothDevice;
@VisibleForTesting
List<MediaDevice> mMediaDevices = new ArrayList<>();
@@ -143,7 +144,7 @@
final CachedBluetoothDevice cachedDevice =
((BluetoothMediaDevice) device).getCachedDevice();
if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
- device.setState(MediaDeviceState.STATE_CONNECTING);
+ mOnTransferBluetoothDevice = connectDevice;
cachedDevice.connect();
return;
}
@@ -389,6 +390,10 @@
mCurrentConnectedDevice = infoMediaDevice != null
? infoMediaDevice : updateCurrentConnectedDevice();
dispatchDeviceListUpdate();
+ if (mOnTransferBluetoothDevice != null && mOnTransferBluetoothDevice.isConnected()) {
+ connectDevice(mOnTransferBluetoothDevice);
+ mOnTransferBluetoothDevice = null;
+ }
}
private List<MediaDevice> buildDisconnectedBluetoothDevice() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
index ed0857c..204a933 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 @@
}
@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 6b3a97f..4c61ef5 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 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 @@
when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
- mInfoMediaDevice1 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
- TEST_PACKAGE_NAME);
+ mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
+ TEST_PACKAGE_NAME));
mInfoMediaDevice2 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo2,
TEST_PACKAGE_NAME);
mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
@@ -565,6 +566,34 @@
}
@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 d350d9d..d320df9 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 @@
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
- Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED,
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 4d33b62..8801a9c 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.COMPONENT_NAME_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.JSON_OBJECT_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
-import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.TILE_LIST_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR;
@@ -82,7 +82,6 @@
Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index af74121..b22caf0 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 @@
Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
SecureSettingsProto.Accessibility.LARGE_POINTER_ICON);
dumpSetting(s, p,
- Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED,
- SecureSettingsProto.Accessibility.SHORTCUT_ENABLED);
- dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
SecureSettingsProto.Accessibility.SHORTCUT_ON_LOCK_SCREEN);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5a9d749..2fde87c 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 @@
}
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 @@
currentVersion = 188;
}
+ if (currentVersion == 188) {
+ // Deprecate ACCESSIBILITY_SHORTCUT_ENABLED, and migrate it
+ // to ACCESSIBILITY_SHORTCUT_TARGET_SERVICE.
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ final Setting shortcutEnabled = secureSettings.getSettingLocked(
+ "accessibility_shortcut_enabled");
+ if ("0".equals(shortcutEnabled.getValue())) {
+ // Clear shortcut key targets list setting.
+ secureSettings.insertSettingLocked(
+ Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ "", null /* tag */, false /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ secureSettings.deleteSettingLocked("accessibility_shortcut_enabled");
+ currentVersion = 189;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 64e5237..0ae00e1 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -1019,8 +1019,11 @@
* 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 @@
/**
* User-provided, detailed description of the bugreport; when set, will be added to the body
- * of the {@link Intent#ACTION_SEND_MULTIPLE} intent.
+ * of the {@link Intent#ACTION_SEND_MULTIPLE} intent. This is shown in the app where the
+ * bugreport is being shared as an attachment. This is not related/dependant on
+ * {@code shareDescription}.
*/
private String description;
@@ -2130,7 +2135,6 @@
return new BugreportInfo[size];
}
};
-
}
@GuardedBy("mLock")
diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
index a06f434..65b04fd 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 0000000..d67c81d
--- /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/keyguard_media_header.xml b/packages/SystemUI/res/layout/keyguard_media_header.xml
index 9c2d244..de9ef21 100644
--- a/packages/SystemUI/res/layout/keyguard_media_header.xml
+++ b/packages/SystemUI/res/layout/keyguard_media_header.xml
@@ -52,7 +52,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:gravity="center_horizontal|fill_vertical"
+ android:gravity="center"
android:padding="16dp"
>
<ImageView
@@ -67,7 +67,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/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml
index 9ef8c1d..fe8557b 100644
--- a/packages/SystemUI/res/layout/qs_media_panel.xml
+++ b/packages/SystemUI/res/layout/qs_media_panel.xml
@@ -56,7 +56,7 @@
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
- android:layout_height="@dimen/qs_media_album_size"
+ android:layout_height="wrap_content"
android:layout_weight="1"
>
<LinearLayout
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e45cbec..432cd74 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1150,8 +1150,8 @@
<dimen name="bubble_overflow_height">380dp</dimen>
<!-- Bubble overflow padding when there are no bubbles -->
<dimen name="bubble_overflow_empty_state_padding">16dp</dimen>
- <!-- Margin of overflow bubbles -->
- <dimen name="bubble_overflow_margin">16dp</dimen>
+ <!-- Padding of container for overflow bubbles -->
+ <dimen name="bubble_overflow_padding">5dp</dimen>
<!-- Height of the triangle that points to the expanded bubble -->
<dimen name="bubble_pointer_height">4dp</dimen>
<!-- Width of the triangle that points to the expanded bubble -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 07bd3a0..1369350 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 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 @@
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 @@
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 0000000..487c295
--- /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
index b001726..d154434 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
@@ -32,6 +32,9 @@
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;
@@ -64,39 +67,47 @@
private final Context mContext;
private final Executor mBackgroundExecutor;
- private float mAlbumArtRadius;
- private int mAlbumArtSize;
- private View mMediaNotifView;
+ private final KeyguardMediaViewModel mViewModel;
+ private KeyguardMediaObserver mObserver;
@Inject
public KeyguardMediaPlayer(Context context, @Background Executor backgroundExecutor) {
mContext = context;
mBackgroundExecutor = backgroundExecutor;
- loadDimens();
+ mViewModel = new KeyguardMediaViewModel(context);
}
/** Binds media controls to a view hierarchy. */
public void bindView(View v) {
- if (mMediaNotifView != null) {
+ if (mObserver != null) {
throw new IllegalStateException("cannot bind views, already bound");
}
- mMediaNotifView = v;
- loadDimens();
+ 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 (mMediaNotifView == null) {
+ if (mObserver == null) {
throw new IllegalStateException("cannot unbind views, nothing bound");
}
- mMediaNotifView = null;
+ mViewModel.getKeyguardMedia().removeObserver(mObserver);
+ mObserver = null;
}
/** Clear the media controls because there isn't an active session. */
public void clearControls() {
- if (mMediaNotifView != null) {
- mMediaNotifView.setVisibility(View.GONE);
- }
+ mBackgroundExecutor.execute(mViewModel::clearControls);
}
/**
@@ -110,157 +121,244 @@
*/
public void updateControls(NotificationEntry entry, Icon appIcon,
MediaMetadata mediaMetadata) {
- if (mMediaNotifView == null) {
+ if (mObserver == null) {
throw new IllegalStateException("cannot update controls, views not bound");
}
if (mediaMetadata == null) {
- throw new IllegalArgumentException("media metadata was 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;
}
- mMediaNotifView.setVisibility(View.VISIBLE);
+ mBackgroundExecutor.execute(() -> mViewModel.updateControls(entry, appIcon, mediaMetadata));
+ }
- Notification notif = entry.getSbn().getNotification();
+ /** ViewModel for KeyguardMediaControls. */
+ private static final class KeyguardMediaViewModel {
- // Computed foreground and background color based on album art.
- int fgColor = notif.color;
- int bgColor = entry.getRow() == null ? -1 : entry.getRow().getCurrentBackgroundTint();
- Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
- if (artworkBitmap == null) {
- artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
- }
- if (artworkBitmap != null) {
- // If we have art, get colors from that
- Palette p = MediaNotificationProcessor.generateArtworkPaletteBuilder(artworkBitmap)
- .generate();
- Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(p);
- bgColor = swatch.getRgb();
- fgColor = MediaNotificationProcessor.selectForegroundColor(bgColor, p);
- }
- // Make sure colors will be legible
- boolean isDark = !ContrastColorUtil.isColorLight(bgColor);
- fgColor = ContrastColorUtil.resolveContrastColor(mContext, fgColor, bgColor,
- isDark);
- fgColor = ContrastColorUtil.ensureTextContrast(fgColor, bgColor, isDark);
+ 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;
- // Album art
- ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
- if (albumView != null) {
- // Resize art in a background thread
- final Bitmap bm = artworkBitmap;
- mBackgroundExecutor.execute(() -> processAlbumArt(bm, albumView));
+ KeyguardMediaViewModel(Context context) {
+ mContext = context;
+ loadDimens();
}
- // App icon
- ImageView appIconView = mMediaNotifView.findViewById(R.id.icon);
- if (appIconView != null) {
- Drawable iconDrawable = appIcon.loadDrawable(mContext);
- iconDrawable.setTint(fgColor);
- appIconView.setImageDrawable(iconDrawable);
+ /** Close the media player because there isn't an active session. */
+ public void clearControls() {
+ synchronized (mActionsLock) {
+ mActions = null;
+ }
+ mMedia.postValue(null);
}
- // App name
- TextView appName = mMediaNotifView.findViewById(R.id.app_name);
- if (appName != 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 appNameString = builder.loadHeaderAppName();
- appName.setText(appNameString);
- appName.setTextColor(fgColor);
- }
+ String app = builder.loadHeaderAppName();
- // Song name
- TextView titleText = mMediaNotifView.findViewById(R.id.header_title);
- if (titleText != null) {
- String songName = mediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
- titleText.setText(songName);
- titleText.setTextColor(fgColor);
- }
+ // App Icon
+ Drawable appIconDrawable = appIcon.loadDrawable(mContext);
- // Artist name
- TextView artistText = mMediaNotifView.findViewById(R.id.header_artist);
- if (artistText != null) {
- String artistName = mediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
- artistText.setText(artistName);
- artistText.setTextColor(fgColor);
- }
+ // Song name
+ String song = mediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
- // Background color
- if (mMediaNotifView instanceof MediaHeaderView) {
- MediaHeaderView head = (MediaHeaderView) mMediaNotifView;
- head.setBackgroundColor(bgColor);
- }
+ // Artist name
+ String artist = mediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
- // Control buttons
- final List<Icon> icons = new ArrayList<>();
- final List<PendingIntent> intents = new ArrayList<>();
- Notification.Action[] actions = notif.actions;
- final int[] actionsToShow = notif.extras.getIntArray(Notification.EXTRA_COMPACT_ACTIONS);
+ // 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);
- for (int i = 0; i < ACTION_IDS.length; i++) {
- if (actionsToShow != null && actions != null && i < actionsToShow.length
- && actionsToShow[i] < actions.length) {
- final int idx = actionsToShow[i];
- icons.add(actions[idx].getIcon());
- intents.add(actions[idx].actionIntent);
- } else {
- icons.add(null);
- intents.add(null);
+ Context packageContext = entry.getSbn().getPackageContext(mContext);
+ for (int i = 0; i < ACTION_IDS.length; i++) {
+ 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);
}
- Context packageContext = entry.getSbn().getPackageContext(mContext);
- for (int i = 0; i < ACTION_IDS.length; i++) {
- ImageButton button = mMediaNotifView.findViewById(ACTION_IDS[i]);
- if (button == null) {
- continue;
+ /** 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);
+ }
}
- Icon icon = icons.get(i);
- if (icon == null) {
- button.setVisibility(View.GONE);
- } else {
- button.setVisibility(View.VISIBLE);
- button.setImageDrawable(icon.loadDrawable(packageContext));
- button.setImageTintList(ColorStateList.valueOf(fgColor));
- final PendingIntent intent = intents.get(i);
- if (intent != null) {
- button.setOnClickListener(v -> {
- try {
- intent.send();
- } catch (PendingIntent.CanceledException e) {
- Log.d(TAG, "failed to send action intent", e);
- }
- });
+ 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);
+ }
}
- /**
- * Process album art for layout
- * @param albumArt bitmap to use for album art
- * @param albumView view to hold the album art
- */
- private void processAlbumArt(Bitmap albumArt, ImageView albumView) {
- RoundedBitmapDrawable roundedDrawable = null;
- if (albumArt != null) {
- Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
- Bitmap scaled = Bitmap.createScaledBitmap(original, mAlbumArtSize, mAlbumArtSize,
- false);
- roundedDrawable = RoundedBitmapDrawableFactory.create(mContext.getResources(), scaled);
- roundedDrawable.setCornerRadius(mAlbumArtRadius);
- } else {
- Log.e(TAG, "No album art available");
+ /** 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]));
+ }
}
- // Now that it's resized, update the UI
- final RoundedBitmapDrawable result = roundedDrawable;
- albumView.post(() -> {
- albumView.setImageDrawable(result);
- albumView.setVisibility(result == null ? View.GONE : View.VISIBLE);
- });
- }
+ /** 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);
- private void loadDimens() {
- mAlbumArtRadius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
- mAlbumArtSize = (int) mContext.getResources().getDimension(
- R.dimen.qs_media_album_size);
+ // 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/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index c9ce8a1..01c2faa 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.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 @@
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 @@
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 @@
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
dumpManager.registerDumpable(TAG, this);
mContext = context;
mShadeController = shadeController;
@@ -340,6 +344,7 @@
});
configurationController.addCallback(this /* configurationListener */);
+ mSysUiState = sysUiState;
mBubbleData = data;
mBubbleData.setListener(mBubbleDataListener);
@@ -593,7 +598,8 @@
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 @@
String key = orderedKeys[i];
NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
rankingMap.getRanking(key, mTmpRanking);
- if (mBubbleData.hasBubbleWithKey(key) && !mTmpRanking.canBubble()) {
+ boolean isActiveBubble = mBubbleData.hasBubbleWithKey(key);
+ if (isActiveBubble && !mTmpRanking.canBubble()) {
mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
- } else if (entry != null && mTmpRanking.isBubble()) {
+ } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
entry.setFlagBubble(true);
onEntryUpdated(entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 077ffd3..2bd1518 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 @@
b -> {
notificationEntryUpdated(bubble, /* suppressFlyout */
false, /* showInShade */ true);
- setSelectedBubbleInternal(bubble);
+ setSelectedBubble(bubble);
},
mContext, stack, factory);
dispatchPendingChanges();
@@ -233,6 +233,7 @@
Bubble b = mOverflowBubbles.get(i);
if (b.getKey().equals(entry.getKey())) {
moveOverflowBubbleToPending(b);
+ b.setEntry(entry);
return b;
}
}
@@ -240,6 +241,7 @@
for (int i = 0; i < mPendingBubbles.size(); i++) {
Bubble b = mPendingBubbles.get(i);
if (b.getKey().equals(entry.getKey())) {
+ b.setEntry(entry);
return b;
}
}
@@ -759,6 +761,17 @@
}
@VisibleForTesting(visibility = PRIVATE)
+ Bubble getOverflowBubbleWithKey(String key) {
+ for (int i = 0; i < mOverflowBubbles.size(); i++) {
+ Bubble bubble = mOverflowBubbles.get(i);
+ if (bubble.getKey().equals(key)) {
+ return bubble;
+ }
+ }
+ return null;
+ }
+
+ @VisibleForTesting(visibility = PRIVATE)
void setTimeSource(TimeSource timeSource) {
mTimeSource = timeSource;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 7636c67..b651985 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_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 @@
mEmptyState = findViewById(R.id.bubble_overflow_empty_state);
mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
- mRecyclerView.setLayoutManager(
- new GridLayoutManager(getApplicationContext(),
- getResources().getInteger(R.integer.bubbles_overflow_columns)));
- int bubbleMargin = getResources().getDimensionPixelSize(R.dimen.bubble_overflow_margin);
+ Resources res = getResources();
+ final int columns = res.getInteger(R.integer.bubbles_overflow_columns);
+ mRecyclerView.setLayoutManager(
+ 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;
+
mAdapter = new BubbleOverflowAdapter(mOverflowBubbles,
- mBubbleController::promoteBubbleFromOverflow, bubbleMargin);
+ mBubbleController::promoteBubbleFromOverflow, viewWidth, viewHeight);
mRecyclerView.setAdapter(mAdapter);
onDataChanged(mBubbleController.getOverflowBubbles());
mBubbleController.setOverflowCallback(() -> {
@@ -139,39 +154,48 @@
class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> {
private Consumer<Bubble> mPromoteBubbleFromOverflow;
private List<Bubble> mBubbles;
- private int mBubbleMargin;
+ private int mWidth;
+ private int mHeight;
- public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble,
- int bubbleMargin) {
+ public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble, int width,
+ int height) {
mBubbles = list;
mPromoteBubbleFromOverflow = promoteBubble;
- mBubbleMargin = bubbleMargin;
+ mWidth = width;
+ mHeight = height;
}
@Override
public BubbleOverflowAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
- BadgedImageView view = (BadgedImageView) LayoutInflater.from(parent.getContext())
- .inflate(R.layout.bubble_view, parent, false);
+ LinearLayout overflowView = (LinearLayout) LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.bubble_overflow_view, parent, false);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- );
- params.setMargins(mBubbleMargin, mBubbleMargin, mBubbleMargin, mBubbleMargin);
- view.setLayoutParams(params);
- return new ViewHolder(view);
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ params.width = mWidth;
+ params.height = mHeight;
+ overflowView.setLayoutParams(params);
+ return new ViewHolder(overflowView);
}
@Override
public void onBindViewHolder(ViewHolder vh, int index) {
- Bubble bubble = mBubbles.get(index);
+ Bubble b = mBubbles.get(index);
- vh.mBadgedImageView.update(bubble);
- vh.mBadgedImageView.setOnClickListener(view -> {
- mBubbles.remove(bubble);
+ vh.iconView.update(b);
+ vh.iconView.setOnClickListener(view -> {
+ mBubbles.remove(b);
notifyDataSetChanged();
- mPromoteBubbleFromOverflow.accept(bubble);
+ mPromoteBubbleFromOverflow.accept(b);
});
+
+ Bubble.FlyoutMessage message = b.getFlyoutMessage();
+ if (message != null && message.senderName != null) {
+ vh.textView.setText(message.senderName);
+ } else {
+ vh.textView.setText(b.getAppName());
+ }
}
@Override
@@ -180,11 +204,13 @@
}
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 541c8cf..7191a20 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -80,6 +80,8 @@
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
@@ -241,6 +243,7 @@
private BubbleTouchHandler mTouchHandler;
private BubbleController.BubbleExpandListener mExpandListener;
+ private SysUiState mSysUiState;
private boolean mViewUpdatedRequested = false;
private boolean mIsExpansionAnimating = false;
@@ -437,7 +440,8 @@
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
super(context);
mBubbleData = data;
@@ -445,6 +449,8 @@
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);
@@ -1055,6 +1061,11 @@
if (shouldExpand == mIsExpanded) {
return;
}
+
+ mSysUiState
+ .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
+ .commitUpdate(mContext.getDisplayId());
+
if (mIsExpanded) {
animateCollapse();
logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 27c9e98..e84e932 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 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 @@
NotifPipeline notifPipeline,
FeatureFlags featureFlags,
DumpManager dumpManager,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState) {
return new BubbleController(
context,
notificationShadeWindowController,
@@ -79,6 +81,7 @@
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 6c49c82..118fcbb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -252,10 +252,17 @@
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 @@
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 @@
private fun createRemovedStatus(
componentName: ComponentName,
controlInfo: ControlInfo,
+ structure: CharSequence,
setRemoved: Boolean = true
): ControlStatus {
val intent = Intent(Intent.ACTION_MAIN).apply {
@@ -384,6 +390,8 @@
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)
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 09d7d26..b329991 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 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 73539f9..6514ca4 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 @@
.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 @@
.alpha(0)
.translationX(mGlobalActionsLayout.getAnimationOffsetX())
.translationY(mGlobalActionsLayout.getAnimationOffsetY())
- .setDuration(300)
+ .setDuration(550)
.withEndAction(this::completeDismiss)
.setInterpolator(new LogAccelerateInterpolator())
.setUpdateListener(animation -> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 2cc3d9e..96494cf 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 @@
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 902b578..6ce5e7c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -346,6 +346,7 @@
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper
.crop(tx, mLeash, destinationBounds)
+ .resetScale(tx, mLeash, destinationBounds)
.round(tx, mLeash, mInPip);
scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE, null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 014f3b5..0b07655 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 @@
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 @@
final Rect currentPipBounds = mMotionHelper.getBounds();
mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(),
mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
- mMinSize.y, mMaxSize, true, true));
+ mMinSize.y, mMaxSize, true,
+ mLastDownBounds.width() > mLastDownBounds.height()));
mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
null);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5ccf8c7..33cc086 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 @@
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 @@
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 @@
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 @@
if (mLocalMediaManager != null) {
mLocalMediaManager.stopScan();
mLocalMediaManager.unregisterCallback(mDeviceCallback);
+ mLocalMediaManager = null;
}
super.onDetachedFromWindow();
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
index 3cdc01d..dea8c32 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 @@
@Override
protected void onInactive() {
super.onInactive();
- mTracker.startTracking();
+ mTracker.stopTracking();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 3879c16..1aa7831 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.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 @@
private int mDividerInsets;
private final Display mDefaultDisplay;
+ private boolean mSupportSplitScreenMultiWindow;
private int mDividerSize;
private int mTouchElevation;
@@ -282,6 +284,8 @@
final DisplayManager displayManager =
(DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ mSupportSplitScreenMultiWindow =
+ ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
}
@Override
@@ -354,6 +358,11 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (!mSupportSplitScreenMultiWindow) {
+ super.onLayout(changed, left, top, right, bottom);
+ return;
+ }
+
if (mFirstLayout) {
// Wait for first layout so that the ViewRootImpl surface has been created.
initializeSurfaceState();
@@ -1085,6 +1094,13 @@
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 c4089e5..6cb7f4f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -26,12 +26,15 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
+import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
-import android.window.ITaskOrganizer;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.window.ITaskOrganizer;
+
+import java.util.ArrayList;
class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub {
private static final String TAG = "SplitScreenTaskOrganizer";
@@ -43,6 +46,8 @@
SurfaceControl mSecondarySurface;
SurfaceControl mPrimaryDim;
SurfaceControl mSecondaryDim;
+ ArrayList<SurfaceControl> mHomeAndRecentsSurfaces = new ArrayList<>();
+ Rect mHomeBounds = new Rect();
final Divider mDivider;
SplitScreenTaskOrganizer(Divider divider) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 8724e49..6ed7afe 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -29,9 +29,9 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
+import android.view.WindowManagerGlobal;
import android.window.IWindowContainer;
import android.window.WindowContainerTransaction;
-import android.view.WindowManagerGlobal;
import android.window.WindowOrganizer;
import com.android.internal.annotations.GuardedBy;
@@ -157,6 +157,7 @@
for (int i = homeStacks.size() - 1; i >= 0; --i) {
wct.setBounds(homeStacks.get(i), homeBounds);
}
+ layout.mTiles.mHomeBounds.set(homeBounds);
return isHomeResizable;
}
@@ -180,13 +181,17 @@
if (rootTasks.isEmpty()) {
return false;
}
+ tiles.mHomeAndRecentsSurfaces.clear();
for (int i = rootTasks.size() - 1; i >= 0; --i) {
- if (rootTasks.get(i).configuration.windowConfiguration.getWindowingMode()
+ final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
+ if (isHomeOrRecentTask(rootTask)) {
+ tiles.mHomeAndRecentsSurfaces.add(rootTask.token.getLeash());
+ }
+ if (rootTask.configuration.windowConfiguration.getWindowingMode()
!= WINDOWING_MODE_FULLSCREEN) {
continue;
}
- wct.reparent(rootTasks.get(i).token, tiles.mSecondary.token,
- true /* onTop */);
+ wct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */);
}
boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
WindowOrganizer.applyTransaction(wct);
@@ -213,6 +218,7 @@
// Set launch root first so that any task created after getChildContainers and
// before reparent (pretty unlikely) are put into fullscreen.
TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
+ tiles.mHomeAndRecentsSurfaces.clear();
// TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
// plus specific APIs to clean this up.
List<ActivityManager.RunningTaskInfo> primaryChildren =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 8945f36..a3faa80 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 @@
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 @@
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 @@
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 @@
}
override fun onDozingChanged(isDozing: Boolean) {
- if (isDozing && shadeSpring.isRunning) {
- shadeSpring.skipToEnd()
+ if (isDozing) {
+ shadeSpring.finishIfRunning()
+ globalActionsSpring.finishIfRunning()
}
}
}
@@ -174,10 +148,6 @@
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 @@
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 @@
}
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/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index c6d84ff..d2f781d 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 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/collection/NotifViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
index 0437877..cf670bd 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 @@
// 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 @@
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 7019b5b..808e1b3 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 @@
/** 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 f7d6cef..19f7cef 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 @@
// 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 @@
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 @@
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 @@
* 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 @@
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 @@
};
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 0000000..261ae07
--- /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 aaf71f5..b773856 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 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 98104f8..03c0ae6 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 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 7e9e760..e9cbf32 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 void attach(NotifPipeline pipeline) {
mStatusBarStateController.addCallback(mStatusBarStateCallback);
- pipeline.addPreGroupFilter(mNotifFilter);
+ pipeline.addPreGroupFilter(mSuspendedFilter);
+ pipeline.addPreGroupFilter(mDozingFilter);
}
/**
@@ -53,33 +54,30 @@
* 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 763547c..e946cf1 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.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 @@
fun logFilterChanged(
key: String,
- prevFilter: String?,
- newFilter: String?
+ prevFilter: NotifFilter?,
+ newFilter: NotifFilter?
) {
buffer.log(TAG, INFO, {
str1 = key
- str2 = prevFilter
- str3 = newFilter
+ str2 = prevFilter?.name
+ str3 = newFilter?.name
}, {
"Filter changed for $str1: $str2 -> $str3"
})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index be8af82..823b186 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
@@ -6478,7 +6478,7 @@
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 adca10f..ecfe116 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 @@
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 @@
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 @@
}
private boolean isWithinTouchRegion(int x, int y) {
- // Disallow if too far from the edge
- if (x > mEdgeWidthLeft + mLeftInset
- && x < (mDisplaySize.x - mEdgeWidthRight - mRightInset)) {
- return false;
- }
-
// Disallow if we are in the bottom gesture area
if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
return false;
}
- // Always allow if the user is in a transient sticky immersive state
- if (mIsNavBarShownTransiently) {
- return true;
+ // 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;
}
- 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);
+ // 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) {
+ mLogGesture = true;
+ return withinRange;
}
- return !isInExcludedRegion;
+
+ 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;
+ }
+
+ 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 @@
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/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index cf9d43e..d70484e 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.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 {
+public class LockIcon extends KeyguardAffordanceView {
- 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;
+ 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 SparseArray<Drawable> mDrawableCache = new SparseArray<>();
- 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);
- }
+ private final OnPreDrawListener mOnPreDrawListener = new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getViewTreeObserver().removeOnPreDrawListener(this);
- @Override
- public void onKeyguardFadingAwayChanged() {
- if (!mKeyguardStateController.isKeyguardFadingAway()) {
- mBouncerPreHideAnimation = false;
- if (mBlockUpdates) {
- mBlockUpdates = false;
- update(true /* force */);
- }
- }
- }
+ int newState = mStateProvider.getState();
+ Drawable icon = getIcon(newState);
+ setImageDrawable(icon, false);
- @Override
- public void onUnlockedChanged() {
- update();
- }
- };
+ if (newState == STATE_SCANNING_FACE) {
+ announceForAccessibility(getResources().getString(
+ R.string.accessibility_scanning_face));
+ }
- @Inject
- public LockIcon(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- AccessibilityController accessibilityController,
- KeyguardBypassController bypassController,
- NotificationWakeUpCoordinator wakeUpCoordinator,
- KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManager) {
+ 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
+ && newState == mStateProvider.getState()
+ && newState == STATE_SCANNING_FACE) {
+ animation.start();
+ } else {
+ Trace.endAsyncSection("LockIcon#Animation", newState);
+ }
+ }
+ });
+ Trace.beginAsyncSection("LockIcon#Animation", newState);
+ animation.start();
+ }
+
+ return true;
+ }
+ };
+
+ public LockIcon(Context context, AttributeSet attrs) {
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);
- }
-
- /**
- * If we're currently presenting an authentication error message.
- */
- public void setTransientBiometricsError(boolean transientBiometricsError) {
- mTransientBiometricsError = transientBiometricsError;
- update();
+ void setStateProvider(StateProvider stateProvider) {
+ mStateProvider = stateProvider;
}
@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);
- }
- }
-
- @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;
- setImageDrawable(icon, false);
- if (mIsFaceUnlockState) {
- announceForAccessibility(getContext().getString(
- R.string.accessibility_scanning_face));
- }
-
- if (animation != null && isAnim) {
- 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.start();
- }
- }
- updateDarkTint();
-
- updateIconVisibility();
- updateClickability();
-
- return true;
+ 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 @@
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;
+ void setDozeAmount(float dozeAmount) {
+ mDozeAmount = dozeAmount;
+ updateDarkTint();
+ }
+
+ 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));
}
- boolean canLock = mKeyguardStateController.isMethodSecure()
- && mKeyguardStateController.canDismissLockScreen();
- boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
- setClickable(clickToUnlock);
- setLongClickable(canLock && !clickToUnlock);
- setFocusable(mAccessibilityController.isAccessibilityEnabled());
+
+ return mDrawableCache.get(iconRes);
}
- @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));
- }
- }
-
- private int getIconForState(int state) {
+ static int getIconForState(int state) {
int iconRes;
switch (state) {
case STATE_LOCKED:
@@ -343,11 +196,7 @@
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 @@
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 @@
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 @@
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;
- }
+ interface StateProvider {
+ int getState();
}
- /**
- * When keyguard is in pulsing (AOD2) state.
- * @param pulsing {@code true} when pulsing.
- */
- public void setPulsing(boolean pulsing) {
- mPulsing = pulsing;
- update();
- }
-
- 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 2b1a8a4..f7c861b 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.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 @@
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 @@
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 @@
mConfigurationController.removeCallback(mConfigurationListener);
mNotificationWakeUpCoordinator.removeListener(mWakeUpListener);
mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
-
+ mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
mDockManager.ifPresent(dockManager -> dockManager.removeListener(mDockEventListener));
}
@@ -91,32 +120,44 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 @@
* 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 @@
}
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/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 1c1e7c4..977a307 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 @@
}
};
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 760a6d6..c6f7983 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 @@
}
// padding needed for corner cutout.
- int leftCornerCutoutPadding = 0;
- int rightCornerCutoutPadding = 0;
+ int leftCornerCutoutPadding = cutout.getSafeInsetLeft();
+ int rightCornerCutoutPadding = cutout.getSafeInsetRight();
if (cornerCutoutPadding != null) {
- leftCornerCutoutPadding = cornerCutoutPadding.first;
- rightCornerCutoutPadding = cornerCutoutPadding.second;
+ leftCornerCutoutPadding = Math.max(leftCornerCutoutPadding, cornerCutoutPadding.first);
+ rightCornerCutoutPadding = Math.max(rightCornerCutoutPadding,
+ cornerCutoutPadding.second);
}
return new Pair<>(
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 56aae17..c637123 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.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 @@
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 5411839..bae5bb4 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.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 @@
}
if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()
!= pd.mRotation && isImeShowing(displayId)) {
- pd.startAnimation(true);
+ pd.startAnimation(true, false /* forceRestart */);
}
}
@@ -200,7 +201,15 @@
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 @@
return;
}
if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
- startAnimation(true /* show */);
+ startAnimation(true /* show */, false /* forceRestart */);
}
@Override
@@ -221,7 +230,7 @@
return;
}
if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
- startAnimation(false /* show */);
+ startAnimation(false /* show */, false /* forceRestart */);
}
/**
@@ -239,7 +248,7 @@
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 @@
+ (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 @@
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
index 464a740..072bc44 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
@@ -22,6 +22,8 @@
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
@@ -50,25 +52,46 @@
private lateinit var mediaMetadata: MediaMetadata.Builder
private lateinit var entry: NotificationEntryBuilder
@Mock private lateinit var mockView: View
- private lateinit var textView: TextView
+ 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)
- mockView = mock(View::class.java)
- textView = TextView(context)
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
@@ -87,34 +110,36 @@
@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() {
- whenever<TextView>(mockView.findViewById(R.id.header_title)).thenReturn(textView)
val song: String = "Song"
mediaMetadata.putText(MediaMetadata.METADATA_KEY_TITLE, song)
keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
- assertThat(textView.getText()).isEqualTo(song)
+ assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
+ assertThat(songView.getText()).isEqualTo(song)
}
@Test
public fun testArtistName() {
- whenever<TextView>(mockView.findViewById(R.id.header_artist)).thenReturn(textView)
val artist: String = "Artist"
mediaMetadata.putText(MediaMetadata.METADATA_KEY_ARTIST, artist)
keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
- assertThat(textView.getText()).isEqualTo(artist)
+ assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
+ assertThat(artistView.getText()).isEqualTo(artist)
}
}
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 6e612d7..6f3fbb9 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.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.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 @@
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
+ private SysUiState mSysUiState;
+ private boolean mSysUiStateBubblesExpanded;
+
@Captor
private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
@Captor
@@ -229,6 +236,11 @@
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 @@
mNotifPipeline,
mFeatureFlagsOldPipeline,
mDumpManager,
- mFloatingContentCoordinator);
+ mFloatingContentCoordinator,
+ mSysUiState);
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
mBubbleController.setExpandListener(mBubbleExpandListener);
@@ -277,6 +290,7 @@
assertTrue(mBubbleController.hasBubbles());
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -284,6 +298,7 @@
assertFalse(mBubbleController.hasBubbles());
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -300,6 +315,25 @@
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 @@
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getEntry().getSbn()), anyInt());
assertFalse(mBubbleController.hasBubbles());
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -340,6 +376,8 @@
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 @@
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 @@
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
assertFalse(mBubbleController.isStackExpanded());
assertFalse(mNotificationShadeWindowController.getBubbleExpanded());
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -395,6 +437,8 @@
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 @@
// Collapse
mBubbleController.collapseStack();
assertFalse(mBubbleController.isStackExpanded());
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -437,6 +483,8 @@
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 @@
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 @@
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleController.expandStack();
+ assertTrue(mSysUiStateBubblesExpanded);
+
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
@@ -522,6 +574,8 @@
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
assertFalse(mBubbleController.hasBubbles());
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -541,6 +595,8 @@
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -559,6 +615,8 @@
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+ assertTrue(mSysUiStateBubblesExpanded);
}
@Test
@@ -579,6 +637,8 @@
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -605,6 +665,8 @@
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+ assertFalse(mSysUiStateBubblesExpanded);
}
@Test
@@ -619,6 +681,8 @@
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 6244644..a31e3f8d 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.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 @@
private KeyguardBypassController mKeyguardBypassController;
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
+
+ private SysUiState mSysUiState = new SysUiState();
+
@Captor
private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
private TestableBubbleController mBubbleController;
@@ -242,7 +246,8 @@
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 d3d90c4..f486102 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 @@
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 @@
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 d5a654d..eb4d438 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
@@ -307,6 +307,7 @@
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 +338,7 @@
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)
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 f061f34..f4583f9 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.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.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 @@
@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 @@
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 @@
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 @@
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/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 261dc82..f4fbd7b 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 @@
/* 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 0000000..87fc020
--- /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 c4f3a16..4f48108 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 @@
}
@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 @@
* 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 e84f9cf..85acbe6 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 @@
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.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 @@
@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 @@
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 @@
.build());
// THEN filter out the notification
- assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertTrue(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
}
@Test
@@ -98,13 +101,13 @@
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 @@
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/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
index 487885a..85b5d70 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.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.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 @@
private KeyguardBypassController mKeyguardBypassController;
@Mock
private DockManager mDockManager;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private HeadsUpManagerPhone mHeadsUpManagerPhone;
@Before
@@ -81,7 +89,8 @@
mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils,
mShadeController, mAccessibilityController, mKeyguardIndicationController,
mStatusBarStateController, mConfigurationController, mNotificationWakeUpCoordinator,
- mKeyguardBypassController, mDockManager);
+ mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources,
+ mHeadsUpManagerPhone);
mLockIconController.attach(mLockIcon);
}
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 2fbba68..6af5fe5 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -60,6 +60,7 @@
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 @@
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 36113ac..c84892d 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -303,7 +303,8 @@
final UserManager userManager = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
- mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
+ mTetheringRestriction = new UserRestrictionActionListener(
+ userManager, this, mNotificationUpdater);
mExecutor = new TetheringThreadExecutor(mHandler);
mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
@@ -369,9 +370,10 @@
mActiveDataSubId = subId;
updateConfiguration();
+ mNotificationUpdater.onActiveDataSubscriptionIdChanged(subId);
// To avoid launching unexpected provisioning checks, ignore re-provisioning
// when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning()
- // ill be triggered again when CarrierConfig is loaded.
+ // will be triggered again when CarrierConfig is loaded.
if (mEntitlementMgr.getCarrierConfig(mConfig) != null) {
mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
} else {
@@ -431,9 +433,7 @@
// Called by wifi when the number of soft AP clients changed.
@Override
public void onConnectedClientsChanged(final List<WifiClient> clients) {
- if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, clients)) {
- reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
- }
+ updateConnectedClients(clients);
}
}
@@ -635,7 +635,10 @@
Context.ETHERNET_SERVICE);
synchronized (mPublicSync) {
if (enable) {
- if (mEthernetCallback != null) return TETHER_ERROR_NO_ERROR;
+ if (mEthernetCallback != null) {
+ Log.d(TAG, "Ethernet tethering already started");
+ return TETHER_ERROR_NO_ERROR;
+ }
mEthernetCallback = new EthernetCallback();
mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback);
@@ -996,11 +999,14 @@
protected static class UserRestrictionActionListener {
private final UserManager mUserManager;
private final Tethering mWrapper;
+ private final TetheringNotificationUpdater mNotificationUpdater;
public boolean mDisallowTethering;
- public UserRestrictionActionListener(UserManager um, Tethering wrapper) {
+ public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper,
+ @NonNull TetheringNotificationUpdater updater) {
mUserManager = um;
mWrapper = wrapper;
+ mNotificationUpdater = updater;
mDisallowTethering = false;
}
@@ -1019,13 +1025,21 @@
return;
}
- // TODO: Add user restrictions notification.
- final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0);
-
- if (newlyDisallowed && isTetheringActiveOnDevice) {
- mWrapper.untetherAll();
- // TODO(b/148139325): send tetheringSupported on restriction change
+ if (!newlyDisallowed) {
+ // Clear the restricted notification when user is allowed to have tethering
+ // function.
+ mNotificationUpdater.tetheringRestrictionLifted();
+ return;
}
+
+ // Restricted notification is shown when tethering function is disallowed on
+ // user's device.
+ mNotificationUpdater.notifyTetheringDisabledByRestriction();
+
+ // Untether from all downstreams since tethering is disallowed.
+ mWrapper.untetherAll();
+
+ // TODO(b/148139325): send tetheringSupported on restriction change
}
}
@@ -1559,6 +1573,7 @@
mIPv6TetheringCoordinator.removeActiveDownstream(who);
mOffload.excludeDownstreamInterface(who.interfaceName());
mForwardedDownstreams.remove(who);
+ updateConnectedClients(null /* wifiClients */);
// If this is a Wi-Fi interface, tell WifiManager of any errors
// or the inactive serving state.
@@ -2141,6 +2156,12 @@
return false;
}
+ private void updateConnectedClients(final List<WifiClient> wifiClients) {
+ if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, wifiClients)) {
+ reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
+ }
+ }
+
private IpServer.Callback makeControlCallback() {
return new IpServer.Callback() {
@Override
@@ -2155,10 +2176,7 @@
@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 b97f752..992cdd8 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.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 @@
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 @@
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 void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
if (mDownstreamTypesMask == downstreamTypesMask) return;
mDownstreamTypesMask = downstreamTypesMask;
- updateNotification();
+ updateEnableNotification();
}
- private void updateNotification() {
+ /** Called when active data subscription id changed */
+ public void onActiveDataSubscriptionIdChanged(final int subId) {
+ if (mActiveDataSubId == subId) return;
+ mActiveDataSubId = subId;
+ updateEnableNotification();
+ }
+
+ @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 @@
*
* @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 @@
*
* @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 @@
}
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 @@
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 @@
.setContentIntent(pi)
.build();
- mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification);
+ mNotificationManager.notify(null /* tag */, id, notification);
}
}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt
new file mode 100644
index 0000000..b869491
--- /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 60d7ad1..5ead110 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 @@
private PhoneStateListener mPhoneStateListener;
private InterfaceConfigurationParcel mInterfaceConfiguration;
-
private class TestContext extends BroadcastInterceptingContext {
TestContext(Context base) {
super(base);
@@ -1073,13 +1072,15 @@
when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions);
final Tethering.UserRestrictionActionListener ural =
- new Tethering.UserRestrictionActionListener(mUserManager, mockTethering);
+ new Tethering.UserRestrictionActionListener(
+ mUserManager, mockTethering, mNotificationUpdater);
ural.mDisallowTethering = currentDisallow;
ural.onUserRestrictionsChanged();
- verify(mockTethering, times(expectedInteractionsWithShowNotification))
- .untetherAll();
+ verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification))
+ .notifyTetheringDisabledByRestriction();
+ verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll();
}
@Test
@@ -1087,7 +1088,7 @@
final String[] emptyActiveIfacesList = new String[]{};
final boolean currDisallow = false;
final boolean nextDisallow = true;
- final int expectedInteractionsWithShowNotification = 0;
+ final int expectedInteractionsWithShowNotification = 1;
runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList,
expectedInteractionsWithShowNotification);
@@ -1399,6 +1400,7 @@
mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration();
assertEquals(fakeSubId, newConfig.activeDataSubId);
+ verify(mNotificationUpdater, times(1)).onActiveDataSubscriptionIdChanged(eq(fakeSubId));
}
@Test
diff --git a/services/Android.bp b/services/Android.bp
index 490481c..52c5993 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -142,6 +142,11 @@
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 60c3d78..1b180e3 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 @@
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
userState.mUserId, currentTargets, str -> str);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
-
- // Disable accessibility shortcut key if there's no shortcut installed.
- if (currentTargets.isEmpty()) {
- final long identity = Binder.clearCallingIdentity();
- try {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 0, userState.mUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
}
private boolean canRequestAndRequestsTouchExplorationLocked(
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index e49c1ed..c6a54fc 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.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.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 @@
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 @@
@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 @@
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 @@
}
/**
+ * 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 6fbe141..d1805d9 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.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.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.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 @@
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 @@
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 @@
mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
mContentCaptureManagerInternal = LocalServices.getService(
ContentCaptureManagerInternal.class);
-
+ mDisabledInfoCache = disableCache;
updateLocked(disabled);
}
@@ -1045,45 +1033,7 @@
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 @@
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 @@
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 @@
}
}
- // 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 5de8171..4ba2c3d 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 @@
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 c39e93a..2306329 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 @@
Slog.w(TAG, "InlinePresentation not found in dataset");
continue;
}
- if (!includeDataset(dataset, fieldIndex, filterText)) {
+ if (!inlinePresentation.isPinned() // don't filter pinned suggestions
+ && !includeDataset(dataset, fieldIndex, filterText)) {
continue;
}
InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
index b54ec4e..0fdabd0 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 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 @@
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 @@
}
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 7828050..cf53b16 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 @@
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 @@
}
@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/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index f1f5005..8dd4fa6 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -1101,6 +1101,9 @@
* 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 03ca1c6..1bf559a 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -112,9 +112,13 @@
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 @@
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 @@
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 @@
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 @@
mBluetoothLock.readLock().unlock();
}
- mQuietEnable = (msg.arg1 == 1);
+ mQuietEnable = (quietEnable == 1);
if (mBluetooth == null) {
handleEnable(mQuietEnable);
} else {
@@ -1711,8 +1728,8 @@
// 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,34 +1739,109 @@
// 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));
- 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));
+ mWaitForDisableRetry = 0;
+ Message disableDelayedMsg =
+ mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0);
+ mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
} else {
mEnable = false;
handleDisable();
}
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) {
if (DBG) {
@@ -2124,6 +2216,7 @@
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 @@
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 ec84ae7..76a8e14 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_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 java.util.Map.Entry;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
@@ -2702,10 +2704,18 @@
switch (msg.what) {
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
- final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
+ NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
if (networkCapabilities.hasConnectivityManagedCapability()) {
Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
}
+ if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+ // Make sure the original object is not mutated. NetworkAgent normally
+ // makes a copy of the capabilities when sending the message through
+ // the Messenger, but if this ever changes, not making a defensive copy
+ // here will give attack vectors to clients using this code path.
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ networkCapabilities.restrictCapabilitesForTestNetwork();
+ }
updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
break;
}
@@ -5778,7 +5788,16 @@
public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
- enforceNetworkFactoryPermission();
+ if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+ enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
+ // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
+ // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
+ // sees capabilities that may be malicious, which might prevent mistakes in the future.
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ networkCapabilities.restrictCapabilitesForTestNetwork();
+ } else {
+ enforceNetworkFactoryPermission();
+ }
LinkProperties lp = new LinkProperties(linkProperties);
lp.ensureDirectlyConnectedRoutes();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0bf81e0..4d8c86c 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.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.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 @@
refreshIsolatedStorageSettings();
}
});
+ updateLegacyStorageOpSticky();
// For now, simply clone property when it changes
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
mContext.getMainExecutor(), (properties) -> {
@@ -1778,6 +1781,13 @@
}
}
+ 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 @@
}
}
+ @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 @@
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 8f914fe..94d6b13 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.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 @@
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 @@
runnable.run();
} catch (RuntimeException e) {
Slog.e(TAG, "Failure in " + description + ": " + e, e);
+ traceLog.traceEnd();
throw e;
}
synchronized (mPendingTasks) {
@@ -108,6 +112,7 @@
if (IS_DEBUGGABLE) {
Slog.d(TAG, "Finished executing " + description);
}
+ traceLog.traceEnd();
});
}
@@ -132,7 +137,10 @@
*/
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 @@
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
dumpStackTraces();
+ t.traceEnd();
throw new IllegalStateException(TAG + " init interrupted");
}
if (!terminated) {
@@ -160,11 +169,13 @@
synchronized (sInstance.mPendingTasks) {
copy.addAll(sInstance.mPendingTasks);
}
+ t.traceEnd();
throw new IllegalStateException("Cannot shutdown. Unstarted tasks "
+ unstartedRunnables + " Unfinished tasks " + copy);
}
sInstance = null; // Make eligible for GC
Slog.d(TAG, "Shutdown successful");
+ t.traceEnd();
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4cfcd2b..8b2976d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8780,6 +8780,27 @@
}
@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 03f4a54..ba89fce 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 @@
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 @@
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 4d08bd2..bee0e05 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.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.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Activity manager code dealing with processes.
@@ -2155,15 +2157,6 @@
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 @@
app.setHasForegroundActivities(true);
}
+ final Map<String, Pair<String, Long>> pkgDataInfoMap;
+ final Map<String, Pair<String, Long>> whitelistedAppDataInfoMap;
+ boolean 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);
- final Map<String, Pair<String, Long>> pkgDataInfoMap;
- 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;
- }
+ 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 @@
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 @@
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 1f0146a..94675ab 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -776,6 +776,8 @@
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 @@
mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
- mRecordMonitor = new RecordingActivityMonitor(mContext);
-
readAndSetLowRamDevice();
mIsCallScreeningModeSupported = AudioSystem.isCallScreeningModeSupported();
@@ -1981,8 +1981,7 @@
}
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 @@
!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 @@
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 @@
// 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 @@
}
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 @@
if (streamType == AudioSystem.STREAM_MUSIC) {
flags = updateFlagsForTvPlatform(flags);
- if (mFullVolumeDevices.contains(device)) {
+ if (isFullVolumeDevice(device)) {
flags &= ~AudioManager.FLAG_SHOW_UI;
}
}
@@ -2826,7 +2831,7 @@
int device,
boolean force,
String caller) {
- if (mFullVolumeDevices.contains(device)) {
+ if (isFullVolumeDevice(device)) {
return;
}
VolumeStreamState streamState = mStreamStates[streamType];
@@ -3036,7 +3041,7 @@
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 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 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 @@
} 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 @@
} 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 @@
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 @@
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 5c50962..65f2218 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 @@
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.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 @@
// 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 @@
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 @@
* 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 @@
}
/**
+ * 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 d45ffd9..ff8e3a9 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 @@
// 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 @@
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 766e5c4..5d334c2 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 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 @@
}
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 e7c09ba..233416d 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 @@
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 @@
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 @@
+ " 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 @@
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 @@
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 @@
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 ebd407d..45b9383 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 @@
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 @@
}
@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 fd54129..3ecf87c 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 @@
}
@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 acb1a2f..8520f5a 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 @@
}
@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 05a757b..8eb7710 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 @@
+ "id=" + physicalDisplayId
+ ", state=" + Display.stateToString(state) + ")");
}
- mBacklight.setVrMode(isVrEnabled);
+ if (mBacklight != null) {
+ mBacklight.setVrMode(isVrEnabled);
+ }
}
private void setDisplayState(int state) {
@@ -708,7 +710,9 @@
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 87fe785..ad08663 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 @@
*/
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 @@
final long ident = Binder.clearCallingIdentity();
try {
+ Intent intent = new Intent(INTENT_CHECK_USER_CONSENT);
+ intent.setPackage(callingPackage);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ getContext().sendBroadcast(intent, android.Manifest.permission.DUMP);
+
mPendingReports.authorizeReport(callingUid, callingPackage,
receiverClass, reportId, flags, listener);
} finally {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 0b22586..e6129b9 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -76,7 +76,6 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputMonitor;
-import android.view.InputWindowHandle;
import android.view.KeyEvent;
import android.view.PointerIcon;
import android.view.Surface;
@@ -221,8 +220,7 @@
int policyFlags);
private static native VerifiedInputEvent nativeVerifyInputEvent(long ptr, InputEvent event);
private static native void nativeToggleCapsLock(long ptr, int deviceId);
- private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles,
- int displayId);
+ private static native void nativeDisplayRemoved(long ptr, int displayId);
private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
private static native void nativeSetFocusedApplication(long ptr,
@@ -1536,7 +1534,7 @@
/** Clean up input window handles of the given display. */
public void onDisplayRemoved(int displayId) {
- nativeSetInputWindows(mPtr, null /* windowHandles */, displayId);
+ nativeDisplayRemoved(mPtr, displayId);
}
@Override
diff --git a/services/core/java/com/android/server/lights/LightsManager.java b/services/core/java/com/android/server/lights/LightsManager.java
index 521913a..706c741 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 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 a42dec8..3c6e8d2 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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,39 +451,40 @@
}
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);
}
- // 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]);
+ 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);
}
}
}
+ 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));
+ }
+ }
+
@Override
public void onStart() {
publishLocalService(LightsManager.class, mService);
@@ -505,25 +506,14 @@
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/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 58e332a..c1fbcfb 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -653,9 +653,6 @@
mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
- mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
- GnssLocationProvider.this::onNetworkAvailable, mLooper);
-
// App ops service to keep track of who is accessing the GPS
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -677,6 +674,9 @@
mNIHandler = new GpsNetInitiatedHandler(context,
mNetInitiatedListener,
mSuplEsEnabled);
+ mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
+ GnssLocationProvider.this::onNetworkAvailable, mLooper, mNIHandler);
+
sendMessage(INITIALIZE_HANDLER, 0, null);
mGnssStatusListenerHelper = new GnssStatusListenerHelper(mContext, mHandler) {
diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
index 93227bd..5d6474b 100644
--- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
@@ -29,12 +29,22 @@
import android.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 @@
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 @@
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 @@
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 @@
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 @@
}
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ // During an emergency call with an active sub id, get the Telephony Manager specific
+ // to the active sub to get the correct value from getServiceState and getNetworkType
+ if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {
+ TelephonyManager subIdTelManager =
+ phone.createForSubscriptionId(mActiveSubId);
+ if (subIdTelManager != null) {
+ phone = subIdTelManager;
+ }
+ }
ServiceState serviceState = phone.getServiceState();
String projection = null;
String selection = null;
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index 7792123..f2eb176 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -157,8 +157,8 @@
}
/**
- * @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) {
@@ -184,8 +184,17 @@
}
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 35dec5a..31dc094 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -139,6 +139,7 @@
import android.app.usage.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;
@@ -387,10 +388,9 @@
* 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;
@@ -1579,7 +1579,9 @@
}
} 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);
@@ -2751,24 +2753,18 @@
@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);
@@ -2807,11 +2803,15 @@
}
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());
@@ -2824,11 +2824,6 @@
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;
@@ -3453,7 +3448,7 @@
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()))));
@@ -3476,7 +3471,7 @@
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))));
@@ -3638,13 +3633,23 @@
}
/**
+ * @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,
@@ -3654,7 +3659,8 @@
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()];
@@ -3736,12 +3742,24 @@
}
/**
- * 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,
@@ -3751,7 +3769,8 @@
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);
@@ -3767,7 +3786,8 @@
@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,
@@ -3775,7 +3795,8 @@
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");
@@ -4174,7 +4195,7 @@
@Override
public int getInterruptionFilterFromListener(INotificationListener token)
throws RemoteException {
- synchronized (mNotificationLight) {
+ synchronized (mNotificationLock) {
return mInterruptionFilter;
}
}
@@ -5647,7 +5668,8 @@
}
}
- 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)) {
@@ -6753,7 +6775,7 @@
if (canShowLightsLocked(record, aboveThreshold)) {
mLights.add(key);
updateLightsLocked();
- if (mUseAttentionLight) {
+ if (mUseAttentionLight && mAttentionLight != null) {
mAttentionLight.pulse();
}
blink = true;
@@ -7954,6 +7976,10 @@
@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 7bbb3b1..f1ce3a7 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 @@
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 @@
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/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 7069818..4087675 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 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";
@@ -447,7 +448,7 @@
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;
}
@@ -577,6 +578,24 @@
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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b79f75a..58a9d9c 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.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.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 @@
private final OverlayConfig mOverlayConfig;
+ @GuardedBy("itself")
+ final private ArrayList<IPackageChangeObserver> mPackageChangeObservers =
+ new ArrayList<>();
+
/**
* Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
*
@@ -5064,15 +5070,16 @@
* action and a {@code android.intent.category.BROWSABLE} category</li>
* </ul>
*/
- int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps) {
+ int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
+ boolean matchSystemOnly) {
return updateFlagsForResolve(flags, userId, callingUid,
- wantInstantApps, false /*onlyExposedExplicitly*/);
+ wantInstantApps, matchSystemOnly, false /*onlyExposedExplicitly*/);
}
int updateFlagsForResolve(int flags, int userId, int callingUid,
- boolean wantInstantApps, boolean onlyExposedExplicitly) {
+ boolean wantInstantApps, boolean onlyExposedExplicitly, boolean matchSystemOnly) {
// Safe mode means we shouldn't match any third-party components
- if (mSafeMode) {
+ if (mSafeMode || matchSystemOnly) {
flags |= PackageManager.MATCH_SYSTEM_ONLY;
}
if (getInstantAppPackageName(callingUid) != null) {
@@ -6164,7 +6171,8 @@
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart);
+ flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
@@ -6196,7 +6204,8 @@
intent = updateIntentForResolve(intent);
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
final int flags = updateFlagsForResolve(
- 0, userId, callingUid, false /*includeInstantApps*/);
+ 0, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
synchronized (mLock) {
@@ -6517,7 +6526,8 @@
android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
flags = updateFlagsForResolve(
- flags, userId, callingUid, false /*includeInstantApps*/);
+ flags, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
intent = updateIntentForResolve(intent);
// writer
synchronized (mLock) {
@@ -6729,7 +6739,8 @@
}
synchronized (mLock) {
int flags = updateFlagsForResolve(0, parent.id, callingUid,
- false /*includeInstantApps*/);
+ false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
intent, resolvedType, flags, sourceUserId, parent.id);
return xpDomainInfo != null;
@@ -6815,7 +6826,8 @@
}
flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
- comp != null || pkgName != null /*onlyExposedExplicitly*/);
+ comp != null || pkgName != null /*onlyExposedExplicitly*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
@@ -7600,7 +7612,8 @@
String resolvedType, int flags, int userId) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent activity options");
@@ -7786,7 +7799,8 @@
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -7876,7 +7890,8 @@
private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
int userId, int callingUid) {
if (!mUserManager.exists(userId)) return null;
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ false /* matchSystemOnly */);
List<ResolveInfo> query = queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
if (query != null) {
@@ -7907,7 +7922,8 @@
false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps);
+ flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
+ false /* matchSystemOnly */);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -8044,7 +8060,8 @@
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ false /* matchSystemOnly */);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -16458,9 +16475,56 @@
// BackgroundDexOptService will remove it from its blacklist.
// TODO: Layering violation
BackgroundDexOptService.notifyPackageChanged(packageName);
+
+ notifyPackageChangeObserversOnUpdate(reconciledPkg);
}
}
+ private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
+ final PackageSetting pkgSetting = reconciledPkg.pkgSetting;
+ final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.installResult;
+ final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.removedInfo;
+
+ PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
+ pkgChangeEvent.packageName = pkgSetting.pkg.getPackageName();
+ pkgChangeEvent.version = pkgSetting.versionCode;
+ pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.lastUpdateTime;
+ pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.isUpdate);
+ pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.dataRemoved);
+ pkgChangeEvent.isDeleted = false;
+
+ notifyPackageChangeObservers(pkgChangeEvent);
+ }
+
+ private void notifyPackageChangeObserversOnDelete(String packageName, long version) {
+ PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
+ pkgChangeEvent.packageName = packageName;
+ pkgChangeEvent.version = version;
+ pkgChangeEvent.lastUpdateTimeMillis = 0L;
+ pkgChangeEvent.newInstalled = false;
+ pkgChangeEvent.dataRemoved = false;
+ pkgChangeEvent.isDeleted = true;
+
+ notifyPackageChangeObservers(pkgChangeEvent);
+ }
+
+ private void notifyPackageChangeObservers(PackageChangeEvent event) {
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "notifyPackageChangeObservers");
+ synchronized (mPackageChangeObservers) {
+ for(IPackageChangeObserver observer : mPackageChangeObservers) {
+ try {
+ observer.onPackageChanged(event);
+ } catch(RemoteException e) {
+ Log.wtf(TAG, e);
+ }
+ }
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
/**
* The set of data needed to successfully install the prepared package. This includes data that
* will be used to scan and reconcile the package.
@@ -17521,6 +17585,7 @@
} catch (RemoteException e) {
Log.i(TAG, "Observer no longer exists.");
} //end catch
+ notifyPackageChangeObserversOnDelete(packageName, versionCode);
});
}
@@ -22980,8 +23045,49 @@
}
}
+ private final class PackageChangeObserverDeathRecipient implements IBinder.DeathRecipient {
+ private final IPackageChangeObserver mObserver;
+
+ PackageChangeObserverDeathRecipient(IPackageChangeObserver observer) {
+ mObserver = observer;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mPackageChangeObservers) {
+ mPackageChangeObservers.remove(mObserver);
+ Log.d(TAG, "Size of mPackageChangeObservers after removing dead observer is "
+ + mPackageChangeObservers.size());
+ }
+ }
+ }
+
private class PackageManagerNative extends IPackageManagerNative.Stub {
@Override
+ public void registerPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
+ synchronized (mPackageChangeObservers) {
+ try {
+ observer.asBinder().linkToDeath(
+ new PackageChangeObserverDeathRecipient(observer), 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ mPackageChangeObservers.add(observer);
+ Log.d(TAG, "Size of mPackageChangeObservers after registry is "
+ + mPackageChangeObservers.size());
+ }
+ }
+
+ @Override
+ public void unregisterPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
+ synchronized (mPackageChangeObservers) {
+ mPackageChangeObservers.remove(observer);
+ Log.d(TAG, "Size of mPackageChangeObservers after unregistry is "
+ + mPackageChangeObservers.size());
+ }
+ }
+
+ @Override
public String[] getAllPackages() {
return PackageManagerService.this.getAllPackages().toArray(new String[0]);
}
@@ -24381,7 +24487,8 @@
} 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 3cc10d1..5a1e8e2 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 @@
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 79d0c2d..04c965e 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.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISALLOWED;
+import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED;
import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
@@ -3238,31 +3240,25 @@
@Override
public List<String> getAutoRevokeExemptionRequestedPackages(int userId) {
- mContext.enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
- "Must hold " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
-
- List<String> result = new ArrayList<>();
- mPackageManagerInt.forEachInstalledPackage(pkg -> {
- if (pkg.isDontAutoRevokePermmissions()) {
- result.add(pkg.getPackageName());
- }
- }, userId);
-
- return result;
+ return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISCOURAGED, userId);
}
@Override
public List<String> getAutoRevokeExemptionGrantedPackages(int userId) {
+ return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISALLOWED, userId);
+ }
+
+ @NonNull
+ private List<String> getPackagesWithAutoRevokePolicy(int autoRevokePolicy, int userId) {
mContext.enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
"Must hold " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
List<String> result = new ArrayList<>();
mPackageManagerInt.forEachInstalledPackage(pkg -> {
- if (pkg.isAllowDontAutoRevokePermmissions()) {
+ if (pkg.getAutoRevokePermissions() == autoRevokePolicy) {
result.add(pkg.getPackageName());
}
}, userId);
-
return result;
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 161f304..27288d8 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.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.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.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 @@
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 @@
manager.updateUserSensitiveForApp(uid);
}
}, UserHandle.ALL, intentFilter, null, null);
- */
}
/**
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index 39aeafc..d6c48a0 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_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.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 @@
}
};
+ 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 @@
shouldPreserveLegacyExternalStorage = pkg.hasPreserveLegacyExternalStorage()
&& smInternal.hasLegacyExternalStorage(appInfo.uid);
targetSDK = getMinimumTargetSDK(context, appInfo, user);
+ // LEGACY_STORAGE op is normally sticky for apps targetig <= Q.
+ // However, this device can be configured to make it non-sticky.
+ boolean isLegacyAppOpSticky = isLegacyStorageAppOpStickyGlobal
+ && targetSDK <= Build.VERSION_CODES.Q;
shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0
- || (targetSDK > Build.VERSION_CODES.Q
- && !shouldPreserveLegacyExternalStorage);
+ || (!isLegacyAppOpSticky && !shouldPreserveLegacyExternalStorage);
} else {
isWhiteListed = false;
shouldApplyRestriction = false;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 5025835..86ff926 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3531,7 +3531,9 @@
}
// 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) {
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index acf3f79..74958b6 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 74c3a9e..2783d0b 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 @@
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 @@
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 @@
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 63048f6..a9e8d3f 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.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 @@
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 @@
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 @@
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/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 722d9f7..ddf166e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2441,9 +2441,8 @@
* 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);
}
@@ -2511,6 +2510,8 @@
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;
@@ -2520,10 +2521,8 @@
}
// 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);
@@ -2556,7 +2555,7 @@
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);
@@ -2571,6 +2570,8 @@
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);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6d53786..2f814f5 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 @@
/** 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 @@
} 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 @@
* and {@link #shouldPauseActivity(ActivityRecord)}.
*/
private boolean shouldStartActivity() {
- return mVisibleRequested && isState(STOPPED);
+ return mVisibleRequested && (isState(STOPPED) || isState(STOPPING));
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index e8bfe8e..2378813 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.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 @@
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 @@
}
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 @@
}
@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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- @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()
@@ -2359,7 +2343,7 @@
// 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);
+ (ar) -> ar.occludesParent(), true, 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 +2572,13 @@
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();
@@ -3085,9 +3076,8 @@
}
// 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 = getActivity((ar) -> ar.occludesParent(), r,
+ false /*includeBoundary*/, true /*traverseTopToBottom*/);
if (occludingActivity != null) return false;
if (r.finishing) Slog.e(TAG, "willActivityBeVisible: Returning false,"
@@ -3542,6 +3532,10 @@
@Override
void onChildPositionChanged(WindowContainer child) {
+ if (isOrganized()) {
+ mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
+ }
+
if (!mChildren.contains(child)) {
return;
}
@@ -3572,11 +3566,6 @@
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 +3603,7 @@
@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 +3613,6 @@
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 +3636,6 @@
@Override
void onDisplayChanged(DisplayContent dc) {
- if (mTile != null && dc != mTile.getDisplay()) {
- mTile.removeChild(this);
- }
super.onDisplayChanged(dc);
if (isRootTask()) {
updateSurfaceBounds();
@@ -3781,20 +3748,6 @@
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 +3778,6 @@
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 +3822,7 @@
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 57f357d..4652f49 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 @@
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 @@
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 @@
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 @@
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 @@
"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 14ca7cb..da1c045 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.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.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 @@
}
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 edc87e5..2263795 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 @@
/**
* 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 f7278e7..a5b0026 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -31,12 +31,12 @@
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -122,6 +122,7 @@
import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.Manifest;
import android.annotation.IntDef;
@@ -144,7 +145,6 @@
import android.app.IAssistDataReceiver;
import android.app.INotificationManager;
import android.app.IRequestFinishCallback;
-import android.window.ITaskOrganizerController;
import android.app.ITaskStackListener;
import android.app.Notification;
import android.app.NotificationManager;
@@ -230,9 +230,9 @@
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
+import android.view.WindowManager;
import android.window.IWindowOrganizerController;
import android.window.WindowContainerTransaction;
-import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -1274,9 +1274,13 @@
a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchActivityType(ACTIVITY_TYPE_DREAM);
+
try {
getActivityStartController().obtainStarter(intent, "dream")
.setActivityInfo(a)
+ .setActivityOptions(options.toBundle())
.setIsDream(true)
.execute();
return true;
@@ -2349,16 +2353,18 @@
}
final ActivityStack stack = task.getStack();
- // Convert some windowing-mode changes into root-task reparents for split-screen.
- if (stack.getTile() != null) {
- stack.getDisplay().onSplitScreenModeDismissed();
- }
if (toTop) {
stack.moveToFront("setTaskWindowingMode", task);
}
- stack.setWindowingMode(windowingMode);
- stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
- true /* notifyClients */);
+ // Convert some windowing-mode changes into root-task reparents for split-screen.
+ if (stack.inSplitScreenWindowingMode()) {
+ stack.getDisplay().onSplitScreenModeDismissed();
+
+ } else {
+ stack.setWindowingMode(windowingMode);
+ stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+ true /* notifyClients */);
+ }
return true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2755,24 +2761,22 @@
}
final int prevMode = task.getWindowingMode();
- moveTaskToSplitScreenPrimaryTile(task, toTop);
+ moveTaskToSplitScreenPrimaryTask(task, toTop);
return prevMode != task.getWindowingMode();
}
- void moveTaskToSplitScreenPrimaryTile(Task task, boolean toTop) {
- ActivityStack stack = task.getStack();
- TaskTile tile = null;
- for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) {
- tile = stack.getDisplay().getStackAt(i).asTile();
- if (tile != null && tile.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- break;
- }
+ void moveTaskToSplitScreenPrimaryTask(Task task, boolean toTop) {
+ final DisplayContent display = task.getDisplayContent();
+ final ActivityStack primarySplitTask = display.getRootSplitScreenPrimaryTask();
+ if (primarySplitTask == null) {
+ throw new IllegalStateException("Can't enter split without associated organized task");
}
- if (tile == null) {
- throw new IllegalStateException("Can't enter split without associated tile");
+
+ if (toTop) {
+ display.positionStackAt(POSITION_TOP, primarySplitTask, false /* includingParents */);
}
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
+ wct.reparent(task.getStack().mRemoteToken, primarySplitTask.mRemoteToken, toTop);
mWindowOrganizerController.applyTransaction(wct);
}
@@ -3239,7 +3243,8 @@
final ActivityStack stack = r.getRootTask();
final Task task = stack.getDisplay().createStack(stack.getWindowingMode(),
- stack.getActivityType(), !ON_TOP, ainfo, intent);
+ stack.getActivityType(), !ON_TOP, ainfo, intent,
+ false /* createdByOrganizer */);
if (!mRecentTasks.addToBottom(task)) {
// The app has too many tasks already and we can't add any more
@@ -4278,19 +4283,9 @@
try {
synchronized (mGlobalLock) {
final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
- TaskTile primary = null;
- TaskTile secondary = null;
- for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final TaskTile t = dc.getStackAt(i).asTile();
- if (t == null) {
- continue;
- }
- if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- primary = t;
- } else if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- secondary = t;
- }
- }
+ final Task primary = dc.getRootSplitScreenPrimaryTask();
+ final Task secondary = dc.getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
+ && t.inSplitScreenSecondaryWindowingMode());
if (primary == null || secondary == null) {
return;
}
@@ -7477,10 +7472,10 @@
}
@Override
- public ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId,
- boolean isLowResolution) {
+ public ActivityManager.TaskSnapshot getTaskSnapshotBlocking(
+ int taskId, boolean isLowResolution) {
return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution,
- false /* restoreFromDisk */);
+ true /* restoreFromDisk */);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 33dd9cf..1036af6 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 @@
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/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7df731b..a90016a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -200,7 +200,6 @@
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;
@@ -217,6 +216,7 @@
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;
@@ -456,6 +456,8 @@
private final Configuration mTmpConfiguration = new Configuration();
+ private ArrayList<Task> mTmpTasks = new ArrayList<>();
+
/** Remove this display when animation on it has completed. */
private boolean mDeferredRemoval;
@@ -654,8 +656,11 @@
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;
@@ -748,6 +753,12 @@
+ " 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").
@@ -1306,8 +1317,6 @@
if (mDisplayRotation.isWaitingForRemoteRotation()) {
return;
}
- // Clear the record because the display will sync to current rotation.
- mFixedRotationLaunchingApp = null;
final boolean configUpdated = updateDisplayOverrideConfigurationLocked();
if (configUpdated) {
@@ -1498,7 +1507,7 @@
startFixedRotationTransform(r, rotation);
mAppTransition.registerListenerLocked(new WindowManagerInternal.AppTransitionListener() {
void done() {
- r.clearFixedRotationTransform();
+ r.finishFixedRotationTransform();
mAppTransition.unregisterListener(this);
}
@@ -1527,7 +1536,8 @@
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;
}
@@ -1573,7 +1583,7 @@
* @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();
@@ -2128,12 +2138,13 @@
}
/** @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() {
@@ -2600,7 +2611,7 @@
}
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);
@@ -2623,8 +2634,7 @@
// 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);
@@ -4014,6 +4024,9 @@
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 */);
@@ -4328,15 +4341,8 @@
@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) {
@@ -4375,10 +4381,6 @@
}
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)) {
@@ -4390,27 +4392,26 @@
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;
}
}
@@ -4430,6 +4431,7 @@
position = findPositionForStack(position, stack, true /* adding */);
super.addChild(stack, position);
+ mAtmService.updateSleepIfNeededLocked();
// The reparenting case is handled in WindowContainer.
if (!stack.mReparenting) {
@@ -4441,6 +4443,7 @@
protected void removeChild(ActivityStack stack) {
super.removeChild(stack);
mDisplayContent.onStackRemoved(stack);
+ mAtmService.updateSleepIfNeededLocked();
removeStackReferenceIfNeeded(stack);
}
@@ -4495,6 +4498,10 @@
*/
private int findPositionForStack(int requestedPosition, ActivityStack stack,
boolean adding) {
+ if (stack.isActivityTypeDream()) {
+ return POSITION_TOP;
+ }
+
if (stack.inPinnedWindowingMode()) {
return POSITION_TOP;
}
@@ -4645,10 +4652,9 @@
// 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) {
@@ -4792,17 +4798,6 @@
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> {
@@ -4983,7 +4978,7 @@
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} */
@@ -5654,7 +5649,14 @@
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) {
@@ -5665,7 +5667,6 @@
mPreferredTopFocusableStack = null;
}
releaseSelfIfNeeded();
- mAtmService.updateSleepIfNeededLocked();
onStackOrderChanged(stack);
}
@@ -5703,6 +5704,14 @@
"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);
@@ -5712,7 +5721,7 @@
// 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;
@@ -5755,18 +5764,54 @@
/**
* 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);
}
/**
@@ -5784,7 +5829,8 @@
// 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
@@ -5793,7 +5839,8 @@
}
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 */);
}
/**
@@ -5805,25 +5852,29 @@
* {@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);
@@ -5842,39 +5893,39 @@
}
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.");
@@ -5884,19 +5935,25 @@
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;
}
@@ -6031,7 +6088,7 @@
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);
}
@@ -6103,7 +6160,15 @@
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);
}
}
@@ -6117,13 +6182,8 @@
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);
@@ -6141,6 +6201,24 @@
}
}
+ 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.
@@ -6253,7 +6331,7 @@
}
}
- 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
@@ -6278,10 +6356,6 @@
}
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()) {
@@ -6418,15 +6492,20 @@
@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());
+ 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 =
- getRequestedOverrideConfiguration().diff(overrideConfiguration);
+ mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration);
super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
mCurrentOverrideConfigurationChanges = 0;
mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration, this);
@@ -6503,7 +6582,7 @@
// 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 */);
@@ -6607,27 +6686,35 @@
* 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;
}
@@ -6636,7 +6723,12 @@
|| 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;
}
}
@@ -6652,15 +6744,25 @@
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,
@@ -6697,19 +6799,6 @@
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 5e88fb0..f593393 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_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;
@@ -1493,14 +1492,8 @@
*/
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();
@@ -1527,25 +1520,7 @@
&& (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.
@@ -1567,6 +1542,28 @@
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()) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 60b817c..af89a05 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 @@
prepareNormalRotationAnimation();
}
- // TODO(b/147469351): Remove the restriction.
- if (mDisplayContent.mFixedRotationLaunchingApp == null) {
- // Give a remote handler (system ui) some time to reposition things.
- startRemoteRotation(oldRotation, mRotation);
- }
+ // Give a remote handler (system ui) some time to reposition things.
+ startRemoteRotation(oldRotation, mRotation);
return true;
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 88cdd17..18332b9 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -62,7 +62,7 @@
// When true, need to call updateInputWindowsLw().
private boolean mUpdateInputWindowsNeeded = true;
private boolean mUpdateInputWindowsPending;
- private boolean mApplyImmediately;
+ private boolean mUpdateInputWindowsImmediately;
// Currently focused input window handle.
private InputWindowHandle mFocusedInputWindowHandle;
@@ -347,14 +347,20 @@
}
}
- void updateInputWindowsImmediately() {
+ /**
+ * Immediately update the input transaction and merge into the passing Transaction that could be
+ * collected and applied later.
+ */
+ void updateInputWindowsImmediately(SurfaceControl.Transaction t) {
mHandler.removeCallbacks(mUpdateInputWindows);
- mApplyImmediately = true;
+ mUpdateInputWindowsImmediately = true;
mUpdateInputWindows.run();
- mApplyImmediately = false;
+ mUpdateInputWindowsImmediately = false;
+ t.merge(mInputTransaction);
}
- /* Called when the current input focus changes.
+ /**
+ * Called when the current input focus changes.
* Layer assignment is assumed to be complete by the time this is called.
*/
public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
@@ -465,10 +471,7 @@
if (mAddWallpaperInputConsumerHandle) {
mWallpaperInputConsumer.show(mInputTransaction, 0);
}
-
- if (mApplyImmediately) {
- mInputTransaction.apply();
- } else {
+ if (!mUpdateInputWindowsImmediately) {
mDisplayContent.getPendingTransaction().merge(mInputTransaction);
mDisplayContent.scheduleAnimation();
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index bb02789..fda70d1 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.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 @@
|| 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 @@
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 a4bdfb3..04454a5 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 @@
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 @@
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 @@
}
/** @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 @@
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 44034ed..6b39fd2 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 @@
// 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 bd5666d..244ba82 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -21,6 +21,7 @@
import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -1298,6 +1299,7 @@
switch (task.getActivityType()) {
case ACTIVITY_TYPE_HOME:
case ACTIVITY_TYPE_RECENTS:
+ case ACTIVITY_TYPE_DREAM:
// Ignore certain activity types completely
return false;
case ACTIVITY_TYPE_ASSISTANT:
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index adafdec..9089240 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -388,11 +388,12 @@
// surfaces needs to be done immediately.
mWindowManager.executeAppTransition();
- if (targetStack.getTile() != null) {
+ final Task rootTask = targetStack.getRootTask();
+ if (rootTask.isOrganized()) {
// Client state may have changed during the recents animation, so force
// send task info so the client can synchronize its state.
mService.mTaskOrganizerController.dispatchTaskInfoChanged(
- targetStack.mTile, true /* force */);
+ rootTask, true /* force */);
}
} catch (Exception e) {
Slog.e(TAG, "Failed to clean up recents activity", e);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 3e5cb50..a30b70d 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 @@
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 45f8a15..420997a 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_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,29 +232,6 @@
}
final ActivityTaskManagerService atmService = mTargetStack.mAtmService;
- final ArrayList<Task> createdTasks = new ArrayList<>();
- 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)) {
- // 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();
- 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*/);
- 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) {
@@ -264,16 +240,33 @@
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");
+ while (!mPendingReparentActivities.isEmpty()) {
+ final ActivityRecord r = mPendingReparentActivities.remove(0);
+ 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 = task;
+ if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
+ + r + " out to bottom task " + targetTask);
+ }
+ 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;
+ }
+ r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded");
+ atmService.mStackSupervisor.mRecentTasks.add(targetTask);
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ebf1bc9..6e56bf4 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 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -1970,8 +1971,7 @@
final int focusStackId = topFocusedStack != null
? topFocusedStack.getRootTaskId() : INVALID_TASK_ID;
// We dismiss the docked stack whenever we switch users.
- final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask();
- if (dockedStack != null) {
+ if (getDefaultDisplay().isSplitScreenModeActivated()) {
getDefaultDisplay().onSplitScreenModeDismissed();
}
// Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
@@ -2110,20 +2110,18 @@
final ActivityStack stack;
if (singleActivity) {
stack = r.getRootTask();
+ stack.setWindowingMode(WINDOWING_MODE_PINNED);
} else {
- // In the case of multiple activities, we will create a new stack for it and then
- // move the PIP activity into the stack.
- // We will then perform a windowing mode change for both scenarios.
- stack = display.createStack(
- r.getRootTask().getRequestedOverrideWindowingMode(),
- r.getActivityType(), ON_TOP, r.info, r.intent);
+ // In the case of multiple activities, we will create a new task for it and then
+ // move the PIP activity into the task.
+ stack = display.createStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP,
+ r.info, r.intent, false /* createdByOrganizer */);
+
// There are multiple activities in the task and moving the top activity should
// reveal/leave the other activities in their original task.
r.reparent(stack, MAX_VALUE, "moveActivityToStack");
}
- stack.setWindowingMode(WINDOWING_MODE_PINNED);
-
// Reset the state that indicates it can enter PiP while pausing after we've moved it
// to the pinned stack
r.supportsEnterPipOnTaskSwitch = false;
@@ -2799,16 +2797,19 @@
if (stack == null && r != null) {
stack = r.getRootTask();
}
+ int windowingMode = launchParams != null ? launchParams.mWindowingMode
+ : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
if (stack != null) {
display = stack.getDisplay();
if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
- int windowingMode = launchParams != null ? launchParams.mWindowingMode
- : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
windowingMode = display.resolveWindowingMode(r, options, candidateTask,
activityType);
}
- if (stack.isCompatible(windowingMode, activityType)) {
+ // Always allow organized tasks that created by organizer since the activity type
+ // of an organized task is decided by the activity type of its top child, which
+ // could be incompatible with the given windowing mode and activity type.
+ if (stack.isCompatible(windowingMode, activityType) || stack.mCreatedByOrganizer) {
return stack;
}
if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
@@ -2826,6 +2827,10 @@
if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
display = getDefaultDisplay();
+ if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+ windowingMode = display.resolveWindowingMode(r, options, candidateTask,
+ activityType);
+ }
}
return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
@@ -2847,7 +2852,8 @@
* @param candidateTask The possible task the activity might be put in.
* @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
*/
- private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+ @VisibleForTesting
+ ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
@Nullable Task candidateTask, @Nullable ActivityOptions options,
@Nullable LaunchParamsController.LaunchParams launchParams) {
final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
@@ -2868,6 +2874,13 @@
if (attachedDisplayId == INVALID_DISPLAY || attachedDisplayId == displayId) {
return candidateTask.getStack();
}
+ // Or the candidate task is already a root task that can be reused by reparenting
+ // it to the target display.
+ if (candidateTask.isRootTask()) {
+ final ActivityStack stack = candidateTask.getStack();
+ displayContent.moveStackToDisplay(stack, true /* onTop */);
+ return stack;
+ }
}
int windowingMode;
@@ -2915,11 +2928,10 @@
case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
+ case ACTIVITY_TYPE_DREAM: return r.isActivityTypeDream();
}
- // TODO(task-hierarchy): Find another way to differentiate tile from normal stack once it is
- // part of the hierarchy
- if (stack instanceof TaskTile) {
- // Don't launch directly into tiles.
+ if (stack.mCreatedByOrganizer) {
+ // Don't launch directly into task created by organizer...but why can't we?
return false;
}
// There is a 1-to-1 relationship between stack and task when not in
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 55dc694..7a41ea5 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.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.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.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
-import android.window.ITaskOrganizer;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.window.ITaskOrganizer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
@@ -489,6 +491,17 @@
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.
*/
@@ -578,6 +591,14 @@
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;
}
@@ -586,11 +607,7 @@
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());
}
@@ -1349,7 +1366,7 @@
if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
return applicationType;
}
- return getChildAt(0).getActivityType();
+ return getTopChild().getActivityType();
}
@Override
@@ -1362,6 +1379,12 @@
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();
@@ -1402,6 +1425,11 @@
@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");
}
@@ -1449,8 +1477,9 @@
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);
}
@@ -1862,7 +1891,12 @@
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) {
@@ -2296,18 +2330,30 @@
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) {
@@ -2396,7 +2442,9 @@
}
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
@@ -2580,7 +2628,7 @@
// 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.
@@ -3199,6 +3247,20 @@
}
@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();
}
@@ -3364,14 +3426,12 @@
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;
@@ -3402,10 +3462,6 @@
return this;
}
- TaskTile asTile() {
- return null;
- }
-
// TODO(task-merge): Figure-out how this should work with hierarchy tasks.
boolean shouldBeVisible(ActivityRecord starting) {
return true;
@@ -3701,8 +3757,9 @@
}
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,
@@ -3724,8 +3781,9 @@
*/
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,
@@ -4001,21 +4059,29 @@
@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 (isOrganized()) {
+ if (isOrganized() && isAlwaysOnTop()) {
return;
}
super.reparentSurfaceControl(t, newParent);
@@ -4045,11 +4111,13 @@
return true;
}
- // Called on Binder death.
- void taskOrganizerDied() {
+ void taskOrganizerUnregistered() {
mTaskOrganizer = null;
mLastTaskOrganizerWindowingMode = -1;
onTaskOrganizerChanged();
+ if (mCreatedByOrganizer) {
+ removeImmediately();
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 5523678..4382e9d 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -27,19 +28,22 @@
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import android.window.ITaskOrganizerController;
+import android.util.SparseArray;
import android.window.ITaskOrganizer;
+import android.window.ITaskOrganizerController;
import android.window.IWindowContainer;
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.WeakHashMap;
@@ -49,6 +53,7 @@
*/
class TaskOrganizerController extends ITaskOrganizerController.Stub {
private static final String TAG = "TaskOrganizerController";
+ private static final LinkedList<TaskOrganizerState> EMPTY_LIST = new LinkedList<>();
/**
* Masks specifying which configurations are important to report back to an organizer when
@@ -71,32 +76,20 @@
@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;
+ private class TaskOrganizerState {
+ private final ITaskOrganizer mOrganizer;
+ private final DeathRecipient mDeathRecipient;
+ private final int mWindowingMode;
+ private final ArrayList<Task> mOrganizedTasks = new ArrayList<>();
- 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;
-
-
- TaskOrganizerState(ITaskOrganizer organizer, int windowingMode,
- @Nullable TaskOrganizerState replacing) {
+ TaskOrganizerState(ITaskOrganizer organizer, int windowingMode) {
mOrganizer = organizer;
mDeathRecipient = new DeathRecipient(organizer, windowingMode);
try {
@@ -105,7 +98,6 @@
Slog.e(TAG, "TaskOrganizer failed to register death recipient");
}
mWindowingMode = windowingMode;
- mReplacementFor = replacing;
}
void addTask(Task t) {
@@ -127,35 +119,26 @@
}
void dispose() {
- mDisposed = true;
releaseTasks();
- handleReplacement();
+ mTaskOrganizersForWindowingMode.get(mWindowingMode).remove(this);
}
- void releaseTasks() {
+ private void releaseTasks() {
for (int i = mOrganizedTasks.size() - 1; i >= 0; i--) {
final Task t = mOrganizedTasks.get(i);
- t.taskOrganizerDied();
removeTask(t);
- }
- }
-
- void handleReplacement() {
- if (mReplacementFor != null && !mReplacementFor.mDisposed) {
- mTaskOrganizersForWindowingMode.put(mWindowingMode, mReplacementFor);
+ t.taskOrganizerUnregistered();
}
}
void unlinkDeath() {
- mDisposed = true;
mOrganizer.asBinder().unlinkToDeath(mDeathRecipient, 0);
}
- };
+ }
-
- final HashMap<Integer, TaskOrganizerState> mTaskOrganizersForWindowingMode = new HashMap();
- final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap();
-
+ private final SparseArray<LinkedList<TaskOrganizerState>> mTaskOrganizersForWindowingMode =
+ new SparseArray<>();
+ private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
@@ -194,11 +177,17 @@
Slog.w(TAG, "Task organizer already exists for windowing mode: "
+ windowingMode);
}
- final TaskOrganizerState previousState =
- mTaskOrganizersForWindowingMode.get(windowingMode);
- final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode,
- previousState);
- mTaskOrganizersForWindowingMode.put(windowingMode, state);
+
+ LinkedList<TaskOrganizerState> states;
+ if (mTaskOrganizersForWindowingMode.contains(windowingMode)) {
+ states = mTaskOrganizersForWindowingMode.get(windowingMode);
+ } else {
+ states = new LinkedList<>();
+ mTaskOrganizersForWindowingMode.put(windowingMode, states);
+ }
+ final TaskOrganizerState previousState = states.peekLast();
+ final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode);
+ states.add(state);
mTaskOrganizerStates.put(organizer.asBinder(), state);
if (previousState == null) {
@@ -219,16 +208,14 @@
@Override
public void unregisterTaskOrganizer(ITaskOrganizer organizer) {
- final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
+ final TaskOrganizerState state = mTaskOrganizerStates.remove(organizer.asBinder());
state.unlinkDeath();
- if (mTaskOrganizersForWindowingMode.get(state.mWindowingMode) == state) {
- mTaskOrganizersForWindowingMode.remove(state.mWindowingMode);
- }
state.dispose();
}
ITaskOrganizer getTaskOrganizer(int windowingMode) {
- final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode);
+ final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode,
+ EMPTY_LIST).peekLast();
if (state == null) {
return null;
}
@@ -255,11 +242,12 @@
if (display == null) {
return null;
}
- final int nextId = display.getNextStackId();
- TaskTile tile = new TaskTile(mService, nextId, windowingMode);
- display.addTile(tile);
- RunningTaskInfo out = tile.getTaskInfo();
- mLastSentTaskInfos.put(tile, out);
+
+ final Task task = display.getOrCreateStack(windowingMode, ACTIVITY_TYPE_UNDEFINED,
+ false /* onTop */, new Intent(), null /* candidateTask */,
+ true /* createdByOrganizer */);
+ RunningTaskInfo out = task.getTaskInfo();
+ mLastSentTaskInfos.put(task, out);
return out;
}
} finally {
@@ -273,11 +261,13 @@
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- TaskTile tile = TaskTile.forToken(token.asBinder());
- if (tile == null) {
- return false;
+ final Task task = WindowContainer.fromBinder(token.asBinder()).asTask();
+ if (task == null) return false;
+ if (!task.mCreatedByOrganizer) {
+ throw new IllegalArgumentException(
+ "Attempt to delete task not created by organizer task=" + task);
}
- tile.removeImmediately();
+ task.removeImmediately();
return true;
}
} finally {
@@ -358,12 +348,7 @@
if (task == null) {
return null;
}
- ActivityStack rootTask = (ActivityStack) task.getRootTask();
- final TaskTile tile = rootTask.getTile();
- if (tile != null) {
- rootTask = tile;
- }
- return rootTask.mRemoteToken;
+ return task.getRootTask().mRemoteToken;
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -371,7 +356,7 @@
}
@Override
- public void setLaunchRoot(int displayId, @Nullable IWindowContainer tile) {
+ public void setLaunchRoot(int displayId, @Nullable IWindowContainer token) {
enforceStackPermission("setLaunchRoot()");
final long origId = Binder.clearCallingIdentity();
try {
@@ -380,16 +365,21 @@
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) {
- throw new RuntimeException("Can't set launch root for display " + displayId
- + " to task on display " + taskTile.getDisplay().getDisplayId());
+ if (!task.mCreatedByOrganizer) {
+ throw new IllegalArgumentException("Attempt to set task not created by "
+ + "organizer as launch root task=" + task);
}
- display.mLaunchTile = taskTile;
+ if (task.getDisplayContent() != display) {
+ throw new RuntimeException("Can't set launch root for display " + displayId
+ + " to task on display " + task.getDisplayContent().getDisplayId());
+ }
+ display.mLaunchRootTask = task;
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -411,25 +401,25 @@
Slog.e(TAG, "Can't get children of " + parent + " because it is not valid.");
return null;
}
- // For now, only support returning children of persistent root tasks (of which the
- // only current implementation is TaskTile).
- if (!(container instanceof TaskTile)) {
+ final Task task = container.asTask();
+ if (task == null) {
+ Slog.e(TAG, container + " is not a task...");
+ return null;
+ }
+ // For now, only support returning children of tasks created by the organizer.
+ if (!task.mCreatedByOrganizer) {
Slog.w(TAG, "Can only get children of root tasks created via createRootTask");
return null;
}
ArrayList<RunningTaskInfo> out = new ArrayList<>();
- // Tiles aren't real parents, so we need to go through stacks on the display to
- // ensure correct ordering.
- final DisplayContent dc = container.getDisplayContent();
- for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final ActivityStack as = dc.getStackAt(i);
- if (as.getTile() == container) {
- if (activityTypes != null
- && !ArrayUtils.contains(activityTypes, as.getActivityType())) {
- continue;
- }
- out.add(as.getTaskInfo());
+ for (int i = task.getChildCount() - 1; i >= 0; --i) {
+ final Task child = task.getChildAt(i).asTask();
+ if (child == null) continue;
+ if (activityTypes != null
+ && !ArrayUtils.contains(activityTypes, child.getActivityType())) {
+ continue;
}
+ out.add(child.getTaskInfo());
}
return out;
}
@@ -451,12 +441,7 @@
}
ArrayList<RunningTaskInfo> out = new ArrayList<>();
for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final ActivityStack task = dc.getStackAt(i);
- if (task.getTile() != null) {
- // a tile is supposed to look like a parent, so don't include their
- // "children" here. They can be accessed via getChildTasks()
- continue;
- }
+ final Task task = dc.getStackAt(i);
if (activityTypes != null
&& !ArrayUtils.contains(activityTypes, task.getActivityType())) {
continue;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index f046e8a..be0d6f8 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -268,8 +268,9 @@
mDisplayContent.getDisplayRotation().pause();
// Notify InputMonitor to take mDragWindowHandle.
- mDisplayContent.getInputMonitor().updateInputWindowsImmediately();
- new SurfaceControl.Transaction().syncInputWindows().apply(true);
+ final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
+ mDisplayContent.getInputMonitor().updateInputWindowsImmediately(t);
+ t.syncInputWindows().apply();
final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
deleted file mode 100644
index 51142b1..0000000
--- 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) ((RemoteToken) token).getContainer();
- } catch (ClassCastException e) {
- Slog.w(TAG, "Bad tile token: " + token, e);
- return null;
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 23ba528..0757725 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7746,19 +7746,23 @@
public void syncInputTransactions() {
waitForAnimationsToComplete();
+ // Collect all input transactions from all displays to make sure we could sync all input
+ // windows at same time.
+ final SurfaceControl.Transaction t = mTransactionFactory.get();
synchronized (mGlobalLock) {
mWindowPlacerLocked.performSurfacePlacementIfScheduled();
mRoot.forAllDisplays(displayContent ->
- displayContent.getInputMonitor().updateInputWindowsImmediately());
+ displayContent.getInputMonitor().updateInputWindowsImmediately(t));
}
- mTransactionFactory.get().syncInputWindows().apply(true);
+ t.syncInputWindows().apply();
}
private void waitForAnimationsToComplete() {
synchronized (mGlobalLock) {
long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS;
- while (mRoot.isAnimating(TRANSITION | CHILDREN) && timeoutRemaining > 0) {
+ while ((mAnimator.isAnimationScheduled()
+ || mRoot.isAnimating(TRANSITION | CHILDREN)) && timeoutRemaining > 0) {
long startTime = System.currentTimeMillis();
try {
mGlobalLock.wait(timeoutRemaining);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index e416e80..5f21e17 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -159,17 +159,7 @@
false /* preserveWindow */);
try {
for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
- final WindowContainer wc = haveConfigChanges.valueAt(i);
- final Task task = wc.asTask();
- final TaskTile tile = task != null ? task.asTile() : null;
- if (tile != null) {
- // Special case for tile. Can't override normal forAllActivities
- // because it generates duplicate calls and messes up existing
- // code-paths.
- tile.forAllTileActivities(f);
- } else {
- wc.forAllActivities(f);
- }
+ haveConfigChanges.valueAt(i).forAllActivities(f);
}
} finally {
f.recycle();
@@ -223,51 +213,65 @@
private int sanitizeAndApplyHierarchyOp(WindowContainer container,
WindowContainerTransaction.HierarchyOp hop) {
- if (!(container instanceof Task)) {
+ final Task task = container.asTask();
+ if (task == null) {
throw new IllegalArgumentException("Invalid container in hierarchy op");
}
- if (container.getDisplayContent() == null) {
- Slog.w(TAG, "Container is no longer attached: " + container);
+ final DisplayContent dc = task.getDisplayContent();
+ if (dc == null) {
+ Slog.w(TAG, "Container is no longer attached: " + task);
return 0;
}
+ final ActivityStack as = (ActivityStack) task;
+
if (hop.isReparent()) {
- // special case for tiles since they are "virtual" parents
- if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
- ActivityStack as = (ActivityStack) container;
- TaskTile newParent = hop.getNewParent() == null ? null
- : (TaskTile) WindowContainer.fromBinder(hop.getNewParent());
- if (as.getTile() != newParent) {
- if (as.getTile() != null) {
- as.getTile().removeChild(as);
+ final boolean isNonOrganizedRootableTask =
+ (task.isRootTask() && !task.mCreatedByOrganizer)
+ || task.getParent().asTask().mCreatedByOrganizer;
+ if (isNonOrganizedRootableTask) {
+ Task newParent = hop.getNewParent() == null ? null
+ : WindowContainer.fromBinder(hop.getNewParent()).asTask();
+ if (task.getParent() != newParent) {
+ if (newParent == null) {
+ // Re-parent task to display as a root task.
+ dc.moveStackToDisplay(as, hop.getToTop());
+ } else if (newParent.inMultiWindowMode() && !task.isResizeable()
+ && task.isLeafTask()) {
+ Slog.w(TAG, "Can't support task that doesn't support multi-window mode in"
+ + " multi-window mode... newParent=" + newParent + " task=" + task);
+ return 0;
+ } else {
+ // Clear the window crop on root task since it may not be updated after
+ // reparent (no longer be a root task)
+ task.getSurfaceControl().setWindowCrop(null);
+ task.reparent((ActivityStack) newParent,
+ hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+ false /*moveParents*/, "sanitizeAndApplyHierarchyOp");
}
- if (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);
+ final ActivityStack rootTask =
+ (ActivityStack) (newParent != null ? newParent : task.getRootTask());
+ if (hop.getToTop()) {
+ as.getDisplay().positionStackAtTop(rootTask, false /* includingParents */);
+ } else {
+ as.getDisplay().positionStackAtBottom(rootTask);
+ }
}
- } else if (container instanceof Task) {
+ } else {
throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
}
} else {
// Ugh, of course ActivityStack has its own special reorder logic...
- if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
- ActivityStack as = (ActivityStack) container;
+ if (task.isRootTask()) {
if (hop.getToTop()) {
- as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+ dc.positionStackAtTop(as, false /* includingParents */);
} else {
- as.getDisplay().positionStackAtBottom(as);
+ dc.positionStackAtBottom(as);
}
} else {
- container.getParent().positionChildAt(
+ task.getParent().positionChildAt(
hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
- container, false /* includingParents */);
+ task, false /* includingParents */);
}
}
return TRANSACT_EFFECTS_LIFECYCLE;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 161152b..c4d700c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -131,6 +131,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
@@ -138,7 +139,6 @@
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
-import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
@@ -608,6 +608,11 @@
boolean mSeamlesslyRotated = false;
/**
+ * Indicates if this window is behind IME. Only windows behind IME can get insets from IME.
+ */
+ boolean mBehindIme = false;
+
+ /**
* Surface insets from the previous call to relayout(), used to track
* if we are changing the Surface insets.
*/
@@ -2270,9 +2275,9 @@
return false;
}
- if (PixelFormat.formatHasAlpha(mAttrs.format) && mAttrs.alpha == 0) {
- // Support legacy use cases where completely transparent windows can still be ime target
- // with FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
+ if (PixelFormat.formatHasAlpha(mAttrs.format)) {
+ // Support legacy use cases where transparent windows can still be ime target with
+ // FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
// Certain apps listen for IME insets using transparent windows and ADJUST_NOTHING to
// manually synchronize app content to IME animation b/144619551.
// TODO(b/145812508): remove this once new focus management is complete b/141738570
@@ -3338,7 +3343,7 @@
}
final ActivityStack stack = task.getStack();
- if (stack == null) {
+ if (stack == null || stack.mCreatedByOrganizer) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 850c362..3c2b6ec 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 @@
}
/**
- * 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() {
- final FixedRotationTransformState state = mFixedRotationTransformState;
- if (state == null || state.mOwner != this) {
+ void finishFixedRotationTransform() {
+ if (mFixedRotationTransformState == null || mFixedRotationTransformState.mOwner != this) {
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.
- 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 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) {
- onMergedOverrideConfigurationChanged();
+ 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) {
+ return;
+ }
+
+ state.resetTransform();
+ // Clear the flag so if the display will be updated to the same orientation, the transform
+ // won't take effect.
+ state.mIsTransforming = false;
+ if (applyDisplayRotation != null) {
+ applyDisplayRotation.run();
+ }
+ // The state is cleared at the end, because it is used to indicate that other windows can
+ // use seamless rotation when applying rotation to display.
for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) {
state.mAssociatedTokens.get(i).mFixedRotationTransformState = null;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e3f9ae8..9bc5d34 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -206,7 +206,7 @@
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
status_t pilferPointers(const sp<IBinder>& token);
- void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId);
+ void displayRemoved(JNIEnv* env, int32_t displayId);
void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj);
void setFocusedDisplay(JNIEnv* env, int32_t displayId);
void setInputDispatchMode(bool enabled, bool frozen);
@@ -771,55 +771,10 @@
}
}
-void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray,
- int32_t displayId) {
- std::vector<sp<InputWindowHandle> > windowHandles;
-
- if (windowHandleObjArray) {
- jsize length = env->GetArrayLength(windowHandleObjArray);
- for (jsize i = 0; i < length; i++) {
- jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i);
- if (! windowHandleObj) {
- break; // found null element indicating end of used portion of the array
- }
-
- sp<InputWindowHandle> windowHandle =
- android_view_InputWindowHandle_getHandle(env, windowHandleObj);
- if (windowHandle != nullptr) {
- windowHandles.push_back(windowHandle);
- }
- env->DeleteLocalRef(windowHandleObj);
- }
- }
-
- mInputManager->getDispatcher()->setInputWindows(windowHandles, displayId);
-
- // Do this after the dispatcher has updated the window handle state.
- bool newPointerGesturesEnabled = true;
- size_t numWindows = windowHandles.size();
- for (size_t i = 0; i < numWindows; i++) {
- const sp<InputWindowHandle>& windowHandle = windowHandles[i];
- const InputWindowInfo* windowInfo = windowHandle->getInfo();
- if (windowInfo && windowInfo->hasFocus && (windowInfo->inputFeatures
- & InputWindowInfo::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) {
- newPointerGesturesEnabled = false;
- }
- }
-
- bool pointerGesturesEnabledChanged = false;
- { // acquire lock
- AutoMutex _l(mLock);
-
- if (mLocked.pointerGesturesEnabled != newPointerGesturesEnabled) {
- mLocked.pointerGesturesEnabled = newPointerGesturesEnabled;
- pointerGesturesEnabledChanged = true;
- }
- } // release lock
-
- if (pointerGesturesEnabledChanged) {
- mInputManager->getReader()->requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT);
- }
+void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) {
+ // Set an empty list to remove all handles from the specific display.
+ std::vector<sp<InputWindowHandle>> windowHandles;
+ mInputManager->getDispatcher()->setInputWindows({{displayId, windowHandles}});
}
void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId,
@@ -1567,11 +1522,10 @@
im->getInputManager()->getReader()->toggleCapsLockState(deviceId);
}
-static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobjectArray windowHandleObjArray, jint displayId) {
+static void nativeDisplayRemoved(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->setInputWindows(env, windowHandleObjArray, displayId);
+ im->displayRemoved(env, displayId);
}
static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */,
@@ -1815,8 +1769,7 @@
{"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;",
(void*)nativeVerifyInputEvent},
{"nativeToggleCapsLock", "(JI)V", (void*)nativeToggleCapsLock},
- {"nativeSetInputWindows", "(J[Landroid/view/InputWindowHandle;I)V",
- (void*)nativeSetInputWindows},
+ {"nativeDisplayRemoved", "(JI)V", (void*)nativeDisplayRemoved},
{"nativeSetFocusedApplication", "(JILandroid/view/InputApplicationHandle;)V",
(void*)nativeSetFocusedApplication},
{"nativeSetFocusedDisplay", "(JI)V", (void*)nativeSetFocusedDisplay},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 023a1e8..e304bca 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4434,6 +4434,11 @@
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);
@@ -7166,7 +7171,9 @@
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");
}
@@ -13070,26 +13077,25 @@
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));
+ 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;
+ }
- 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 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
@@ -13098,6 +13104,8 @@
!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 {
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index acdb6814..138f982 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 @@
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 @@
@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 @@
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;
}
@@ -451,6 +462,13 @@
.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/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 1212f20..109c119 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">
+ 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.READ_LOGS"/>
+ <uses-permission android:name="android.permission.ACCESS_VR_MANAGER"/>
+ <uses-permission android:name="android.permission.ACCOUNT_MANAGER"/>
+ <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"/>
+ <uses-permission android:name="android.permission.MANAGE_APP_TOKENS"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
+ <uses-permission android:name="android.permission.REAL_GET_TASKS"/>
+ <uses-permission android:name="android.permission.GET_DETAILED_TASKS"/>
+ <uses-permission android:name="android.permission.REORDER_TASKS"/>
+ <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY"/>
+ <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
+ <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.MANAGE_USERS"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+ <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS"/>
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/>
+ <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/>
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+ <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
+ <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>
+ <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
+ <uses-permission android:name="android.permission.DELETE_PACKAGES"/>
+ <uses-permission android:name="android.permission.GET_APP_OPS_STATS"/>
+ <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS"/>
<uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES"/>
- <uses-permission android:name="android.permission.DEVICE_POWER" />
- <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
- <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
- <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
- <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
- <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.STORAGE_INTERNAL" />
- <uses-permission android:name="android.permission.WATCH_APPOPS" />
+ <uses-permission android:name="android.permission.DEVICE_POWER"/>
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+ <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+ <uses-permission android:name="android.permission.STATUS_BAR_SERVICE"/>
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER"/>
+ <uses-permission android:name="android.permission.READ_FRAME_BUFFER"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.STORAGE_INTERNAL"/>
+ <uses-permission android:name="android.permission.WATCH_APPOPS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.SUSPEND_APPS"/>
- <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
- <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
+ <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
+ <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
<uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
- <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
- <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
- <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/>
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
+ <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
<uses-permission android:name="android.permission.HARDWARE_TEST"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
- <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
- <uses-permission android:name="android.permission.DUMP" />
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+ <uses-permission android:name="android.permission.DUMP"/>
<uses-permission android:name="android.permission.READ_DREAM_STATE"/>
<uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
- android:targetSdkVersion="26"/>
+ android:targetSdkVersion="26"/>
<application android:testOnly="true">
- <uses-library android:name="android.test.runner" />
+ <uses-library android:name="android.test.runner"/>
<service android:name="com.android.server.accounts.TestAccountType1AuthenticatorService"
- android:exported="false">
+ android:exported="false">
<intent-filter>
- <action android:name="android.accounts.AccountAuthenticator" />
+ <action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
- android:resource="@xml/test_account_type1_authenticator" />
+ android:resource="@xml/test_account_type1_authenticator"/>
</service>
<service android:name="com.android.server.accounts.TestAccountType2AuthenticatorService"
- android:exported="false">
+ android:exported="false">
<intent-filter>
- <action android:name="android.accounts.AccountAuthenticator" />
+ <action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
- android:resource="@xml/test_account_type2_authenticator" />
+ android:resource="@xml/test_account_type2_authenticator"/>
</service>
<receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver"
- android:permission="android.permission.BIND_DEVICE_ADMIN">
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin_sample" />
+ android:resource="@xml/device_admin_sample"/>
<intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
</intent-filter>
</receiver>
<receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"
- android:permission="android.permission.BIND_DEVICE_ADMIN">
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin_sample" />
+ android:resource="@xml/device_admin_sample"/>
<intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
</intent-filter>
</receiver>
<receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin2"
- android:permission="android.permission.BIND_DEVICE_ADMIN">
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin_sample" />
+ android:resource="@xml/device_admin_sample"/>
<intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
</intent-filter>
</receiver>
<receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin3"
- android:permission="android.permission.BIND_DEVICE_ADMIN">
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin_sample" />
+ android:resource="@xml/device_admin_sample"/>
<intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
</intent-filter>
</receiver>
<receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$AdminNoPerm">
<meta-data android:name="android.app.device_admin"
- android:resource="@xml/device_admin_sample" />
+ android:resource="@xml/device_admin_sample"/>
<intent-filter>
- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
</intent-filter>
</receiver>
<service android:name="com.android.server.job.MockPriorityJobService"
- android:permission="android.permission.BIND_JOB_SERVICE" />
+ android:permission="android.permission.BIND_JOB_SERVICE"/>
- <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity" />
- <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" />
- <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" />
+ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity"/>
+ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2"/>
+ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3"/>
<activity android:name="com.android.server.pm.ShortcutTestActivity"
- android:enabled="true" android:exported="true" />
+ android:enabled="true"
+ android:exported="true"/>
<activity android:name="com.android.server.pm.SuspendedDetailsActivity"
- android:enabled="true"
- android:permission="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS">
+ android:enabled="true"
+ android:permission="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS">
<intent-filter>
- <action android:name="android.intent.action.SHOW_SUSPENDED_APP_DETAILS" />
- <category android:name="android.intent.category.DEFAULT" />
+ <action android:name="android.intent.action.SHOW_SUSPENDED_APP_DETAILS"/>
+ <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
- <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity" />
- <activity android:name="com.android.server.adb.AdbDebuggingManagerTestActivity" />
+ <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity"/>
+ <activity android:name="com.android.server.adb.AdbDebuggingManagerTestActivity"/>
<activity-alias android:name="a.ShortcutEnabled"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity"
- android:enabled="true" android:exported="true">
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true"
+ android:exported="true">
</activity-alias>
<activity-alias android:name="a.ShortcutDisabled"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity"
- android:enabled="false" android:exported="true">
- <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/>
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="false"
+ android:exported="true">
+ <meta-data android:name="android.app.shortcuts"
+ android:resource="@xml/shortcut_5"/>
</activity-alias>
<activity-alias android:name="a.ShortcutUnexported"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity"
- android:enabled="true" android:exported="false">
- <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/>
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true"
+ android:exported="false">
+ <meta-data android:name="android.app.shortcuts"
+ android:resource="@xml/shortcut_5"/>
</activity-alias>
<activity-alias android:name="a.Shortcut1"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity"
- android:enabled="true" android:exported="true">
- <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_1"/>
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true"
+ android:exported="true">
+ <meta-data android:name="android.app.shortcuts"
+ android:resource="@xml/shortcut_1"/>
</activity-alias>
<activity-alias android:name="a.ShortcutConfigActivity"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity">
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity">
<intent-filter>
- <action android:name="android.intent.action.CREATE_SHORTCUT" />
+ <action android:name="android.intent.action.CREATE_SHORTCUT"/>
</intent-filter>
</activity-alias>
<activity-alias android:name="a.DisabledMain"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity"
- android:enabled="false" android:exported="true">
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="false"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.LAUNCHER" />
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias android:name="a.UnexportedMain"
- android:targetActivity="com.android.server.pm.ShortcutTestActivity"
- android:enabled="true" android:exported="false">
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true"
+ android:exported="false">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.LAUNCHER" />
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<receiver android:name="com.android.server.appwidget.DummyAppWidget">
<intent-filter>
- <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
<meta-data android:name="android.appwidget.provider"
- android:resource="@xml/dummy_appwidget_info" />
+ android:resource="@xml/dummy_appwidget_info"/>
</receiver>
</application>
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.frameworks.servicestests"
- android:label="Frameworks Services Tests" />
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.servicestests"
+ android:label="Frameworks Services Tests"/>
</manifest>
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 3ad9054..d292526 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 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 @@
}
@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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
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 @@
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 @@
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 7c6ac17..baf551e 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 @@
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 @@
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 @@
when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
.thenReturn(true);
- when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false);
when(getServices().userManager.getProfileParent(DpmMockContext.CALLER_USER_HANDLE))
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
when(getServices().userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
true)).thenReturn(true);
setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE);
- mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
}
public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_primaryUser()
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/Android.bp b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/Android.bp
new file mode 100644
index 0000000..eb1a292
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/Android.bp
@@ -0,0 +1,37 @@
+android_test {
+ name: "TunerResourceManagerTests",
+
+ // Include all test java files.
+ srcs: [
+ "*.java",
+ ],
+
+ static_libs: [
+ "frameworks-base-testutils",
+ "services.core",
+ "services.devicepolicy",
+ "guava",
+ "androidx.test.core",
+ "androidx.test.ext.truth",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "platform-test-annotations",
+ "truth-prebuilt",
+ "testables",
+ "testng",
+ "servicestests-utils",
+ "service-permission",
+
+ ],
+
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ platform_apis: true,
+ test_suites: ["general-tests", "device-tests"],
+ compile_multilib: "both",
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/AndroidManifest.xml b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/AndroidManifest.xml
new file mode 100644
index 0000000..9fa100d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.tv.tunerresourcemanager">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.tv.tunerresourcemanager"
+ android:label="Tuner Resource Manager Test Cases">
+ </instrumentation>
+</manifest>
+
+
+
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/AndroidTest.xml b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/AndroidTest.xml
new file mode 100644
index 0000000..e3ea6a0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Tests for Tuner Resource Manager">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="TunerResourceManagerTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="TunerResourceManagerTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.server.tv.tunerresourcemanager" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 155c6dd..fcbd507 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.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 @@
* 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 ab5665b..2ff178e 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 @@
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 @@
* Tests for {@link UseCasePriorityHints} class.
*/
@SmallTest
+@Presubmit
@RunWith(JUnit4.class)
public class UseCasePriorityHintsTest {
private static final String TAG = "UseCasePriorityHintsTest";
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
index d7fc97c..2578ca8 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.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.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 @@
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 3c29445..d2417f9 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -6372,13 +6372,41 @@
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 52fc3de..12934ee 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_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.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 @@
}
@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 6ec0f91..ed400ea 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.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 @@
// 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 @@
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,10 +1013,10 @@
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
IWindowContainer primary = mService.mTaskOrganizerController.createRootTask(
displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token;
- mPrimary = TaskTile.forToken(primary.asBinder());
+ mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask();
IWindowContainer secondary = mService.mTaskOrganizerController.createRootTask(
displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token;
- mSecondary = TaskTile.forToken(secondary.asBinder());
+ mSecondary = WindowContainer.fromBinder(secondary.asBinder()).asTask();
}
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
@@ -1042,7 +1041,7 @@
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 4634e2d..716369d 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 @@
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/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index bfb126f..db7bce4 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 @@
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 @@
}
@Test
+ public void testStripForDispatch_belowIme() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+ getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+
+ assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+ }
+
+ @Test
+ public void testStripForDispatch_aboveIme() {
+ final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+ getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+
+ assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+ }
+
+ @Test
+ public void testStripForDispatch_childWindow_altFocusable() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+ final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
+ child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
+
+ final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+ // IME cannot be the IME target.
+ ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+
+ getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+
+ assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME));
+ }
+
+ @Test
+ public void testStripForDispatch_childWindow_splitScreen() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+ final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
+ child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+ child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+ final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+ // IME cannot be the IME target.
+ ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+
+ getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+
+ assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME));
+ }
+
+ @Test
public void testImeForDispatch() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 406affc..f242989 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -403,11 +403,11 @@
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 0ef2582..e841e43 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 @@
}
@Test
+ public void testGetValidLaunchStackOnDisplayWithCandidateRootTask() {
+ // Create a root task with an activity on secondary display.
+ final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mService, 300,
+ 600).build();
+ final Task task = new ActivityTestsBase.StackBuilder(mRootWindowContainer).setDisplay(
+ secondaryDisplay).build();
+ final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder(mService)
+ .setTask(task).build();
+
+ // Make sure the root task is valid and can be reused on default display.
+ final ActivityStack stack = mRootWindowContainer.getValidLaunchStackOnDisplay(
+ DEFAULT_DISPLAY, activity, task, null, null);
+ assertEquals(task, stack);
+ }
+
+ @Test
public void testSwitchUser_missingHomeRootTask() {
doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedStack();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index a96f401..ed635ce 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.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;
@@ -347,45 +348,62 @@
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);
}
@@ -415,8 +433,10 @@
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);
@@ -424,19 +444,24 @@
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);
}
@@ -457,9 +482,11 @@
}
};
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);
@@ -522,13 +549,11 @@
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;
}
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 a25acae..56c19a4 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.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 @@
assertFalse(factory.mCreated);
Task.create(mService, 0 /*taskId*/, 0 /*activityType*/,
- new ActivityInfo(), new Intent());
+ new ActivityInfo(), new Intent(), false /* createdByOrganizer */);
assertTrue(factory.mCreated);
} finally {
@@ -939,6 +940,20 @@
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 @@
@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 85e4a16..e95ccab 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 @@
// 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 3196758..00cb6dc 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 @@
@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 488ee78..47bf148 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 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 @@
@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 @@
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 @@
@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 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 @@
@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 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 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 @@
}
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 @@
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 0dca006..ffd25c0 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2633,6 +2633,7 @@
* @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 @@
* @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 @@
* @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 @@
* @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 0b33174..7e02966 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -442,16 +442,40 @@
// 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) {
}
- } catch (SecurityException | PackageManager.NameNotFoundException e) {
+ } 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) {
+ }
+ }
+ }
}
+
// Can be read with READ_SMS too.
try {
context.enforcePermission(android.Manifest.permission.READ_SMS, pid, uid, message);
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 1c92705b..d00049c 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -314,6 +314,8 @@
/**
* 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 76d2df9..9d55f10 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -145,6 +145,8 @@
/**
* Get the signal strength as dBm.
+ *
+ * @return the RSSI of the measured cell.
*/
@Override
public int getDbm() {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 5d3cc44..2facd5a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -944,6 +944,18 @@
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 @@
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/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 2f9a1c8..f444b77 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -509,16 +509,68 @@
for (int i = 0; i < IORAP_COMPILE_CMD_TIMEOUT; ++i) {
IorapCompilationStatus status = waitForIorapCompiled(appPkgName);
if (status == IorapCompilationStatus.COMPLETE) {
+ Log.v(TAG, "compileAppForIorap: success");
+ logDumpsysIorapd(appPkgName);
return true;
} else if (status == IorapCompilationStatus.INSUFFICIENT_TRACES) {
+ Log.e(TAG, "compileAppForIorap: failed due to insufficient traces");
+ logDumpsysIorapd(appPkgName);
return false;
} // else INCOMPLETE. keep asking iorapd if it's done yet.
sleep(1000);
}
+ Log.e(TAG, "compileAppForIorap: failed due to timeout");
+ logDumpsysIorapd(appPkgName);
return false;
}
+ /** Save the contents of $(adb shell dumpsys iorapd) to the launch_logs directory. */
+ private void logDumpsysIorapd(String packageName) throws IOException {
+ InstrumentationTestRunner instrumentation =
+ (InstrumentationTestRunner)getInstrumentation();
+ Bundle args = instrumentation.getArguments();
+
+ String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY);
+
+ // Root directory for applaunch file to log the app launch output
+ // Will be useful in case of simpleperf command is used
+ File launchRootDir = null;
+ if (null != launchDirectory && !launchDirectory.isEmpty()) {
+ launchRootDir = new File(launchDirectory);
+ if (!launchRootDir.exists() && !launchRootDir.mkdirs()) {
+ throw new IOException("Unable to create the destination directory "
+ + launchRootDir + ". Try disabling selinux.");
+ }
+ } else {
+ Log.w(TAG, "logDumpsysIorapd: Missing launch-directory arg");
+ return;
+ }
+
+ File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+
+ if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
+ throw new IOException("Unable to create the lauch file sub directory "
+ + launchSubDir + ". Try disabling selinux.");
+ }
+ String path = "iorapd_dumpsys_" + packageName + "_" + System.nanoTime() + ".txt";
+ File file = new File(launchSubDir, path);
+ try (FileOutputStream outputStream = new FileOutputStream(file);
+ BufferedWriter writer = new BufferedWriter(
+ new OutputStreamWriter(outputStream));
+ ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
+ executeShellCommand(IORAP_DUMPSYS_CMD);
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+ new FileInputStream(result.getFileDescriptor())))) {
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ writer.write(line + "\n");
+ }
+ }
+
+ Log.v(TAG, "logDumpsysIorapd: Saved to file: " + path);
+ }
+
enum IorapCompilationStatus {
INCOMPLETE,
COMPLETE,
@@ -715,7 +767,7 @@
.executeShellCommand(String.format("setprop iorapd.readahead.enable %b", enable));
getInstrumentation().getUiAutomation()
.executeShellCommand("start iorapd");
- sleep(2000); // give enough time for iorapd to start back up.
+ sleep(3000); // give enough time for iorapd to start back up.
if (enable) {
mIorapStatus = IorapStatus.ENABLED;
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
index 42908be..35a6c26 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 @@
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 @@
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/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index 269cec1..7b85cc8 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 5a92d68..cab8b42 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 @@
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 @@
// 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/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
index 548af0c..498cb7c 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 @@
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.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.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 @@
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 @@
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 b2e8c37..916c339 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -463,7 +463,9 @@
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 @@
// from nc2.
assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
- assertTrue(TEST_SSID.equals(nc2.getSsid()));
+ if (isAtLeastR()) {
+ assertTrue(TEST_SSID.equals(nc2.getSsid()));
+ }
nc1.setSSID(DIFFERENT_TEST_SSID);
nc2.set(nc1);
assertEquals(nc1, nc2);
- assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
+ if (isAtLeastR()) {
+ assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
+ }
nc1.setUids(uidRange(10, 13));
nc2.set(nc1); // Overwrites, as opposed to combineCapabilities
diff --git a/tools/stats_log_api_gen/.clang-format b/tools/stats_log_api_gen/.clang-format
new file mode 100644
index 0000000..cead3a0
--- /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 47eb63e..bf39093 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 @@
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 @@
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 @@
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 @@
/**
* 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++;
+ }
+
+ // 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;
}
}
- }
- return errorCount;
+ // 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 @@
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 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);
+ 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;
+ 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());
+ }
+ continue;
}
}
- // 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());
- }
+ 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;
}
+
+ 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 (dbg) {
+ printf("%s is whitelisted\n", atomField->name().c_str());
+ }
+ }
+
+ 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());
+ }
+ }
+
+ 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;
+ }
+
+ atoms->decls.insert(atomDecl);
+ FieldNumberToAnnotations& fieldNumberToAnnotations = atoms->signatureInfoMap[signature];
+ populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations);
+
+ 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(" %s (%d)\n", atomField->name().c_str(), atomField->number());
+ 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");
}
- // 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());
-
- if (atomField->options().GetExtension(os::statsd::allow_from_any_uid) == true) {
- atomDecl.whitelisted = true;
- }
-
- 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;
- }
-
- atoms->decls.insert(atomDecl);
- FieldNumberToAnnotations& fieldNumberToAnnotations = atoms->signatureInfoMap[signature];
- populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations);
-
- 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");
- }
- 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 c6dad1d..2aedb21 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_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_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_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_OBJECT = -1,
+ JAVA_TYPE_BYTE_ARRAY = -2,
} java_type_t;
enum AnnotationType {
@@ -84,8 +86,10 @@
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 @@
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 @@
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 @@
bool whitelisted = false;
+ bool truncateTimestamp = false;
+
AtomDecl();
AtomDecl(const AtomDecl& that);
AtomDecl(int code, const string& name, const string& message);
@@ -169,10 +181,9 @@
* 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 4f66f68..6944752 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 @@
" 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 @@
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 @@
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 @@
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 @@
"\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 @@
"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 @@
"\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 @@
"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 @@
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 d04e65a..ffe9e43 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 18508d2..5a22b5c 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 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 @@
// 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 @@
// 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 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 @@
// 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 @@
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 @@
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 @@
// 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 4e1365e..8b3b505 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 329c25d..7d22583 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 @@
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 @@
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 @@
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());
+ 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;
+ // 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 @@
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 @@
// 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();
+ 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;
+ 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
-// 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 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 0f33b6c..f1cfc44 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 @@
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 4f791a3..fda5736 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -1,21 +1,20 @@
-#include "Collation.h"
-#include "atoms_info_writer.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 <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <map>
#include <set>
#include <vector>
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#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"
using namespace google::protobuf;
using namespace std;
@@ -25,25 +24,34 @@
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 @@
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 @@
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 @@
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 @@
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 @@
fclose(out);
}
-
// Write the .cpp file
if (cppFilename.size() != 0) {
FILE* out = fopen(cppFilename.c_str(), "w");
@@ -240,13 +245,14 @@
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 @@
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 @@
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 @@
/**
* 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 90dcae4..0cf3225 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 @@
}
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 @@
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 @@
} 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 @@
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 @@
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 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 @@
}
write_native_method_signature(out, "int stats_write_non_chained", signature,
- attributionDecl, " {");
+ attributionDecl, " {");
vector<java_type_t> newSignature;
@@ -212,17 +207,14 @@
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 @@
}
}
-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 @@
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 @@
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 @@
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 6e60325..264d4db 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 5032ac0..9878926 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -15,12 +15,11 @@
*/
#include <gtest/gtest.h>
-
-#include "frameworks/base/tools/stats_log_api_gen/test.pb.h"
-#include "Collation.h"
-
#include <stdio.h>
+#include "Collation.h"
+#include "frameworks/base/tools/stats_log_api_gen/test.pb.h"
+
namespace android {
namespace stats_log_api_gen {
@@ -31,14 +30,13 @@
/**
* 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 @@
/**
* 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 @@
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 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, 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 @@
* 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 @@
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 @@
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 7314127..0262488 100644
--- a/tools/stats_log_api_gen/utils.cpp
+++ b/tools/stats_log_api_gen/utils.cpp
@@ -22,9 +22,9 @@
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 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 @@
}
// 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 @@
}
}
-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 @@
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 @@
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_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_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 @@
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_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_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 @@
}
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 @@
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_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 @@
// 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 @@
}
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 a6b3ef9..468f323 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_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_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 9d9a495..d0f1a26 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -170,24 +170,23 @@
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 a269e17..457e0db 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -516,9 +516,6 @@
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 @@
/**
* 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 ba68d17..b110a61 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -2356,6 +2356,8 @@
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 @@
return mPasspointUniqueId;
}
+ /**
+ * If network is one of the most recently connected.
+ * For framework internal use only. Do not parcel.
+ * @hide
+ */
+ public boolean isMostRecentlyConnected = false;
+
}